@contrail/flexplm 1.3.1-alpha.eae3d9f → 1.3.1-alpha.f54fe36

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.
Files changed (111) hide show
  1. package/lib/cli/commands/compile.d.ts +1 -0
  2. package/lib/cli/commands/compile.js +71 -0
  3. package/lib/cli/commands/compile.spec.d.ts +1 -0
  4. package/lib/cli/commands/compile.spec.js +80 -0
  5. package/lib/cli/commands/create.d.ts +1 -0
  6. package/lib/cli/commands/create.js +75 -0
  7. package/lib/cli/commands/create.spec.d.ts +1 -0
  8. package/lib/cli/commands/create.spec.js +78 -0
  9. package/lib/cli/commands/upload.d.ts +10 -0
  10. package/lib/cli/commands/upload.js +219 -0
  11. package/lib/cli/commands/upload.spec.d.ts +1 -0
  12. package/lib/cli/commands/upload.spec.js +88 -0
  13. package/lib/cli/index.d.ts +2 -0
  14. package/lib/cli/index.js +64 -0
  15. package/lib/cli/index.spec.d.ts +1 -0
  16. package/lib/cli/index.spec.js +79 -0
  17. package/lib/cli/template/mapping-template.ts.template +18 -0
  18. package/lib/entity-processor/base-entity-processor.d.ts +47 -0
  19. package/lib/entity-processor/base-entity-processor.js +53 -0
  20. package/lib/entity-processor/base-entity-processor.spec.js +1 -0
  21. package/lib/index.d.ts +1 -0
  22. package/lib/index.js +1 -0
  23. package/lib/interfaces/mapping-file.d.ts +429 -0
  24. package/lib/interfaces/mapping-file.js +2 -0
  25. package/lib/publish/base-process-publish-assortment.d.ts +25 -0
  26. package/lib/publish/base-process-publish-assortment.js +60 -6
  27. package/lib/publish/base-process-publish-assortment.spec.js +22 -4
  28. package/lib/publish/mockData.js +5 -0
  29. package/lib/transform/identifier-conversion-spec-mockData.js +34 -6
  30. package/lib/transform/identifier-conversion.d.ts +36 -0
  31. package/lib/transform/identifier-conversion.js +36 -0
  32. package/lib/transform/identifier-conversion.spec.js +4 -0
  33. package/lib/util/config-defaults.js +3 -0
  34. package/lib/util/config-defaults.spec.js +9 -0
  35. package/lib/util/data-converter-spec-mockData.js +17 -3
  36. package/lib/util/data-converter.d.ts +97 -0
  37. package/lib/util/data-converter.js +127 -1
  38. package/lib/util/data-converter.spec.js +2 -0
  39. package/lib/util/error-response-object.d.ts +5 -0
  40. package/lib/util/error-response-object.js +7 -0
  41. package/lib/util/event-short-message-status.js +1 -0
  42. package/lib/util/federation.js +8 -0
  43. package/lib/util/flexplm-connect.d.ts +7 -0
  44. package/lib/util/flexplm-connect.js +14 -0
  45. package/lib/util/logger-config.js +1 -0
  46. package/lib/util/map-util-spec-mockData.js +17 -3
  47. package/lib/util/map-utils.d.ts +27 -0
  48. package/lib/util/map-utils.js +27 -0
  49. package/lib/util/thumbnail-util.d.ts +21 -0
  50. package/lib/util/thumbnail-util.js +28 -1
  51. package/lib/util/thumbnail-util.spec.js +6 -0
  52. package/lib/util/type-conversion-utils-spec-mockData.js +3 -3
  53. package/lib/util/type-conversion-utils.d.ts +140 -0
  54. package/lib/util/type-conversion-utils.js +143 -0
  55. package/lib/util/type-defaults.d.ts +58 -0
  56. package/lib/util/type-defaults.js +58 -0
  57. package/lib/util/type-defaults.spec.js +5 -5
  58. package/lib/util/type-utils.d.ts +21 -0
  59. package/lib/util/type-utils.js +23 -0
  60. package/lib/util/type-utils.spec.js +2 -0
  61. package/package.json +21 -6
  62. package/scripts/copy-template.js +10 -0
  63. package/.github/pull_request_template.md +0 -31
  64. package/.github/workflows/flexplm-lib.yml +0 -27
  65. package/.github/workflows/publish-to-npm.yml +0 -121
  66. package/CHANGELOG.md +0 -32
  67. package/publish.bat +0 -5
  68. package/publish.sh +0 -5
  69. package/src/entity-processor/base-entity-processor.spec.ts +0 -460
  70. package/src/entity-processor/base-entity-processor.ts +0 -515
  71. package/src/flexplm-request.ts +0 -28
  72. package/src/flexplm-utils.spec.ts +0 -27
  73. package/src/flexplm-utils.ts +0 -29
  74. package/src/index.ts +0 -22
  75. package/src/interfaces/interfaces.ts +0 -122
  76. package/src/interfaces/item-family-changes.ts +0 -67
  77. package/src/interfaces/publish-change-data.ts +0 -43
  78. package/src/publish/base-process-publish-assortment-callback.ts +0 -50
  79. package/src/publish/base-process-publish-assortment.spec.ts +0 -1992
  80. package/src/publish/base-process-publish-assortment.ts +0 -1134
  81. package/src/publish/mockData.ts +0 -4561
  82. package/src/transform/identifier-conversion-spec-mockData.ts +0 -496
  83. package/src/transform/identifier-conversion.spec.ts +0 -354
  84. package/src/transform/identifier-conversion.ts +0 -282
  85. package/src/util/config-defaults.spec.ts +0 -350
  86. package/src/util/config-defaults.ts +0 -93
  87. package/src/util/data-converter-spec-mockData.ts +0 -231
  88. package/src/util/data-converter.spec.ts +0 -1041
  89. package/src/util/data-converter.ts +0 -762
  90. package/src/util/error-response-object.spec.ts +0 -116
  91. package/src/util/error-response-object.ts +0 -50
  92. package/src/util/event-short-message-status.ts +0 -22
  93. package/src/util/federation.ts +0 -172
  94. package/src/util/flexplm-connect.spec.ts +0 -132
  95. package/src/util/flexplm-connect.ts +0 -208
  96. package/src/util/logger-config.ts +0 -20
  97. package/src/util/map-util-spec-mockData.ts +0 -231
  98. package/src/util/map-utils.spec.ts +0 -103
  99. package/src/util/map-utils.ts +0 -41
  100. package/src/util/mockData.ts +0 -101
  101. package/src/util/thumbnail-util.spec.ts +0 -508
  102. package/src/util/thumbnail-util.ts +0 -272
  103. package/src/util/type-conversion-utils-spec-mockData.ts +0 -271
  104. package/src/util/type-conversion-utils.spec.ts +0 -968
  105. package/src/util/type-conversion-utils.ts +0 -460
  106. package/src/util/type-defaults.spec.ts +0 -669
  107. package/src/util/type-defaults.ts +0 -281
  108. package/src/util/type-utils.spec.ts +0 -227
  109. package/src/util/type-utils.ts +0 -144
  110. package/tsconfig.json +0 -24
  111. package/tslint.json +0 -57
@@ -0,0 +1,429 @@
1
+ import { FunctionTransformers, RekeyTransformers, TransformDefinition, TransformTask } from "@contrail/transform-data";
2
+ /**
3
+ * One direction of a {@link MappingSection} (either `vibe2flex` or `flex2vibe`).
4
+ *
5
+ * Holds the per-direction transform pipeline plus the named lookup tables
6
+ * (`rekey`, `removeKey`, `valueTransform`) that the tasks in
7
+ * {@link DirectionalSection.transformOrder} reference by key. A REKEY task
8
+ * configured with `rekeyTransformersKey: 'rekey'` resolves to
9
+ * {@link DirectionalSection.rekey} on the same directional section.
10
+ *
11
+ * The named members below cover the conventional shape, but the section
12
+ * is open: each task in a pipeline resolves its config by reading the
13
+ * sibling property whose name is supplied by the matching `*Key` pointer
14
+ * on the task. That means a directional section can hold:
15
+ * - additional `TransformTask[]` pipelines under names other than
16
+ * `transformOrder` (selected by passing a non-default `orderKey` to
17
+ * `MapFileUtil.getTransformTasks`)
18
+ * - additional REKEY / REMOVE / MORPH / VALUE_TRANSFORM / CONDITIONAL
19
+ * lookup tables under arbitrary names referenced by the task's
20
+ * `rekeyTransformersKey`, `removeKeysKey`, `functionTransformersKey`,
21
+ * or `conditionalTransformDefinitionsKey`.
22
+ *
23
+ * @example
24
+ * vibe2flex: {
25
+ * transformOrder: [
26
+ * { processor: 'VALUE_TRANSFORM', functionTransformersKey: 'valueTransform' },
27
+ * { processor: 'REKEY', rekeyDelete: true, rekeyKeepEmptyValues: true, rekeyTransformersKey: 'rekey' },
28
+ * { processor: 'REMOVE', removeKeysKey: 'removeKey' },
29
+ * ],
30
+ * rekey: { productName: 'name', custBrand: 'brand' },
31
+ * valueTransform: {
32
+ * itemNumber: (row) => '' + row['itemNumber'],
33
+ * },
34
+ * removeKey: ['patternReference', 'vibeOnlyProp'],
35
+ * }
36
+ */
37
+ export interface DirectionalSection {
38
+ /**
39
+ * Ordered pipeline of transforms applied to each row in this direction.
40
+ *
41
+ * Each task is dispatched by its `processor` discriminator
42
+ * (REKEY, REMOVE, VALUE_TRANSFORM, MORPH, CONDITIONAL) and reads its
43
+ * configuration from the corresponding sibling property on this section
44
+ * (e.g. a REKEY task with `rekeyTransformersKey: 'rekey'` reads
45
+ * {@link DirectionalSection.rekey}).
46
+ *
47
+ * @example
48
+ * transformOrder: [
49
+ * { processor: 'REKEY', rekeyDelete: true, rekeyKeepEmptyValues: true, rekeyTransformersKey: 'rekey' },
50
+ * { processor: 'REMOVE', removeKeysKey: 'removeKey' },
51
+ * ]
52
+ */
53
+ transformOrder?: TransformTask[];
54
+ /**
55
+ * Resolves the FlexPLM object class (vibe2flex) or VibeIQ entity class
56
+ * (flex2vibe) for a row when it cannot be derived from the row itself.
57
+ *
58
+ * Consulted by `TypeConversionUtils.getObjectClass` /
59
+ * `getEntityClassFromObject` after the explicit `flexPLMObjectClass` /
60
+ * `vibeIQEntityClass` property and before the `TypeDefaults` fallback.
61
+ *
62
+ * @example
63
+ * // vibe2flex
64
+ * getClass: () => 'LCSRevisableEntity'
65
+ * // flex2vibe
66
+ * getClass: () => 'custom-entity'
67
+ */
68
+ getClass?: (entity: Record<string, unknown>) => string;
69
+ /**
70
+ * Resolves the FlexPLM type path (vibe2flex) or VibeIQ type path
71
+ * (flex2vibe) for a row when it cannot be derived from the row itself.
72
+ *
73
+ * Consulted by `TypeConversionUtils.getObjectTypePath` /
74
+ * `getEntityTypePathFromObject` after the explicit `flexPLMTypePath` /
75
+ * `vibeIQTypePath` property and before the `TypeDefaults` fallback.
76
+ *
77
+ * @example
78
+ * // vibe2flex
79
+ * getSoftType: () => 'Revisable Entity\\Product Concept'
80
+ * // flex2vibe
81
+ * getSoftType: () => 'custom-entity:styleConcept'
82
+ */
83
+ getSoftType?: (entity: Record<string, unknown>) => string;
84
+ /**
85
+ * Lookup table consumed by REKEY tasks in {@link DirectionalSection.transformOrder}.
86
+ *
87
+ * Keyed `newKey` → `existingKey`: each entry copies (or moves, when the
88
+ * task sets `rekeyDelete: true`) the value at `existingKey` onto `newKey`.
89
+ * Referenced by `rekeyTransformersKey` on the REKEY task.
90
+ *
91
+ * @example
92
+ * rekey: {
93
+ * productName: 'name',
94
+ * custBrand: 'brand',
95
+ * custStyleNumber: 'styleNumber',
96
+ * }
97
+ */
98
+ rekey?: Record<string, unknown>;
99
+ /**
100
+ * Lookup table consumed by REMOVE tasks in {@link DirectionalSection.transformOrder}.
101
+ *
102
+ * Names of keys to delete from each row. Referenced by `removeKeysKey`
103
+ * on the REMOVE task.
104
+ *
105
+ * @example
106
+ * removeKey: ['patternReference', 'vibeOnlyProp']
107
+ */
108
+ removeKey?: string[];
109
+ /**
110
+ * Lookup table consumed by VALUE_TRANSFORM tasks in
111
+ * {@link DirectionalSection.transformOrder}.
112
+ *
113
+ * Each entry's key is the property the function's return value is written
114
+ * to on the row; the function receives the full row plus optional
115
+ * dependencies. Referenced by `functionTransformersKey` on the
116
+ * VALUE_TRANSFORM task.
117
+ *
118
+ * @example
119
+ * valueTransform: {
120
+ * itemNumber: (row) => {
121
+ * const numValue = parseInt(row['itemNumber'], 10);
122
+ * return isNaN(numValue) ? row['itemNumber'] : numValue;
123
+ * },
124
+ * }
125
+ */
126
+ valueTransform?: Record<string, (row: Record<string, unknown>, dependencies?: unknown) => unknown>;
127
+ /**
128
+ * Lookup table consumed by MORPH tasks in {@link DirectionalSection.transformOrder}.
129
+ *
130
+ * Each entry's function receives the full row plus optional dependencies
131
+ * and may rewrite/augment it in place; unlike {@link DirectionalSection.valueTransform},
132
+ * the function key is just an identifier and is not the destination
133
+ * property. Referenced by `functionTransformersKey` on the MORPH task.
134
+ *
135
+ * @example
136
+ * morphTransform: {
137
+ * morph1: (row) => {
138
+ * const val = row['productStatus'];
139
+ * if (val && Object.keys(val).length === 0) {
140
+ * delete row['productStatus'];
141
+ * }
142
+ * },
143
+ * }
144
+ */
145
+ morphTransform?: Record<string, (row: Record<string, unknown>, dependencies?: unknown) => unknown>;
146
+ /**
147
+ * Catch-all for additional pipelines and per-processor lookup tables
148
+ * referenced by sibling `*Key` pointers on tasks. Each entry should be
149
+ * one of:
150
+ * - {@link TransformTask}[] — alternate pipeline (read it by passing
151
+ * its property name as `orderKey` to `MapFileUtil.getTransformTasks`)
152
+ * - {@link RekeyTransformers} — `newKey` → `existingKey` map for REKEY
153
+ * - `string[]` — keys to delete for REMOVE
154
+ * - {@link FunctionTransformers} — Record<string, Function> for MORPH or VALUE_TRANSFORM
155
+ * - {@link TransformDefinition}[] — conditional rules for CONDITIONAL
156
+ */
157
+ [key: string]: TransformTask[] | TransformDefinition[] | RekeyTransformers | FunctionTransformers | Record<string, unknown> | string[] | ((entity: Record<string, unknown>) => string) | ((entity: Record<string, unknown>) => string[]) | ((row: Record<string, unknown>, dependencies?: unknown) => unknown) | undefined;
158
+ }
159
+ /**
160
+ * Per-entity-type configuration block, keyed in the {@link MappingFile}
161
+ * by the section key returned from
162
+ * {@link TypeConversionEntry.getMapKey} (e.g. `LCSProduct`,
163
+ * `'Exchange Rate'`).
164
+ *
165
+ * Holds direction-agnostic settings (ownership, identifier/informational
166
+ * properties, creation and image-sync gates) plus the two
167
+ * {@link DirectionalSection}s that drive the actual data transforms.
168
+ *
169
+ * @example
170
+ * LCSProduct: {
171
+ * vibeOwningKeys: ['itemNumber', 'lifecycleStage'],
172
+ * getIdentifierProperties: () => ['styleNumber'],
173
+ * vibe2flex: {
174
+ * transformOrder: [
175
+ * { processor: 'REKEY', rekeyDelete: true, rekeyKeepEmptyValues: true, rekeyTransformersKey: 'rekey' },
176
+ * ],
177
+ * rekey: { productName: 'name' },
178
+ * },
179
+ * flex2vibe: {
180
+ * transformOrder: [
181
+ * { processor: 'REKEY', rekeyDelete: true, rekeyKeepEmptyValues: true, rekeyTransformersKey: 'rekey' },
182
+ * ],
183
+ * rekey: { name: 'productName' },
184
+ * },
185
+ * }
186
+ */
187
+ export interface MappingSection {
188
+ /**
189
+ * These are the slugs of the properties that the VibeIQ system owns. So
190
+ * even if data comes in from an external source, it is ignored and the
191
+ * values are not overwritten.
192
+ *
193
+ * @example
194
+ * vibeOwningKeys: ['itemNumber', 'lifecycleStage']
195
+ */
196
+ vibeOwningKeys?: string[];
197
+ /**
198
+ * Tag identifying which identity service uniqueness pool this entity participates in.
199
+ * There can only be one entity with a given value for the property(defined by `getIdentifierProperties`) within the same uniqueness pool.
200
+ *
201
+ * @example
202
+ * uniquenessPool: 'item'
203
+ */
204
+ uniquenessPool?: string;
205
+ /**
206
+ * Gate controlling whether an inbound FlexPLM object is allowed to
207
+ * create a new VibeIQ entity. When the function returns `false`, the
208
+ * inbound row may still update an existing entity but will not create
209
+ * one. Consulted by `TypeConversionUtils.isInboundCreatableFromObject`;
210
+ * defaults to `false` when the function is absent.
211
+ *
212
+ * @example
213
+ * isInboundCreatable: () => true
214
+ */
215
+ isInboundCreatable?: (entity: Record<string, unknown>, context?: unknown) => boolean;
216
+ /**
217
+ * Gate controlling whether an outbound VibeIQ entity is allowed to
218
+ * create a new FlexPLM object. When the function returns `false`, the
219
+ * outbound publish becomes an update-only operation. Consulted by
220
+ * `TypeConversionUtils.isOutboundCreatableFromEntity`; defaults to
221
+ * `true` when the function is absent.
222
+ *
223
+ * @example
224
+ * isOutboundCreatable: () => false
225
+ */
226
+ isOutboundCreatable?: (entity: Record<string, unknown>, context?: unknown) => boolean;
227
+ /**
228
+ * Gate controlling whether image/thumbnail content should be synced
229
+ * from FlexPLM into VibeIQ for this entity type. Consulted by
230
+ * `TypeConversionUtils.syncInboundImages`; defaults to `false` when
231
+ * the function is absent. Typically aligned with whichever system
232
+ * owns creation of the entity.
233
+ *
234
+ * @example
235
+ * syncInboundImages: () => true
236
+ */
237
+ syncInboundImages?: (entity: Record<string, unknown>, context?: unknown) => boolean;
238
+ /**
239
+ * Gate controlling whether image/thumbnail content should be synced
240
+ * from VibeIQ out to FlexPLM for this entity type. Consulted by
241
+ * `TypeConversionUtils.syncOutboundImages`; defaults to `true` when
242
+ * the function is absent. Typically aligned with whichever system
243
+ * owns creation of the entity.
244
+ *
245
+ * @example
246
+ * syncOutboundImages: () => false
247
+ */
248
+ syncOutboundImages?: (entity: Record<string, unknown>, context?: unknown) => boolean;
249
+ /**
250
+ * Returns the property names that uniquely identify this entity. Used
251
+ * by `TypeConversionUtils.getIdentifierProperties` to drive lookup and
252
+ * matching when an explicit `flexPLMIdentifierProperties` /
253
+ * `vibeIQIdentifierProperties` is not present on the row, falling
254
+ * back to `TypeDefaults` when this is absent.
255
+ *
256
+ * @example
257
+ * getIdentifierProperties: () => ['itemNumber']
258
+ */
259
+ getIdentifierProperties?: (entity: Record<string, unknown>) => string[];
260
+ /**
261
+ * Returns supplemental property names that should be carried alongside
262
+ * identifiers (e.g. display names) but do not participate in identity.
263
+ * Consulted by `TypeConversionUtils.getInformationalProperties` with
264
+ * the same lookup precedence as {@link MappingSection.getIdentifierProperties}.
265
+ *
266
+ * @example
267
+ * getInformationalProperties: () => ['longName']
268
+ */
269
+ getInformationalProperties?: (entity: Record<string, unknown>) => string[];
270
+ /** Transform configuration applied when sending data from VibeIQ to FlexPLM. */
271
+ vibe2flex?: DirectionalSection;
272
+ /** Transform configuration applied when receiving data from FlexPLM into VibeIQ. */
273
+ flex2vibe?: DirectionalSection;
274
+ }
275
+ /**
276
+ * Entry in the {@link TypeConversionSection} that maps a single source
277
+ * type onto the {@link MappingSection} key that should handle it.
278
+ *
279
+ * Useful for fanning a single VibeIQ `entityType` (e.g. `'custom-entity'`)
280
+ * out to multiple sections based on the row's `typePath`.
281
+ */
282
+ export interface TypeConversionEntry {
283
+ /**
284
+ * Returns the {@link MappingFile} key whose {@link MappingSection}
285
+ * should be used to transform `entity`. Returning an empty string
286
+ * signals that no section applies and the row should be skipped.
287
+ *
288
+ * @example
289
+ * getMapKey: (entity) => {
290
+ * switch (entity['typePath']) {
291
+ * case 'custom-entity:exchangeRate': return 'Exchange Rate';
292
+ * case 'custom-entity:styleConcept': return 'Style Concept Master';
293
+ * default: return '';
294
+ * }
295
+ * }
296
+ */
297
+ getMapKey: (entity: Record<string, unknown>) => string;
298
+ }
299
+ /**
300
+ * One direction of {@link TypeConversion}, keyed by the source-system
301
+ * type:
302
+ * - In `vibe2flex`, keys are VibeIQ entity types (e.g. `'custom-entity'`,
303
+ * `'size-range-template'`) — see `TypeConversionUtils.getEntityType`.
304
+ * - In `flex2vibe`, keys are FlexPLM object classes (e.g. `LCSProduct`) —
305
+ * see `TypeConversionUtils.getObjectType`.
306
+ */
307
+ export interface TypeConversionSection {
308
+ [type: string]: TypeConversionEntry;
309
+ }
310
+ /**
311
+ * Top-level routing table that resolves a row to its
312
+ * {@link MappingSection} key for each direction.
313
+ *
314
+ * `TypeConversionUtils.getMapKey` / `getMapKeyFromObject` look up the
315
+ * row's type in the appropriate side, invoke
316
+ * {@link TypeConversionEntry.getMapKey}, and use the returned string
317
+ * as the key into the rest of the {@link MappingFile}.
318
+ *
319
+ * @example
320
+ * typeConversion: {
321
+ * vibe2flex: {
322
+ * 'custom-entity': {
323
+ * getMapKey: (entity) => {
324
+ * switch (entity['typePath']) {
325
+ * case 'custom-entity:exchangeRate': return 'Exchange Rate';
326
+ * default: return '';
327
+ * }
328
+ * },
329
+ * },
330
+ * 'size-range-template': {
331
+ * getMapKey: () => 'size-range-template',
332
+ * },
333
+ * },
334
+ * flex2vibe: {
335
+ * LCSRevisableEntity: {
336
+ * getMapKey: (object) => {
337
+ * switch (object['flexPLMTypePath']) {
338
+ * case 'Revisable Entity\\Exchange Rate': return 'Exchange Rate';
339
+ * default: return '';
340
+ * }
341
+ * },
342
+ * },
343
+ * },
344
+ * }
345
+ */
346
+ export interface TypeConversion {
347
+ /** Routes outbound VibeIQ entities to their FlexPLM mapping sections. */
348
+ vibe2flex: TypeConversionSection;
349
+ /** Routes inbound FlexPLM objects to their VibeIQ mapping sections. */
350
+ flex2vibe: TypeConversionSection;
351
+ }
352
+ /** Identifies the application and tenant that owns a {@link MappingFile}. */
353
+ export interface OrgInfo {
354
+ /** This is the identifier for the application, and is used to set the owner of the mapping file */
355
+ appIdentifier: '@vibeiq/flexplm-connector';
356
+ /** The name of the organization using the mapping file. */
357
+ orgName: string;
358
+ }
359
+ interface MappingFileBase {
360
+ /** The information about the organization using the mapping file */
361
+ orgInfo: OrgInfo;
362
+ /** The type conversion information for the mapping file. */
363
+ typeConversion: TypeConversion;
364
+ }
365
+ /**
366
+ * Full mapping file driving bidirectional sync between VibeIQ and FlexPLM
367
+ * for a single tenant.
368
+ *
369
+ * Combines the fixed {@link MappingFileBase} fields (`orgInfo`,
370
+ * `typeConversion`) with an open set of {@link MappingSection} entries
371
+ * keyed by the strings returned from
372
+ * {@link TypeConversionEntry.getMapKey} (e.g. `LCSProduct`, `LCSSKU`,
373
+ * `'Exchange Rate'`).
374
+ *
375
+ * @example
376
+ * export const mapping: MappingFile = {
377
+ * orgInfo: {
378
+ * appIdentifier: '@vibeiq/flexplm-connector',
379
+ * orgName: 'acme',
380
+ * },
381
+ * typeConversion: {
382
+ * vibe2flex: {
383
+ * 'custom-entity': {
384
+ * getMapKey: (entity) => {
385
+ * switch (entity['typePath']) {
386
+ * case 'custom-entity:exchangeRate': return 'Exchange Rate';
387
+ * default: return '';
388
+ * }
389
+ * },
390
+ * },
391
+ * 'size-range-template': {
392
+ * getMapKey: () => 'size-range-template',
393
+ * },
394
+ * },
395
+ * flex2vibe: {
396
+ * LCSRevisableEntity: {
397
+ * getMapKey: (object) => {
398
+ * switch (object['flexPLMTypePath']) {
399
+ * case 'Revisable Entity\\Exchange Rate': return 'Exchange Rate';
400
+ * default: return '';
401
+ * }
402
+ * },
403
+ * },
404
+ * },
405
+ * },
406
+ * 'Exchange Rate': {
407
+ * vibe2flex: {
408
+ * transformOrder: [
409
+ * { processor: 'REKEY', rekeyDelete: true, rekeyKeepEmptyValues: true, rekeyTransformersKey: 'rekey' },
410
+ * ],
411
+ * rekey: { exchangeRateDescription: 'name' },
412
+ * getClass: () => 'LCSRevisableEntity',
413
+ * getSoftType: () => 'Revisable Entity\\Exchange Rate',
414
+ * },
415
+ * flex2vibe: {
416
+ * transformOrder: [
417
+ * { processor: 'REKEY', rekeyDelete: true, rekeyKeepEmptyValues: true, rekeyTransformersKey: 'rekey' },
418
+ * ],
419
+ * rekey: { name: 'exchangeRateDescription' },
420
+ * getClass: () => 'custom-entity',
421
+ * getSoftType: () => 'custom-entity:exchangeRate',
422
+ * },
423
+ * },
424
+ * };
425
+ */
426
+ export type MappingFile = MappingFileBase & {
427
+ [mapKey: string]: MappingSection | MappingFileBase[keyof MappingFileBase];
428
+ };
429
+ export {};
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -32,6 +32,15 @@ export declare class BaseProcessPublishAssortment {
32
32
  skip_await?: undefined;
33
33
  }>;
34
34
  getPublishInfo(assortmentId: string, assortmentPublishChangeId: string, apcHistory: any, publisher: any): Promise<any>;
35
+ /** Gets the version number of the snapshot that was created for the publish change.
36
+ * But if no snapshot was found for the publish change, and the last snapshot was
37
+ * created before the publish change, then it returns 'after: versionNumber' where
38
+ * versionNumber is the version number of the last snapshot.
39
+ * If no snapshot was found for the publish change, and the last snapshot was
40
+ * created after the publish change, then it returns 'unknown'.
41
+ *
42
+ * @returns versionNumber or 'unknown' or 'after: versionNumber'
43
+ */
35
44
  getSnapshotVersion(assortment: any, apc: any): Promise<number | string>;
36
45
  getSeasonFederation(assortmentId: any): Promise<SeasonFederation>;
37
46
  getAssortment(assortmentId: any): Promise<any>;
@@ -82,6 +91,22 @@ export declare class BaseProcessPublishAssortment {
82
91
  private getCurrentDateString;
83
92
  getItemFamilyChanges(pcd: PublishChangeData, changeDetail: any, assortmentItemFullChangeMap: Map<string, any>, assortmentItemDeleteMap: Map<string, any>): Map<string, ItemFamilyChanges>;
84
93
  getEventsForPublishChangeData(publishChangeData: PublishChangeData): Promise<SeasonalPayload[]>;
94
+ /**Returns the events for a given ItemFamilyChanges object
95
+ *
96
+ * Cases:
97
+ * Add just family:
98
+ * Add option to family (with existing option):
99
+ * Add option to family (no options on assortment):
100
+ * Remove family and option:
101
+ * Remove only option and leave family:
102
+ * Remove one option of multiple for family:
103
+ *
104
+ * @param itemFamilyChanges
105
+ * @param assortmentId
106
+ * @param assortmentFederationId
107
+ * @param itemToFederatedIdMapping
108
+ * @returns
109
+ */
85
110
  getEventsForItemFamilyChanges(itemFamilyChanges: ItemFamilyChanges, assortmentId: string, seasonFed: SeasonFederation, itemToFederatedIdMapping: Map<string, string>): Promise<SeasonalPayload[]>;
86
111
  getProjectItem(itemFamilyChanges: ItemFamilyChanges, id: string): any;
87
112
  getSeasonalData(projectItem: any): Promise<object>;
@@ -13,7 +13,7 @@ const app_framework_1 = require("@contrail/app-framework");
13
13
  const event_short_message_status_1 = require("../util/event-short-message-status");
14
14
  class BaseProcessPublishAssortment {
15
15
  constructor(_config, _dc, _mapFileUtil) {
16
- this.TTL = 30 * 24 * 60 * 60 * 1000;
16
+ this.TTL = 30 * 24 * 60 * 60 * 1000; // 30 days
17
17
  this.cache = {
18
18
  carriedFromSeason: {}
19
19
  };
@@ -52,13 +52,14 @@ class BaseProcessPublishAssortment {
52
52
  }
53
53
  apcHistory = await this.getApcHistory(assortmentId);
54
54
  const sinceDate = await this.getSinceDate(assortmentId, assortmentPublishChangeId, apcHistory);
55
+ //Get detail information
55
56
  const assortmentPublishChange = await this.downloadAssortmentPublishChange(assortmentId, assortmentPublishChangeId);
56
57
  publisher = this.getPublisher(assortmentPublishChange);
57
58
  const changeDetail = await this.downloadHydratedChangeDetail(assortmentPublishChange);
58
59
  const assortmentBaseline = await this.downloadAssortmentBaseline(assortmentPublishChange);
59
60
  const deleteChanges = await this.getDeleteChanges(assortmentPublishChange, apcHistory, assortmentBaseline, sinceDate);
60
61
  const releasedForDevelopmentItemIds = this.getReleasedForDevelopmentItemAndFamilyIds(assortmentBaseline, deleteChanges);
61
- const itemToFederatedIdMapping = await this.getItemFederatedIds();
62
+ const itemToFederatedIdMapping = await this.getItemFederatedIds( /*allItemIds*/);
62
63
  const pcd = new publish_change_data_1.PublishChangeData(assortmentId, seasonFed, assortmentPublishChangeId, sinceDate, publisher);
63
64
  pcd.itemToFederatedIdMapping = itemToFederatedIdMapping;
64
65
  pcd.releasedForDevelopmentItemIds = releasedForDevelopmentItemIds;
@@ -94,6 +95,15 @@ class BaseProcessPublishAssortment {
94
95
  };
95
96
  return publishInfo;
96
97
  }
98
+ /** Gets the version number of the snapshot that was created for the publish change.
99
+ * But if no snapshot was found for the publish change, and the last snapshot was
100
+ * created before the publish change, then it returns 'after: versionNumber' where
101
+ * versionNumber is the version number of the last snapshot.
102
+ * If no snapshot was found for the publish change, and the last snapshot was
103
+ * created after the publish change, then it returns 'unknown'.
104
+ *
105
+ * @returns versionNumber or 'unknown' or 'after: versionNumber'
106
+ */
97
107
  async getSnapshotVersion(assortment, apc) {
98
108
  const entityReference = assortment?.createdForReference;
99
109
  const createdOnString = apc?.createdOn;
@@ -101,7 +111,7 @@ class BaseProcessPublishAssortment {
101
111
  return 'unknown';
102
112
  }
103
113
  const createdOnDate = new Date(createdOnString);
104
- createdOnDate.setMonth(createdOnDate.getMonth() - 1);
114
+ createdOnDate.setMonth(createdOnDate.getMonth() - 1); //subtract 1 month
105
115
  const createdOnStringMinus1 = createdOnDate.toISOString();
106
116
  const createdOn = 'ISGREATERTHAN ' + createdOnStringMinus1;
107
117
  const snapshots = await new sdk_1.Entities().get({
@@ -348,6 +358,7 @@ class BaseProcessPublishAssortment {
348
358
  console.info('sinceDateMilliseconds: ' + sinceDateMilliseconds);
349
359
  console.info('apcDateMilliseconds: ' + previousApcDate);
350
360
  }
361
+ //if only 1 apc, no processing needed
351
362
  if (sinceDateMilliseconds !== previousApcDate) {
352
363
  console.info('sinceDateMilliseconds !== apcDateMilliseconds');
353
364
  const currentAssortmentItemIds = this.getBaselineItemIds(assortmentBaseline);
@@ -433,12 +444,27 @@ class BaseProcessPublishAssortment {
433
444
  previousBaseline = await this.downloadAssortmentBaseline(previousApc);
434
445
  }
435
446
  const deleteIds = apc?.detail?.deletes.map(dItem => dItem?.id);
447
+ //building deletes based on previous baseline; because some APCs don't have delete data
436
448
  const deleteArray = previousBaseline?.assortmentItems.filter(aItem => deleteIds.includes(aItem?.itemId));
437
449
  console.info('deleteArray.length: ' + deleteArray.length);
438
450
  return deleteArray;
439
451
  }
440
- async getItemFederatedIds() {
452
+ async getItemFederatedIds( /*itemIds*/) {
441
453
  const itemFederatedIds = new Map();
454
+ // const expandedItemIds = itemIds.map(id => 'item:' + id);
455
+ // const fedRecords = await new Federation(this.logger).getFederationRecordsFromIdsBulk(expandedItemIds);
456
+ // for (let i = 0; i < fedRecords.length; i++) {
457
+ // const federationRecord = fedRecords[i];
458
+ // // console.log('federationRecord: ' + JSON.stringify(federationRecord));
459
+ // // console.log('federationRecord.reference: ' + federationRecord.reference);
460
+ // // console.log('federationRecord.mappedReference: ' + federationRecord.mappedReference);
461
+ // let vibeId = federationRecord.reference;
462
+ // if (vibeId && vibeId.startsWith('item:')) {
463
+ // vibeId = vibeId.substring(5);
464
+ // }
465
+ // itemFederatedIds.set(vibeId, federationRecord.mappedReference);
466
+ // }
467
+ // console.log('itemFederatedIds: ' + JSON.stringify(Object.fromEntries(itemFederatedIds)));
442
468
  return itemFederatedIds;
443
469
  }
444
470
  getFullChangeAssortmentMap(fullChange) {
@@ -565,7 +591,7 @@ class BaseProcessPublishAssortment {
565
591
  case 'vibeiqfile-dontsendtoflexplm':
566
592
  return this.handleVibeIQFile(events, eventType, sendMode);
567
593
  default:
568
- return {};
594
+ return {}; // Or handle other cases as required
569
595
  }
570
596
  }
571
597
  async sendToFlexPLM(events, eventType) {
@@ -735,7 +761,7 @@ class BaseProcessPublishAssortment {
735
761
  else {
736
762
  ifc.colorDeletes.push(itemId);
737
763
  }
738
- }
764
+ } //End deletes for loop
739
765
  if (app_framework_1.Logger.isDebugOn()) {
740
766
  console.debug('returning size: ' + itemFamilyChanges.size);
741
767
  for (const [key, value] of itemFamilyChanges) {
@@ -755,6 +781,22 @@ class BaseProcessPublishAssortment {
755
781
  }
756
782
  return seasonalPayloads;
757
783
  }
784
+ /**Returns the events for a given ItemFamilyChanges object
785
+ *
786
+ * Cases:
787
+ * Add just family:
788
+ * Add option to family (with existing option):
789
+ * Add option to family (no options on assortment):
790
+ * Remove family and option:
791
+ * Remove only option and leave family:
792
+ * Remove one option of multiple for family:
793
+ *
794
+ * @param itemFamilyChanges
795
+ * @param assortmentId
796
+ * @param assortmentFederationId
797
+ * @param itemToFederatedIdMapping
798
+ * @returns
799
+ */
758
800
  async getEventsForItemFamilyChanges(itemFamilyChanges, assortmentId, seasonFed, itemToFederatedIdMapping) {
759
801
  console.info('getEventsForItemFamilyChanges()');
760
802
  const events = [];
@@ -764,7 +806,10 @@ class BaseProcessPublishAssortment {
764
806
  const familyAssortmentItem = itemFamilyChanges.familyAdd || itemFamilyChanges.familyUpdate || itemFamilyChanges.familyDelete
765
807
  || itemFamilyChanges.colorAdds.length > 0 || itemFamilyChanges.colorUpdates.length > 0 || itemFamilyChanges.colorDeletes.length > 0
766
808
  || (projectItem && this.updatedSinceDate(projectItem, itemFamilyChanges.sinceDate));
809
+ //familyItemRemoved is used when adding the first option to an assortment
810
+ //and will have updates for the family item.
767
811
  if (familyAssortmentItem) {
812
+ //Product-season add
768
813
  const entityReference = itemFamilyChanges.itemFamilyId;
769
814
  const prodEntityData = (itemFamilyChanges.assortmentItemFullChangeMap.has(entityReference))
770
815
  ? itemFamilyChanges.assortmentItemFullChangeMap.get(entityReference)?.item
@@ -790,6 +835,9 @@ class BaseProcessPublishAssortment {
790
835
  }
791
836
  events.push(psUpsert);
792
837
  }
838
+ //colorway-season adds
839
+ //colorAdds
840
+ //colorUpdates
793
841
  const colorwayChanges = [];
794
842
  colorwayChanges.push(...itemFamilyChanges.colorAdds);
795
843
  colorwayChanges.push(...itemFamilyChanges.colorUpdates);
@@ -830,6 +878,9 @@ class BaseProcessPublishAssortment {
830
878
  const aItem = itemFamilyChanges?.assortmentItemFullChangeMap.get(id);
831
879
  projectItem = aItem?.projectItem;
832
880
  }
881
+ /////////////////////////////////////////////////////////////////////////////
882
+ //Get from option assortmentItem because family not in assortmentItemFullChangeMap:start
883
+ /////////////////////////////////////////////////////////////////////////////
833
884
  if (id === itemFamilyChanges.itemFamilyId && Object.keys(projectItem).length == 0) {
834
885
  for (const asstItem of itemFamilyChanges.assortmentItemFullChangeMap.values()) {
835
886
  if (asstItem?.familyProjectItem) {
@@ -838,6 +889,9 @@ class BaseProcessPublishAssortment {
838
889
  }
839
890
  }
840
891
  }
892
+ /////////////////////////////////////////////////////////////////////////////
893
+ //Get from option assortmentItem because family not in assortmentItemFullChangeMap:end
894
+ /////////////////////////////////////////////////////////////////////////////
841
895
  return projectItem;
842
896
  }
843
897
  async getSeasonalData(projectItem) {