@contrail/flexplm 1.5.0 → 1.5.1-alpha.5843ce4
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/CHANGELOG.md +4 -0
- package/lib/publish/base-process-publish-assortment.d.ts +1 -0
- package/lib/publish/base-process-publish-assortment.js +40 -7
- package/lib/publish/base-process-publish-assortment.spec.js +139 -0
- package/package.json +1 -1
- package/src/publish/base-process-publish-assortment.spec.ts +163 -1
- package/src/publish/base-process-publish-assortment.ts +45 -8
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,10 @@ Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.5.1] - 2026-05-14
|
|
11
|
+
### Added
|
|
12
|
+
- Added sending an external event with the publish payload and trigger key of `VibeIQ|AssortmentPublishedToFlexPLM` to enable secondary events to run on the event / data which was generated. Also includes the payload in the data which can be passed to the next action.
|
|
13
|
+
|
|
10
14
|
## [1.5.0] - 2026-05-12
|
|
11
15
|
### Added
|
|
12
16
|
- Added support for Inbound `LCSMaterial` to sync to the entity class `item` with type path `item:material` and `itemNumber` as identifier. This is controlled by an `LCSMaterial.processAsItem` (default `false`) config default.
|
|
@@ -79,6 +79,7 @@ export declare class BaseProcessPublishAssortment {
|
|
|
79
79
|
private sendToFlexPLM;
|
|
80
80
|
private saveToLocalFile;
|
|
81
81
|
private handleVibeIQFile;
|
|
82
|
+
sendPublishPayloadEvent(outboundEvent: any): Promise<void>;
|
|
82
83
|
private getCurrentDateString;
|
|
83
84
|
getItemFamilyChanges(pcd: PublishChangeData, changeDetail: any, assortmentItemFullChangeMap: Map<string, any>, assortmentItemDeleteMap: Map<string, any>): Map<string, ItemFamilyChanges>;
|
|
84
85
|
getEventsForPublishChangeData(publishChangeData: PublishChangeData): Promise<SeasonalPayload[]>;
|
|
@@ -569,14 +569,22 @@ class BaseProcessPublishAssortment {
|
|
|
569
569
|
}
|
|
570
570
|
}
|
|
571
571
|
async sendToFlexPLM(events, eventType) {
|
|
572
|
-
const
|
|
572
|
+
const outboundPublishEvent = {
|
|
573
573
|
taskId: this.config.taskId,
|
|
574
574
|
eventType,
|
|
575
575
|
objectClass: 'LCSSeason',
|
|
576
576
|
events
|
|
577
577
|
};
|
|
578
578
|
const flexPLMConnect = new flexplm_connect_1.FlexPLMConnect(this.config);
|
|
579
|
-
|
|
579
|
+
const [result] = await Promise.all([
|
|
580
|
+
flexPLMConnect.sendToFlexPLM(outboundPublishEvent),
|
|
581
|
+
this.sendPublishPayloadEvent(outboundPublishEvent)
|
|
582
|
+
]);
|
|
583
|
+
const isResultObject = typeof result === 'object' && result !== null;
|
|
584
|
+
const resultWithOutboundPublishEvent = isResultObject
|
|
585
|
+
? { ...result, outboundPublishEvent }
|
|
586
|
+
: { result, outboundPublishEvent };
|
|
587
|
+
return resultWithOutboundPublishEvent;
|
|
580
588
|
}
|
|
581
589
|
async saveToLocalFile(events, eventType) {
|
|
582
590
|
let results = {};
|
|
@@ -588,15 +596,16 @@ class BaseProcessPublishAssortment {
|
|
|
588
596
|
const eventDirName = 'events-output';
|
|
589
597
|
const fileName = `${path.sep}${eventDirName}${path.sep}events-${dateString}.json`;
|
|
590
598
|
fileHandle = await fsPromise.open('.' + fileName, 'a');
|
|
591
|
-
const
|
|
599
|
+
const outboundPublishEvent = {
|
|
592
600
|
taskId: this.config.taskId,
|
|
593
601
|
eventType,
|
|
594
602
|
objectClass: 'LCSSeason',
|
|
595
603
|
events
|
|
596
604
|
};
|
|
597
|
-
await fileHandle.writeFile(JSON.stringify(
|
|
605
|
+
await fileHandle.writeFile(JSON.stringify(outboundPublishEvent));
|
|
598
606
|
results = {
|
|
599
607
|
message: 'Successfully Saved File',
|
|
608
|
+
outboundPublishEvent,
|
|
600
609
|
fileLocation: dirName + fileName
|
|
601
610
|
};
|
|
602
611
|
}
|
|
@@ -612,7 +621,7 @@ class BaseProcessPublishAssortment {
|
|
|
612
621
|
const eventBuffer = Buffer.from(JSON.stringify(events), 'utf-8');
|
|
613
622
|
const fileName = 'ASYNC_PUBLISH_SEASON-events.json';
|
|
614
623
|
const uploadFile = await new sdk_1.Files().createAndUploadFileFromBuffer(eventBuffer, 'application/json', fileName, null, this.TTL);
|
|
615
|
-
const
|
|
624
|
+
const outboundPublishEvent = {
|
|
616
625
|
taskId: this.config.taskId,
|
|
617
626
|
eventType,
|
|
618
627
|
objectClass: 'LCSSeason',
|
|
@@ -622,15 +631,39 @@ class BaseProcessPublishAssortment {
|
|
|
622
631
|
};
|
|
623
632
|
if (sendMode === 'vibeiqfile') {
|
|
624
633
|
const flexPLMConnect = new flexplm_connect_1.FlexPLMConnect(this.config);
|
|
625
|
-
|
|
634
|
+
const [result] = await Promise.all([
|
|
635
|
+
flexPLMConnect.sendToFlexPLM(outboundPublishEvent),
|
|
636
|
+
this.sendPublishPayloadEvent(outboundPublishEvent)
|
|
637
|
+
]);
|
|
638
|
+
return { ...result, outboundPublishEvent };
|
|
626
639
|
}
|
|
627
640
|
else {
|
|
641
|
+
await this.sendPublishPayloadEvent(outboundPublishEvent);
|
|
628
642
|
return {
|
|
629
643
|
message: 'Successfully Uploaded File.',
|
|
630
|
-
|
|
644
|
+
outboundPublishEvent
|
|
631
645
|
};
|
|
632
646
|
}
|
|
633
647
|
}
|
|
648
|
+
async sendPublishPayloadEvent(outboundEvent) {
|
|
649
|
+
console.info('sendPublishPayloadEvent()');
|
|
650
|
+
let initialEvent = {};
|
|
651
|
+
if (this.config?.event) {
|
|
652
|
+
initialEvent = (typeof this.config?.event === 'string')
|
|
653
|
+
? JSON.parse(this.config?.event)
|
|
654
|
+
: this.config?.event;
|
|
655
|
+
}
|
|
656
|
+
const eventBody = {
|
|
657
|
+
originSystemType: 'VibeIQ',
|
|
658
|
+
objectClass: 'AssortmentPublishedToFlexPLM',
|
|
659
|
+
outboundEvent,
|
|
660
|
+
initialEvent
|
|
661
|
+
};
|
|
662
|
+
await new sdk_1.Entities().create({
|
|
663
|
+
entityName: 'external-event',
|
|
664
|
+
object: eventBody
|
|
665
|
+
});
|
|
666
|
+
}
|
|
634
667
|
getCurrentDateString() {
|
|
635
668
|
const d = new Date();
|
|
636
669
|
return '' + d.getUTCFullYear()
|
|
@@ -9,6 +9,7 @@ const sdk_1 = require("@contrail/sdk");
|
|
|
9
9
|
const type_conversion_utils_1 = require("../util/type-conversion-utils");
|
|
10
10
|
const map_utils_1 = require("../util/map-utils");
|
|
11
11
|
const item_family_changes_1 = require("../interfaces/item-family-changes");
|
|
12
|
+
const flexplm_connect_1 = require("../util/flexplm-connect");
|
|
12
13
|
let federatedId = '';
|
|
13
14
|
jest.mock('../util/data-converter', () => {
|
|
14
15
|
return {
|
|
@@ -30,6 +31,13 @@ jest.mock('../util/federation', () => {
|
|
|
30
31
|
});
|
|
31
32
|
let entityObject = {};
|
|
32
33
|
let getOptionsObject = {};
|
|
34
|
+
let createCallArg = undefined;
|
|
35
|
+
let fileUploadCalls = [];
|
|
36
|
+
let fileUploadResult = {
|
|
37
|
+
id: 'file-123',
|
|
38
|
+
downloadUrl: 'https://download.url',
|
|
39
|
+
adminDownloadUrl: 'https://admin.download.url'
|
|
40
|
+
};
|
|
33
41
|
jest.mock('@contrail/sdk', () => {
|
|
34
42
|
return {
|
|
35
43
|
Entities: class {
|
|
@@ -37,6 +45,18 @@ jest.mock('@contrail/sdk', () => {
|
|
|
37
45
|
getOptionsObject = _getOtionsObject;
|
|
38
46
|
return entityObject;
|
|
39
47
|
}
|
|
48
|
+
create(arg) {
|
|
49
|
+
createCallArg = arg;
|
|
50
|
+
return Promise.resolve({});
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
Files: class {
|
|
54
|
+
createAndUploadFileFromBuffer(buffer, mimeType, fileName, _x, ttl) {
|
|
55
|
+
fileUploadCalls.push({ buffer, mimeType, fileName, ttl });
|
|
56
|
+
return Promise.resolve(fileUploadResult);
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
Request: class {
|
|
40
60
|
}
|
|
41
61
|
};
|
|
42
62
|
});
|
|
@@ -1668,3 +1688,122 @@ describe('getEventsForItemFamilyChanges - conditional eventType', () => {
|
|
|
1668
1688
|
expect(type_conversion_utils_1.TypeConversionUtils.isOutboundCreatableFromEntity).toHaveBeenNthCalledWith(2, undefined, mapFileUtil, colorProjectItem, { item: itemData, assortment });
|
|
1669
1689
|
});
|
|
1670
1690
|
});
|
|
1691
|
+
describe('sendToFlexPLM / handleVibeIQFile / sendPublishPayloadEvent', () => {
|
|
1692
|
+
const config = {
|
|
1693
|
+
taskId: 'task-abc',
|
|
1694
|
+
event: '{"sourceEventId":"src-1"}'
|
|
1695
|
+
};
|
|
1696
|
+
const mapFileUtil = new transform_data_1.MapFileUtil(new sdk_1.Entities());
|
|
1697
|
+
const dc = new data_converter_1.DataConverter(config, mapFileUtil);
|
|
1698
|
+
const events = [{ objectClass: 'LCSProductSeasonLink' }];
|
|
1699
|
+
const eventType = 'ASYNC_PUBLISH_SEASON';
|
|
1700
|
+
beforeEach(() => {
|
|
1701
|
+
createCallArg = undefined;
|
|
1702
|
+
fileUploadCalls = [];
|
|
1703
|
+
fileUploadResult = {
|
|
1704
|
+
id: 'file-123',
|
|
1705
|
+
downloadUrl: 'https://download.url',
|
|
1706
|
+
adminDownloadUrl: 'https://admin.download.url'
|
|
1707
|
+
};
|
|
1708
|
+
});
|
|
1709
|
+
afterEach(() => {
|
|
1710
|
+
jest.restoreAllMocks();
|
|
1711
|
+
});
|
|
1712
|
+
it('sendToFlexPLM merges outboundPublishEvent into result and calls sendPublishPayloadEvent in parallel', async () => {
|
|
1713
|
+
const bppa = new base_process_publish_assortment_1.BaseProcessPublishAssortment(config, dc, mapFileUtil);
|
|
1714
|
+
const flexResponse = { status: 200, data: { ok: true } };
|
|
1715
|
+
let flexResolved = false;
|
|
1716
|
+
let eventResolved = false;
|
|
1717
|
+
const spyFlex = jest.spyOn(flexplm_connect_1.FlexPLMConnect.prototype, 'sendToFlexPLM')
|
|
1718
|
+
.mockImplementation(async () => {
|
|
1719
|
+
await new Promise(r => setTimeout(r, 5));
|
|
1720
|
+
flexResolved = true;
|
|
1721
|
+
return flexResponse;
|
|
1722
|
+
});
|
|
1723
|
+
const spyEvent = jest.spyOn(bppa, 'sendPublishPayloadEvent')
|
|
1724
|
+
.mockImplementation(async () => {
|
|
1725
|
+
await new Promise(r => setTimeout(r, 5));
|
|
1726
|
+
eventResolved = true;
|
|
1727
|
+
});
|
|
1728
|
+
const result = await bppa.sendToFlexPLM(events, eventType);
|
|
1729
|
+
expect(spyFlex).toHaveBeenCalledTimes(1);
|
|
1730
|
+
expect(spyEvent).toHaveBeenCalledTimes(1);
|
|
1731
|
+
expect(flexResolved).toBe(true);
|
|
1732
|
+
expect(eventResolved).toBe(true);
|
|
1733
|
+
const passedOutboundPublishEvent = spyFlex.mock.calls[0][0];
|
|
1734
|
+
expect(passedOutboundPublishEvent.taskId).toBe('task-abc');
|
|
1735
|
+
expect(passedOutboundPublishEvent.eventType).toBe(eventType);
|
|
1736
|
+
expect(passedOutboundPublishEvent.objectClass).toBe('LCSSeason');
|
|
1737
|
+
expect(passedOutboundPublishEvent.events).toBe(events);
|
|
1738
|
+
expect(spyEvent).toHaveBeenCalledWith(passedOutboundPublishEvent);
|
|
1739
|
+
expect(result).toEqual({ ...flexResponse, outboundPublishEvent: passedOutboundPublishEvent });
|
|
1740
|
+
});
|
|
1741
|
+
it('handleVibeIQFile (vibeiqfile) merges outboundPublishEvent into FlexPLM result and calls sendPublishPayloadEvent', async () => {
|
|
1742
|
+
const bppa = new base_process_publish_assortment_1.BaseProcessPublishAssortment(config, dc, mapFileUtil);
|
|
1743
|
+
const flexResponse = { status: 200, data: { ok: true } };
|
|
1744
|
+
const spyFlex = jest.spyOn(flexplm_connect_1.FlexPLMConnect.prototype, 'sendToFlexPLM')
|
|
1745
|
+
.mockResolvedValue(flexResponse);
|
|
1746
|
+
const spyEvent = jest.spyOn(bppa, 'sendPublishPayloadEvent')
|
|
1747
|
+
.mockResolvedValue(undefined);
|
|
1748
|
+
const result = await bppa.handleVibeIQFile(events, eventType, 'vibeiqfile');
|
|
1749
|
+
expect(fileUploadCalls).toHaveLength(1);
|
|
1750
|
+
expect(fileUploadCalls[0].fileName).toBe('ASYNC_PUBLISH_SEASON-events.json');
|
|
1751
|
+
expect(fileUploadCalls[0].mimeType).toBe('application/json');
|
|
1752
|
+
const passedOutboundPublishEvent = spyFlex.mock.calls[0][0];
|
|
1753
|
+
expect(passedOutboundPublishEvent.taskId).toBe('task-abc');
|
|
1754
|
+
expect(passedOutboundPublishEvent.eventType).toBe(eventType);
|
|
1755
|
+
expect(passedOutboundPublishEvent.objectClass).toBe('LCSSeason');
|
|
1756
|
+
expect(passedOutboundPublishEvent.eventsFileId).toBe('file-123');
|
|
1757
|
+
expect(passedOutboundPublishEvent.eventsDownloadLink).toBe('https://download.url');
|
|
1758
|
+
expect(passedOutboundPublishEvent.eventsAdminDownloadLink).toBe('https://admin.download.url');
|
|
1759
|
+
expect(spyFlex).toHaveBeenCalledTimes(1);
|
|
1760
|
+
expect(spyEvent).toHaveBeenCalledTimes(1);
|
|
1761
|
+
expect(spyEvent).toHaveBeenCalledWith(passedOutboundPublishEvent);
|
|
1762
|
+
expect(result).toEqual({ ...flexResponse, outboundPublishEvent: passedOutboundPublishEvent });
|
|
1763
|
+
});
|
|
1764
|
+
it('handleVibeIQFile (vibeiqfile-dontsendtoflexplm) skips FlexPLM but still calls sendPublishPayloadEvent', async () => {
|
|
1765
|
+
const bppa = new base_process_publish_assortment_1.BaseProcessPublishAssortment(config, dc, mapFileUtil);
|
|
1766
|
+
const spyFlex = jest.spyOn(flexplm_connect_1.FlexPLMConnect.prototype, 'sendToFlexPLM')
|
|
1767
|
+
.mockResolvedValue({});
|
|
1768
|
+
const spyEvent = jest.spyOn(bppa, 'sendPublishPayloadEvent')
|
|
1769
|
+
.mockResolvedValue(undefined);
|
|
1770
|
+
const result = await bppa.handleVibeIQFile(events, eventType, 'vibeiqfile-dontsendtoflexplm');
|
|
1771
|
+
expect(spyFlex).not.toHaveBeenCalled();
|
|
1772
|
+
expect(spyEvent).toHaveBeenCalledTimes(1);
|
|
1773
|
+
const passedOutboundPublishEvent = spyEvent.mock.calls[0][0];
|
|
1774
|
+
expect(passedOutboundPublishEvent.eventsFileId).toBe('file-123');
|
|
1775
|
+
expect(result).toEqual({
|
|
1776
|
+
message: 'Successfully Uploaded File.',
|
|
1777
|
+
outboundPublishEvent: passedOutboundPublishEvent
|
|
1778
|
+
});
|
|
1779
|
+
});
|
|
1780
|
+
it('sendPublishPayloadEvent creates an external-event with initialEvent parsed from config', async () => {
|
|
1781
|
+
const bppa = new base_process_publish_assortment_1.BaseProcessPublishAssortment(config, dc, mapFileUtil);
|
|
1782
|
+
const outboundEvent = { foo: 'bar' };
|
|
1783
|
+
await bppa.sendPublishPayloadEvent(outboundEvent);
|
|
1784
|
+
expect(createCallArg).toEqual({
|
|
1785
|
+
entityName: 'external-event',
|
|
1786
|
+
object: {
|
|
1787
|
+
originSystemType: 'VibeIQ',
|
|
1788
|
+
objectClass: 'AssortmentPublishedToFlexPLM',
|
|
1789
|
+
outboundEvent,
|
|
1790
|
+
initialEvent: { sourceEventId: 'src-1' }
|
|
1791
|
+
}
|
|
1792
|
+
});
|
|
1793
|
+
});
|
|
1794
|
+
it('sendPublishPayloadEvent defaults initialEvent to {} when config has no event', async () => {
|
|
1795
|
+
const bareConfig = { taskId: 'task-x' };
|
|
1796
|
+
const bppa = new base_process_publish_assortment_1.BaseProcessPublishAssortment(bareConfig, dc, mapFileUtil);
|
|
1797
|
+
const outboundEvent = { hello: 'world' };
|
|
1798
|
+
await bppa.sendPublishPayloadEvent(outboundEvent);
|
|
1799
|
+
expect(createCallArg).toEqual({
|
|
1800
|
+
entityName: 'external-event',
|
|
1801
|
+
object: {
|
|
1802
|
+
originSystemType: 'VibeIQ',
|
|
1803
|
+
objectClass: 'AssortmentPublishedToFlexPLM',
|
|
1804
|
+
outboundEvent,
|
|
1805
|
+
initialEvent: {}
|
|
1806
|
+
}
|
|
1807
|
+
});
|
|
1808
|
+
});
|
|
1809
|
+
});
|
package/package.json
CHANGED
|
@@ -8,6 +8,7 @@ import { Entities } from '@contrail/sdk';
|
|
|
8
8
|
import { TypeConversionUtils } from '../util/type-conversion-utils';
|
|
9
9
|
import { MapUtil } from '../util/map-utils';
|
|
10
10
|
import { ItemFamilyChanges } from '../interfaces/item-family-changes';
|
|
11
|
+
import { FlexPLMConnect } from '../util/flexplm-connect';
|
|
11
12
|
|
|
12
13
|
let federatedId = '';
|
|
13
14
|
jest.mock('../util/data-converter', () => {
|
|
@@ -32,6 +33,13 @@ jest.mock('../util/federation', () => {
|
|
|
32
33
|
});
|
|
33
34
|
let entityObject = {};
|
|
34
35
|
let getOptionsObject = {};
|
|
36
|
+
let createCallArg: any = undefined;
|
|
37
|
+
let fileUploadCalls: any[] = [];
|
|
38
|
+
let fileUploadResult: any = {
|
|
39
|
+
id: 'file-123',
|
|
40
|
+
downloadUrl: 'https://download.url',
|
|
41
|
+
adminDownloadUrl: 'https://admin.download.url'
|
|
42
|
+
};
|
|
35
43
|
jest.mock('@contrail/sdk', () => {
|
|
36
44
|
return {
|
|
37
45
|
Entities: class{
|
|
@@ -39,7 +47,18 @@ jest.mock('@contrail/sdk', () => {
|
|
|
39
47
|
getOptionsObject = _getOtionsObject;
|
|
40
48
|
return entityObject;
|
|
41
49
|
}
|
|
42
|
-
|
|
50
|
+
create(arg){
|
|
51
|
+
createCallArg = arg;
|
|
52
|
+
return Promise.resolve({});
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
Files: class{
|
|
56
|
+
createAndUploadFileFromBuffer(buffer, mimeType, fileName, _x, ttl){
|
|
57
|
+
fileUploadCalls.push({ buffer, mimeType, fileName, ttl });
|
|
58
|
+
return Promise.resolve(fileUploadResult);
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
Request: class{}
|
|
43
62
|
};
|
|
44
63
|
});
|
|
45
64
|
describe('process publish assortment', () => {
|
|
@@ -1989,4 +2008,147 @@ describe('getEventsForItemFamilyChanges - conditional eventType', () => {
|
|
|
1989
2008
|
);
|
|
1990
2009
|
});
|
|
1991
2010
|
|
|
2011
|
+
});
|
|
2012
|
+
|
|
2013
|
+
describe('sendToFlexPLM / handleVibeIQFile / sendPublishPayloadEvent', () => {
|
|
2014
|
+
const config = {
|
|
2015
|
+
taskId: 'task-abc',
|
|
2016
|
+
event: '{"sourceEventId":"src-1"}'
|
|
2017
|
+
} as unknown as FCConfig;
|
|
2018
|
+
const mapFileUtil = new MapFileUtil(new Entities());
|
|
2019
|
+
const dc = new DataConverter(config, mapFileUtil);
|
|
2020
|
+
const events = [{ objectClass: 'LCSProductSeasonLink' }] as any;
|
|
2021
|
+
const eventType = 'ASYNC_PUBLISH_SEASON';
|
|
2022
|
+
|
|
2023
|
+
beforeEach(() => {
|
|
2024
|
+
createCallArg = undefined;
|
|
2025
|
+
fileUploadCalls = [];
|
|
2026
|
+
fileUploadResult = {
|
|
2027
|
+
id: 'file-123',
|
|
2028
|
+
downloadUrl: 'https://download.url',
|
|
2029
|
+
adminDownloadUrl: 'https://admin.download.url'
|
|
2030
|
+
};
|
|
2031
|
+
});
|
|
2032
|
+
afterEach(() => {
|
|
2033
|
+
jest.restoreAllMocks();
|
|
2034
|
+
});
|
|
2035
|
+
|
|
2036
|
+
it('sendToFlexPLM merges outboundPublishEvent into result and calls sendPublishPayloadEvent in parallel', async () => {
|
|
2037
|
+
const bppa = new BaseProcessPublishAssortment(config, dc, mapFileUtil);
|
|
2038
|
+
const flexResponse = { status: 200, data: { ok: true } };
|
|
2039
|
+
|
|
2040
|
+
let flexResolved = false;
|
|
2041
|
+
let eventResolved = false;
|
|
2042
|
+
const spyFlex = jest.spyOn(FlexPLMConnect.prototype, 'sendToFlexPLM')
|
|
2043
|
+
.mockImplementation(async () => {
|
|
2044
|
+
await new Promise(r => setTimeout(r, 5));
|
|
2045
|
+
flexResolved = true;
|
|
2046
|
+
return flexResponse as any;
|
|
2047
|
+
});
|
|
2048
|
+
const spyEvent = jest.spyOn(bppa, 'sendPublishPayloadEvent')
|
|
2049
|
+
.mockImplementation(async () => {
|
|
2050
|
+
await new Promise(r => setTimeout(r, 5));
|
|
2051
|
+
eventResolved = true;
|
|
2052
|
+
});
|
|
2053
|
+
|
|
2054
|
+
const result = await (bppa as any).sendToFlexPLM(events, eventType);
|
|
2055
|
+
|
|
2056
|
+
expect(spyFlex).toHaveBeenCalledTimes(1);
|
|
2057
|
+
expect(spyEvent).toHaveBeenCalledTimes(1);
|
|
2058
|
+
expect(flexResolved).toBe(true);
|
|
2059
|
+
expect(eventResolved).toBe(true);
|
|
2060
|
+
|
|
2061
|
+
const passedOutboundPublishEvent = spyFlex.mock.calls[0][0] as any;
|
|
2062
|
+
expect(passedOutboundPublishEvent.taskId).toBe('task-abc');
|
|
2063
|
+
expect(passedOutboundPublishEvent.eventType).toBe(eventType);
|
|
2064
|
+
expect(passedOutboundPublishEvent.objectClass).toBe('LCSSeason');
|
|
2065
|
+
expect(passedOutboundPublishEvent.events).toBe(events);
|
|
2066
|
+
expect(spyEvent).toHaveBeenCalledWith(passedOutboundPublishEvent);
|
|
2067
|
+
expect(result).toEqual({ ...flexResponse, outboundPublishEvent: passedOutboundPublishEvent });
|
|
2068
|
+
});
|
|
2069
|
+
|
|
2070
|
+
it('handleVibeIQFile (vibeiqfile) merges outboundPublishEvent into FlexPLM result and calls sendPublishPayloadEvent', async () => {
|
|
2071
|
+
const bppa = new BaseProcessPublishAssortment(config, dc, mapFileUtil);
|
|
2072
|
+
const flexResponse = { status: 200, data: { ok: true } };
|
|
2073
|
+
|
|
2074
|
+
const spyFlex = jest.spyOn(FlexPLMConnect.prototype, 'sendToFlexPLM')
|
|
2075
|
+
.mockResolvedValue(flexResponse as any);
|
|
2076
|
+
const spyEvent = jest.spyOn(bppa, 'sendPublishPayloadEvent')
|
|
2077
|
+
.mockResolvedValue(undefined as any);
|
|
2078
|
+
|
|
2079
|
+
const result = await (bppa as any).handleVibeIQFile(events, eventType, 'vibeiqfile');
|
|
2080
|
+
|
|
2081
|
+
expect(fileUploadCalls).toHaveLength(1);
|
|
2082
|
+
expect(fileUploadCalls[0].fileName).toBe('ASYNC_PUBLISH_SEASON-events.json');
|
|
2083
|
+
expect(fileUploadCalls[0].mimeType).toBe('application/json');
|
|
2084
|
+
|
|
2085
|
+
const passedOutboundPublishEvent = spyFlex.mock.calls[0][0] as any;
|
|
2086
|
+
expect(passedOutboundPublishEvent.taskId).toBe('task-abc');
|
|
2087
|
+
expect(passedOutboundPublishEvent.eventType).toBe(eventType);
|
|
2088
|
+
expect(passedOutboundPublishEvent.objectClass).toBe('LCSSeason');
|
|
2089
|
+
expect(passedOutboundPublishEvent.eventsFileId).toBe('file-123');
|
|
2090
|
+
expect(passedOutboundPublishEvent.eventsDownloadLink).toBe('https://download.url');
|
|
2091
|
+
expect(passedOutboundPublishEvent.eventsAdminDownloadLink).toBe('https://admin.download.url');
|
|
2092
|
+
|
|
2093
|
+
expect(spyFlex).toHaveBeenCalledTimes(1);
|
|
2094
|
+
expect(spyEvent).toHaveBeenCalledTimes(1);
|
|
2095
|
+
expect(spyEvent).toHaveBeenCalledWith(passedOutboundPublishEvent);
|
|
2096
|
+
expect(result).toEqual({ ...flexResponse, outboundPublishEvent: passedOutboundPublishEvent });
|
|
2097
|
+
});
|
|
2098
|
+
|
|
2099
|
+
it('handleVibeIQFile (vibeiqfile-dontsendtoflexplm) skips FlexPLM but still calls sendPublishPayloadEvent', async () => {
|
|
2100
|
+
const bppa = new BaseProcessPublishAssortment(config, dc, mapFileUtil);
|
|
2101
|
+
|
|
2102
|
+
const spyFlex = jest.spyOn(FlexPLMConnect.prototype, 'sendToFlexPLM')
|
|
2103
|
+
.mockResolvedValue({} as any);
|
|
2104
|
+
const spyEvent = jest.spyOn(bppa, 'sendPublishPayloadEvent')
|
|
2105
|
+
.mockResolvedValue(undefined as any);
|
|
2106
|
+
|
|
2107
|
+
const result = await (bppa as any).handleVibeIQFile(events, eventType, 'vibeiqfile-dontsendtoflexplm');
|
|
2108
|
+
|
|
2109
|
+
expect(spyFlex).not.toHaveBeenCalled();
|
|
2110
|
+
expect(spyEvent).toHaveBeenCalledTimes(1);
|
|
2111
|
+
|
|
2112
|
+
const passedOutboundPublishEvent = spyEvent.mock.calls[0][0] as any;
|
|
2113
|
+
expect(passedOutboundPublishEvent.eventsFileId).toBe('file-123');
|
|
2114
|
+
expect(result).toEqual({
|
|
2115
|
+
message: 'Successfully Uploaded File.',
|
|
2116
|
+
outboundPublishEvent: passedOutboundPublishEvent
|
|
2117
|
+
});
|
|
2118
|
+
});
|
|
2119
|
+
|
|
2120
|
+
it('sendPublishPayloadEvent creates an external-event with initialEvent parsed from config', async () => {
|
|
2121
|
+
const bppa = new BaseProcessPublishAssortment(config, dc, mapFileUtil);
|
|
2122
|
+
const outboundEvent = { foo: 'bar' };
|
|
2123
|
+
|
|
2124
|
+
await bppa.sendPublishPayloadEvent(outboundEvent);
|
|
2125
|
+
|
|
2126
|
+
expect(createCallArg).toEqual({
|
|
2127
|
+
entityName: 'external-event',
|
|
2128
|
+
object: {
|
|
2129
|
+
originSystemType: 'VibeIQ',
|
|
2130
|
+
objectClass: 'AssortmentPublishedToFlexPLM',
|
|
2131
|
+
outboundEvent,
|
|
2132
|
+
initialEvent: { sourceEventId: 'src-1' }
|
|
2133
|
+
}
|
|
2134
|
+
});
|
|
2135
|
+
});
|
|
2136
|
+
|
|
2137
|
+
it('sendPublishPayloadEvent defaults initialEvent to {} when config has no event', async () => {
|
|
2138
|
+
const bareConfig = { taskId: 'task-x' } as unknown as FCConfig;
|
|
2139
|
+
const bppa = new BaseProcessPublishAssortment(bareConfig, dc, mapFileUtil);
|
|
2140
|
+
const outboundEvent = { hello: 'world' };
|
|
2141
|
+
|
|
2142
|
+
await bppa.sendPublishPayloadEvent(outboundEvent);
|
|
2143
|
+
|
|
2144
|
+
expect(createCallArg).toEqual({
|
|
2145
|
+
entityName: 'external-event',
|
|
2146
|
+
object: {
|
|
2147
|
+
originSystemType: 'VibeIQ',
|
|
2148
|
+
objectClass: 'AssortmentPublishedToFlexPLM',
|
|
2149
|
+
outboundEvent,
|
|
2150
|
+
initialEvent: {}
|
|
2151
|
+
}
|
|
2152
|
+
});
|
|
2153
|
+
});
|
|
1992
2154
|
});
|
|
@@ -685,7 +685,7 @@ export class BaseProcessPublishAssortment {
|
|
|
685
685
|
}
|
|
686
686
|
|
|
687
687
|
private async sendToFlexPLM(events: SeasonalPayload[], eventType: string) {
|
|
688
|
-
const
|
|
688
|
+
const outboundPublishEvent: AsyncEventsPayloadType = {
|
|
689
689
|
taskId: this.config.taskId,
|
|
690
690
|
eventType,
|
|
691
691
|
objectClass: 'LCSSeason',
|
|
@@ -693,7 +693,16 @@ export class BaseProcessPublishAssortment {
|
|
|
693
693
|
};
|
|
694
694
|
|
|
695
695
|
const flexPLMConnect = new FlexPLMConnect(this.config);
|
|
696
|
-
|
|
696
|
+
const [result] = await Promise.all([
|
|
697
|
+
flexPLMConnect.sendToFlexPLM(outboundPublishEvent),
|
|
698
|
+
this.sendPublishPayloadEvent(outboundPublishEvent)
|
|
699
|
+
]);
|
|
700
|
+
const isResultObject = typeof result === 'object' && result !== null;
|
|
701
|
+
const resultWithOutboundPublishEvent = isResultObject
|
|
702
|
+
? { ...result, outboundPublishEvent }
|
|
703
|
+
: { result, outboundPublishEvent };
|
|
704
|
+
|
|
705
|
+
return resultWithOutboundPublishEvent;
|
|
697
706
|
}
|
|
698
707
|
|
|
699
708
|
private async saveToLocalFile(events: SeasonalPayload[], eventType: string) {
|
|
@@ -708,17 +717,18 @@ export class BaseProcessPublishAssortment {
|
|
|
708
717
|
const fileName = `${path.sep}${eventDirName}${path.sep}events-${dateString}.json`;
|
|
709
718
|
fileHandle = await fsPromise.open('.' + fileName, 'a');
|
|
710
719
|
|
|
711
|
-
const
|
|
720
|
+
const outboundPublishEvent: AsyncEventsPayloadType = {
|
|
712
721
|
taskId: this.config.taskId,
|
|
713
722
|
eventType,
|
|
714
723
|
objectClass: 'LCSSeason',
|
|
715
724
|
events
|
|
716
725
|
};
|
|
717
726
|
|
|
718
|
-
await fileHandle.writeFile(JSON.stringify(
|
|
727
|
+
await fileHandle.writeFile(JSON.stringify(outboundPublishEvent));
|
|
719
728
|
|
|
720
729
|
results = {
|
|
721
730
|
message: 'Successfully Saved File',
|
|
731
|
+
outboundPublishEvent,
|
|
722
732
|
fileLocation: dirName + fileName
|
|
723
733
|
};
|
|
724
734
|
} catch (e) {
|
|
@@ -735,26 +745,53 @@ export class BaseProcessPublishAssortment {
|
|
|
735
745
|
const fileName = 'ASYNC_PUBLISH_SEASON-events.json';
|
|
736
746
|
const uploadFile = await new Files().createAndUploadFileFromBuffer(eventBuffer, 'application/json', fileName, null, this.TTL);
|
|
737
747
|
|
|
738
|
-
const
|
|
748
|
+
const outboundPublishEvent: AsyncEventsPayloadType = {
|
|
739
749
|
taskId: this.config.taskId,
|
|
740
750
|
eventType,
|
|
741
751
|
objectClass: 'LCSSeason',
|
|
742
752
|
eventsFileId: uploadFile.id,
|
|
743
753
|
eventsDownloadLink: uploadFile.downloadUrl,
|
|
744
754
|
eventsAdminDownloadLink: uploadFile.adminDownloadUrl
|
|
745
|
-
};
|
|
755
|
+
};
|
|
746
756
|
|
|
747
757
|
if (sendMode === 'vibeiqfile') {
|
|
748
758
|
const flexPLMConnect = new FlexPLMConnect(this.config);
|
|
749
|
-
|
|
759
|
+
const [result] = await Promise.all([
|
|
760
|
+
flexPLMConnect.sendToFlexPLM(outboundPublishEvent),
|
|
761
|
+
this.sendPublishPayloadEvent(outboundPublishEvent)
|
|
762
|
+
]);
|
|
763
|
+
return { ...result, outboundPublishEvent };
|
|
750
764
|
} else {
|
|
765
|
+
await this.sendPublishPayloadEvent(outboundPublishEvent);
|
|
751
766
|
return {
|
|
752
767
|
message: 'Successfully Uploaded File.',
|
|
753
|
-
|
|
768
|
+
outboundPublishEvent
|
|
754
769
|
};
|
|
755
770
|
}
|
|
756
771
|
}
|
|
757
772
|
|
|
773
|
+
public async sendPublishPayloadEvent(outboundEvent: any){
|
|
774
|
+
console.info('sendPublishPayloadEvent()');
|
|
775
|
+
let initialEvent = {};
|
|
776
|
+
if(this.config?.event){
|
|
777
|
+
initialEvent = (typeof this.config?.event === 'string')
|
|
778
|
+
? JSON.parse(this.config?.event)
|
|
779
|
+
: this.config?.event;
|
|
780
|
+
}
|
|
781
|
+
const eventBody = {
|
|
782
|
+
originSystemType: 'VibeIQ',
|
|
783
|
+
objectClass: 'AssortmentPublishedToFlexPLM',
|
|
784
|
+
outboundEvent,
|
|
785
|
+
initialEvent
|
|
786
|
+
};
|
|
787
|
+
|
|
788
|
+
await new Entities().create({
|
|
789
|
+
entityName: 'external-event',
|
|
790
|
+
object: eventBody
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
|
|
758
795
|
private getCurrentDateString() {
|
|
759
796
|
const d = new Date();
|
|
760
797
|
return '' + d.getUTCFullYear()
|