@contrail/flexplm 1.3.0 → 1.3.1-alpha.585afc1

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 (149) 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 +89 -42
  19. package/lib/entity-processor/base-entity-processor.js +438 -385
  20. package/lib/entity-processor/base-entity-processor.spec.d.ts +1 -1
  21. package/lib/entity-processor/base-entity-processor.spec.js +398 -397
  22. package/lib/flexplm-request.d.ts +3 -3
  23. package/lib/flexplm-request.js +34 -34
  24. package/lib/flexplm-utils.d.ts +5 -5
  25. package/lib/flexplm-utils.js +33 -33
  26. package/lib/flexplm-utils.spec.d.ts +1 -1
  27. package/lib/flexplm-utils.spec.js +26 -26
  28. package/lib/index.d.ts +23 -22
  29. package/lib/index.js +39 -38
  30. package/lib/interfaces/interfaces.d.ts +105 -105
  31. package/lib/interfaces/interfaces.js +2 -2
  32. package/lib/interfaces/item-family-changes.d.ts +20 -20
  33. package/lib/interfaces/item-family-changes.js +56 -56
  34. package/lib/interfaces/mapping-file.d.ts +429 -0
  35. package/lib/interfaces/mapping-file.js +2 -0
  36. package/lib/interfaces/publish-change-data.d.ts +19 -19
  37. package/lib/interfaces/publish-change-data.js +32 -32
  38. package/lib/publish/base-process-publish-assortment-callback.d.ts +9 -9
  39. package/lib/publish/base-process-publish-assortment-callback.js +38 -38
  40. package/lib/publish/base-process-publish-assortment.d.ts +118 -93
  41. package/lib/publish/base-process-publish-assortment.js +998 -944
  42. package/lib/publish/base-process-publish-assortment.spec.d.ts +1 -1
  43. package/lib/publish/base-process-publish-assortment.spec.js +1688 -1670
  44. package/lib/publish/mockData.d.ts +1389 -1389
  45. package/lib/publish/mockData.js +4524 -4519
  46. package/lib/transform/identifier-conversion-spec-mockData.js +472 -444
  47. package/lib/transform/identifier-conversion.d.ts +51 -15
  48. package/lib/transform/identifier-conversion.js +248 -212
  49. package/lib/transform/identifier-conversion.spec.d.ts +1 -1
  50. package/lib/transform/identifier-conversion.spec.js +343 -339
  51. package/lib/util/config-defaults.d.ts +8 -8
  52. package/lib/util/config-defaults.js +88 -85
  53. package/lib/util/config-defaults.spec.d.ts +1 -1
  54. package/lib/util/config-defaults.spec.js +302 -293
  55. package/lib/util/data-converter-spec-mockData.js +219 -205
  56. package/lib/util/data-converter.d.ts +136 -39
  57. package/lib/util/data-converter.js +718 -592
  58. package/lib/util/data-converter.spec.d.ts +1 -1
  59. package/lib/util/data-converter.spec.js +906 -904
  60. package/lib/util/error-response-object.d.ts +9 -4
  61. package/lib/util/error-response-object.js +54 -47
  62. package/lib/util/error-response-object.spec.d.ts +1 -1
  63. package/lib/util/error-response-object.spec.js +99 -99
  64. package/lib/util/event-short-message-status.d.ts +19 -19
  65. package/lib/util/event-short-message-status.js +24 -23
  66. package/lib/util/federation.d.ts +15 -15
  67. package/lib/util/federation.js +157 -149
  68. package/lib/util/flexplm-connect.d.ts +29 -22
  69. package/lib/util/flexplm-connect.js +190 -176
  70. package/lib/util/flexplm-connect.spec.d.ts +1 -1
  71. package/lib/util/flexplm-connect.spec.js +88 -88
  72. package/lib/util/logger-config.d.ts +1 -1
  73. package/lib/util/logger-config.js +27 -26
  74. package/lib/util/map-util-spec-mockData.js +219 -205
  75. package/lib/util/map-utils.d.ts +33 -6
  76. package/lib/util/map-utils.js +42 -15
  77. package/lib/util/map-utils.spec.d.ts +1 -1
  78. package/lib/util/map-utils.spec.js +89 -89
  79. package/lib/util/mockData.d.ts +80 -80
  80. package/lib/util/mockData.js +103 -103
  81. package/lib/util/thumbnail-util.d.ts +55 -34
  82. package/lib/util/thumbnail-util.js +242 -215
  83. package/lib/util/thumbnail-util.spec.d.ts +1 -1
  84. package/lib/util/thumbnail-util.spec.js +440 -434
  85. package/lib/util/type-conversion-utils-spec-mockData.js +259 -259
  86. package/lib/util/type-conversion-utils.d.ts +163 -23
  87. package/lib/util/type-conversion-utils.js +408 -265
  88. package/lib/util/type-conversion-utils.spec.d.ts +1 -1
  89. package/lib/util/type-conversion-utils.spec.js +868 -868
  90. package/lib/util/type-defaults.d.ts +74 -16
  91. package/lib/util/type-defaults.js +279 -221
  92. package/lib/util/type-defaults.spec.d.ts +1 -1
  93. package/lib/util/type-defaults.spec.js +516 -516
  94. package/lib/util/type-utils.d.ts +34 -13
  95. package/lib/util/type-utils.js +137 -114
  96. package/lib/util/type-utils.spec.d.ts +1 -1
  97. package/lib/util/type-utils.spec.js +192 -190
  98. package/package.json +21 -6
  99. package/scripts/copy-template.js +10 -0
  100. package/.claude/settings.local.json +0 -8
  101. package/.github/pull_request_template.md +0 -31
  102. package/.github/workflows/flexplm-lib.yml +0 -27
  103. package/.github/workflows/publish-to-npm.yml +0 -124
  104. package/CHANGELOG.md +0 -32
  105. package/publish.bat +0 -5
  106. package/publish.sh +0 -5
  107. package/src/entity-processor/base-entity-processor.spec.ts +0 -460
  108. package/src/entity-processor/base-entity-processor.ts +0 -515
  109. package/src/flexplm-request.ts +0 -28
  110. package/src/flexplm-utils.spec.ts +0 -27
  111. package/src/flexplm-utils.ts +0 -29
  112. package/src/index.ts +0 -22
  113. package/src/interfaces/interfaces.ts +0 -122
  114. package/src/interfaces/item-family-changes.ts +0 -67
  115. package/src/interfaces/publish-change-data.ts +0 -43
  116. package/src/publish/base-process-publish-assortment-callback.ts +0 -50
  117. package/src/publish/base-process-publish-assortment.spec.ts +0 -1992
  118. package/src/publish/base-process-publish-assortment.ts +0 -1134
  119. package/src/publish/mockData.ts +0 -4561
  120. package/src/transform/identifier-conversion-spec-mockData.ts +0 -496
  121. package/src/transform/identifier-conversion.spec.ts +0 -354
  122. package/src/transform/identifier-conversion.ts +0 -282
  123. package/src/util/config-defaults.spec.ts +0 -350
  124. package/src/util/config-defaults.ts +0 -93
  125. package/src/util/data-converter-spec-mockData.ts +0 -231
  126. package/src/util/data-converter.spec.ts +0 -1041
  127. package/src/util/data-converter.ts +0 -762
  128. package/src/util/error-response-object.spec.ts +0 -116
  129. package/src/util/error-response-object.ts +0 -50
  130. package/src/util/event-short-message-status.ts +0 -22
  131. package/src/util/federation.ts +0 -172
  132. package/src/util/flexplm-connect.spec.ts +0 -132
  133. package/src/util/flexplm-connect.ts +0 -208
  134. package/src/util/logger-config.ts +0 -20
  135. package/src/util/map-util-spec-mockData.ts +0 -231
  136. package/src/util/map-utils.spec.ts +0 -103
  137. package/src/util/map-utils.ts +0 -41
  138. package/src/util/mockData.ts +0 -101
  139. package/src/util/thumbnail-util.spec.ts +0 -508
  140. package/src/util/thumbnail-util.ts +0 -272
  141. package/src/util/type-conversion-utils-spec-mockData.ts +0 -271
  142. package/src/util/type-conversion-utils.spec.ts +0 -968
  143. package/src/util/type-conversion-utils.ts +0 -460
  144. package/src/util/type-defaults.spec.ts +0 -669
  145. package/src/util/type-defaults.ts +0 -281
  146. package/src/util/type-utils.spec.ts +0 -227
  147. package/src/util/type-utils.ts +0 -144
  148. package/tsconfig.json +0 -29
  149. package/tslint.json +0 -57
@@ -1,944 +1,998 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.BaseProcessPublishAssortment = void 0;
4
- const sdk_1 = require("@contrail/sdk");
5
- const flexplm_connect_1 = require("../util/flexplm-connect");
6
- const item_family_changes_1 = require("../interfaces/item-family-changes");
7
- const publish_change_data_1 = require("../interfaces/publish-change-data");
8
- const map_utils_1 = require("../util/map-utils");
9
- const type_conversion_utils_1 = require("../util/type-conversion-utils");
10
- const fsPromise = require("fs/promises");
11
- const path = require("path");
12
- const app_framework_1 = require("@contrail/app-framework");
13
- const event_short_message_status_1 = require("../util/event-short-message-status");
14
- class BaseProcessPublishAssortment {
15
- constructor(_config, _dc, _mapFileUtil) {
16
- this.TTL = 30 * 24 * 60 * 60 * 1000;
17
- this.cache = {
18
- carriedFromSeason: {}
19
- };
20
- this.config = _config;
21
- this.dc = _dc;
22
- this.mapFileUtil = _mapFileUtil;
23
- this.transformMapFile = this.config?.transformMapFile;
24
- this.orgSlug = this.config?.orgSlug || 'unset-orgSlug';
25
- }
26
- async process(event) {
27
- const assortmentPublishChangeId = event.assortmentPublishChangeId;
28
- let apcHistory;
29
- let publisher;
30
- const assortmentId = event.assortmentId;
31
- try {
32
- console.info('process-start!');
33
- let seasonFed;
34
- try {
35
- seasonFed = await this.getSeasonFederation(assortmentId);
36
- }
37
- catch (e) {
38
- const message = e.message;
39
- const eventStatus = (message.includes(BaseProcessPublishAssortment.ASSORTMENT_NOT_PUBLISHABLE)) ? event_short_message_status_1.EventShortMessageStatus.NOT_PUBLISHABLE :
40
- (message.startsWith(BaseProcessPublishAssortment.ASSORTMENT_NO_FED_INFO)) ? event_short_message_status_1.EventShortMessageStatus.NO_FEDERATION_INFO :
41
- event_short_message_status_1.EventShortMessageStatus.FAILURE;
42
- const processStatus = 'BaseProcessPublishAssortment: ' + eventStatus
43
- + ': assortmentId: ' + event.assortmentId + ': assortmentPublishChangeId: ' + event.assortmentPublishChangeId
44
- + ', orgSlug: ' + this.orgSlug;
45
- console.log(JSON.stringify(processStatus));
46
- console.log(message);
47
- const output = {
48
- results: { message, event },
49
- skip_await: true
50
- };
51
- return output;
52
- }
53
- apcHistory = await this.getApcHistory(assortmentId);
54
- const sinceDate = await this.getSinceDate(assortmentId, assortmentPublishChangeId, apcHistory);
55
- const assortmentPublishChange = await this.downloadAssortmentPublishChange(assortmentId, assortmentPublishChangeId);
56
- publisher = this.getPublisher(assortmentPublishChange);
57
- const changeDetail = await this.downloadHydratedChangeDetail(assortmentPublishChange);
58
- const assortmentBaseline = await this.downloadAssortmentBaseline(assortmentPublishChange);
59
- const deleteChanges = await this.getDeleteChanges(assortmentPublishChange, apcHistory, assortmentBaseline, sinceDate);
60
- const releasedForDevelopmentItemIds = this.getReleasedForDevelopmentItemAndFamilyIds(assortmentBaseline, deleteChanges);
61
- const itemToFederatedIdMapping = await this.getItemFederatedIds();
62
- const pcd = new publish_change_data_1.PublishChangeData(assortmentId, seasonFed, assortmentPublishChangeId, sinceDate, publisher);
63
- pcd.itemToFederatedIdMapping = itemToFederatedIdMapping;
64
- pcd.releasedForDevelopmentItemIds = releasedForDevelopmentItemIds;
65
- const output = await this.processPublish(pcd, changeDetail, assortmentBaseline, deleteChanges);
66
- output['publishInfo'] = await this.getPublishInfo(assortmentId, assortmentPublishChangeId, apcHistory, publisher);
67
- console.info('process-end');
68
- return output;
69
- }
70
- catch (e) {
71
- try {
72
- const publishInfo = await this.getPublishInfo(assortmentId, assortmentPublishChangeId, apcHistory, publisher);
73
- e.publishInfo = publishInfo;
74
- }
75
- catch (e2) {
76
- console.log('catch e2: ' + e2.message);
77
- }
78
- console.log('catch e: ' + e.message);
79
- throw e;
80
- }
81
- }
82
- async getPublishInfo(assortmentId, assortmentPublishChangeId, apcHistory, publisher) {
83
- const apc = apcHistory?.find(apc => apc?.id === assortmentPublishChangeId) || {};
84
- const assortment = await this.getAssortment(assortmentId);
85
- const assortmentName = assortment?.name;
86
- const versionHistoryNumber = await this.getSnapshotVersion(assortment, apc);
87
- const publishInfo = {
88
- assortmentName,
89
- versionName: apc?.versionName,
90
- versionComments: apc?.versionComments,
91
- publishDate: apc?.createdOn,
92
- publisher,
93
- versionHistoryNumber
94
- };
95
- return publishInfo;
96
- }
97
- async getSnapshotVersion(assortment, apc) {
98
- const entityReference = assortment?.createdForReference;
99
- const createdOnString = apc?.createdOn;
100
- if (!entityReference || !createdOnString || !apc?.id) {
101
- return 'unknown';
102
- }
103
- const createdOnDate = new Date(createdOnString);
104
- createdOnDate.setMonth(createdOnDate.getMonth() - 1);
105
- const createdOnStringMinus1 = createdOnDate.toISOString();
106
- const createdOn = 'ISGREATERTHAN ' + createdOnStringMinus1;
107
- const snapshots = await new sdk_1.Entities().get({
108
- entityName: 'entity-snapshot',
109
- criteria: {
110
- entityReference,
111
- createdOn
112
- }
113
- });
114
- const assortmentPublishChangeId = apc?.id;
115
- const snapshot = snapshots.find(snap => snap?.assortmentChangeId === assortmentPublishChangeId);
116
- let snapshotVersion = 'unknown';
117
- if (snapshot && snapshot.versionNumber) {
118
- snapshotVersion = snapshot.versionNumber;
119
- }
120
- else if (!snapshot && snapshots.length > 0) {
121
- const createdOnTime = new Date(createdOnString).getTime();
122
- const lastSnapshot = snapshots[snapshots.length - 1];
123
- if (lastSnapshot?.createdOn && createdOnTime > new Date(lastSnapshot.createdOn).getTime()) {
124
- snapshotVersion = 'after: ' + lastSnapshot.versionNumber;
125
- }
126
- }
127
- return snapshotVersion;
128
- }
129
- async getSeasonFederation(assortmentId) {
130
- const assortment = await this.getAssortment(assortmentId);
131
- console.error('assortment-name: ' + assortment?.name);
132
- const publishToFlexPLM = assortment?.publishToFlexPLM;
133
- if (!publishToFlexPLM) {
134
- throw new Error(BaseProcessPublishAssortment.ASSORTMENT_NOT_PUBLISHABLE);
135
- }
136
- const assortmentObj = await this.dc.getFlexPLMObjectData(assortment, [], true);
137
- let seasonObj = {
138
- entityReference: 'assortment:' + assortmentId,
139
- objectClass: 'LCSSeason',
140
- };
141
- const identifierKeys = await type_conversion_utils_1.TypeConversionUtils.getIdentifierProperties(this.transformMapFile, this.mapFileUtil, assortmentObj);
142
- const informationKeys = await type_conversion_utils_1.TypeConversionUtils.getInformationalProperties(this.transformMapFile, this.mapFileUtil, assortmentObj);
143
- for (const key of identifierKeys.concat(informationKeys)) {
144
- if (assortmentObj[key]) {
145
- seasonObj[key] = assortmentObj[key];
146
- }
147
- }
148
- const objectKeys = Object.keys(seasonObj);
149
- const hasAllIdentifiers = identifierKeys.every(key => objectKeys.includes(key));
150
- if (!hasAllIdentifiers) {
151
- throw new Error(BaseProcessPublishAssortment.ASSORTMENT_NO_FED_INFO + identifierKeys);
152
- }
153
- seasonObj = await map_utils_1.MapUtil.applyTransformMap(this.transformMapFile, this.mapFileUtil, seasonObj, 'LCSSeason', 'vibe2flex');
154
- return seasonObj;
155
- }
156
- async getAssortment(assortmentId) {
157
- if (this.assortment != undefined) {
158
- return this.assortment;
159
- }
160
- this.assortment = await new sdk_1.Entities().get({
161
- entityName: 'assortment',
162
- id: assortmentId
163
- });
164
- return this.assortment;
165
- }
166
- async getSinceDate(assortmentId, assortmentPublishChangeId, apcHistory) {
167
- let sinceDate = null;
168
- let pastPublishCount = 1;
169
- if (this.config?.sinceDate) {
170
- const sinceDateString = '' + this.config?.sinceDate;
171
- if (sinceDateString && sinceDateString.indexOf('-') > 0) {
172
- const sinceDateParts = sinceDateString.split('-');
173
- if (sinceDateParts.length == 3) {
174
- const year = parseInt(sinceDateParts[0]);
175
- const month = parseInt(sinceDateParts[1]) - 1;
176
- const day = parseInt(sinceDateParts[2]);
177
- const minutes = 0 - new Date().getTimezoneOffset();
178
- sinceDate = this.getSinceDateFromAPCSpecificDate(apcHistory, assortmentPublishChangeId, new Date(year, month, day, 0, minutes, 0));
179
- }
180
- }
181
- else if (sinceDateString && sinceDateString.indexOf(':') > 0) {
182
- const sinceDateParts = sinceDateString.split(':');
183
- const type = sinceDateParts[0];
184
- const count = parseInt(sinceDateParts[1]);
185
- if ('numberofdays' == type.toLocaleLowerCase()) {
186
- sinceDate = this.getSinceDateDaysPrevious(apcHistory, assortmentPublishChangeId, count);
187
- }
188
- else if ('numberofpublishes' == type.toLocaleLowerCase()) {
189
- pastPublishCount = count;
190
- }
191
- }
192
- }
193
- if (sinceDate == null) {
194
- sinceDate = this.getSinceDateFromAPCs(apcHistory, assortmentPublishChangeId, pastPublishCount);
195
- }
196
- if (sinceDate == null) {
197
- throw new Error('Couldnt set sinceDate');
198
- }
199
- console.info('getSinceDate: ' + sinceDate);
200
- return sinceDate;
201
- }
202
- async getApcHistory(assortmentId) {
203
- const resource = `/assortments/${assortmentId}/history/`;
204
- return sdk_1.Request.request(resource, {
205
- method: 'GET',
206
- headers: {
207
- Accept: 'application/json',
208
- 'Content-Type': 'application/json'
209
- }
210
- });
211
- }
212
- updatedSinceDate(entity, sinceDate) {
213
- const updatedOn = entity?.updatedOn;
214
- return (updatedOn && new Date(updatedOn) > sinceDate);
215
- }
216
- getSinceDateFromAPCSpecificDate(apcHistory, assortmentPublishChangeId, specificDate) {
217
- const apc = apcHistory.find(apc => apc?.id === assortmentPublishChangeId);
218
- const apcDate = new Date(apc?.createdOn);
219
- let afterDate = apcDate;
220
- let beforeDate = new Date(0);
221
- const sinceDate = specificDate;
222
- for (const tempapc of apcHistory) {
223
- if (assortmentPublishChangeId !== tempapc?.id) {
224
- const tempDate = new Date(tempapc?.createdOn);
225
- if (tempDate < apcDate) {
226
- if (tempDate > sinceDate && tempDate < afterDate) {
227
- afterDate = tempDate;
228
- }
229
- else if (tempDate < sinceDate && tempDate > beforeDate) {
230
- beforeDate = tempDate;
231
- }
232
- }
233
- }
234
- }
235
- const selectedDate = (beforeDate.getTime() !== 0) ? beforeDate : afterDate;
236
- return selectedDate;
237
- }
238
- getSinceDateDaysPrevious(apcHistory, assortmentPublishChangeId, daysBack) {
239
- const apc = apcHistory.find(apc => apc?.id === assortmentPublishChangeId);
240
- const apcDate = new Date(apc?.createdOn);
241
- let afterDate = apcDate;
242
- let beforeDate = new Date(0);
243
- const sinceDate = new Date(apcDate.getUTCFullYear(), apcDate.getUTCMonth(), apcDate.getUTCDate() - daysBack, 0, (0 - apcDate.getTimezoneOffset()), 0);
244
- for (const tempapc of apcHistory) {
245
- if (assortmentPublishChangeId !== tempapc?.id) {
246
- const tempDate = new Date(tempapc?.createdOn);
247
- if (tempDate < apcDate) {
248
- if (tempDate > sinceDate && tempDate < afterDate) {
249
- afterDate = tempDate;
250
- }
251
- else if (tempDate < sinceDate && tempDate > beforeDate) {
252
- beforeDate = tempDate;
253
- }
254
- }
255
- }
256
- }
257
- const selectedDate = (beforeDate.getTime() !== 0) ? beforeDate : afterDate;
258
- return selectedDate;
259
- }
260
- getSinceDateFromAPCs(apcHistory, assortmentPublishChangeId, pastPublishCount = 1) {
261
- if (apcHistory.length === 1) {
262
- return new Date(0);
263
- }
264
- const apcIndex = apcHistory.findIndex(apc => apc.id === assortmentPublishChangeId);
265
- if (apcIndex === 0) {
266
- return new Date(0);
267
- }
268
- const apc = apcHistory[apcIndex];
269
- const apcDate = new Date(apc?.createdOn);
270
- let sinceDate = new Date(0);
271
- const dateArray = [];
272
- for (const tempapc of apcHistory) {
273
- if (assortmentPublishChangeId !== tempapc?.id) {
274
- const tempDate = new Date(tempapc?.createdOn);
275
- if (tempDate < apcDate) {
276
- dateArray.push(tempDate);
277
- }
278
- }
279
- }
280
- dateArray.sort((a, b) => { return Date.parse(a) - Date.parse(b); });
281
- const arrIndex = (dateArray.length - pastPublishCount > 0) ? dateArray.length - pastPublishCount : 0;
282
- sinceDate = dateArray[arrIndex];
283
- return sinceDate;
284
- }
285
- getPublisher(assortmentPublishChange) {
286
- const createdBy = assortmentPublishChange?.createdBy;
287
- const publisher = {};
288
- const includeKeys = ['email', 'firstName', 'isSsoUser', 'lastName'];
289
- for (const [key, value] of Object.entries(createdBy)) {
290
- if (includeKeys.includes(key)) {
291
- publisher[key] = value;
292
- }
293
- }
294
- return publisher;
295
- }
296
- async downloadAssortmentPublishChange(assortmentId, assortmentPublishChangeId) {
297
- const resourceString = '/assortments/' + assortmentId + '/history/' + assortmentPublishChangeId + '?relations=createdBy';
298
- return await sdk_1.Request.request(resourceString, {
299
- method: 'GET',
300
- Headers: {
301
- Accept: 'application/json',
302
- 'Content-Type': 'application/json'
303
- }
304
- });
305
- }
306
- async downloadHydratedChangeDetail(assortmentPublishChange) {
307
- try {
308
- console.info('downloadHydratedChangeDetail-start');
309
- const link = assortmentPublishChange?.hydratedDetailDownloadLink;
310
- const response = await fetch(link);
311
- const data = await response.json();
312
- return data;
313
- }
314
- catch (e) {
315
- console.log('Error hydratedDetailDownloadLink: ' + e.message);
316
- }
317
- return undefined;
318
- }
319
- async downloadAssortmentBaseline(assortmentPublishChange) {
320
- console.info('downloadAssortmentBaseline-start');
321
- try {
322
- const link = assortmentPublishChange?.assortmentBaselineDownloadLink;
323
- const response = await fetch(link);
324
- const data = await response.json();
325
- return data;
326
- }
327
- catch (e) {
328
- console.log('Error assortmentBaselineDownloadLink: ' + e.message);
329
- }
330
- return undefined;
331
- }
332
- async getDeleteChanges(assortmentPublishChange, apcHistory, assortmentBaseline, sinceDate) {
333
- console.info('getDeleteChanges(): ' + assortmentPublishChange?.id);
334
- console.info(sinceDate);
335
- const currentAPCIndex = apcHistory.findIndex(pc => pc?.id === assortmentPublishChange?.id);
336
- console.info(' currentAPCIndex: ' + currentAPCIndex);
337
- if (currentAPCIndex == 0) {
338
- return [];
339
- }
340
- const previousApcIndex = (currentAPCIndex > 0) ? currentAPCIndex - 1 : 0;
341
- const apcCreatedOn = apcHistory[previousApcIndex]?.createdOn;
342
- const apcDeletes = await this.downloadDeleteChanges(assortmentPublishChange);
343
- let previousApcDate = Date.parse(apcCreatedOn);
344
- const sinceDateMilliseconds = sinceDate.getTime();
345
- if (app_framework_1.Logger.isInfoOn()) {
346
- console.info('apcCreatedOn: ' + apcCreatedOn);
347
- console.info('sinceDate: ' + sinceDate);
348
- console.info('sinceDateMilliseconds: ' + sinceDateMilliseconds);
349
- console.info('apcDateMilliseconds: ' + previousApcDate);
350
- }
351
- if (sinceDateMilliseconds !== previousApcDate) {
352
- console.info('sinceDateMilliseconds !== apcDateMilliseconds');
353
- const currentAssortmentItemIds = this.getBaselineItemIds(assortmentBaseline);
354
- const deleteIds = (apcDeletes.length === 0)
355
- ? []
356
- : apcDeletes.map(item => item?.itemId);
357
- for (let i = currentAPCIndex - 1; i > 0; i--) {
358
- const workingAPC = apcHistory[i];
359
- previousApcDate = Date.parse(workingAPC?.createdOn);
360
- if (sinceDateMilliseconds === previousApcDate) {
361
- break;
362
- }
363
- if (workingAPC?.deletes > 0) {
364
- const previousApc = (i > 0)
365
- ? apcHistory[i - 1]
366
- : undefined;
367
- const deleteChanges = await this.buildDeleteChanges(workingAPC, previousApc);
368
- console.info('checking deleteChanges');
369
- for (const dItem of deleteChanges) {
370
- const dItemId = dItem?.itemId;
371
- console.info(dItemId);
372
- if (!currentAssortmentItemIds.includes(dItemId) && !deleteIds.includes(dItemId)) {
373
- console.info('adding');
374
- deleteIds.push(dItemId);
375
- apcDeletes.push(dItem);
376
- }
377
- }
378
- }
379
- }
380
- console.info('getDeleteChanges()-currentAssortmentItemIds: ' + currentAssortmentItemIds);
381
- console.info('getDeleteChanges()-deleteIds: ' + deleteIds);
382
- }
383
- return apcDeletes;
384
- }
385
- getBaselineItemIds(assortmentBaseline) {
386
- const itemIds = [];
387
- const assortmentItemsArray = assortmentBaseline?.assortmentItems;
388
- for (const aItem of assortmentItemsArray) {
389
- itemIds.push(aItem?.itemId);
390
- }
391
- return itemIds;
392
- }
393
- async downloadDeleteChanges(apc) {
394
- console.info('deleteDataDownloadLink-start');
395
- try {
396
- const link = apc?.deleteDataDownloadLink;
397
- const response = await fetch(link);
398
- const data = await response.json();
399
- return data;
400
- }
401
- catch (e) {
402
- console.log('Error deleteDataDownloadLink: ' + e.message);
403
- }
404
- return undefined;
405
- }
406
- async buildDeleteChanges(apc, previousApc) {
407
- console.info('buildDeleteChanges()');
408
- if (apc?.deleteDataDownloadLink) {
409
- const deleteChanges = await this.downloadDeleteChanges(apc);
410
- if (deleteChanges) {
411
- return deleteChanges;
412
- }
413
- }
414
- console.info('pulling down full APC');
415
- apc = await this.downloadAssortmentPublishChange(apc?.assortmentId, apc?.id);
416
- if (apc?.deleteDataDownloadLink) {
417
- const deleteChanges = await this.downloadDeleteChanges(apc);
418
- if (deleteChanges) {
419
- return deleteChanges;
420
- }
421
- }
422
- if (!previousApc) {
423
- throw new Error(BaseProcessPublishAssortment.NOT_ABLE_TO_PROCESS_DELETE_CHANGES);
424
- }
425
- let previousBaseline;
426
- console.info('check previousApc');
427
- if (previousApc?.assortmentBaselineDownloadLink) {
428
- previousBaseline = await this.downloadAssortmentBaseline(previousApc);
429
- }
430
- else {
431
- console.info('previousApc pulling down full APC');
432
- previousApc = await this.downloadAssortmentPublishChange(previousApc?.assortmentId, previousApc?.id);
433
- previousBaseline = await this.downloadAssortmentBaseline(previousApc);
434
- }
435
- const deleteIds = apc?.detail?.deletes.map(dItem => dItem?.id);
436
- const deleteArray = previousBaseline?.assortmentItems.filter(aItem => deleteIds.includes(aItem?.itemId));
437
- console.info('deleteArray.length: ' + deleteArray.length);
438
- return deleteArray;
439
- }
440
- async getItemFederatedIds() {
441
- const itemFederatedIds = new Map();
442
- return itemFederatedIds;
443
- }
444
- getFullChangeAssortmentMap(fullChange) {
445
- const assortmentItemsArray = fullChange?.assortmentItems || [];
446
- const assortmentItemsMap = new Map();
447
- for (const aItem of assortmentItemsArray) {
448
- const itemId = aItem?.itemId;
449
- assortmentItemsMap.set(itemId, aItem);
450
- }
451
- return assortmentItemsMap;
452
- }
453
- getDeleteChangesAssortmentMap(deleteChanges) {
454
- const deleteItems = new Map();
455
- for (const dItem of deleteChanges) {
456
- deleteItems.set(dItem?.itemId, dItem);
457
- }
458
- return deleteItems;
459
- }
460
- getReleasedForDevelopmentItemAndFamilyIds(fullChange, deleteChanges) {
461
- console.info('getReleasedForDevelopmentItemAndFamilyIds');
462
- const releasedForDevelopmentItemIds = [];
463
- const itemFamilySet = new Set();
464
- const assortmentItemsArray = fullChange?.assortmentItems;
465
- for (const aItem of assortmentItemsArray) {
466
- const meetsCriteria = this.meetsCriteria(aItem);
467
- const item = aItem?.item;
468
- const itemId = item?.id;
469
- const itemFamilyId = item?.itemFamilyId;
470
- if (meetsCriteria) {
471
- releasedForDevelopmentItemIds.push(itemId);
472
- if (!itemFamilySet.has(itemFamilyId) && itemId !== itemFamilyId) {
473
- const familyItem = item?.itemFamily;
474
- const familyItemMeetsCriteria = this.meetsCriteria({ item: familyItem });
475
- if (familyItemMeetsCriteria) {
476
- releasedForDevelopmentItemIds.push(itemFamilyId);
477
- }
478
- itemFamilySet.add(itemFamilyId);
479
- }
480
- }
481
- }
482
- for (const dItem of deleteChanges) {
483
- const meetsCriteria = this.meetsCriteria(dItem);
484
- const item = dItem?.item;
485
- const itemId = item?.id;
486
- const itemFamilyId = item?.itemFamilyId;
487
- if (meetsCriteria) {
488
- releasedForDevelopmentItemIds.push(itemId);
489
- if (!itemFamilySet.has(itemFamilyId) && itemId !== itemFamilyId) {
490
- const familyItem = item?.itemFamily;
491
- if (familyItem) {
492
- const familyItemMeetsCriteria = this.meetsCriteria({ item: familyItem });
493
- if (familyItemMeetsCriteria) {
494
- releasedForDevelopmentItemIds.push(itemFamilyId);
495
- }
496
- itemFamilySet.add(itemFamilyId);
497
- }
498
- else {
499
- itemFamilySet.add(itemFamilyId);
500
- }
501
- }
502
- }
503
- }
504
- console.info('releasedForDevelopmentItemIds: ' + releasedForDevelopmentItemIds);
505
- return releasedForDevelopmentItemIds;
506
- }
507
- meetsCriteria(aItem) {
508
- const item = aItem?.item;
509
- const lifecycleStage = item?.lifecycleStage;
510
- return !!lifecycleStage && !this.config.itemPreDevelopmentLifecycleStages.includes(lifecycleStage);
511
- }
512
- async processPublish(pcd, changeDetail, fullChange, deleteChanges) {
513
- console.info('processPublish-start');
514
- const event = {
515
- assortmentId: pcd.assortmentId,
516
- assortmentPublishChangeId: pcd.assortmentPublishChangeId
517
- };
518
- const assortmentItemFullChangeMap = this.getFullChangeAssortmentMap(fullChange);
519
- const assortmentItemDeleteMap = this.getDeleteChangesAssortmentMap(deleteChanges);
520
- pcd.itemFamilyChanges = this.getItemFamilyChanges(pcd, changeDetail, assortmentItemFullChangeMap, assortmentItemDeleteMap);
521
- const events = await this.getEventsForPublishChangeData(pcd);
522
- if (events.length === 0) {
523
- const processStatus = 'BaseProcessPublishAssortment: ' + event_short_message_status_1.EventShortMessageStatus.NO_EVENTS_TO_SEND
524
- + ': assortmentId: ' + pcd.assortmentId + ': assortmentPublishChangeId: ' + pcd.assortmentPublishChangeId
525
- + ', orgSlug: ' + this.orgSlug;
526
- console.log(processStatus);
527
- const message = 'No Events to Send; returning';
528
- console.log(message);
529
- return {
530
- results: { message, event },
531
- skip_await: true
532
- };
533
- }
534
- const results = await this.sendEvents(events);
535
- const resultsCount = this.getResultsCount(events);
536
- const processStatus = 'BaseProcessPublishAssortment: ' + event_short_message_status_1.EventShortMessageStatus.SENDING_EVENTS
537
- + ': assortmentId: ' + pcd.assortmentId + ': assortmentPublishChangeId: ' + pcd.assortmentPublishChangeId
538
- + ': resultsCount: ' + JSON.stringify(resultsCount)
539
- + ', orgSlug: ' + this.orgSlug;
540
- console.log(processStatus);
541
- return { results, event, resultsCount };
542
- }
543
- getResultsCount(events) {
544
- return events.reduce((acc, event) => {
545
- const objectClass = event?.objectClass;
546
- if (objectClass === 'LCSProductSeasonLink') {
547
- acc.productSeasons++;
548
- }
549
- else if (objectClass === 'LCSSKUSeasonLink') {
550
- acc.colorwaySeasons++;
551
- }
552
- return acc;
553
- }, { productSeasons: 0, colorwaySeasons: 0 });
554
- }
555
- async sendEvents(events) {
556
- console.info('sendEvents()');
557
- const eventType = 'ASYNC_PUBLISH_SEASON';
558
- const sendMode = (this.config?.sendMode[eventType] || '').toLowerCase();
559
- switch (sendMode) {
560
- case 'sendtoflexplm':
561
- return this.sendToFlexPLM(events, eventType);
562
- case 'localfile':
563
- return this.saveToLocalFile(events, eventType);
564
- case 'vibeiqfile':
565
- case 'vibeiqfile-dontsendtoflexplm':
566
- return this.handleVibeIQFile(events, eventType, sendMode);
567
- default:
568
- return {};
569
- }
570
- }
571
- async sendToFlexPLM(events, eventType) {
572
- const asyncEvent = {
573
- taskId: this.config.taskId,
574
- eventType,
575
- objectClass: 'LCSSeason',
576
- events
577
- };
578
- const flexPLMConnect = new flexplm_connect_1.FlexPLMConnect(this.config);
579
- return await flexPLMConnect.sendToFlexPLM(asyncEvent);
580
- }
581
- async saveToLocalFile(events, eventType) {
582
- let results = {};
583
- let fileHandle;
584
- try {
585
- const dateString = this.getCurrentDateString();
586
- const dirName = await path.resolve();
587
- await fsPromise.mkdir(dirName, { recursive: true });
588
- const eventDirName = 'events-output';
589
- const fileName = `${path.sep}${eventDirName}${path.sep}events-${dateString}.json`;
590
- fileHandle = await fsPromise.open('.' + fileName, 'a');
591
- const asyncEvent = {
592
- taskId: this.config.taskId,
593
- eventType,
594
- objectClass: 'LCSSeason',
595
- events
596
- };
597
- await fileHandle.writeFile(JSON.stringify(asyncEvent));
598
- results = {
599
- message: 'Successfully Saved File',
600
- fileLocation: dirName + fileName
601
- };
602
- }
603
- catch (e) {
604
- results['error'] = e.message;
605
- }
606
- finally {
607
- await fileHandle?.close();
608
- }
609
- return results;
610
- }
611
- async handleVibeIQFile(events, eventType, sendMode) {
612
- const eventBuffer = Buffer.from(JSON.stringify(events), 'utf-8');
613
- const fileName = 'ASYNC_PUBLISH_SEASON-events.json';
614
- const uploadFile = await new sdk_1.Files().createAndUploadFileFromBuffer(eventBuffer, 'application/json', fileName, null, this.TTL);
615
- const asyncEvent = {
616
- taskId: this.config.taskId,
617
- eventType,
618
- objectClass: 'LCSSeason',
619
- eventsFileId: uploadFile.id,
620
- eventsDownloadLink: uploadFile.downloadUrl,
621
- eventsAdminDownloadLink: uploadFile.adminDownloadUrl
622
- };
623
- if (sendMode === 'vibeiqfile') {
624
- const flexPLMConnect = new flexplm_connect_1.FlexPLMConnect(this.config);
625
- return await flexPLMConnect.sendToFlexPLM(asyncEvent);
626
- }
627
- else {
628
- return {
629
- message: 'Successfully Uploaded File.',
630
- asyncEvent
631
- };
632
- }
633
- }
634
- getCurrentDateString() {
635
- const d = new Date();
636
- return '' + d.getUTCFullYear()
637
- + '-' + d.getUTCMonth()
638
- + '-' + (d.getUTCDate() + 1)
639
- + '-' + d.getUTCHours()
640
- + '-' + d.getUTCMinutes()
641
- + '-' + d.getUTCSeconds()
642
- + '-' + d.getUTCMilliseconds();
643
- }
644
- getItemFamilyChanges(pcd, changeDetail, assortmentItemFullChangeMap, assortmentItemDeleteMap) {
645
- console.info('getItemFamilyChanges-start');
646
- const itemFamilyChanges = new Map();
647
- const { adds, deletes, updates, familyItemsRemoved } = changeDetail;
648
- const addIds = adds.map(item => item.id);
649
- const deleteIds = deletes.map(item => item.id);
650
- const updateIds = updates.map(item => item.id);
651
- const familyItemsRemovedIds = familyItemsRemoved.map(item => item.itemId);
652
- for (const [itemId, aItem] of assortmentItemFullChangeMap) {
653
- const projectItem = aItem?.projectItem;
654
- const itemFamilyId = aItem?.item?.itemFamilyId;
655
- if (!pcd.releasedForDevelopmentItemIds.includes(itemFamilyId) || !pcd.releasedForDevelopmentItemIds.includes(itemId)) {
656
- continue;
657
- }
658
- const ifc = itemFamilyChanges.get(itemFamilyId) || new item_family_changes_1.ItemFamilyChanges(itemFamilyId, pcd.sinceDate);
659
- if (!itemFamilyChanges.has(itemFamilyId)) {
660
- ifc.itemFamilyObject = itemId === itemFamilyId ? aItem?.item : aItem?.item?.itemFamily;
661
- itemFamilyChanges.set(itemFamilyId, ifc);
662
- }
663
- ifc.assortmentItemFullChangeMap.set(itemId, aItem);
664
- if (pcd.itemToFederatedIdMapping.has(itemId)) {
665
- ifc.itemToFederatedIdMapping.set(itemId, pcd.itemToFederatedIdMapping.get(itemId));
666
- }
667
- if (itemId === itemFamilyId) {
668
- if (addIds.includes(itemId))
669
- ifc.familyAdd = true;
670
- else if (deleteIds.includes(itemId))
671
- ifc.familyDelete = true;
672
- else if (updateIds.includes(itemId))
673
- ifc.familyUpdate = true;
674
- else if (familyItemsRemovedIds.includes(itemId))
675
- ifc.familyItemRemoved = true;
676
- if (projectItem) {
677
- const updatedOnDate = new Date(projectItem.updatedOn);
678
- if (updatedOnDate > pcd.sinceDate) {
679
- ifc.familyUpdate = true;
680
- }
681
- }
682
- if (projectItem && !projectItem?.roles && aItem?.item?.roles) {
683
- projectItem.roles = aItem?.item?.roles;
684
- }
685
- }
686
- else {
687
- if (addIds.includes(itemId))
688
- ifc.colorAdds.push(itemId);
689
- else if (deleteIds.includes(itemId))
690
- ifc.colorDeletes.push(itemId);
691
- else if (updateIds.includes(itemId))
692
- ifc.colorUpdates.push(itemId);
693
- else if (projectItem && new Date(projectItem.updatedOn) > pcd.sinceDate)
694
- ifc.colorUpdates.push(itemId);
695
- else
696
- ifc.colorUnchanged.push(itemId);
697
- if (projectItem && !projectItem?.roles && aItem?.item?.roles) {
698
- projectItem.roles = aItem?.item?.roles;
699
- }
700
- }
701
- }
702
- for (const [itemId, delEntity] of assortmentItemDeleteMap) {
703
- const itemFamilyId = delEntity?.item?.itemFamilyId;
704
- const item = delEntity?.item;
705
- if (!pcd.releasedForDevelopmentItemIds.includes(itemFamilyId) || !pcd.releasedForDevelopmentItemIds.includes(itemId)) {
706
- continue;
707
- }
708
- else {
709
- if (!item) {
710
- console.error('Failed to find deleted item entity');
711
- console.error(' itemFamilyId: ' + itemFamilyId + ' -itemId: ' + itemId);
712
- continue;
713
- }
714
- }
715
- let ifc = itemFamilyChanges.get(itemFamilyId);
716
- if (!ifc) {
717
- ifc = new item_family_changes_1.ItemFamilyChanges(itemFamilyId, pcd.sinceDate);
718
- if (itemId === itemFamilyId) {
719
- ifc.familyDelete = true;
720
- ifc.itemFamilyObject = item;
721
- }
722
- else {
723
- ifc.itemFamilyObject = item?.itemFamily;
724
- }
725
- itemFamilyChanges.set(itemFamilyId, ifc);
726
- }
727
- if (pcd.itemToFederatedIdMapping.has(itemId)) {
728
- ifc.itemToFederatedIdMapping.set(itemId, pcd.itemToFederatedIdMapping.get(itemId));
729
- }
730
- ifc.assortmentItemDeleteMap.set(itemId, delEntity);
731
- ifc.assortmentItemFullChangeMap.set(itemId, delEntity);
732
- if (itemId === itemFamilyId) {
733
- ifc.familyDelete = true;
734
- }
735
- else {
736
- ifc.colorDeletes.push(itemId);
737
- }
738
- }
739
- if (app_framework_1.Logger.isDebugOn()) {
740
- console.debug('returning size: ' + itemFamilyChanges.size);
741
- for (const [key, value] of itemFamilyChanges) {
742
- console.debug(key + ' => ' + value);
743
- }
744
- }
745
- return itemFamilyChanges;
746
- }
747
- async getEventsForPublishChangeData(publishChangeData) {
748
- console.info('getEventsForPublishChangeData-start');
749
- const seasonalPayloads = [];
750
- for (const itemFamilyChange of publishChangeData.itemFamilyChanges.values()) {
751
- const events = await this.getEventsForItemFamilyChanges(itemFamilyChange, publishChangeData.assortmentId, publishChangeData.seasonFed, publishChangeData.itemToFederatedIdMapping);
752
- if (events.length > 0) {
753
- seasonalPayloads.push(...events);
754
- }
755
- }
756
- return seasonalPayloads;
757
- }
758
- async getEventsForItemFamilyChanges(itemFamilyChanges, assortmentId, seasonFed, itemToFederatedIdMapping) {
759
- console.info('getEventsForItemFamilyChanges()');
760
- const events = [];
761
- const LCSSeason = Object.assign({}, seasonFed);
762
- const assortment = await this.getAssortment(assortmentId);
763
- const projectItem = this.getProjectItem(itemFamilyChanges, itemFamilyChanges.itemFamilyId);
764
- const familyAssortmentItem = itemFamilyChanges.familyAdd || itemFamilyChanges.familyUpdate || itemFamilyChanges.familyDelete
765
- || itemFamilyChanges.colorAdds.length > 0 || itemFamilyChanges.colorUpdates.length > 0 || itemFamilyChanges.colorDeletes.length > 0
766
- || (projectItem && this.updatedSinceDate(projectItem, itemFamilyChanges.sinceDate));
767
- if (familyAssortmentItem) {
768
- const entityReference = itemFamilyChanges.itemFamilyId;
769
- const prodEntityData = (itemFamilyChanges.assortmentItemFullChangeMap.has(entityReference))
770
- ? itemFamilyChanges.assortmentItemFullChangeMap.get(entityReference)?.item
771
- : itemFamilyChanges.itemFamilyObject;
772
- let seasonalData = await this.getSeasonalData(projectItem);
773
- seasonalData = await map_utils_1.MapUtil.applyTransformMap(this.transformMapFile, this.mapFileUtil, seasonalData, 'LCSProductSeasonLink', 'vibe2flex');
774
- const LCSProduct = await this.getProductFederation(entityReference, itemToFederatedIdMapping, prodEntityData);
775
- const isCreatable = await type_conversion_utils_1.TypeConversionUtils.isOutboundCreatableFromEntity(this.transformMapFile, this.mapFileUtil, projectItem, { item: prodEntityData, assortment });
776
- const eventType = isCreatable ? 'UPSERT_ON_SEASON' : 'UPDATE_ON_SEASON';
777
- const psUpsert = {
778
- eventType,
779
- objectClass: 'LCSProductSeasonLink',
780
- entityReference: 'item:' + entityReference,
781
- LCSSeason,
782
- LCSProduct,
783
- data: seasonalData
784
- };
785
- if ('ASSORTMENT' === projectItem.addedFromSource && projectItem.addedFromAssortment) {
786
- const carriedFromSeason = await this.getCarriedFromSeason(projectItem);
787
- if (carriedFromSeason) {
788
- psUpsert['carriedFromSeason'] = carriedFromSeason;
789
- }
790
- }
791
- events.push(psUpsert);
792
- }
793
- const colorwayChanges = [];
794
- colorwayChanges.push(...itemFamilyChanges.colorAdds);
795
- colorwayChanges.push(...itemFamilyChanges.colorUpdates);
796
- colorwayChanges.push(...itemFamilyChanges.colorDeletes);
797
- for (const entityReference of colorwayChanges) {
798
- let item = {};
799
- if (itemFamilyChanges.assortmentItemFullChangeMap.has(entityReference)) {
800
- const fullAi = itemFamilyChanges.assortmentItemFullChangeMap.get(entityReference);
801
- item = fullAi?.item;
802
- }
803
- const projectItem = this.getProjectItem(itemFamilyChanges, entityReference);
804
- let seasonalData = await this.getSeasonalData(projectItem);
805
- seasonalData = await map_utils_1.MapUtil.applyTransformMap(this.transformMapFile, this.mapFileUtil, seasonalData, 'LCSSKUSeasonLink', 'vibe2flex');
806
- const LCSSKU = await this.getSKUFederation(entityReference, itemToFederatedIdMapping, item);
807
- const isCreatable = await type_conversion_utils_1.TypeConversionUtils.isOutboundCreatableFromEntity(this.transformMapFile, this.mapFileUtil, projectItem, { item, assortment });
808
- const eventType = isCreatable ? 'UPSERT_ON_SEASON' : 'UPDATE_ON_SEASON';
809
- const csUpsert = {
810
- eventType,
811
- objectClass: 'LCSSKUSeasonLink',
812
- entityReference: 'item:' + entityReference,
813
- LCSSeason,
814
- LCSSKU,
815
- data: seasonalData
816
- };
817
- if ('ASSORTMENT' === projectItem.addedFromSource && projectItem.addedFromAssortment) {
818
- const carriedFromSeason = await this.getCarriedFromSeason(projectItem);
819
- if (carriedFromSeason) {
820
- csUpsert['carriedFromSeason'] = carriedFromSeason;
821
- }
822
- }
823
- events.push(csUpsert);
824
- }
825
- return events;
826
- }
827
- getProjectItem(itemFamilyChanges, id) {
828
- let projectItem = {};
829
- if (itemFamilyChanges?.assortmentItemFullChangeMap.has(id)) {
830
- const aItem = itemFamilyChanges?.assortmentItemFullChangeMap.get(id);
831
- projectItem = aItem?.projectItem;
832
- }
833
- if (id === itemFamilyChanges.itemFamilyId && Object.keys(projectItem).length == 0) {
834
- for (const asstItem of itemFamilyChanges.assortmentItemFullChangeMap.values()) {
835
- if (asstItem?.familyProjectItem) {
836
- projectItem = asstItem?.familyProjectItem;
837
- break;
838
- }
839
- }
840
- }
841
- return projectItem;
842
- }
843
- async getSeasonalData(projectItem) {
844
- return await this.dc.getFlexPLMObjectData(projectItem, [], true);
845
- }
846
- async getProductFederation(entityReference, itemToFederatedIdMapping, itemFamilyObject) {
847
- const itemObj = await this.dc.getFlexPLMObjectData(itemFamilyObject, [], true);
848
- let prodObj = {
849
- entityReference: 'item:' + entityReference,
850
- objectClass: 'LCSProduct',
851
- };
852
- const identifierKeys = await type_conversion_utils_1.TypeConversionUtils.getIdentifierProperties(this.transformMapFile, this.mapFileUtil, itemFamilyObject);
853
- const informationKeys = await type_conversion_utils_1.TypeConversionUtils.getInformationalProperties(this.transformMapFile, this.mapFileUtil, itemFamilyObject);
854
- for (const key of identifierKeys.concat(informationKeys)) {
855
- if (itemObj[key]) {
856
- prodObj[key] = itemObj[key];
857
- }
858
- }
859
- if (!prodObj?.vibeIQIdentifier) {
860
- prodObj.vibeIQIdentifier = itemFamilyObject?.identifier || itemFamilyObject?.itemNumber;
861
- }
862
- prodObj = await map_utils_1.MapUtil.applyTransformMap(this.transformMapFile, this.mapFileUtil, prodObj, 'LCSProduct', 'vibe2flex');
863
- return prodObj;
864
- }
865
- async getSKUFederation(entityReference, itemToFederatedIdMapping, itemObject) {
866
- const itemObj = await this.dc.getFlexPLMObjectData(itemObject, [], true);
867
- let skuObj = {
868
- entityReference: 'item:' + entityReference,
869
- objectClass: 'LCSSKU',
870
- };
871
- const identifierKeys = await type_conversion_utils_1.TypeConversionUtils.getIdentifierProperties(this.transformMapFile, this.mapFileUtil, itemObject);
872
- const informationKeys = await type_conversion_utils_1.TypeConversionUtils.getInformationalProperties(this.transformMapFile, this.mapFileUtil, itemObject);
873
- for (const key of identifierKeys.concat(informationKeys)) {
874
- if (itemObj[key]) {
875
- skuObj[key] = itemObj[key];
876
- }
877
- }
878
- if (!skuObj?.vibeIQIdentifier) {
879
- skuObj.vibeIQIdentifier = itemObject?.identifier || itemObject?.itemNumber;
880
- }
881
- skuObj = await map_utils_1.MapUtil.applyTransformMap(this.transformMapFile, this.mapFileUtil, skuObj, 'LCSSKU', 'vibe2flex');
882
- return skuObj;
883
- }
884
- async getCarriedFromSeason(projectItem) {
885
- const addedFromAssortment = projectItem.addedFromAssortment;
886
- if (this.cache.carriedFromSeason[addedFromAssortment]) {
887
- return this.cache.carriedFromSeason[addedFromAssortment];
888
- }
889
- let seasonFederation = undefined;
890
- const assortmentEntity = await this.getAssormentEntityFromId(addedFromAssortment);
891
- if (assortmentEntity) {
892
- seasonFederation = await this.getSeasonFederationFromAssortment(assortmentEntity);
893
- }
894
- this.cache.carriedFromSeason[addedFromAssortment] = seasonFederation;
895
- return seasonFederation;
896
- }
897
- async getAssormentEntityFromId(assortmentId) {
898
- let assortment;
899
- try {
900
- assortment = await new sdk_1.Entities().get({
901
- entityName: 'assortment',
902
- id: assortmentId
903
- });
904
- }
905
- catch (e) {
906
- console.warn(`No Assortment found for id: ${assortmentId}`);
907
- }
908
- return assortment;
909
- }
910
- async getSeasonFederationFromAssortment(assortment) {
911
- assortment['flex2vibeMapKeyRoot'] = 'LCSSeason';
912
- const flexPLMTypePath = await type_conversion_utils_1.TypeConversionUtils.getObjectTypePath(this.transformMapFile, this.mapFileUtil, assortment);
913
- let seasonObj = {
914
- entityReference: 'assortment:' + assortment.id,
915
- objectClass: 'LCSSeason',
916
- flexPLMTypePath
917
- };
918
- try {
919
- const assortmentObj = await this.dc.getFlexPLMObjectData(assortment, [], true);
920
- const identifierKeys = await type_conversion_utils_1.TypeConversionUtils.getIdentifierProperties(this.transformMapFile, this.mapFileUtil, assortment);
921
- const informationKeys = await type_conversion_utils_1.TypeConversionUtils.getInformationalProperties(this.transformMapFile, this.mapFileUtil, assortment);
922
- for (const key of identifierKeys.concat(informationKeys)) {
923
- if (assortmentObj[key]) {
924
- seasonObj[key] = assortmentObj[key];
925
- }
926
- }
927
- const objectKeys = Object.keys(seasonObj);
928
- const hasAllIdentifiers = identifierKeys.every(key => objectKeys.includes(key));
929
- if (!hasAllIdentifiers) {
930
- return undefined;
931
- }
932
- const mapKey = await type_conversion_utils_1.TypeConversionUtils.getMapKey(this.transformMapFile, this.mapFileUtil, assortment, type_conversion_utils_1.TypeConversionUtils.VIBE2FLEX_DIRECTION);
933
- seasonObj = await map_utils_1.MapUtil.applyTransformMap(this.transformMapFile, this.mapFileUtil, seasonObj, mapKey, type_conversion_utils_1.TypeConversionUtils.VIBE2FLEX_DIRECTION);
934
- }
935
- finally {
936
- delete assortment['flex2vibeMapKeyRoot'];
937
- }
938
- return seasonObj;
939
- }
940
- }
941
- exports.BaseProcessPublishAssortment = BaseProcessPublishAssortment;
942
- BaseProcessPublishAssortment.ASSORTMENT_NOT_PUBLISHABLE = 'Assortment isn\'t marked for publishing to FlexPLM';
943
- BaseProcessPublishAssortment.ASSORTMENT_NO_FED_INFO = 'Assortment doesn\'t have all "identifier" properties, so there is no processing. Identifier properties: ';
944
- BaseProcessPublishAssortment.NOT_ABLE_TO_PROCESS_DELETE_CHANGES = 'Error: Not able to process delete changes';
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BaseProcessPublishAssortment = void 0;
4
+ const sdk_1 = require("@contrail/sdk");
5
+ const flexplm_connect_1 = require("../util/flexplm-connect");
6
+ const item_family_changes_1 = require("../interfaces/item-family-changes");
7
+ const publish_change_data_1 = require("../interfaces/publish-change-data");
8
+ const map_utils_1 = require("../util/map-utils");
9
+ const type_conversion_utils_1 = require("../util/type-conversion-utils");
10
+ const fsPromise = require("fs/promises");
11
+ const path = require("path");
12
+ const app_framework_1 = require("@contrail/app-framework");
13
+ const event_short_message_status_1 = require("../util/event-short-message-status");
14
+ class BaseProcessPublishAssortment {
15
+ constructor(_config, _dc, _mapFileUtil) {
16
+ this.TTL = 30 * 24 * 60 * 60 * 1000; // 30 days
17
+ this.cache = {
18
+ carriedFromSeason: {}
19
+ };
20
+ this.config = _config;
21
+ this.dc = _dc;
22
+ this.mapFileUtil = _mapFileUtil;
23
+ this.transformMapFile = this.config?.transformMapFile;
24
+ this.orgSlug = this.config?.orgSlug || 'unset-orgSlug';
25
+ }
26
+ async process(event) {
27
+ const assortmentPublishChangeId = event.assortmentPublishChangeId;
28
+ let apcHistory;
29
+ let publisher;
30
+ const assortmentId = event.assortmentId;
31
+ try {
32
+ console.info('process-start!');
33
+ let seasonFed;
34
+ try {
35
+ seasonFed = await this.getSeasonFederation(assortmentId);
36
+ }
37
+ catch (e) {
38
+ const message = e.message;
39
+ const eventStatus = (message.includes(BaseProcessPublishAssortment.ASSORTMENT_NOT_PUBLISHABLE)) ? event_short_message_status_1.EventShortMessageStatus.NOT_PUBLISHABLE :
40
+ (message.startsWith(BaseProcessPublishAssortment.ASSORTMENT_NO_FED_INFO)) ? event_short_message_status_1.EventShortMessageStatus.NO_FEDERATION_INFO :
41
+ event_short_message_status_1.EventShortMessageStatus.FAILURE;
42
+ const processStatus = 'BaseProcessPublishAssortment: ' + eventStatus
43
+ + ': assortmentId: ' + event.assortmentId + ': assortmentPublishChangeId: ' + event.assortmentPublishChangeId
44
+ + ', orgSlug: ' + this.orgSlug;
45
+ console.log(JSON.stringify(processStatus));
46
+ console.log(message);
47
+ const output = {
48
+ results: { message, event },
49
+ skip_await: true
50
+ };
51
+ return output;
52
+ }
53
+ apcHistory = await this.getApcHistory(assortmentId);
54
+ const sinceDate = await this.getSinceDate(assortmentId, assortmentPublishChangeId, apcHistory);
55
+ //Get detail information
56
+ const assortmentPublishChange = await this.downloadAssortmentPublishChange(assortmentId, assortmentPublishChangeId);
57
+ publisher = this.getPublisher(assortmentPublishChange);
58
+ const changeDetail = await this.downloadHydratedChangeDetail(assortmentPublishChange);
59
+ const assortmentBaseline = await this.downloadAssortmentBaseline(assortmentPublishChange);
60
+ const deleteChanges = await this.getDeleteChanges(assortmentPublishChange, apcHistory, assortmentBaseline, sinceDate);
61
+ const releasedForDevelopmentItemIds = this.getReleasedForDevelopmentItemAndFamilyIds(assortmentBaseline, deleteChanges);
62
+ const itemToFederatedIdMapping = await this.getItemFederatedIds( /*allItemIds*/);
63
+ const pcd = new publish_change_data_1.PublishChangeData(assortmentId, seasonFed, assortmentPublishChangeId, sinceDate, publisher);
64
+ pcd.itemToFederatedIdMapping = itemToFederatedIdMapping;
65
+ pcd.releasedForDevelopmentItemIds = releasedForDevelopmentItemIds;
66
+ const output = await this.processPublish(pcd, changeDetail, assortmentBaseline, deleteChanges);
67
+ output['publishInfo'] = await this.getPublishInfo(assortmentId, assortmentPublishChangeId, apcHistory, publisher);
68
+ console.info('process-end');
69
+ return output;
70
+ }
71
+ catch (e) {
72
+ try {
73
+ const publishInfo = await this.getPublishInfo(assortmentId, assortmentPublishChangeId, apcHistory, publisher);
74
+ e.publishInfo = publishInfo;
75
+ }
76
+ catch (e2) {
77
+ console.log('catch e2: ' + e2.message);
78
+ }
79
+ console.log('catch e: ' + e.message);
80
+ throw e;
81
+ }
82
+ }
83
+ async getPublishInfo(assortmentId, assortmentPublishChangeId, apcHistory, publisher) {
84
+ const apc = apcHistory?.find(apc => apc?.id === assortmentPublishChangeId) || {};
85
+ const assortment = await this.getAssortment(assortmentId);
86
+ const assortmentName = assortment?.name;
87
+ const versionHistoryNumber = await this.getSnapshotVersion(assortment, apc);
88
+ const publishInfo = {
89
+ assortmentName,
90
+ versionName: apc?.versionName,
91
+ versionComments: apc?.versionComments,
92
+ publishDate: apc?.createdOn,
93
+ publisher,
94
+ versionHistoryNumber
95
+ };
96
+ return publishInfo;
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
+ */
107
+ async getSnapshotVersion(assortment, apc) {
108
+ const entityReference = assortment?.createdForReference;
109
+ const createdOnString = apc?.createdOn;
110
+ if (!entityReference || !createdOnString || !apc?.id) {
111
+ return 'unknown';
112
+ }
113
+ const createdOnDate = new Date(createdOnString);
114
+ createdOnDate.setMonth(createdOnDate.getMonth() - 1); //subtract 1 month
115
+ const createdOnStringMinus1 = createdOnDate.toISOString();
116
+ const createdOn = 'ISGREATERTHAN ' + createdOnStringMinus1;
117
+ const snapshots = await new sdk_1.Entities().get({
118
+ entityName: 'entity-snapshot',
119
+ criteria: {
120
+ entityReference,
121
+ createdOn
122
+ }
123
+ });
124
+ const assortmentPublishChangeId = apc?.id;
125
+ const snapshot = snapshots.find(snap => snap?.assortmentChangeId === assortmentPublishChangeId);
126
+ let snapshotVersion = 'unknown';
127
+ if (snapshot && snapshot.versionNumber) {
128
+ snapshotVersion = snapshot.versionNumber;
129
+ }
130
+ else if (!snapshot && snapshots.length > 0) {
131
+ const createdOnTime = new Date(createdOnString).getTime();
132
+ const lastSnapshot = snapshots[snapshots.length - 1];
133
+ if (lastSnapshot?.createdOn && createdOnTime > new Date(lastSnapshot.createdOn).getTime()) {
134
+ snapshotVersion = 'after: ' + lastSnapshot.versionNumber;
135
+ }
136
+ }
137
+ return snapshotVersion;
138
+ }
139
+ async getSeasonFederation(assortmentId) {
140
+ const assortment = await this.getAssortment(assortmentId);
141
+ console.error('assortment-name: ' + assortment?.name);
142
+ const publishToFlexPLM = assortment?.publishToFlexPLM;
143
+ if (!publishToFlexPLM) {
144
+ throw new Error(BaseProcessPublishAssortment.ASSORTMENT_NOT_PUBLISHABLE);
145
+ }
146
+ const assortmentObj = await this.dc.getFlexPLMObjectData(assortment, [], true);
147
+ let seasonObj = {
148
+ entityReference: 'assortment:' + assortmentId,
149
+ objectClass: 'LCSSeason',
150
+ };
151
+ const identifierKeys = await type_conversion_utils_1.TypeConversionUtils.getIdentifierProperties(this.transformMapFile, this.mapFileUtil, assortmentObj);
152
+ const informationKeys = await type_conversion_utils_1.TypeConversionUtils.getInformationalProperties(this.transformMapFile, this.mapFileUtil, assortmentObj);
153
+ for (const key of identifierKeys.concat(informationKeys)) {
154
+ if (assortmentObj[key]) {
155
+ seasonObj[key] = assortmentObj[key];
156
+ }
157
+ }
158
+ const objectKeys = Object.keys(seasonObj);
159
+ const hasAllIdentifiers = identifierKeys.every(key => objectKeys.includes(key));
160
+ if (!hasAllIdentifiers) {
161
+ throw new Error(BaseProcessPublishAssortment.ASSORTMENT_NO_FED_INFO + identifierKeys);
162
+ }
163
+ seasonObj = await map_utils_1.MapUtil.applyTransformMap(this.transformMapFile, this.mapFileUtil, seasonObj, 'LCSSeason', 'vibe2flex');
164
+ return seasonObj;
165
+ }
166
+ async getAssortment(assortmentId) {
167
+ if (this.assortment != undefined) {
168
+ return this.assortment;
169
+ }
170
+ this.assortment = await new sdk_1.Entities().get({
171
+ entityName: 'assortment',
172
+ id: assortmentId
173
+ });
174
+ return this.assortment;
175
+ }
176
+ async getSinceDate(assortmentId, assortmentPublishChangeId, apcHistory) {
177
+ let sinceDate = null;
178
+ let pastPublishCount = 1;
179
+ if (this.config?.sinceDate) {
180
+ const sinceDateString = '' + this.config?.sinceDate;
181
+ if (sinceDateString && sinceDateString.indexOf('-') > 0) {
182
+ const sinceDateParts = sinceDateString.split('-');
183
+ if (sinceDateParts.length == 3) {
184
+ const year = parseInt(sinceDateParts[0]);
185
+ const month = parseInt(sinceDateParts[1]) - 1;
186
+ const day = parseInt(sinceDateParts[2]);
187
+ const minutes = 0 - new Date().getTimezoneOffset();
188
+ sinceDate = this.getSinceDateFromAPCSpecificDate(apcHistory, assortmentPublishChangeId, new Date(year, month, day, 0, minutes, 0));
189
+ }
190
+ }
191
+ else if (sinceDateString && sinceDateString.indexOf(':') > 0) {
192
+ const sinceDateParts = sinceDateString.split(':');
193
+ const type = sinceDateParts[0];
194
+ const count = parseInt(sinceDateParts[1]);
195
+ if ('numberofdays' == type.toLocaleLowerCase()) {
196
+ sinceDate = this.getSinceDateDaysPrevious(apcHistory, assortmentPublishChangeId, count);
197
+ }
198
+ else if ('numberofpublishes' == type.toLocaleLowerCase()) {
199
+ pastPublishCount = count;
200
+ }
201
+ }
202
+ }
203
+ if (sinceDate == null) {
204
+ sinceDate = this.getSinceDateFromAPCs(apcHistory, assortmentPublishChangeId, pastPublishCount);
205
+ }
206
+ if (sinceDate == null) {
207
+ throw new Error('Couldnt set sinceDate');
208
+ }
209
+ console.info('getSinceDate: ' + sinceDate);
210
+ return sinceDate;
211
+ }
212
+ async getApcHistory(assortmentId) {
213
+ const resource = `/assortments/${assortmentId}/history/`;
214
+ return sdk_1.Request.request(resource, {
215
+ method: 'GET',
216
+ headers: {
217
+ Accept: 'application/json',
218
+ 'Content-Type': 'application/json'
219
+ }
220
+ });
221
+ }
222
+ updatedSinceDate(entity, sinceDate) {
223
+ const updatedOn = entity?.updatedOn;
224
+ return (updatedOn && new Date(updatedOn) > sinceDate);
225
+ }
226
+ getSinceDateFromAPCSpecificDate(apcHistory, assortmentPublishChangeId, specificDate) {
227
+ const apc = apcHistory.find(apc => apc?.id === assortmentPublishChangeId);
228
+ const apcDate = new Date(apc?.createdOn);
229
+ let afterDate = apcDate;
230
+ let beforeDate = new Date(0);
231
+ const sinceDate = specificDate;
232
+ for (const tempapc of apcHistory) {
233
+ if (assortmentPublishChangeId !== tempapc?.id) {
234
+ const tempDate = new Date(tempapc?.createdOn);
235
+ if (tempDate < apcDate) {
236
+ if (tempDate > sinceDate && tempDate < afterDate) {
237
+ afterDate = tempDate;
238
+ }
239
+ else if (tempDate < sinceDate && tempDate > beforeDate) {
240
+ beforeDate = tempDate;
241
+ }
242
+ }
243
+ }
244
+ }
245
+ const selectedDate = (beforeDate.getTime() !== 0) ? beforeDate : afterDate;
246
+ return selectedDate;
247
+ }
248
+ getSinceDateDaysPrevious(apcHistory, assortmentPublishChangeId, daysBack) {
249
+ const apc = apcHistory.find(apc => apc?.id === assortmentPublishChangeId);
250
+ const apcDate = new Date(apc?.createdOn);
251
+ let afterDate = apcDate;
252
+ let beforeDate = new Date(0);
253
+ const sinceDate = new Date(apcDate.getUTCFullYear(), apcDate.getUTCMonth(), apcDate.getUTCDate() - daysBack, 0, (0 - apcDate.getTimezoneOffset()), 0);
254
+ for (const tempapc of apcHistory) {
255
+ if (assortmentPublishChangeId !== tempapc?.id) {
256
+ const tempDate = new Date(tempapc?.createdOn);
257
+ if (tempDate < apcDate) {
258
+ if (tempDate > sinceDate && tempDate < afterDate) {
259
+ afterDate = tempDate;
260
+ }
261
+ else if (tempDate < sinceDate && tempDate > beforeDate) {
262
+ beforeDate = tempDate;
263
+ }
264
+ }
265
+ }
266
+ }
267
+ const selectedDate = (beforeDate.getTime() !== 0) ? beforeDate : afterDate;
268
+ return selectedDate;
269
+ }
270
+ getSinceDateFromAPCs(apcHistory, assortmentPublishChangeId, pastPublishCount = 1) {
271
+ if (apcHistory.length === 1) {
272
+ return new Date(0);
273
+ }
274
+ const apcIndex = apcHistory.findIndex(apc => apc.id === assortmentPublishChangeId);
275
+ if (apcIndex === 0) {
276
+ return new Date(0);
277
+ }
278
+ const apc = apcHistory[apcIndex];
279
+ const apcDate = new Date(apc?.createdOn);
280
+ let sinceDate = new Date(0);
281
+ const dateArray = [];
282
+ for (const tempapc of apcHistory) {
283
+ if (assortmentPublishChangeId !== tempapc?.id) {
284
+ const tempDate = new Date(tempapc?.createdOn);
285
+ if (tempDate < apcDate) {
286
+ dateArray.push(tempDate);
287
+ }
288
+ }
289
+ }
290
+ dateArray.sort((a, b) => { return Date.parse(a) - Date.parse(b); });
291
+ const arrIndex = (dateArray.length - pastPublishCount > 0) ? dateArray.length - pastPublishCount : 0;
292
+ sinceDate = dateArray[arrIndex];
293
+ return sinceDate;
294
+ }
295
+ getPublisher(assortmentPublishChange) {
296
+ const createdBy = assortmentPublishChange?.createdBy;
297
+ const publisher = {};
298
+ const includeKeys = ['email', 'firstName', 'isSsoUser', 'lastName'];
299
+ for (const [key, value] of Object.entries(createdBy)) {
300
+ if (includeKeys.includes(key)) {
301
+ publisher[key] = value;
302
+ }
303
+ }
304
+ return publisher;
305
+ }
306
+ async downloadAssortmentPublishChange(assortmentId, assortmentPublishChangeId) {
307
+ const resourceString = '/assortments/' + assortmentId + '/history/' + assortmentPublishChangeId + '?relations=createdBy';
308
+ return await sdk_1.Request.request(resourceString, {
309
+ method: 'GET',
310
+ Headers: {
311
+ Accept: 'application/json',
312
+ 'Content-Type': 'application/json'
313
+ }
314
+ });
315
+ }
316
+ async downloadHydratedChangeDetail(assortmentPublishChange) {
317
+ try {
318
+ console.info('downloadHydratedChangeDetail-start');
319
+ const link = assortmentPublishChange?.hydratedDetailDownloadLink;
320
+ const response = await fetch(link);
321
+ const data = await response.json();
322
+ return data;
323
+ }
324
+ catch (e) {
325
+ console.log('Error hydratedDetailDownloadLink: ' + e.message);
326
+ }
327
+ return undefined;
328
+ }
329
+ async downloadAssortmentBaseline(assortmentPublishChange) {
330
+ console.info('downloadAssortmentBaseline-start');
331
+ try {
332
+ const link = assortmentPublishChange?.assortmentBaselineDownloadLink;
333
+ const response = await fetch(link);
334
+ const data = await response.json();
335
+ return data;
336
+ }
337
+ catch (e) {
338
+ console.log('Error assortmentBaselineDownloadLink: ' + e.message);
339
+ }
340
+ return undefined;
341
+ }
342
+ async getDeleteChanges(assortmentPublishChange, apcHistory, assortmentBaseline, sinceDate) {
343
+ console.info('getDeleteChanges(): ' + assortmentPublishChange?.id);
344
+ console.info(sinceDate);
345
+ const currentAPCIndex = apcHistory.findIndex(pc => pc?.id === assortmentPublishChange?.id);
346
+ console.info(' currentAPCIndex: ' + currentAPCIndex);
347
+ if (currentAPCIndex == 0) {
348
+ return [];
349
+ }
350
+ const previousApcIndex = (currentAPCIndex > 0) ? currentAPCIndex - 1 : 0;
351
+ const apcCreatedOn = apcHistory[previousApcIndex]?.createdOn;
352
+ const apcDeletes = await this.downloadDeleteChanges(assortmentPublishChange);
353
+ let previousApcDate = Date.parse(apcCreatedOn);
354
+ const sinceDateMilliseconds = sinceDate.getTime();
355
+ if (app_framework_1.Logger.isInfoOn()) {
356
+ console.info('apcCreatedOn: ' + apcCreatedOn);
357
+ console.info('sinceDate: ' + sinceDate);
358
+ console.info('sinceDateMilliseconds: ' + sinceDateMilliseconds);
359
+ console.info('apcDateMilliseconds: ' + previousApcDate);
360
+ }
361
+ //if only 1 apc, no processing needed
362
+ if (sinceDateMilliseconds !== previousApcDate) {
363
+ console.info('sinceDateMilliseconds !== apcDateMilliseconds');
364
+ const currentAssortmentItemIds = this.getBaselineItemIds(assortmentBaseline);
365
+ const deleteIds = (apcDeletes.length === 0)
366
+ ? []
367
+ : apcDeletes.map(item => item?.itemId);
368
+ for (let i = currentAPCIndex - 1; i > 0; i--) {
369
+ const workingAPC = apcHistory[i];
370
+ previousApcDate = Date.parse(workingAPC?.createdOn);
371
+ if (sinceDateMilliseconds === previousApcDate) {
372
+ break;
373
+ }
374
+ if (workingAPC?.deletes > 0) {
375
+ const previousApc = (i > 0)
376
+ ? apcHistory[i - 1]
377
+ : undefined;
378
+ const deleteChanges = await this.buildDeleteChanges(workingAPC, previousApc);
379
+ console.info('checking deleteChanges');
380
+ for (const dItem of deleteChanges) {
381
+ const dItemId = dItem?.itemId;
382
+ console.info(dItemId);
383
+ if (!currentAssortmentItemIds.includes(dItemId) && !deleteIds.includes(dItemId)) {
384
+ console.info('adding');
385
+ deleteIds.push(dItemId);
386
+ apcDeletes.push(dItem);
387
+ }
388
+ }
389
+ }
390
+ }
391
+ console.info('getDeleteChanges()-currentAssortmentItemIds: ' + currentAssortmentItemIds);
392
+ console.info('getDeleteChanges()-deleteIds: ' + deleteIds);
393
+ }
394
+ return apcDeletes;
395
+ }
396
+ getBaselineItemIds(assortmentBaseline) {
397
+ const itemIds = [];
398
+ const assortmentItemsArray = assortmentBaseline?.assortmentItems;
399
+ for (const aItem of assortmentItemsArray) {
400
+ itemIds.push(aItem?.itemId);
401
+ }
402
+ return itemIds;
403
+ }
404
+ async downloadDeleteChanges(apc) {
405
+ console.info('deleteDataDownloadLink-start');
406
+ try {
407
+ const link = apc?.deleteDataDownloadLink;
408
+ const response = await fetch(link);
409
+ const data = await response.json();
410
+ return data;
411
+ }
412
+ catch (e) {
413
+ console.log('Error deleteDataDownloadLink: ' + e.message);
414
+ }
415
+ return undefined;
416
+ }
417
+ async buildDeleteChanges(apc, previousApc) {
418
+ console.info('buildDeleteChanges()');
419
+ if (apc?.deleteDataDownloadLink) {
420
+ const deleteChanges = await this.downloadDeleteChanges(apc);
421
+ if (deleteChanges) {
422
+ return deleteChanges;
423
+ }
424
+ }
425
+ console.info('pulling down full APC');
426
+ apc = await this.downloadAssortmentPublishChange(apc?.assortmentId, apc?.id);
427
+ if (apc?.deleteDataDownloadLink) {
428
+ const deleteChanges = await this.downloadDeleteChanges(apc);
429
+ if (deleteChanges) {
430
+ return deleteChanges;
431
+ }
432
+ }
433
+ if (!previousApc) {
434
+ throw new Error(BaseProcessPublishAssortment.NOT_ABLE_TO_PROCESS_DELETE_CHANGES);
435
+ }
436
+ let previousBaseline;
437
+ console.info('check previousApc');
438
+ if (previousApc?.assortmentBaselineDownloadLink) {
439
+ previousBaseline = await this.downloadAssortmentBaseline(previousApc);
440
+ }
441
+ else {
442
+ console.info('previousApc pulling down full APC');
443
+ previousApc = await this.downloadAssortmentPublishChange(previousApc?.assortmentId, previousApc?.id);
444
+ previousBaseline = await this.downloadAssortmentBaseline(previousApc);
445
+ }
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
448
+ const deleteArray = previousBaseline?.assortmentItems.filter(aItem => deleteIds.includes(aItem?.itemId));
449
+ console.info('deleteArray.length: ' + deleteArray.length);
450
+ return deleteArray;
451
+ }
452
+ async getItemFederatedIds( /*itemIds*/) {
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)));
468
+ return itemFederatedIds;
469
+ }
470
+ getFullChangeAssortmentMap(fullChange) {
471
+ const assortmentItemsArray = fullChange?.assortmentItems || [];
472
+ const assortmentItemsMap = new Map();
473
+ for (const aItem of assortmentItemsArray) {
474
+ const itemId = aItem?.itemId;
475
+ assortmentItemsMap.set(itemId, aItem);
476
+ }
477
+ return assortmentItemsMap;
478
+ }
479
+ getDeleteChangesAssortmentMap(deleteChanges) {
480
+ const deleteItems = new Map();
481
+ for (const dItem of deleteChanges) {
482
+ deleteItems.set(dItem?.itemId, dItem);
483
+ }
484
+ return deleteItems;
485
+ }
486
+ getReleasedForDevelopmentItemAndFamilyIds(fullChange, deleteChanges) {
487
+ console.info('getReleasedForDevelopmentItemAndFamilyIds');
488
+ const releasedForDevelopmentItemIds = [];
489
+ const itemFamilySet = new Set();
490
+ const assortmentItemsArray = fullChange?.assortmentItems;
491
+ for (const aItem of assortmentItemsArray) {
492
+ const meetsCriteria = this.meetsCriteria(aItem);
493
+ const item = aItem?.item;
494
+ const itemId = item?.id;
495
+ const itemFamilyId = item?.itemFamilyId;
496
+ if (meetsCriteria) {
497
+ releasedForDevelopmentItemIds.push(itemId);
498
+ if (!itemFamilySet.has(itemFamilyId) && itemId !== itemFamilyId) {
499
+ const familyItem = item?.itemFamily;
500
+ const familyItemMeetsCriteria = this.meetsCriteria({ item: familyItem });
501
+ if (familyItemMeetsCriteria) {
502
+ releasedForDevelopmentItemIds.push(itemFamilyId);
503
+ }
504
+ itemFamilySet.add(itemFamilyId);
505
+ }
506
+ }
507
+ }
508
+ for (const dItem of deleteChanges) {
509
+ const meetsCriteria = this.meetsCriteria(dItem);
510
+ const item = dItem?.item;
511
+ const itemId = item?.id;
512
+ const itemFamilyId = item?.itemFamilyId;
513
+ if (meetsCriteria) {
514
+ releasedForDevelopmentItemIds.push(itemId);
515
+ if (!itemFamilySet.has(itemFamilyId) && itemId !== itemFamilyId) {
516
+ const familyItem = item?.itemFamily;
517
+ if (familyItem) {
518
+ const familyItemMeetsCriteria = this.meetsCriteria({ item: familyItem });
519
+ if (familyItemMeetsCriteria) {
520
+ releasedForDevelopmentItemIds.push(itemFamilyId);
521
+ }
522
+ itemFamilySet.add(itemFamilyId);
523
+ }
524
+ else {
525
+ itemFamilySet.add(itemFamilyId);
526
+ }
527
+ }
528
+ }
529
+ }
530
+ console.info('releasedForDevelopmentItemIds: ' + releasedForDevelopmentItemIds);
531
+ return releasedForDevelopmentItemIds;
532
+ }
533
+ meetsCriteria(aItem) {
534
+ const item = aItem?.item;
535
+ const lifecycleStage = item?.lifecycleStage;
536
+ return !!lifecycleStage && !this.config.itemPreDevelopmentLifecycleStages.includes(lifecycleStage);
537
+ }
538
+ async processPublish(pcd, changeDetail, fullChange, deleteChanges) {
539
+ console.info('processPublish-start');
540
+ const event = {
541
+ assortmentId: pcd.assortmentId,
542
+ assortmentPublishChangeId: pcd.assortmentPublishChangeId
543
+ };
544
+ const assortmentItemFullChangeMap = this.getFullChangeAssortmentMap(fullChange);
545
+ const assortmentItemDeleteMap = this.getDeleteChangesAssortmentMap(deleteChanges);
546
+ pcd.itemFamilyChanges = this.getItemFamilyChanges(pcd, changeDetail, assortmentItemFullChangeMap, assortmentItemDeleteMap);
547
+ const events = await this.getEventsForPublishChangeData(pcd);
548
+ if (events.length === 0) {
549
+ const processStatus = 'BaseProcessPublishAssortment: ' + event_short_message_status_1.EventShortMessageStatus.NO_EVENTS_TO_SEND
550
+ + ': assortmentId: ' + pcd.assortmentId + ': assortmentPublishChangeId: ' + pcd.assortmentPublishChangeId
551
+ + ', orgSlug: ' + this.orgSlug;
552
+ console.log(processStatus);
553
+ const message = 'No Events to Send; returning';
554
+ console.log(message);
555
+ return {
556
+ results: { message, event },
557
+ skip_await: true
558
+ };
559
+ }
560
+ const results = await this.sendEvents(events);
561
+ const resultsCount = this.getResultsCount(events);
562
+ const processStatus = 'BaseProcessPublishAssortment: ' + event_short_message_status_1.EventShortMessageStatus.SENDING_EVENTS
563
+ + ': assortmentId: ' + pcd.assortmentId + ': assortmentPublishChangeId: ' + pcd.assortmentPublishChangeId
564
+ + ': resultsCount: ' + JSON.stringify(resultsCount)
565
+ + ', orgSlug: ' + this.orgSlug;
566
+ console.log(processStatus);
567
+ return { results, event, resultsCount };
568
+ }
569
+ getResultsCount(events) {
570
+ return events.reduce((acc, event) => {
571
+ const objectClass = event?.objectClass;
572
+ if (objectClass === 'LCSProductSeasonLink') {
573
+ acc.productSeasons++;
574
+ }
575
+ else if (objectClass === 'LCSSKUSeasonLink') {
576
+ acc.colorwaySeasons++;
577
+ }
578
+ return acc;
579
+ }, { productSeasons: 0, colorwaySeasons: 0 });
580
+ }
581
+ async sendEvents(events) {
582
+ console.info('sendEvents()');
583
+ const eventType = 'ASYNC_PUBLISH_SEASON';
584
+ const sendMode = (this.config?.sendMode[eventType] || '').toLowerCase();
585
+ switch (sendMode) {
586
+ case 'sendtoflexplm':
587
+ return this.sendToFlexPLM(events, eventType);
588
+ case 'localfile':
589
+ return this.saveToLocalFile(events, eventType);
590
+ case 'vibeiqfile':
591
+ case 'vibeiqfile-dontsendtoflexplm':
592
+ return this.handleVibeIQFile(events, eventType, sendMode);
593
+ default:
594
+ return {}; // Or handle other cases as required
595
+ }
596
+ }
597
+ async sendToFlexPLM(events, eventType) {
598
+ const asyncEvent = {
599
+ taskId: this.config.taskId,
600
+ eventType,
601
+ objectClass: 'LCSSeason',
602
+ events
603
+ };
604
+ const flexPLMConnect = new flexplm_connect_1.FlexPLMConnect(this.config);
605
+ return await flexPLMConnect.sendToFlexPLM(asyncEvent);
606
+ }
607
+ async saveToLocalFile(events, eventType) {
608
+ let results = {};
609
+ let fileHandle;
610
+ try {
611
+ const dateString = this.getCurrentDateString();
612
+ const dirName = await path.resolve();
613
+ await fsPromise.mkdir(dirName, { recursive: true });
614
+ const eventDirName = 'events-output';
615
+ const fileName = `${path.sep}${eventDirName}${path.sep}events-${dateString}.json`;
616
+ fileHandle = await fsPromise.open('.' + fileName, 'a');
617
+ const asyncEvent = {
618
+ taskId: this.config.taskId,
619
+ eventType,
620
+ objectClass: 'LCSSeason',
621
+ events
622
+ };
623
+ await fileHandle.writeFile(JSON.stringify(asyncEvent));
624
+ results = {
625
+ message: 'Successfully Saved File',
626
+ fileLocation: dirName + fileName
627
+ };
628
+ }
629
+ catch (e) {
630
+ results['error'] = e.message;
631
+ }
632
+ finally {
633
+ await fileHandle?.close();
634
+ }
635
+ return results;
636
+ }
637
+ async handleVibeIQFile(events, eventType, sendMode) {
638
+ const eventBuffer = Buffer.from(JSON.stringify(events), 'utf-8');
639
+ const fileName = 'ASYNC_PUBLISH_SEASON-events.json';
640
+ const uploadFile = await new sdk_1.Files().createAndUploadFileFromBuffer(eventBuffer, 'application/json', fileName, null, this.TTL);
641
+ const asyncEvent = {
642
+ taskId: this.config.taskId,
643
+ eventType,
644
+ objectClass: 'LCSSeason',
645
+ eventsFileId: uploadFile.id,
646
+ eventsDownloadLink: uploadFile.downloadUrl,
647
+ eventsAdminDownloadLink: uploadFile.adminDownloadUrl
648
+ };
649
+ if (sendMode === 'vibeiqfile') {
650
+ const flexPLMConnect = new flexplm_connect_1.FlexPLMConnect(this.config);
651
+ return await flexPLMConnect.sendToFlexPLM(asyncEvent);
652
+ }
653
+ else {
654
+ return {
655
+ message: 'Successfully Uploaded File.',
656
+ asyncEvent
657
+ };
658
+ }
659
+ }
660
+ getCurrentDateString() {
661
+ const d = new Date();
662
+ return '' + d.getUTCFullYear()
663
+ + '-' + d.getUTCMonth()
664
+ + '-' + (d.getUTCDate() + 1)
665
+ + '-' + d.getUTCHours()
666
+ + '-' + d.getUTCMinutes()
667
+ + '-' + d.getUTCSeconds()
668
+ + '-' + d.getUTCMilliseconds();
669
+ }
670
+ getItemFamilyChanges(pcd, changeDetail, assortmentItemFullChangeMap, assortmentItemDeleteMap) {
671
+ console.info('getItemFamilyChanges-start');
672
+ const itemFamilyChanges = new Map();
673
+ const { adds, deletes, updates, familyItemsRemoved } = changeDetail;
674
+ const addIds = adds.map(item => item.id);
675
+ const deleteIds = deletes.map(item => item.id);
676
+ const updateIds = updates.map(item => item.id);
677
+ const familyItemsRemovedIds = familyItemsRemoved.map(item => item.itemId);
678
+ for (const [itemId, aItem] of assortmentItemFullChangeMap) {
679
+ const projectItem = aItem?.projectItem;
680
+ const itemFamilyId = aItem?.item?.itemFamilyId;
681
+ if (!pcd.releasedForDevelopmentItemIds.includes(itemFamilyId) || !pcd.releasedForDevelopmentItemIds.includes(itemId)) {
682
+ continue;
683
+ }
684
+ const ifc = itemFamilyChanges.get(itemFamilyId) || new item_family_changes_1.ItemFamilyChanges(itemFamilyId, pcd.sinceDate);
685
+ if (!itemFamilyChanges.has(itemFamilyId)) {
686
+ ifc.itemFamilyObject = itemId === itemFamilyId ? aItem?.item : aItem?.item?.itemFamily;
687
+ itemFamilyChanges.set(itemFamilyId, ifc);
688
+ }
689
+ ifc.assortmentItemFullChangeMap.set(itemId, aItem);
690
+ if (pcd.itemToFederatedIdMapping.has(itemId)) {
691
+ ifc.itemToFederatedIdMapping.set(itemId, pcd.itemToFederatedIdMapping.get(itemId));
692
+ }
693
+ if (itemId === itemFamilyId) {
694
+ if (addIds.includes(itemId))
695
+ ifc.familyAdd = true;
696
+ else if (deleteIds.includes(itemId))
697
+ ifc.familyDelete = true;
698
+ else if (updateIds.includes(itemId))
699
+ ifc.familyUpdate = true;
700
+ else if (familyItemsRemovedIds.includes(itemId))
701
+ ifc.familyItemRemoved = true;
702
+ if (projectItem) {
703
+ const updatedOnDate = new Date(projectItem.updatedOn);
704
+ if (updatedOnDate > pcd.sinceDate) {
705
+ ifc.familyUpdate = true;
706
+ }
707
+ }
708
+ if (projectItem && !projectItem?.roles && aItem?.item?.roles) {
709
+ projectItem.roles = aItem?.item?.roles;
710
+ }
711
+ }
712
+ else {
713
+ if (addIds.includes(itemId))
714
+ ifc.colorAdds.push(itemId);
715
+ else if (deleteIds.includes(itemId))
716
+ ifc.colorDeletes.push(itemId);
717
+ else if (updateIds.includes(itemId))
718
+ ifc.colorUpdates.push(itemId);
719
+ else if (projectItem && new Date(projectItem.updatedOn) > pcd.sinceDate)
720
+ ifc.colorUpdates.push(itemId);
721
+ else
722
+ ifc.colorUnchanged.push(itemId);
723
+ if (projectItem && !projectItem?.roles && aItem?.item?.roles) {
724
+ projectItem.roles = aItem?.item?.roles;
725
+ }
726
+ }
727
+ }
728
+ for (const [itemId, delEntity] of assortmentItemDeleteMap) {
729
+ const itemFamilyId = delEntity?.item?.itemFamilyId;
730
+ const item = delEntity?.item;
731
+ if (!pcd.releasedForDevelopmentItemIds.includes(itemFamilyId) || !pcd.releasedForDevelopmentItemIds.includes(itemId)) {
732
+ continue;
733
+ }
734
+ else {
735
+ if (!item) {
736
+ console.error('Failed to find deleted item entity');
737
+ console.error(' itemFamilyId: ' + itemFamilyId + ' -itemId: ' + itemId);
738
+ continue;
739
+ }
740
+ }
741
+ let ifc = itemFamilyChanges.get(itemFamilyId);
742
+ if (!ifc) {
743
+ ifc = new item_family_changes_1.ItemFamilyChanges(itemFamilyId, pcd.sinceDate);
744
+ if (itemId === itemFamilyId) {
745
+ ifc.familyDelete = true;
746
+ ifc.itemFamilyObject = item;
747
+ }
748
+ else {
749
+ ifc.itemFamilyObject = item?.itemFamily;
750
+ }
751
+ itemFamilyChanges.set(itemFamilyId, ifc);
752
+ }
753
+ if (pcd.itemToFederatedIdMapping.has(itemId)) {
754
+ ifc.itemToFederatedIdMapping.set(itemId, pcd.itemToFederatedIdMapping.get(itemId));
755
+ }
756
+ ifc.assortmentItemDeleteMap.set(itemId, delEntity);
757
+ ifc.assortmentItemFullChangeMap.set(itemId, delEntity);
758
+ if (itemId === itemFamilyId) {
759
+ ifc.familyDelete = true;
760
+ }
761
+ else {
762
+ ifc.colorDeletes.push(itemId);
763
+ }
764
+ } //End deletes for loop
765
+ if (app_framework_1.Logger.isDebugOn()) {
766
+ console.debug('returning size: ' + itemFamilyChanges.size);
767
+ for (const [key, value] of itemFamilyChanges) {
768
+ console.debug(key + ' => ' + value);
769
+ }
770
+ }
771
+ return itemFamilyChanges;
772
+ }
773
+ async getEventsForPublishChangeData(publishChangeData) {
774
+ console.info('getEventsForPublishChangeData-start');
775
+ const seasonalPayloads = [];
776
+ for (const itemFamilyChange of publishChangeData.itemFamilyChanges.values()) {
777
+ const events = await this.getEventsForItemFamilyChanges(itemFamilyChange, publishChangeData.assortmentId, publishChangeData.seasonFed, publishChangeData.itemToFederatedIdMapping);
778
+ if (events.length > 0) {
779
+ seasonalPayloads.push(...events);
780
+ }
781
+ }
782
+ return seasonalPayloads;
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
+ */
800
+ async getEventsForItemFamilyChanges(itemFamilyChanges, assortmentId, seasonFed, itemToFederatedIdMapping) {
801
+ console.info('getEventsForItemFamilyChanges()');
802
+ const events = [];
803
+ const LCSSeason = Object.assign({}, seasonFed);
804
+ const assortment = await this.getAssortment(assortmentId);
805
+ const projectItem = this.getProjectItem(itemFamilyChanges, itemFamilyChanges.itemFamilyId);
806
+ const familyAssortmentItem = itemFamilyChanges.familyAdd || itemFamilyChanges.familyUpdate || itemFamilyChanges.familyDelete
807
+ || itemFamilyChanges.colorAdds.length > 0 || itemFamilyChanges.colorUpdates.length > 0 || itemFamilyChanges.colorDeletes.length > 0
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.
811
+ if (familyAssortmentItem) {
812
+ //Product-season add
813
+ const entityReference = itemFamilyChanges.itemFamilyId;
814
+ const prodEntityData = (itemFamilyChanges.assortmentItemFullChangeMap.has(entityReference))
815
+ ? itemFamilyChanges.assortmentItemFullChangeMap.get(entityReference)?.item
816
+ : itemFamilyChanges.itemFamilyObject;
817
+ let seasonalData = await this.getSeasonalData(projectItem);
818
+ seasonalData = await map_utils_1.MapUtil.applyTransformMap(this.transformMapFile, this.mapFileUtil, seasonalData, 'LCSProductSeasonLink', 'vibe2flex');
819
+ const LCSProduct = await this.getProductFederation(entityReference, itemToFederatedIdMapping, prodEntityData);
820
+ const isCreatable = await type_conversion_utils_1.TypeConversionUtils.isOutboundCreatableFromEntity(this.transformMapFile, this.mapFileUtil, projectItem, { item: prodEntityData, assortment });
821
+ const eventType = isCreatable ? 'UPSERT_ON_SEASON' : 'UPDATE_ON_SEASON';
822
+ const psUpsert = {
823
+ eventType,
824
+ objectClass: 'LCSProductSeasonLink',
825
+ entityReference: 'item:' + entityReference,
826
+ LCSSeason,
827
+ LCSProduct,
828
+ data: seasonalData
829
+ };
830
+ if ('ASSORTMENT' === projectItem.addedFromSource && projectItem.addedFromAssortment) {
831
+ const carriedFromSeason = await this.getCarriedFromSeason(projectItem);
832
+ if (carriedFromSeason) {
833
+ psUpsert['carriedFromSeason'] = carriedFromSeason;
834
+ }
835
+ }
836
+ events.push(psUpsert);
837
+ }
838
+ //colorway-season adds
839
+ //colorAdds
840
+ //colorUpdates
841
+ const colorwayChanges = [];
842
+ colorwayChanges.push(...itemFamilyChanges.colorAdds);
843
+ colorwayChanges.push(...itemFamilyChanges.colorUpdates);
844
+ colorwayChanges.push(...itemFamilyChanges.colorDeletes);
845
+ for (const entityReference of colorwayChanges) {
846
+ let item = {};
847
+ if (itemFamilyChanges.assortmentItemFullChangeMap.has(entityReference)) {
848
+ const fullAi = itemFamilyChanges.assortmentItemFullChangeMap.get(entityReference);
849
+ item = fullAi?.item;
850
+ }
851
+ const projectItem = this.getProjectItem(itemFamilyChanges, entityReference);
852
+ let seasonalData = await this.getSeasonalData(projectItem);
853
+ seasonalData = await map_utils_1.MapUtil.applyTransformMap(this.transformMapFile, this.mapFileUtil, seasonalData, 'LCSSKUSeasonLink', 'vibe2flex');
854
+ const LCSSKU = await this.getSKUFederation(entityReference, itemToFederatedIdMapping, item);
855
+ const isCreatable = await type_conversion_utils_1.TypeConversionUtils.isOutboundCreatableFromEntity(this.transformMapFile, this.mapFileUtil, projectItem, { item, assortment });
856
+ const eventType = isCreatable ? 'UPSERT_ON_SEASON' : 'UPDATE_ON_SEASON';
857
+ const csUpsert = {
858
+ eventType,
859
+ objectClass: 'LCSSKUSeasonLink',
860
+ entityReference: 'item:' + entityReference,
861
+ LCSSeason,
862
+ LCSSKU,
863
+ data: seasonalData
864
+ };
865
+ if ('ASSORTMENT' === projectItem.addedFromSource && projectItem.addedFromAssortment) {
866
+ const carriedFromSeason = await this.getCarriedFromSeason(projectItem);
867
+ if (carriedFromSeason) {
868
+ csUpsert['carriedFromSeason'] = carriedFromSeason;
869
+ }
870
+ }
871
+ events.push(csUpsert);
872
+ }
873
+ return events;
874
+ }
875
+ getProjectItem(itemFamilyChanges, id) {
876
+ let projectItem = {};
877
+ if (itemFamilyChanges?.assortmentItemFullChangeMap.has(id)) {
878
+ const aItem = itemFamilyChanges?.assortmentItemFullChangeMap.get(id);
879
+ projectItem = aItem?.projectItem;
880
+ }
881
+ /////////////////////////////////////////////////////////////////////////////
882
+ //Get from option assortmentItem because family not in assortmentItemFullChangeMap:start
883
+ /////////////////////////////////////////////////////////////////////////////
884
+ if (id === itemFamilyChanges.itemFamilyId && Object.keys(projectItem).length == 0) {
885
+ for (const asstItem of itemFamilyChanges.assortmentItemFullChangeMap.values()) {
886
+ if (asstItem?.familyProjectItem) {
887
+ projectItem = asstItem?.familyProjectItem;
888
+ break;
889
+ }
890
+ }
891
+ }
892
+ /////////////////////////////////////////////////////////////////////////////
893
+ //Get from option assortmentItem because family not in assortmentItemFullChangeMap:end
894
+ /////////////////////////////////////////////////////////////////////////////
895
+ return projectItem;
896
+ }
897
+ async getSeasonalData(projectItem) {
898
+ return await this.dc.getFlexPLMObjectData(projectItem, [], true);
899
+ }
900
+ async getProductFederation(entityReference, itemToFederatedIdMapping, itemFamilyObject) {
901
+ const itemObj = await this.dc.getFlexPLMObjectData(itemFamilyObject, [], true);
902
+ let prodObj = {
903
+ entityReference: 'item:' + entityReference,
904
+ objectClass: 'LCSProduct',
905
+ };
906
+ const identifierKeys = await type_conversion_utils_1.TypeConversionUtils.getIdentifierProperties(this.transformMapFile, this.mapFileUtil, itemFamilyObject);
907
+ const informationKeys = await type_conversion_utils_1.TypeConversionUtils.getInformationalProperties(this.transformMapFile, this.mapFileUtil, itemFamilyObject);
908
+ for (const key of identifierKeys.concat(informationKeys)) {
909
+ if (itemObj[key]) {
910
+ prodObj[key] = itemObj[key];
911
+ }
912
+ }
913
+ if (!prodObj?.vibeIQIdentifier) {
914
+ prodObj.vibeIQIdentifier = itemFamilyObject?.identifier || itemFamilyObject?.itemNumber;
915
+ }
916
+ prodObj = await map_utils_1.MapUtil.applyTransformMap(this.transformMapFile, this.mapFileUtil, prodObj, 'LCSProduct', 'vibe2flex');
917
+ return prodObj;
918
+ }
919
+ async getSKUFederation(entityReference, itemToFederatedIdMapping, itemObject) {
920
+ const itemObj = await this.dc.getFlexPLMObjectData(itemObject, [], true);
921
+ let skuObj = {
922
+ entityReference: 'item:' + entityReference,
923
+ objectClass: 'LCSSKU',
924
+ };
925
+ const identifierKeys = await type_conversion_utils_1.TypeConversionUtils.getIdentifierProperties(this.transformMapFile, this.mapFileUtil, itemObject);
926
+ const informationKeys = await type_conversion_utils_1.TypeConversionUtils.getInformationalProperties(this.transformMapFile, this.mapFileUtil, itemObject);
927
+ for (const key of identifierKeys.concat(informationKeys)) {
928
+ if (itemObj[key]) {
929
+ skuObj[key] = itemObj[key];
930
+ }
931
+ }
932
+ if (!skuObj?.vibeIQIdentifier) {
933
+ skuObj.vibeIQIdentifier = itemObject?.identifier || itemObject?.itemNumber;
934
+ }
935
+ skuObj = await map_utils_1.MapUtil.applyTransformMap(this.transformMapFile, this.mapFileUtil, skuObj, 'LCSSKU', 'vibe2flex');
936
+ return skuObj;
937
+ }
938
+ async getCarriedFromSeason(projectItem) {
939
+ const addedFromAssortment = projectItem.addedFromAssortment;
940
+ if (this.cache.carriedFromSeason[addedFromAssortment]) {
941
+ return this.cache.carriedFromSeason[addedFromAssortment];
942
+ }
943
+ let seasonFederation = undefined;
944
+ const assortmentEntity = await this.getAssormentEntityFromId(addedFromAssortment);
945
+ if (assortmentEntity) {
946
+ seasonFederation = await this.getSeasonFederationFromAssortment(assortmentEntity);
947
+ }
948
+ this.cache.carriedFromSeason[addedFromAssortment] = seasonFederation;
949
+ return seasonFederation;
950
+ }
951
+ async getAssormentEntityFromId(assortmentId) {
952
+ let assortment;
953
+ try {
954
+ assortment = await new sdk_1.Entities().get({
955
+ entityName: 'assortment',
956
+ id: assortmentId
957
+ });
958
+ }
959
+ catch (e) {
960
+ console.warn(`No Assortment found for id: ${assortmentId}`);
961
+ }
962
+ return assortment;
963
+ }
964
+ async getSeasonFederationFromAssortment(assortment) {
965
+ assortment['flex2vibeMapKeyRoot'] = 'LCSSeason';
966
+ const flexPLMTypePath = await type_conversion_utils_1.TypeConversionUtils.getObjectTypePath(this.transformMapFile, this.mapFileUtil, assortment);
967
+ let seasonObj = {
968
+ entityReference: 'assortment:' + assortment.id,
969
+ objectClass: 'LCSSeason',
970
+ flexPLMTypePath
971
+ };
972
+ try {
973
+ const assortmentObj = await this.dc.getFlexPLMObjectData(assortment, [], true);
974
+ const identifierKeys = await type_conversion_utils_1.TypeConversionUtils.getIdentifierProperties(this.transformMapFile, this.mapFileUtil, assortment);
975
+ const informationKeys = await type_conversion_utils_1.TypeConversionUtils.getInformationalProperties(this.transformMapFile, this.mapFileUtil, assortment);
976
+ for (const key of identifierKeys.concat(informationKeys)) {
977
+ if (assortmentObj[key]) {
978
+ seasonObj[key] = assortmentObj[key];
979
+ }
980
+ }
981
+ const objectKeys = Object.keys(seasonObj);
982
+ const hasAllIdentifiers = identifierKeys.every(key => objectKeys.includes(key));
983
+ if (!hasAllIdentifiers) {
984
+ return undefined;
985
+ }
986
+ const mapKey = await type_conversion_utils_1.TypeConversionUtils.getMapKey(this.transformMapFile, this.mapFileUtil, assortment, type_conversion_utils_1.TypeConversionUtils.VIBE2FLEX_DIRECTION);
987
+ seasonObj = await map_utils_1.MapUtil.applyTransformMap(this.transformMapFile, this.mapFileUtil, seasonObj, mapKey, type_conversion_utils_1.TypeConversionUtils.VIBE2FLEX_DIRECTION);
988
+ }
989
+ finally {
990
+ delete assortment['flex2vibeMapKeyRoot'];
991
+ }
992
+ return seasonObj;
993
+ }
994
+ }
995
+ exports.BaseProcessPublishAssortment = BaseProcessPublishAssortment;
996
+ BaseProcessPublishAssortment.ASSORTMENT_NOT_PUBLISHABLE = 'Assortment isn\'t marked for publishing to FlexPLM';
997
+ BaseProcessPublishAssortment.ASSORTMENT_NO_FED_INFO = 'Assortment doesn\'t have all "identifier" properties, so there is no processing. Identifier properties: ';
998
+ BaseProcessPublishAssortment.NOT_ABLE_TO_PROCESS_DELETE_CHANGES = 'Error: Not able to process delete changes';