@contrail/flexplm 1.8.0-alpha.0261c7e → 1.8.0-alpha.f09632d

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,10 +10,10 @@ Core model for contrail, which establishes the base peristence services (without
10
10
  | Identifier property | `vibeIQIdentifier` (same key in both systems; no rekey runs without a mapping) |
11
11
  | Informational property | `name` |
12
12
 
13
- A mapping section for `LCSCountry` overrides any of these. The mapping section must also declare which system creates Countries, with the existing creation gates (there is no default; if it is not declared, the app treats Country as read-only):
13
+ A mapping section for `LCSCountry` overrides any of these. The section must also declare which system creates Countries, with one field (there is no default; if it is missing or invalid, the app treats Country as read-only):
14
14
 
15
15
  ```ts
16
- LCSCountry: { isInboundCreatable: () => true, isOutboundCreatable: () => false }, // FlexPLM creates
17
- LCSCountry: { isInboundCreatable: () => false, isOutboundCreatable: () => true }, // VibeIQ creates
16
+ LCSCountry: { originSystem: 'flexplm' }, // FlexPLM creates Countries
17
+ LCSCountry: { originSystem: 'vibeiq' }, // VibeIQ creates Countries
18
18
  ```
19
- Read the declared origin with `TypeConversionUtils.getOriginSystem(...)`.
19
+ Read the declared origin with `TypeConversionUtils.getOriginSystem(fileId, mapFileUtil, 'LCSCountry')`.
@@ -1,4 +1,6 @@
1
1
  import { FunctionTransformers, RekeyTransformers, TransformDefinition, TransformTask } from "@contrail/transform-data";
2
+ /** The system that originates (creates) an entity type: 'flexplm' or 'vibeiq'. */
3
+ export type OriginSystem = 'flexplm' | 'vibeiq';
2
4
  /**
3
5
  * One direction of a {@link MappingSection} (either `vibe2flex` or `flex2vibe`).
4
6
  *
@@ -340,13 +342,6 @@ export interface MappingSection {
340
342
  * update existing entities.
341
343
  * - Conditional — inspect the row and allow / disallow per record.
342
344
  *
343
- * On the **`LCSCountry`** section this pair declares which system creates
344
- * Countries: `isInboundCreatable: () => true` together with
345
- * `isOutboundCreatable: () => false` means FlexPLM creates them (the
346
- * reverse pair means VibeIQ creates them). Both functions must be present
347
- * and opposite; otherwise the origin is undeclared and the connector treats
348
- * Country as read-only. See `TypeConversionUtils.getOriginSystem`.
349
- *
350
345
  * On the **season-link** sections (`LCSProductSeasonLink` /
351
346
  * `LCSSKUSeasonLink`), `() => true` enables the add-to-project
352
347
  * flow: FlexPLM-driven inbound events can add an item to a Vibe
@@ -522,6 +517,28 @@ export interface MappingSection {
522
517
  * getInformationalProperties: () => ['itemNumber', 'skuName']
523
518
  */
524
519
  getInformationalProperties?: (entity: Record<string, unknown>) => string[];
520
+ /**
521
+ * Which system originates (creates) this entity type: `'flexplm'` or `'vibeiq'`.
522
+ *
523
+ * Currently read only for the **`LCSCountry`** section, where it is
524
+ * **required**: the connector creates Countries in one direction only
525
+ * (`'flexplm'`: FlexPLM creates them and VibeIQ never sends them to
526
+ * FlexPLM; `'vibeiq'`: VibeIQ creates them and inbound events only update
527
+ * existing Countries). There is no default. When it is missing, or is not
528
+ * one of those two values (case and surrounding whitespace are ignored),
529
+ * the origin is undeclared and Country is read-only on both sides, with an
530
+ * error logged. See `TypeConversionUtils.getOriginSystem`.
531
+ *
532
+ * It is a static, type-level value. It does not read the record, and it is
533
+ * independent of {@link MappingSection.isInboundCreatable} and
534
+ * {@link MappingSection.isOutboundCreatable}, which the Country origin
535
+ * check does not consult.
536
+ *
537
+ * @example
538
+ * // FlexPLM creates Countries.
539
+ * LCSCountry: { originSystem: 'flexplm' }
540
+ */
541
+ originSystem?: OriginSystem;
525
542
  /**
526
543
  * Transform configuration applied when sending data from VibeIQ to FlexPLM.
527
544
  * Pipeline runs on rows in their VibeIQ shape and produces rows in their
@@ -714,7 +731,7 @@ interface MappingFileBase {
714
731
  * | `item` | `LCSProduct` / `LCSSKU` | Syncs once `lifecycleStage` is out of `concept`. Sends Primary Viewable as the thumbnail (no other images). Creates `LCSProduct` / `LCSSKU` if missing; auto-retries `LCSSKU` creation when the parent `LCSProduct` isn't ready yet. |
715
732
  * | `color` | `LCSColor` | Syncs on create / update. Sends Primary Viewable as the thumbnail. Creates `LCSColor` if missing. |
716
733
  * | `custom-entity` | `LCSRevisableEntity` | Syncs on create / update. Creates `LCSRevisableEntity` if missing. Workflows should gate on the custom-entity subtype so only the intended subtypes publish. |
717
- * | `custom-entity` (`custom-entity:country`) | `LCSCountry` | Built-in defaults for type path (`Country`), identifier (`vibeIQIdentifier`, same key in both systems) and name; the mapping section overrides each default and **must** declare the origin. Which system creates Countries is declared in the `LCSCountry` mapping section with the `isInboundCreatable` / `isOutboundCreatable` pair: `(true, false)` = FlexPLM creates, `(false, true)` = VibeIQ creates; anything else is undeclared. See `TypeConversionUtils.getOriginSystem`. |
734
+ * | `custom-entity` (`custom-entity:country`) | `LCSCountry` | Built-in defaults for type path (`Country`), identifier (`vibeIQIdentifier`, same key in both systems) and name; the mapping section overrides each default and **must** declare the origin with `originSystem: 'flexplm' \| 'vibeiq'` (see {@link MappingSection.originSystem}); without it Country is read-only. |
718
735
  * | `project-item` | `LCSProductSeasonLink` / `LCSSKUSeasonLink` | Syncs on plan publish. Requires the assortment to have `publishToFlexPLM=true` and `flexPLMSeasonName` set. Adds the Product / SKU to the `LCSSeason` if not already linked. Only project-items changed since the last publish are sent (configurable to reach further back). Processing on the FlexPLM side is async via a Windchill Queue. |
719
736
  * | `item:material` | `LCSMaterial` | Supported via custom mapping section + `typeConversion`. Route `item:material` typePath to a mapping section that declares `getClass: () => 'LCSMaterial'` and `getSoftType: () => 'Material'`. See {@link TypeConversion} for the `LCSMaterial.processAsItem` config flag that pairs with this. |
720
737
  * | `custom-entity` | `LCSLast` / `LCSLifecycleManaged` | **Not yet supported.** |
@@ -732,7 +749,7 @@ interface MappingFileBase {
732
749
  * | `LCSLifecycleManaged` | `custom-entity` | Configurable to create new custom-entities; updates existing. |
733
750
  * | `LCSMaterial` | `custom-entity` (default) or `item:material` | Default: custom-entity. To route to `item:material` instead, set `{ "LCSMaterial": { "processAsItem": true } }` in the connector app config and write a mapping section with `getClass: () => 'item'` + `getSoftType: () => 'item:material'`. Wire it via `flex2vibe.LCSMaterial.getMapKey` returning that section's name. |
734
751
  * | `LCSRevisableEntity` | `custom-entity` | Configurable to create new custom-entities; updates existing. |
735
- * | `LCSCountry` | `custom-entity` (`custom-entity:country`) | Built-in defaults for type path and identifier; the mapping section must declare the origin. Identifier `vibeIQIdentifier` (same key in both systems). Creation is allowed only when the `LCSCountry` section declares FlexPLM as the origin: `isInboundCreatable: () => true` and `isOutboundCreatable: () => false`. |
752
+ * | `LCSCountry` | `custom-entity` (`custom-entity:country`) | Built-in defaults for type path and identifier; the mapping section must declare `originSystem`. Identifier `vibeIQIdentifier` (same key in both systems). Creation is allowed only when `originSystem` is `'flexplm'`. |
736
753
  * | (any) | `item` (direct, no link) | **Not yet supported.** |
737
754
  *
738
755
  * @example
@@ -1,6 +1,5 @@
1
1
  import { MapFileUtil } from '@contrail/transform-data';
2
- /** The system that originates (creates) an entity type: 'flexplm' or 'vibeiq'. */
3
- export type OriginSystem = 'flexplm' | 'vibeiq';
2
+ import { OriginSystem } from '../interfaces/mapping-file';
4
3
  /** This class is helper functions to get the data for converting
5
4
  * VibeIQ entities to / from FlexPLM Objects
6
5
  */
@@ -150,22 +149,20 @@ export declare class TypeConversionUtils {
150
149
  static isInboundCreatableFromObject(fileId: string, mapFileUtil: MapFileUtil, object: any, context?: any): Promise<boolean>;
151
150
  static isOutboundCreatableFromEntity(fileId: string, mapFileUtil: MapFileUtil, entity: any, context?: any): Promise<boolean>;
152
151
  /** Returns which system originates (creates) an entity type, declared in its mapping section
153
- * with the isInboundCreatable / isOutboundCreatable pair:
154
- * - isInboundCreatable true and isOutboundCreatable false -> 'flexplm' (FlexPLM creates)
155
- * - isInboundCreatable false and isOutboundCreatable true -> 'vibeiq' (VibeIQ creates)
156
- * Anything else is undeclared and returns undefined: no mapping file, no section, either function
157
- * missing, both true, both false, or an error while resolving. Unlike isInboundCreatableFromObject /
158
- * isOutboundCreatableFromEntity, there are NO defaults: the origin must be stated explicitly,
159
- * so callers can fail safe (treat the entity as read-only) when it is not.
152
+ * with the `originSystem` field: 'flexplm' (FlexPLM creates) or 'vibeiq' (VibeIQ creates).
153
+ * Case and surrounding whitespace are ignored. Anything else is undeclared and returns undefined:
154
+ * no mapping file, no section, no field, or a value that is not 'flexplm' / 'vibeiq'. There is no
155
+ * default, so callers can fail safe (treat the entity as read-only) when it is not declared.
156
+ *
157
+ * The value is static and type-level: it does not read the record, and it does not consult the
158
+ * isInboundCreatable / isOutboundCreatable gates.
160
159
  *
161
160
  * @param fileId id for mapFile
162
161
  * @param mapFileUtil class to get mapfile
163
- * @param entityOrObject VibeIQ entity (vibe2flex) or FlexPLM object (flex2vibe)
164
- * @param direction VIBE2FLEX_DIRECTION or FLEX2VIBE_DIRECTION; selects how the map section key is resolved
165
- * @param context optional context object passed to the mapping functions
162
+ * @param mapSectionKey key of the map section that declares the origin. Ex: 'LCSCountry'
166
163
  * @returns Promise<'flexplm' | 'vibeiq' | undefined>
167
164
  */
168
- static getOriginSystem(fileId: string, mapFileUtil: MapFileUtil, entityOrObject: any, direction: string, context?: any): Promise<OriginSystem | undefined>;
165
+ static getOriginSystem(fileId: string, mapFileUtil: MapFileUtil, mapSectionKey: string): Promise<OriginSystem | undefined>;
169
166
  /** Takes in a FlexPLM object and determines whether inbound
170
167
  * images should be synced. In most cases, the creation owning system
171
168
  * will also control image syncing. Defaults to false if no mapping exists.
@@ -143,11 +143,6 @@ class TypeConversionUtils {
143
143
  const mappingData = await mapFileUtil.getMappingSection(transformMapFile, 'typeConversion', direction);
144
144
  if (mappingData && mappingData[type] && mappingData[type]['getMapKey']) {
145
145
  const mapKey = await mappingData[type]['getMapKey'](entity);
146
- // An org's custom-entity routing usually returns '' for the subtypes it does not list.
147
- // Country has a built-in section key, so its section is found without a typeConversion entry.
148
- if (!mapKey && type_defaults_1.TypeDefaults.isCountryEntity(entity)) {
149
- return type_defaults_1.TypeDefaults.COUNTRY_OBJECT_CLASS;
150
- }
151
146
  return mapKey;
152
147
  }
153
148
  }
@@ -366,43 +361,29 @@ class TypeConversionUtils {
366
361
  return isOutboundCreatable;
367
362
  }
368
363
  /** Returns which system originates (creates) an entity type, declared in its mapping section
369
- * with the isInboundCreatable / isOutboundCreatable pair:
370
- * - isInboundCreatable true and isOutboundCreatable false -> 'flexplm' (FlexPLM creates)
371
- * - isInboundCreatable false and isOutboundCreatable true -> 'vibeiq' (VibeIQ creates)
372
- * Anything else is undeclared and returns undefined: no mapping file, no section, either function
373
- * missing, both true, both false, or an error while resolving. Unlike isInboundCreatableFromObject /
374
- * isOutboundCreatableFromEntity, there are NO defaults: the origin must be stated explicitly,
375
- * so callers can fail safe (treat the entity as read-only) when it is not.
364
+ * with the `originSystem` field: 'flexplm' (FlexPLM creates) or 'vibeiq' (VibeIQ creates).
365
+ * Case and surrounding whitespace are ignored. Anything else is undeclared and returns undefined:
366
+ * no mapping file, no section, no field, or a value that is not 'flexplm' / 'vibeiq'. There is no
367
+ * default, so callers can fail safe (treat the entity as read-only) when it is not declared.
368
+ *
369
+ * The value is static and type-level: it does not read the record, and it does not consult the
370
+ * isInboundCreatable / isOutboundCreatable gates.
376
371
  *
377
372
  * @param fileId id for mapFile
378
373
  * @param mapFileUtil class to get mapfile
379
- * @param entityOrObject VibeIQ entity (vibe2flex) or FlexPLM object (flex2vibe)
380
- * @param direction VIBE2FLEX_DIRECTION or FLEX2VIBE_DIRECTION; selects how the map section key is resolved
381
- * @param context optional context object passed to the mapping functions
374
+ * @param mapSectionKey key of the map section that declares the origin. Ex: 'LCSCountry'
382
375
  * @returns Promise<'flexplm' | 'vibeiq' | undefined>
383
376
  */
384
- static async getOriginSystem(fileId, mapFileUtil, entityOrObject, direction, context) {
385
- if (!fileId || !entityOrObject) {
377
+ static async getOriginSystem(fileId, mapFileUtil, mapSectionKey) {
378
+ if (!fileId || !mapSectionKey) {
386
379
  return undefined;
387
380
  }
388
381
  try {
389
- const mapKey = (TypeConversionUtils.VIBE2FLEX_DIRECTION === direction)
390
- ? await this.getMapKey(fileId, mapFileUtil, entityOrObject, direction)
391
- : await this.getMapKeyFromObject(fileId, mapFileUtil, entityOrObject, direction);
392
- if (!mapKey) {
393
- return undefined;
394
- }
395
- const mapData = await map_utils_1.MapUtil.getFullMapSection(fileId, mapFileUtil, mapKey);
396
- if (!mapData || typeof mapData['isInboundCreatable'] !== 'function' || typeof mapData['isOutboundCreatable'] !== 'function') {
397
- return undefined;
398
- }
399
- const inbound = !!(await mapData['isInboundCreatable'](entityOrObject, context));
400
- const outbound = !!(await mapData['isOutboundCreatable'](entityOrObject, context));
401
- if (inbound && !outbound) {
402
- return 'flexplm';
403
- }
404
- if (!inbound && outbound) {
405
- return 'vibeiq';
382
+ const mapData = await map_utils_1.MapUtil.getFullMapSection(fileId, mapFileUtil, mapSectionKey);
383
+ const value = mapData?.['originSystem'];
384
+ const normalized = typeof value === 'string' ? value.trim().toLowerCase() : '';
385
+ if (normalized === 'flexplm' || normalized === 'vibeiq') {
386
+ return normalized;
406
387
  }
407
388
  }
408
389
  catch (e) {
@@ -939,14 +939,14 @@ describe('conversion-utils', () => {
939
939
  afterEach(() => {
940
940
  spy.mockRestore();
941
941
  });
942
- it('VibeIQ -> FlexPLM uses defaults', async () => {
942
+ it('should use the defaults when sending a Country from VibeIQ to FlexPLM', async () => {
943
943
  const entity = { entityType: 'custom-entity', typePath: 'custom-entity:country' };
944
944
  expect(await type_conversion_utils_1.TypeConversionUtils.getObjectClass(TRANSFORM_MAP_FILE, mapFileUtil, entity)).toEqual('LCSCountry');
945
945
  expect(await type_conversion_utils_1.TypeConversionUtils.getObjectTypePath(TRANSFORM_MAP_FILE, mapFileUtil, entity)).toEqual('Country');
946
946
  expect(await type_conversion_utils_1.TypeConversionUtils.getIdentifierProperties(TRANSFORM_MAP_FILE, mapFileUtil, entity)).toEqual(['vibeIQIdentifier']);
947
947
  expect(await type_conversion_utils_1.TypeConversionUtils.getInformationalProperties(TRANSFORM_MAP_FILE, mapFileUtil, entity)).toEqual(['name']);
948
948
  });
949
- it('FlexPLM -> VibeIQ uses defaults', async () => {
949
+ it('should use the defaults when receiving a Country from FlexPLM into VibeIQ', async () => {
950
950
  const object = { flexPLMObjectClass: 'LCSCountry', flexPLMTypePath: 'Country' };
951
951
  expect(await type_conversion_utils_1.TypeConversionUtils.getMapKeyFromObject(TRANSFORM_MAP_FILE, mapFileUtil, object, type_conversion_utils_1.TypeConversionUtils.FLEX2VIBE_DIRECTION)).toEqual('LCSCountry');
952
952
  expect(await type_conversion_utils_1.TypeConversionUtils.getEntityClassFromObject(TRANSFORM_MAP_FILE, mapFileUtil, object)).toEqual('custom-entity');
@@ -954,7 +954,7 @@ describe('conversion-utils', () => {
954
954
  expect(await type_conversion_utils_1.TypeConversionUtils.getIdentifierPropertiesFromObject(TRANSFORM_MAP_FILE, mapFileUtil, object)).toEqual(['vibeIQIdentifier']);
955
955
  expect(await type_conversion_utils_1.TypeConversionUtils.getInformationalPropertiesFromObject(TRANSFORM_MAP_FILE, mapFileUtil, object)).toEqual(['name']);
956
956
  });
957
- it('a mapping section still overrides the defaults', async () => {
957
+ it('should let a mapping section override the Country defaults', async () => {
958
958
  const overriding = {
959
959
  typeConversion: { vibe2flex: { 'custom-entity': { getMapKey: () => 'LCSCountry' } }, flex2vibe: {} },
960
960
  LCSCountry: { getIdentifierProperties: () => ['isoCode'] }
@@ -964,18 +964,11 @@ describe('conversion-utils', () => {
964
964
  expect(await type_conversion_utils_1.TypeConversionUtils.getIdentifierProperties(TRANSFORM_MAP_FILE, mapFileUtil, entity)).toEqual(['isoCode']);
965
965
  });
966
966
  });
967
- describe('getOriginSystem (isInboundCreatable / isOutboundCreatable pair)', () => {
967
+ describe('getOriginSystem (originSystem field on the LCSCountry mapping section)', () => {
968
968
  const mapFileUtil = new transform_data_1.MapFileUtil(new sdk_1.Entities());
969
- const countryEntity = { entityType: 'custom-entity', typePath: 'custom-entity:country' };
970
- const countryObject = { flexPLMObjectClass: 'LCSCountry', flexPLMTypePath: 'Country' };
971
969
  let spy;
972
- // Org-style routing: custom-entity subtypes that are not listed return ''
973
- const typeConversion = {
974
- vibe2flex: { 'custom-entity': { getMapKey: () => '' } },
975
- flex2vibe: {},
976
- };
977
970
  function useMap(countrySection) {
978
- const map = { typeConversion };
971
+ const map = { typeConversion: { vibe2flex: { 'custom-entity': { getMapKey: () => '' } }, flex2vibe: {} } };
979
972
  if (countrySection) {
980
973
  map.LCSCountry = countrySection;
981
974
  }
@@ -987,63 +980,67 @@ describe('conversion-utils', () => {
987
980
  afterEach(() => {
988
981
  spy.mockRestore();
989
982
  });
990
- it("is 'flexplm' when inbound is creatable and outbound is not (both directions)", async () => {
991
- useMap({ isInboundCreatable: () => true, isOutboundCreatable: () => false });
992
- expect(await type_conversion_utils_1.TypeConversionUtils.getOriginSystem(TRANSFORM_MAP_FILE, mapFileUtil, countryEntity, type_conversion_utils_1.TypeConversionUtils.VIBE2FLEX_DIRECTION)).toBe('flexplm');
993
- expect(await type_conversion_utils_1.TypeConversionUtils.getOriginSystem(TRANSFORM_MAP_FILE, mapFileUtil, countryObject, type_conversion_utils_1.TypeConversionUtils.FLEX2VIBE_DIRECTION)).toBe('flexplm');
994
- });
995
- it("is 'vibeiq' when outbound is creatable and inbound is not (both directions)", async () => {
996
- useMap({ isInboundCreatable: () => false, isOutboundCreatable: () => true });
997
- expect(await type_conversion_utils_1.TypeConversionUtils.getOriginSystem(TRANSFORM_MAP_FILE, mapFileUtil, countryEntity, type_conversion_utils_1.TypeConversionUtils.VIBE2FLEX_DIRECTION)).toBe('vibeiq');
998
- expect(await type_conversion_utils_1.TypeConversionUtils.getOriginSystem(TRANSFORM_MAP_FILE, mapFileUtil, countryObject, type_conversion_utils_1.TypeConversionUtils.FLEX2VIBE_DIRECTION)).toBe('vibeiq');
983
+ it('should return flexplm when the section declares originSystem flexplm', async () => {
984
+ useMap({ originSystem: 'flexplm' });
985
+ expect(await type_conversion_utils_1.TypeConversionUtils.getOriginSystem(TRANSFORM_MAP_FILE, mapFileUtil, 'LCSCountry')).toBe('flexplm');
999
986
  });
1000
- it('supports async mapping functions', async () => {
1001
- useMap({ isInboundCreatable: async () => true, isOutboundCreatable: async () => false });
1002
- expect(await type_conversion_utils_1.TypeConversionUtils.getOriginSystem(TRANSFORM_MAP_FILE, mapFileUtil, countryObject, type_conversion_utils_1.TypeConversionUtils.FLEX2VIBE_DIRECTION)).toBe('flexplm');
987
+ it('should return vibeiq when the section declares originSystem vibeiq', async () => {
988
+ useMap({ originSystem: 'vibeiq' });
989
+ expect(await type_conversion_utils_1.TypeConversionUtils.getOriginSystem(TRANSFORM_MAP_FILE, mapFileUtil, 'LCSCountry')).toBe('vibeiq');
1003
990
  });
1004
- it('is undefined when both are true', async () => {
1005
- useMap({ isInboundCreatable: () => true, isOutboundCreatable: () => true });
1006
- expect(await type_conversion_utils_1.TypeConversionUtils.getOriginSystem(TRANSFORM_MAP_FILE, mapFileUtil, countryObject, type_conversion_utils_1.TypeConversionUtils.FLEX2VIBE_DIRECTION)).toBeUndefined();
991
+ it('should ignore case and surrounding whitespace in the declared value', async () => {
992
+ useMap({ originSystem: ' FlexPLM ' });
993
+ expect(await type_conversion_utils_1.TypeConversionUtils.getOriginSystem(TRANSFORM_MAP_FILE, mapFileUtil, 'LCSCountry')).toBe('flexplm');
994
+ useMap({ originSystem: 'VibeIQ' });
995
+ expect(await type_conversion_utils_1.TypeConversionUtils.getOriginSystem(TRANSFORM_MAP_FILE, mapFileUtil, 'LCSCountry')).toBe('vibeiq');
1007
996
  });
1008
- it('is undefined when both are false', async () => {
1009
- useMap({ isInboundCreatable: () => false, isOutboundCreatable: () => false });
1010
- expect(await type_conversion_utils_1.TypeConversionUtils.getOriginSystem(TRANSFORM_MAP_FILE, mapFileUtil, countryEntity, type_conversion_utils_1.TypeConversionUtils.VIBE2FLEX_DIRECTION)).toBeUndefined();
997
+ it('should return undefined when the value is not flexplm or vibeiq', async () => {
998
+ for (const value of ['sap', '', ' ', 5, null, {}, ['flexplm']]) {
999
+ useMap({ originSystem: value });
1000
+ expect(await type_conversion_utils_1.TypeConversionUtils.getOriginSystem(TRANSFORM_MAP_FILE, mapFileUtil, 'LCSCountry')).toBeUndefined();
1001
+ }
1011
1002
  });
1012
- it('is undefined when either function is missing (no defaults)', async () => {
1013
- useMap({ isInboundCreatable: () => true });
1014
- expect(await type_conversion_utils_1.TypeConversionUtils.getOriginSystem(TRANSFORM_MAP_FILE, mapFileUtil, countryObject, type_conversion_utils_1.TypeConversionUtils.FLEX2VIBE_DIRECTION)).toBeUndefined();
1015
- useMap({ isOutboundCreatable: () => true });
1016
- expect(await type_conversion_utils_1.TypeConversionUtils.getOriginSystem(TRANSFORM_MAP_FILE, mapFileUtil, countryEntity, type_conversion_utils_1.TypeConversionUtils.VIBE2FLEX_DIRECTION)).toBeUndefined();
1003
+ it('should return undefined when the section does not declare originSystem', async () => {
1004
+ useMap({ getIdentifierProperties: () => ['vibeIQIdentifier'] });
1005
+ expect(await type_conversion_utils_1.TypeConversionUtils.getOriginSystem(TRANSFORM_MAP_FILE, mapFileUtil, 'LCSCountry')).toBeUndefined();
1017
1006
  });
1018
- it('is undefined when there is no LCSCountry section', async () => {
1007
+ it('should return undefined when there is no LCSCountry section', async () => {
1019
1008
  useMap();
1020
- expect(await type_conversion_utils_1.TypeConversionUtils.getOriginSystem(TRANSFORM_MAP_FILE, mapFileUtil, countryEntity, type_conversion_utils_1.TypeConversionUtils.VIBE2FLEX_DIRECTION)).toBeUndefined();
1021
- expect(await type_conversion_utils_1.TypeConversionUtils.getOriginSystem(TRANSFORM_MAP_FILE, mapFileUtil, countryObject, type_conversion_utils_1.TypeConversionUtils.FLEX2VIBE_DIRECTION)).toBeUndefined();
1009
+ expect(await type_conversion_utils_1.TypeConversionUtils.getOriginSystem(TRANSFORM_MAP_FILE, mapFileUtil, 'LCSCountry')).toBeUndefined();
1010
+ });
1011
+ it('should return undefined when there is no mapping file id or section key', async () => {
1012
+ useMap({ originSystem: 'flexplm' });
1013
+ expect(await type_conversion_utils_1.TypeConversionUtils.getOriginSystem('', mapFileUtil, 'LCSCountry')).toBeUndefined();
1014
+ expect(await type_conversion_utils_1.TypeConversionUtils.getOriginSystem(TRANSFORM_MAP_FILE, mapFileUtil, '')).toBeUndefined();
1022
1015
  });
1023
- it('is undefined when there is no mapping file id or no entity', async () => {
1016
+ it('should not consult the creation gates, only the originSystem field', async () => {
1024
1017
  useMap({ isInboundCreatable: () => true, isOutboundCreatable: () => false });
1025
- expect(await type_conversion_utils_1.TypeConversionUtils.getOriginSystem('', mapFileUtil, countryObject, type_conversion_utils_1.TypeConversionUtils.FLEX2VIBE_DIRECTION)).toBeUndefined();
1026
- expect(await type_conversion_utils_1.TypeConversionUtils.getOriginSystem(TRANSFORM_MAP_FILE, mapFileUtil, undefined, type_conversion_utils_1.TypeConversionUtils.FLEX2VIBE_DIRECTION)).toBeUndefined();
1018
+ expect(await type_conversion_utils_1.TypeConversionUtils.getOriginSystem(TRANSFORM_MAP_FILE, mapFileUtil, 'LCSCountry')).toBeUndefined();
1019
+ useMap({ originSystem: 'vibeiq', isInboundCreatable: () => true, isOutboundCreatable: () => false });
1020
+ expect(await type_conversion_utils_1.TypeConversionUtils.getOriginSystem(TRANSFORM_MAP_FILE, mapFileUtil, 'LCSCountry')).toBe('vibeiq');
1027
1021
  });
1028
- it('is undefined (and logs) when a mapping function throws', async () => {
1029
- useMap({ isInboundCreatable: () => { throw new Error('boom'); }, isOutboundCreatable: () => false });
1022
+ it('should read the section named by the key argument', async () => {
1023
+ const map = { typeConversion: {}, countryMapping: { originSystem: 'vibeiq' } };
1024
+ spy.mockImplementation(async () => map);
1025
+ expect(await type_conversion_utils_1.TypeConversionUtils.getOriginSystem(TRANSFORM_MAP_FILE, mapFileUtil, 'countryMapping')).toBe('vibeiq');
1026
+ });
1027
+ it('should return undefined and log when the mapping file cannot be read', async () => {
1028
+ spy.mockImplementation(async () => {
1029
+ throw new Error('boom');
1030
+ });
1030
1031
  const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => undefined);
1031
1032
  try {
1032
- expect(await type_conversion_utils_1.TypeConversionUtils.getOriginSystem(TRANSFORM_MAP_FILE, mapFileUtil, countryObject, type_conversion_utils_1.TypeConversionUtils.FLEX2VIBE_DIRECTION)).toBeUndefined();
1033
+ expect(await type_conversion_utils_1.TypeConversionUtils.getOriginSystem(TRANSFORM_MAP_FILE, mapFileUtil, 'LCSCountry')).toBeUndefined();
1033
1034
  expect(errorSpy).toHaveBeenCalled();
1034
1035
  }
1035
1036
  finally {
1036
1037
  errorSpy.mockRestore();
1037
1038
  }
1038
1039
  });
1039
- it('does not change the creatable defaults of other types', async () => {
1040
- useMap({ isInboundCreatable: () => true, isOutboundCreatable: () => false });
1041
- const other = { entityType: 'custom-entity', typePath: 'custom-entity:exchangeRate' };
1042
- expect(await type_conversion_utils_1.TypeConversionUtils.getMapKey(TRANSFORM_MAP_FILE, mapFileUtil, other, type_conversion_utils_1.TypeConversionUtils.VIBE2FLEX_DIRECTION)).toEqual('');
1043
- });
1044
- it('finds the Country section even when the org custom-entity routing returns an empty key', async () => {
1045
- useMap({ isInboundCreatable: () => true, isOutboundCreatable: () => false });
1046
- expect(await type_conversion_utils_1.TypeConversionUtils.getMapKey(TRANSFORM_MAP_FILE, mapFileUtil, countryEntity, type_conversion_utils_1.TypeConversionUtils.VIBE2FLEX_DIRECTION)).toEqual('LCSCountry');
1040
+ it('should leave the section key of a Country unchanged when the org custom entity routing returns an empty key', async () => {
1041
+ useMap({ originSystem: 'flexplm' });
1042
+ const country = { entityType: 'custom-entity', typePath: 'custom-entity:country' };
1043
+ expect(await type_conversion_utils_1.TypeConversionUtils.getMapKey(TRANSFORM_MAP_FILE, mapFileUtil, country, type_conversion_utils_1.TypeConversionUtils.VIBE2FLEX_DIRECTION)).toEqual('');
1047
1044
  });
1048
1045
  });
1049
1046
  });
@@ -42,7 +42,7 @@ describe('Type Defaults', () => {
42
42
  const objectClass = type_defaults_1.TypeDefaults.getDefaultObjectClass(newData);
43
43
  expect(objectClass).toEqual('LCSColor');
44
44
  });
45
- it('LCSCountry', () => {
45
+ it('should return LCSCountry for a Country custom entity', () => {
46
46
  const newData = {
47
47
  entityType: 'custom-entity',
48
48
  typePath: 'custom-entity:country'
@@ -50,14 +50,14 @@ describe('Type Defaults', () => {
50
50
  const objectClass = type_defaults_1.TypeDefaults.getDefaultObjectClass(newData);
51
51
  expect(objectClass).toEqual('LCSCountry');
52
52
  });
53
- it('LCSCountry - typePath is case-insensitive', () => {
53
+ it('should return LCSCountry when the Country typePath uses different casing', () => {
54
54
  const newData = {
55
55
  entityType: 'custom-entity',
56
56
  typePath: 'custom-entity:Country'
57
57
  };
58
58
  expect(type_defaults_1.TypeDefaults.getDefaultObjectClass(newData)).toEqual('LCSCountry');
59
59
  });
60
- it('other custom-entity subtype is still LCSRevisableEntity', () => {
60
+ it('should return LCSRevisableEntity for a custom entity that is not a Country', () => {
61
61
  const newData = {
62
62
  entityType: 'custom-entity',
63
63
  typePath: 'custom-entity:exchangeRate'
@@ -142,14 +142,14 @@ describe('Type Defaults', () => {
142
142
  const typePath = type_defaults_1.TypeDefaults.getDefaultObjectTypePath(newData);
143
143
  expect(typePath).toEqual('Color');
144
144
  });
145
- it('custom-entity - country', () => {
145
+ it('should return Country as the FlexPLM type path for a Country custom entity', () => {
146
146
  const newData = {
147
147
  entityType: 'custom-entity',
148
148
  typePath: 'custom-entity:country'
149
149
  };
150
150
  expect(type_defaults_1.TypeDefaults.getDefaultObjectTypePath(newData)).toEqual('Country');
151
151
  });
152
- it('custom-entity - other subtype stays Revisable Entity', () => {
152
+ it('should return Revisable Entity as the type path for a custom entity that is not a Country', () => {
153
153
  const newData = {
154
154
  entityType: 'custom-entity',
155
155
  typePath: 'custom-entity:exchangeRate'
@@ -255,7 +255,7 @@ describe('Type Defaults', () => {
255
255
  expect(defaultIdentifiers).toContain('name');
256
256
  expect(defaultIdentifiers).toHaveLength(1);
257
257
  });
258
- it('custom-entity - country', () => {
258
+ it('should return vibeIQIdentifier as the identifier for a Country custom entity', () => {
259
259
  const newData = {
260
260
  entityType: 'custom-entity',
261
261
  typePath: 'custom-entity:country'
@@ -263,7 +263,7 @@ describe('Type Defaults', () => {
263
263
  const defaultIdentifiers = type_defaults_1.TypeDefaults.getDefaultIdentifierProperties(newData);
264
264
  expect(defaultIdentifiers).toEqual(['vibeIQIdentifier']);
265
265
  });
266
- it('custom-entity - other subtype stays name', () => {
266
+ it('should return name as the identifier for a custom entity that is not a Country', () => {
267
267
  const newData = {
268
268
  entityType: 'custom-entity',
269
269
  typePath: 'custom-entity:exchangeRate'
@@ -321,14 +321,14 @@ describe('Type Defaults', () => {
321
321
  const defaultIdentifiers = type_defaults_1.TypeDefaults.getDefaultInformationalProperties(newData);
322
322
  expect(defaultIdentifiers).toHaveLength(0);
323
323
  });
324
- it('custom-entity - country', () => {
324
+ it('should return name as the informational property for a Country custom entity', () => {
325
325
  const newData = {
326
326
  entityType: 'custom-entity',
327
327
  typePath: 'custom-entity:country'
328
328
  };
329
329
  expect(type_defaults_1.TypeDefaults.getDefaultInformationalProperties(newData)).toEqual(['name']);
330
330
  });
331
- it('custom-entity - other subtype has none', () => {
331
+ it('should return no informational properties for a custom entity that is not a Country', () => {
332
332
  const newData = {
333
333
  entityType: 'custom-entity',
334
334
  typePath: 'custom-entity:exchangeRate'
@@ -344,10 +344,10 @@ describe('Type Defaults', () => {
344
344
  });
345
345
  });
346
346
  describe('isCountryEntity', () => {
347
- it('true for custom-entity:country', () => {
347
+ it('should return true for the custom-entity:country type path', () => {
348
348
  expect(type_defaults_1.TypeDefaults.isCountryEntity({ typePath: 'custom-entity:country' })).toBe(true);
349
349
  });
350
- it('false for another subtype, a missing typePath, or undefined', () => {
350
+ it('should return false for another subtype, a missing typePath, or an undefined entity', () => {
351
351
  expect(type_defaults_1.TypeDefaults.isCountryEntity({ typePath: 'custom-entity:other' })).toBe(false);
352
352
  expect(type_defaults_1.TypeDefaults.isCountryEntity({})).toBe(false);
353
353
  expect(type_defaults_1.TypeDefaults.isCountryEntity(undefined)).toBe(false);
@@ -430,13 +430,13 @@ describe('Type Defaults', () => {
430
430
  const entityClass = type_defaults_1.TypeDefaults.getDefaultEntityClass(object);
431
431
  expect(entityClass).toBe('assortment');
432
432
  });
433
- it('custom-entity - LCSCountry', () => {
433
+ it('should return custom-entity as the entity class for LCSCountry', () => {
434
434
  const object = {
435
435
  flexPLMObjectClass: 'LCSCountry'
436
436
  };
437
437
  expect(type_defaults_1.TypeDefaults.getDefaultEntityClass(object)).toBe('custom-entity');
438
438
  });
439
- it('custom-entity - LCSCountry - processAsItem=true', () => {
439
+ it('should return custom-entity for LCSCountry when LCSMaterial processAsItem is true', () => {
440
440
  const object = {
441
441
  flexPLMObjectClass: 'LCSCountry'
442
442
  };
@@ -512,7 +512,7 @@ describe('Type Defaults', () => {
512
512
  const typePath = type_defaults_1.TypeDefaults.getDefaultEntityTypePath(object);
513
513
  expect(typePath).toBe('project-item');
514
514
  });
515
- it('LCSCountry', () => {
515
+ it('should return custom-entity:country as the VibeIQ type path for LCSCountry', () => {
516
516
  const object = {
517
517
  flexPLMObjectClass: 'LCSCountry'
518
518
  };
@@ -600,7 +600,7 @@ describe('Type Defaults', () => {
600
600
  expect(defaultIdentifiers).toContain('name');
601
601
  expect(defaultIdentifiers).toHaveLength(1);
602
602
  });
603
- it('LCSCountry', () => {
603
+ it('should return vibeIQIdentifier as the identifier for an LCSCountry object', () => {
604
604
  const object = {
605
605
  flexPLMObjectClass: 'LCSCountry'
606
606
  };
@@ -646,7 +646,7 @@ describe('Type Defaults', () => {
646
646
  });
647
647
  }); //getDefaultIdentifierPropertiesFromObject
648
648
  describe('getDefaultInformationalPropertiesFromObject', () => {
649
- it('LCSCountry', () => {
649
+ it('should return name as the informational property for an LCSCountry object', () => {
650
650
  const object = {
651
651
  flexPLMObjectClass: 'LCSCountry'
652
652
  };
@@ -47,6 +47,31 @@ describe('TypeUtils.getEventObjectClass() tests', () => {
47
47
  const objectClass = tu.getEventObjectClass(entityType, newData);
48
48
  expect(objectClass).toEqual('LCSRevisableEntity');
49
49
  });
50
+ it('should return LCSCountry for a custom entity with the custom-entity:country typePath', () => {
51
+ const entityType = 'custom-entity';
52
+ const newData = { typePath: 'custom-entity:country' };
53
+ const objectClass = tu.getEventObjectClass(entityType, newData);
54
+ expect(objectClass).toEqual('LCSCountry');
55
+ });
56
+ it('should return LCSCountry when the Country typePath uses different casing', () => {
57
+ const objectClass = tu.getEventObjectClass('custom-entity', { typePath: 'custom-entity:Country' });
58
+ expect(objectClass).toEqual('LCSCountry');
59
+ });
60
+ it('should return LCSRevisableEntity for a custom entity that is not a Country', () => {
61
+ const objectClass = tu.getEventObjectClass('custom-entity', { typePath: 'custom-entity:exchangeRate' });
62
+ expect(objectClass).toEqual('LCSRevisableEntity');
63
+ });
64
+ });
65
+ describe('TypeUtils.getEntityTypeClientOptions() tests', () => {
66
+ const tu = new type_utils_1.TypeUtils();
67
+ it('should resolve LCSCountry to the custom-entity root type', () => {
68
+ const options = tu.getEntityTypeClientOptions('LCSCountry', {});
69
+ expect(options).toEqual({ root: 'custom-entity' });
70
+ });
71
+ it('should keep resolving LCSRevisableEntity to the custom-entity root type', () => {
72
+ const options = tu.getEntityTypeClientOptions('LCSRevisableEntity', {});
73
+ expect(options).toEqual({ root: 'custom-entity' });
74
+ });
50
75
  });
51
76
  describe('filterProperties-item', () => {
52
77
  const itemType = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrail/flexplm",
3
- "version": "1.8.0-alpha.0261c7e",
3
+ "version": "1.8.0-alpha.f09632d",
4
4
  "description": "Library used for integration with flexplm.",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",