@contrail/flexplm 1.8.1 → 2.0.0-alpha.9af4b96
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/__tests__/telemetry-test-utils.d.ts +8 -0
- package/lib/__tests__/telemetry-test-utils.js +30 -0
- package/lib/cli/commands/compile.js +17 -7
- package/lib/cli/commands/compile.spec.js +17 -7
- package/lib/cli/commands/create.js +17 -7
- package/lib/cli/commands/create.spec.js +17 -7
- package/lib/cli/commands/upload.js +17 -7
- package/lib/cli/index.js +2 -2
- package/lib/entity-processor/base-entity-processor.d.ts +29 -0
- package/lib/entity-processor/base-entity-processor.js +238 -143
- package/lib/entity-processor/base-entity-processor.spec.js +212 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/interfaces/interfaces.d.ts +1 -1
- package/lib/publish/base-process-publish-assortment-callback.js +30 -0
- package/lib/publish/base-process-publish-assortment-callback.spec.d.ts +1 -0
- package/lib/publish/base-process-publish-assortment-callback.spec.js +62 -0
- package/lib/publish/base-process-publish-assortment.d.ts +24 -0
- package/lib/publish/base-process-publish-assortment.js +243 -132
- package/lib/publish/base-process-publish-assortment.spec.js +188 -0
- package/lib/util/config-defaults.js +2 -0
- package/lib/util/error-response-object.js +2 -0
- package/lib/util/event-short-message-status.js +1 -1
- package/lib/util/flexplm-connect.d.ts +4 -0
- package/lib/util/flexplm-connect.js +210 -145
- package/lib/util/flexplm-connect.telemetry.spec.d.ts +1 -0
- package/lib/util/flexplm-connect.telemetry.spec.js +212 -0
- package/lib/util/logger-config.js +1 -2
- package/lib/util/telemetry-attributes.d.ts +68 -0
- package/lib/util/telemetry-attributes.js +73 -0
- package/lib/util/telemetry-utils.d.ts +3 -0
- package/lib/util/telemetry-utils.js +10 -0
- package/lib/util/thumbnail-util.d.ts +2 -1
- package/lib/util/thumbnail-util.js +35 -7
- package/lib/util/thumbnail-util.spec.js +42 -0
- package/package.json +10 -3
|
@@ -11,6 +11,12 @@ const fsPromise = require("fs/promises");
|
|
|
11
11
|
const path = require("path");
|
|
12
12
|
const app_framework_1 = require("@contrail/app-framework");
|
|
13
13
|
const event_short_message_status_1 = require("../util/event-short-message-status");
|
|
14
|
+
const telemetry_1 = require("@contrail/telemetry");
|
|
15
|
+
const api_1 = require("@opentelemetry/api");
|
|
16
|
+
const telemetry_attributes_1 = require("../util/telemetry-attributes");
|
|
17
|
+
const telemetry_utils_1 = require("../util/telemetry-utils");
|
|
18
|
+
const PUBLISH_FILE_HYDRATED_DETAIL = 'hydrated_detail';
|
|
19
|
+
const PUBLISH_FILE_BASELINE = 'baseline';
|
|
14
20
|
class BaseProcessPublishAssortment {
|
|
15
21
|
constructor(_config, _dc, _mapFileUtil) {
|
|
16
22
|
this.TTL = 30 * 24 * 60 * 60 * 1000; // 30 days
|
|
@@ -24,61 +30,120 @@ class BaseProcessPublishAssortment {
|
|
|
24
30
|
this.orgSlug = this.config?.orgSlug || 'unset-orgSlug';
|
|
25
31
|
}
|
|
26
32
|
async process(event) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
return (0, telemetry_1.traceSection)('publish_assortment', {}, async (span) => {
|
|
34
|
+
const assortmentPublishChangeId = event.assortmentPublishChangeId;
|
|
35
|
+
let apcHistory;
|
|
36
|
+
let publisher;
|
|
37
|
+
const assortmentId = event.assortmentId;
|
|
38
|
+
(0, telemetry_utils_1.setActiveSpanAttributes)({
|
|
39
|
+
[telemetry_attributes_1.ATTR_FLEXPLM_ASSORTMENT_ID]: assortmentId,
|
|
40
|
+
[telemetry_attributes_1.ATTR_FLEXPLM_PUBLISH_CHANGE_ID]: assortmentPublishChangeId,
|
|
41
|
+
});
|
|
35
42
|
try {
|
|
36
|
-
seasonFed
|
|
43
|
+
let seasonFed;
|
|
44
|
+
try {
|
|
45
|
+
seasonFed = await this.getSeasonFederation(assortmentId);
|
|
46
|
+
}
|
|
47
|
+
catch (e) {
|
|
48
|
+
const message = e.message;
|
|
49
|
+
const eventStatus = (message.includes(BaseProcessPublishAssortment.ASSORTMENT_NOT_PUBLISHABLE)) ? event_short_message_status_1.EventShortMessageStatus.NOT_PUBLISHABLE :
|
|
50
|
+
(message.startsWith(BaseProcessPublishAssortment.ASSORTMENT_NO_FED_INFO)) ? event_short_message_status_1.EventShortMessageStatus.NO_FEDERATION_INFO :
|
|
51
|
+
event_short_message_status_1.EventShortMessageStatus.FAILURE;
|
|
52
|
+
if (eventStatus === event_short_message_status_1.EventShortMessageStatus.FAILURE) {
|
|
53
|
+
span.recordException(e);
|
|
54
|
+
span.setStatus({ code: api_1.SpanStatusCode.ERROR, message });
|
|
55
|
+
}
|
|
56
|
+
this.recordPublishStatus({
|
|
57
|
+
status: eventStatus,
|
|
58
|
+
assortmentId,
|
|
59
|
+
assortmentPublishChangeId,
|
|
60
|
+
message,
|
|
61
|
+
// Only an unexpected error is swallowed here; the other two are expected skips.
|
|
62
|
+
error: eventStatus === event_short_message_status_1.EventShortMessageStatus.FAILURE ? e : undefined,
|
|
63
|
+
});
|
|
64
|
+
const output = {
|
|
65
|
+
results: { message, event },
|
|
66
|
+
skip_await: true
|
|
67
|
+
};
|
|
68
|
+
return output;
|
|
69
|
+
}
|
|
70
|
+
apcHistory = await this.getApcHistory(assortmentId);
|
|
71
|
+
const sinceDate = await this.getSinceDate(assortmentId, assortmentPublishChangeId, apcHistory);
|
|
72
|
+
//Get detail information
|
|
73
|
+
const assortmentPublishChange = await this.downloadAssortmentPublishChange(assortmentId, assortmentPublishChangeId);
|
|
74
|
+
publisher = this.getPublisher(assortmentPublishChange);
|
|
75
|
+
const changeDetail = await this.downloadHydratedChangeDetail(assortmentPublishChange);
|
|
76
|
+
// Without the change detail the counts are unknown, not zero; failed_downloads says why.
|
|
77
|
+
if (changeDetail) {
|
|
78
|
+
span.setAttributes({
|
|
79
|
+
[telemetry_attributes_1.ATTR_FLEXPLM_PUBLISH_ADDED_COUNT]: changeDetail.adds?.length ?? 0,
|
|
80
|
+
[telemetry_attributes_1.ATTR_FLEXPLM_PUBLISH_REMOVED_COUNT]: changeDetail.deletes?.length ?? 0,
|
|
81
|
+
[telemetry_attributes_1.ATTR_FLEXPLM_PUBLISH_CHANGED_COUNT]: changeDetail.updates?.length ?? 0,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
const assortmentBaseline = await this.downloadAssortmentBaseline(assortmentPublishChange);
|
|
85
|
+
const failedDownloads = [!changeDetail && PUBLISH_FILE_HYDRATED_DETAIL, !assortmentBaseline && PUBLISH_FILE_BASELINE].filter(Boolean);
|
|
86
|
+
if (failedDownloads.length > 0) {
|
|
87
|
+
span.setAttribute(telemetry_attributes_1.ATTR_FLEXPLM_PUBLISH_FAILED_DOWNLOADS, failedDownloads);
|
|
88
|
+
}
|
|
89
|
+
const deleteChanges = await this.getDeleteChanges(assortmentPublishChange, apcHistory, assortmentBaseline, sinceDate);
|
|
90
|
+
const releasedForDevelopmentItemIds = this.getReleasedForDevelopmentItemAndFamilyIds(assortmentBaseline, deleteChanges);
|
|
91
|
+
const itemToFederatedIdMapping = await this.getItemFederatedIds( /*allItemIds*/);
|
|
92
|
+
const pcd = new publish_change_data_1.PublishChangeData(assortmentId, seasonFed, assortmentPublishChangeId, sinceDate, publisher);
|
|
93
|
+
pcd.itemToFederatedIdMapping = itemToFederatedIdMapping;
|
|
94
|
+
pcd.releasedForDevelopmentItemIds = releasedForDevelopmentItemIds;
|
|
95
|
+
const output = await this.processPublish(pcd, changeDetail, assortmentBaseline, deleteChanges);
|
|
96
|
+
output['publishInfo'] = await this.getPublishInfo(assortmentId, assortmentPublishChangeId, apcHistory, publisher);
|
|
97
|
+
return output;
|
|
37
98
|
}
|
|
38
99
|
catch (e) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
};
|
|
52
|
-
|
|
100
|
+
try {
|
|
101
|
+
const publishInfo = await this.getPublishInfo(assortmentId, assortmentPublishChangeId, apcHistory, publisher);
|
|
102
|
+
e.publishInfo = publishInfo;
|
|
103
|
+
}
|
|
104
|
+
catch (e2) {
|
|
105
|
+
const assortment = { id: assortmentId, publish_change_id: assortmentPublishChangeId };
|
|
106
|
+
telemetry_1.logger.error({ error: e2, assortment }, 'getPublishInfo failed: %j', assortment);
|
|
107
|
+
}
|
|
108
|
+
this.recordPublishStatus({
|
|
109
|
+
status: event_short_message_status_1.EventShortMessageStatus.FAILURE, assortmentId, assortmentPublishChangeId, error: e,
|
|
110
|
+
// sendAndRecordPublishEvent only sets this after FlexPLM accepted the publish.
|
|
111
|
+
sentUnrecorded: e?.sendPublishPayloadEventResult !== undefined,
|
|
112
|
+
});
|
|
113
|
+
throw e;
|
|
53
114
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
/** Records the result of one assortment publish: attributes on the active span plus one structured log.
|
|
118
|
+
* Not_Publishable / No_Federation_Information are handled skips, so the span stays OK for them.
|
|
119
|
+
*/
|
|
120
|
+
recordPublishStatus(statusObject) {
|
|
121
|
+
const { status, assortmentId, assortmentPublishChangeId, resultsCount, message, error } = statusObject;
|
|
122
|
+
const sentUnrecorded = statusObject.sentUnrecorded || undefined;
|
|
123
|
+
(0, telemetry_utils_1.setActiveSpanAttributes)({
|
|
124
|
+
[telemetry_attributes_1.ATTR_FLEXPLM_STATUS]: status,
|
|
125
|
+
[telemetry_attributes_1.ATTR_FLEXPLM_ASSORTMENT_ID]: assortmentId,
|
|
126
|
+
[telemetry_attributes_1.ATTR_FLEXPLM_PUBLISH_CHANGE_ID]: assortmentPublishChangeId,
|
|
127
|
+
[telemetry_attributes_1.ATTR_FLEXPLM_PUBLISH_PRODUCT_SEASON_COUNT]: resultsCount?.productSeasons,
|
|
128
|
+
[telemetry_attributes_1.ATTR_FLEXPLM_PUBLISH_COLORWAY_SEASON_COUNT]: resultsCount?.colorwaySeasons,
|
|
129
|
+
[telemetry_attributes_1.ATTR_FLEXPLM_PUBLISH_SENT_UNRECORDED]: sentUnrecorded,
|
|
130
|
+
});
|
|
131
|
+
const flexplm = {
|
|
132
|
+
direction: 'publish', status, assortment_id: assortmentId, publish_change_id: assortmentPublishChangeId,
|
|
133
|
+
product_season_count: resultsCount?.productSeasons, colorway_season_count: resultsCount?.colorwaySeasons, reason: message,
|
|
134
|
+
sent_unrecorded: sentUnrecorded,
|
|
135
|
+
};
|
|
136
|
+
if (sentUnrecorded) {
|
|
137
|
+
telemetry_1.logger.error({ error, flexplm }, 'FlexPLM assortment publish sent but not recorded: %j', flexplm);
|
|
71
138
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
console.error('process failed: assortmentId: ' + assortmentId + ': ' + e.message);
|
|
81
|
-
throw e;
|
|
139
|
+
else if (status === event_short_message_status_1.EventShortMessageStatus.FAILURE) {
|
|
140
|
+
telemetry_1.logger.error({ error, flexplm }, 'FlexPLM assortment publish failed: %j', flexplm);
|
|
141
|
+
}
|
|
142
|
+
else if (status === event_short_message_status_1.EventShortMessageStatus.NO_FEDERATION_INFO) {
|
|
143
|
+
telemetry_1.logger.warn({ flexplm }, 'FlexPLM assortment publish skipped: %j', flexplm);
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
telemetry_1.logger.info({ flexplm }, 'FlexPLM assortment publish processed: %j', flexplm);
|
|
82
147
|
}
|
|
83
148
|
}
|
|
84
149
|
async getPublishInfo(assortmentId, assortmentPublishChangeId, apcHistory, publisher) {
|
|
@@ -211,13 +276,18 @@ class BaseProcessPublishAssortment {
|
|
|
211
276
|
return sinceDate;
|
|
212
277
|
}
|
|
213
278
|
async getApcHistory(assortmentId) {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
279
|
+
return (0, telemetry_1.traceSection)('get_publish_history', {}, async (span) => {
|
|
280
|
+
const resource = `/assortments/${assortmentId}/history/`;
|
|
281
|
+
const history = await sdk_1.Request.request(resource, {
|
|
282
|
+
method: 'GET',
|
|
283
|
+
headers: {
|
|
284
|
+
Accept: 'application/json',
|
|
285
|
+
'Content-Type': 'application/json'
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
if (Array.isArray(history))
|
|
289
|
+
span.setAttribute(telemetry_attributes_1.ATTR_FLEXPLM_PUBLISH_HISTORY_COUNT, history.length);
|
|
290
|
+
return history;
|
|
221
291
|
});
|
|
222
292
|
}
|
|
223
293
|
updatedSinceDate(entity, sinceDate) {
|
|
@@ -315,30 +385,43 @@ class BaseProcessPublishAssortment {
|
|
|
315
385
|
});
|
|
316
386
|
}
|
|
317
387
|
async downloadHydratedChangeDetail(assortmentPublishChange) {
|
|
318
|
-
|
|
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.error('Error hydratedDetailDownloadLink: ' + e.message);
|
|
326
|
-
}
|
|
327
|
-
return undefined;
|
|
388
|
+
return this.downloadPublishFile(assortmentPublishChange?.hydratedDetailDownloadLink, PUBLISH_FILE_HYDRATED_DETAIL);
|
|
328
389
|
}
|
|
329
390
|
async downloadAssortmentBaseline(assortmentPublishChange) {
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
391
|
+
return this.downloadPublishFile(assortmentPublishChange?.assortmentBaselineDownloadLink, PUBLISH_FILE_BASELINE);
|
|
392
|
+
}
|
|
393
|
+
/** Downloads one publish file as JSON. Returns undefined when it can't be read, so the publish continues without it;
|
|
394
|
+
* the span is marked ERROR here because nothing is thrown. The link is never recorded (it is a signed URL).
|
|
395
|
+
*/
|
|
396
|
+
async downloadPublishFile(link, file) {
|
|
397
|
+
return (0, telemetry_1.traceSection)('download_publish_file', { [telemetry_attributes_1.ATTR_FLEXPLM_PUBLISH_FILE]: file }, async (span) => {
|
|
398
|
+
let httpStatus;
|
|
399
|
+
try {
|
|
400
|
+
const response = await fetch(link);
|
|
401
|
+
httpStatus = response.status;
|
|
402
|
+
span.setAttribute(telemetry_attributes_1.ATTR_HTTP_RESPONSE_STATUS_CODE, httpStatus);
|
|
403
|
+
if (!response.ok) {
|
|
404
|
+
throw new Error('Download failed: status: ' + httpStatus);
|
|
405
|
+
}
|
|
406
|
+
return await response.json();
|
|
407
|
+
}
|
|
408
|
+
catch (e) {
|
|
409
|
+
span.recordException(e);
|
|
410
|
+
span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: e?.message });
|
|
411
|
+
const flexplm = { direction: 'publish', publish_file: file, http_status: httpStatus };
|
|
412
|
+
telemetry_1.logger.error({ error: e, flexplm }, 'FlexPLM publish file download failed: %j', flexplm);
|
|
413
|
+
return undefined;
|
|
414
|
+
}
|
|
415
|
+
});
|
|
340
416
|
}
|
|
341
417
|
async getDeleteChanges(assortmentPublishChange, apcHistory, assortmentBaseline, sinceDate) {
|
|
418
|
+
return (0, telemetry_1.traceSection)('get_delete_changes', {}, async (span) => {
|
|
419
|
+
const deleteChanges = await this.collectDeleteChanges(assortmentPublishChange, apcHistory, assortmentBaseline, sinceDate);
|
|
420
|
+
span.setAttribute(telemetry_attributes_1.ATTR_FLEXPLM_PUBLISH_DELETE_CHANGE_COUNT, deleteChanges?.length ?? 0);
|
|
421
|
+
return deleteChanges;
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
async collectDeleteChanges(assortmentPublishChange, apcHistory, assortmentBaseline, sinceDate) {
|
|
342
425
|
const currentAPCIndex = apcHistory.findIndex(pc => pc?.id === assortmentPublishChange?.id);
|
|
343
426
|
if (currentAPCIndex == 0) {
|
|
344
427
|
return [];
|
|
@@ -383,6 +466,7 @@ class BaseProcessPublishAssortment {
|
|
|
383
466
|
console.info('getDeleteChanges(): baselineItems: ' + currentAssortmentItemIds.length
|
|
384
467
|
+ ', scanned ' + scannedCount + ' historical delete changes, added ' + addedCount
|
|
385
468
|
+ ', deleteIds: ' + deleteIds.length);
|
|
469
|
+
(0, telemetry_utils_1.setActiveSpanAttributes)({ [telemetry_attributes_1.ATTR_FLEXPLM_PUBLISH_HISTORICAL_DELETE_COUNT]: addedCount });
|
|
386
470
|
console.info('getDeleteChanges()-deleteIds: ' + deleteIds);
|
|
387
471
|
}
|
|
388
472
|
return apcDeletes;
|
|
@@ -559,12 +643,12 @@ class BaseProcessPublishAssortment {
|
|
|
559
643
|
pcd.itemFamilyChanges = this.getItemFamilyChanges(pcd, changeDetail, assortmentItemFullChangeMap, assortmentItemDeleteMap);
|
|
560
644
|
const events = await this.getEventsForPublishChangeData(pcd);
|
|
561
645
|
if (events.length === 0) {
|
|
562
|
-
const processStatus = 'BaseProcessPublishAssortment: ' + event_short_message_status_1.EventShortMessageStatus.NO_EVENTS_TO_SEND
|
|
563
|
-
+ ': assortmentId: ' + pcd.assortmentId + ': assortmentPublishChangeId: ' + pcd.assortmentPublishChangeId
|
|
564
|
-
+ ', orgSlug: ' + this.orgSlug;
|
|
565
|
-
console.log(processStatus);
|
|
566
646
|
const message = 'No Events to Send; returning';
|
|
567
|
-
|
|
647
|
+
this.recordPublishStatus({
|
|
648
|
+
status: event_short_message_status_1.EventShortMessageStatus.NO_EVENTS_TO_SEND,
|
|
649
|
+
assortmentId: pcd.assortmentId,
|
|
650
|
+
assortmentPublishChangeId: pcd.assortmentPublishChangeId,
|
|
651
|
+
});
|
|
568
652
|
return {
|
|
569
653
|
results: { message, event },
|
|
570
654
|
skip_await: true
|
|
@@ -572,11 +656,12 @@ class BaseProcessPublishAssortment {
|
|
|
572
656
|
}
|
|
573
657
|
const results = await this.sendEvents(events);
|
|
574
658
|
const resultsCount = this.getResultsCount(events);
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
659
|
+
this.recordPublishStatus({
|
|
660
|
+
status: event_short_message_status_1.EventShortMessageStatus.SENDING_EVENTS,
|
|
661
|
+
assortmentId: pcd.assortmentId,
|
|
662
|
+
assortmentPublishChangeId: pcd.assortmentPublishChangeId,
|
|
663
|
+
resultsCount,
|
|
664
|
+
});
|
|
580
665
|
return { results, event, resultsCount };
|
|
581
666
|
}
|
|
582
667
|
getResultsCount(events) {
|
|
@@ -595,6 +680,7 @@ class BaseProcessPublishAssortment {
|
|
|
595
680
|
console.info('sendEvents()');
|
|
596
681
|
const eventType = 'ASYNC_PUBLISH_SEASON';
|
|
597
682
|
const sendMode = (this.config?.sendMode[eventType] || '').toLowerCase();
|
|
683
|
+
(0, telemetry_utils_1.setActiveSpanAttributes)({ [telemetry_attributes_1.ATTR_FLEXPLM_PUBLISH_SEND_MODE]: sendMode });
|
|
598
684
|
switch (sendMode) {
|
|
599
685
|
case 'sendtoflexplm':
|
|
600
686
|
return this.sendToFlexPLM(events, eventType);
|
|
@@ -604,22 +690,26 @@ class BaseProcessPublishAssortment {
|
|
|
604
690
|
case 'vibeiqfile-dontsendtoflexplm':
|
|
605
691
|
return this.handleVibeIQFile(events, eventType, sendMode);
|
|
606
692
|
default:
|
|
693
|
+
const flexplm = { direction: 'publish', send_mode: sendMode, payload_count: events?.length ?? 0 };
|
|
694
|
+
telemetry_1.logger.warn({ flexplm }, 'Unknown FlexPLM publish sendMode; events not sent: %j', flexplm);
|
|
607
695
|
return {}; // Or handle other cases as required
|
|
608
696
|
}
|
|
609
697
|
}
|
|
610
698
|
async sendToFlexPLM(events, eventType) {
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
699
|
+
return (0, telemetry_1.traceSection)('send_publish_batch', { [telemetry_attributes_1.ATTR_FLEXPLM_PAYLOAD_COUNT]: events?.length ?? 0 }, async () => {
|
|
700
|
+
const outboundPublishEvent = {
|
|
701
|
+
taskId: this.config.taskId,
|
|
702
|
+
eventType,
|
|
703
|
+
objectClass: 'LCSSeason',
|
|
704
|
+
events
|
|
705
|
+
};
|
|
706
|
+
const flexPLMConnect = new flexplm_connect_1.FlexPLMConnect(this.config);
|
|
707
|
+
const result = await this.sendAndRecordPublishEvent(flexPLMConnect, outboundPublishEvent);
|
|
708
|
+
const isResultObject = typeof result === 'object' && result !== null;
|
|
709
|
+
return isResultObject
|
|
710
|
+
? { ...result, outboundPublishEvent }
|
|
711
|
+
: { result, outboundPublishEvent };
|
|
712
|
+
});
|
|
623
713
|
}
|
|
624
714
|
async sendAndRecordPublishEvent(flexPLMConnect, outboundPublishEvent) {
|
|
625
715
|
let sendResult;
|
|
@@ -668,6 +758,8 @@ class BaseProcessPublishAssortment {
|
|
|
668
758
|
};
|
|
669
759
|
}
|
|
670
760
|
catch (e) {
|
|
761
|
+
const flexplm = { direction: 'publish', send_mode: 'localfile' };
|
|
762
|
+
telemetry_1.logger.error({ error: e, flexplm }, 'Saving FlexPLM publish events to a local file failed: %j', flexplm);
|
|
671
763
|
results['error'] = e.message;
|
|
672
764
|
}
|
|
673
765
|
finally {
|
|
@@ -678,7 +770,7 @@ class BaseProcessPublishAssortment {
|
|
|
678
770
|
async handleVibeIQFile(events, eventType, sendMode) {
|
|
679
771
|
const eventBuffer = Buffer.from(JSON.stringify(events), 'utf-8');
|
|
680
772
|
const fileName = 'ASYNC_PUBLISH_SEASON-events.json';
|
|
681
|
-
const uploadFile = await new sdk_1.Files().createAndUploadFileFromBuffer(eventBuffer, 'application/json', fileName, null, this.TTL);
|
|
773
|
+
const uploadFile = await (0, telemetry_1.traceSection)('upload_publish_events_file', { [telemetry_attributes_1.ATTR_FLEXPLM_PAYLOAD_COUNT]: events?.length ?? 0 }, () => new sdk_1.Files().createAndUploadFileFromBuffer(eventBuffer, 'application/json', fileName, null, this.TTL));
|
|
682
774
|
const outboundPublishEvent = {
|
|
683
775
|
taskId: this.config.taskId,
|
|
684
776
|
eventType,
|
|
@@ -688,12 +780,14 @@ class BaseProcessPublishAssortment {
|
|
|
688
780
|
eventsAdminDownloadLink: uploadFile.adminDownloadUrl
|
|
689
781
|
};
|
|
690
782
|
if (sendMode === 'vibeiqfile') {
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
783
|
+
return (0, telemetry_1.traceSection)('send_publish_batch', { [telemetry_attributes_1.ATTR_FLEXPLM_PAYLOAD_COUNT]: events?.length ?? 0 }, async () => {
|
|
784
|
+
const flexPLMConnect = new flexplm_connect_1.FlexPLMConnect(this.config);
|
|
785
|
+
const result = await this.sendAndRecordPublishEvent(flexPLMConnect, outboundPublishEvent);
|
|
786
|
+
const isResultObject = typeof result === 'object' && result !== null;
|
|
787
|
+
return isResultObject
|
|
788
|
+
? { ...result, outboundPublishEvent }
|
|
789
|
+
: { result, outboundPublishEvent };
|
|
790
|
+
});
|
|
697
791
|
}
|
|
698
792
|
else {
|
|
699
793
|
await this.sendPublishPayloadEvent(outboundPublishEvent);
|
|
@@ -704,22 +798,24 @@ class BaseProcessPublishAssortment {
|
|
|
704
798
|
}
|
|
705
799
|
}
|
|
706
800
|
async sendPublishPayloadEvent(outboundPublishEvent) {
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
801
|
+
return (0, telemetry_1.traceSection)('record_publish_event', {}, async () => {
|
|
802
|
+
console.info('sendPublishPayloadEvent()');
|
|
803
|
+
let initialEvent = {};
|
|
804
|
+
if (this.config?.event) {
|
|
805
|
+
initialEvent = (typeof this.config?.event === 'string')
|
|
806
|
+
? JSON.parse(this.config?.event)
|
|
807
|
+
: this.config?.event;
|
|
808
|
+
}
|
|
809
|
+
const eventBody = {
|
|
810
|
+
originSystemType: 'VibeIQ',
|
|
811
|
+
objectClass: 'AssortmentPublishedToFlexPLM',
|
|
812
|
+
outboundPublishEvent,
|
|
813
|
+
initialEvent
|
|
814
|
+
};
|
|
815
|
+
await new sdk_1.Entities().create({
|
|
816
|
+
entityName: 'external-event',
|
|
817
|
+
object: eventBody
|
|
818
|
+
});
|
|
723
819
|
});
|
|
724
820
|
}
|
|
725
821
|
getCurrentDateString() {
|
|
@@ -812,6 +908,7 @@ class BaseProcessPublishAssortment {
|
|
|
812
908
|
}
|
|
813
909
|
}
|
|
814
910
|
}
|
|
911
|
+
const skippedDeletes = [];
|
|
815
912
|
for (const [itemId, delEntity] of assortmentItemDeleteMap) {
|
|
816
913
|
const itemFamilyId = delEntity?.item?.itemFamilyId;
|
|
817
914
|
const item = delEntity?.item;
|
|
@@ -823,7 +920,7 @@ class BaseProcessPublishAssortment {
|
|
|
823
920
|
}
|
|
824
921
|
else {
|
|
825
922
|
if (!item) {
|
|
826
|
-
|
|
923
|
+
skippedDeletes.push({ item_family_id: itemFamilyId, item_id: itemId });
|
|
827
924
|
continue;
|
|
828
925
|
}
|
|
829
926
|
}
|
|
@@ -855,6 +952,15 @@ class BaseProcessPublishAssortment {
|
|
|
855
952
|
ifc.colorDeletes.push(itemId);
|
|
856
953
|
}
|
|
857
954
|
} //End deletes for loop
|
|
955
|
+
if (skippedDeletes.length > 0) {
|
|
956
|
+
// These deletes are not sent to FlexPLM; one warning for the publish rather than one per item.
|
|
957
|
+
(0, telemetry_utils_1.setActiveSpanAttributes)({ [telemetry_attributes_1.ATTR_FLEXPLM_PUBLISH_SKIPPED_DELETE_COUNT]: skippedDeletes.length });
|
|
958
|
+
const flexplm = {
|
|
959
|
+
direction: 'publish', assortment_id: pcd.assortmentId, publish_change_id: pcd.assortmentPublishChangeId,
|
|
960
|
+
skipped_delete_count: skippedDeletes.length, skipped_deletes: skippedDeletes,
|
|
961
|
+
};
|
|
962
|
+
telemetry_1.logger.warn({ flexplm }, 'FlexPLM publish skipped deleted items with no item entity: %j', flexplm);
|
|
963
|
+
}
|
|
858
964
|
if (app_framework_1.Logger.isDebugOn()) {
|
|
859
965
|
console.debug('returning size: ' + itemFamilyChanges.size);
|
|
860
966
|
for (const [key, value] of itemFamilyChanges) {
|
|
@@ -864,16 +970,21 @@ class BaseProcessPublishAssortment {
|
|
|
864
970
|
return itemFamilyChanges;
|
|
865
971
|
}
|
|
866
972
|
async getEventsForPublishChangeData(publishChangeData) {
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
973
|
+
return (0, telemetry_1.traceSection)('build_publish_events', {
|
|
974
|
+
[telemetry_attributes_1.ATTR_FLEXPLM_PUBLISH_ITEM_FAMILY_COUNT]: publishChangeData.itemFamilyChanges?.size ?? 0,
|
|
975
|
+
}, async (span) => {
|
|
976
|
+
const seasonalPayloads = [];
|
|
977
|
+
for (const itemFamilyChange of publishChangeData.itemFamilyChanges.values()) {
|
|
978
|
+
const events = await this.getEventsForItemFamilyChanges(itemFamilyChange, publishChangeData.assortmentId, publishChangeData.seasonFed, publishChangeData.itemToFederatedIdMapping);
|
|
979
|
+
if (events.length > 0) {
|
|
980
|
+
seasonalPayloads.push(...events);
|
|
981
|
+
}
|
|
872
982
|
}
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
983
|
+
console.info('getEventsForPublishChangeData: itemFamilies: '
|
|
984
|
+
+ publishChangeData.itemFamilyChanges.size + ', payloads: ' + seasonalPayloads.length);
|
|
985
|
+
span.setAttribute(telemetry_attributes_1.ATTR_FLEXPLM_PAYLOAD_COUNT, seasonalPayloads.length);
|
|
986
|
+
return seasonalPayloads;
|
|
987
|
+
});
|
|
877
988
|
}
|
|
878
989
|
/**Returns the events for a given ItemFamilyChanges object
|
|
879
990
|
*
|