@contrail/flexplm 1.6.1-alpha.75cfe7f → 1.6.1-alpha.cc44824

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.
@@ -14,5 +14,6 @@ export declare class UploadCommand {
14
14
  private tryRunGit;
15
15
  private commitToGit;
16
16
  run(args: string[]): Promise<void>;
17
+ private setTransformMapFileOnAppOrg;
17
18
  }
18
19
  export {};
@@ -227,25 +227,28 @@ class UploadCommand {
227
227
  await this.commitToGit([tsAbsPath, absPath], uploadedFile.id, options);
228
228
  }
229
229
  if (options.updateConfig) {
230
- const appOrgs = await new sdk_1.Entities().get({
231
- entityName: 'app-org',
232
- criteria: { appId: app.id },
233
- });
234
- if (!appOrgs || appOrgs.length === 0) {
235
- throw new Error(`Failed to set the file onto the app config for "${appIdentifier}" because it is not installed in org "${orgName}". Install it via the admin console before using --update-config. You can paste the uploaded file's ID into the app config without needing to re-run this command.`);
236
- }
237
- if (appOrgs.length > 1) {
238
- throw new Error(`Failed to set the file onto the app config for "${appIdentifier}" in org "${orgName}" because ${appOrgs.length} installations were identified. Expected one. Please contact customer support for assistance.`);
239
- }
240
- const appOrg = appOrgs[0];
241
- const nextAppConfig = { ...(appOrg.appConfig || {}), transformMapFile: uploadedFile.id };
242
- await new sdk_1.Entities().update({
243
- entityName: 'app-org',
244
- id: appOrg.id,
245
- object: { appConfig: nextAppConfig },
246
- });
247
- console.log(`Successfully set "appConfig.transformMapFile" for installed "${appIdentifier}" to new FILE ID: "${uploadedFile.id}" on org "${orgName}"`);
230
+ await this.setTransformMapFileOnAppOrg(app.id, appIdentifier, orgName, uploadedFile.id);
248
231
  }
249
232
  }
233
+ async setTransformMapFileOnAppOrg(appId, appIdentifier, orgName, fileId) {
234
+ const appOrgs = await new sdk_1.Entities().get({
235
+ entityName: 'app-org',
236
+ criteria: { appId },
237
+ });
238
+ if (!appOrgs || appOrgs.length === 0) {
239
+ throw new Error(`Failed to set the file onto the app config for "${appIdentifier}" because it is not installed in org "${orgName}". Install it via the admin console before using --update-config. You can paste the uploaded file's ID into the app config without needing to re-run this command.`);
240
+ }
241
+ if (appOrgs.length > 1) {
242
+ throw new Error(`Failed to set the file onto the app config for "${appIdentifier}" in org "${orgName}" because ${appOrgs.length} installations were identified. Expected one. Please contact customer support for assistance.`);
243
+ }
244
+ const appOrg = appOrgs[0];
245
+ const nextAppConfig = { ...(appOrg.appConfig || {}), transformMapFile: fileId };
246
+ await new sdk_1.Entities().update({
247
+ entityName: 'app-org',
248
+ id: appOrg.id,
249
+ object: { appConfig: nextAppConfig },
250
+ });
251
+ console.log(`Successfully set "appConfig.transformMapFile" for installed "${appIdentifier}" to new FILE ID: "${fileId}" on org "${orgName}"`);
252
+ }
250
253
  }
251
254
  exports.UploadCommand = UploadCommand;
@@ -86,8 +86,10 @@ export declare class BaseProcessPublishAssortment {
86
86
  getResultsCount(events: any): any;
87
87
  private sendEvents;
88
88
  private sendToFlexPLM;
89
+ private sendAndRecordPublishEvent;
89
90
  private saveToLocalFile;
90
91
  private handleVibeIQFile;
92
+ private sendPublishPayloadEvent;
91
93
  private getCurrentDateString;
92
94
  getItemFamilyChanges(pcd: PublishChangeData, changeDetail: any, assortmentItemFullChangeMap: Map<string, any>, assortmentItemDeleteMap: Map<string, any>): Map<string, ItemFamilyChanges>;
93
95
  getEventsForPublishChangeData(publishChangeData: PublishChangeData): Promise<SeasonalPayload[]>;
@@ -595,14 +595,41 @@ class BaseProcessPublishAssortment {
595
595
  }
596
596
  }
597
597
  async sendToFlexPLM(events, eventType) {
598
- const asyncEvent = {
598
+ const outboundPublishEvent = {
599
599
  taskId: this.config.taskId,
600
600
  eventType,
601
601
  objectClass: 'LCSSeason',
602
602
  events
603
603
  };
604
604
  const flexPLMConnect = new flexplm_connect_1.FlexPLMConnect(this.config);
605
- return await flexPLMConnect.sendToFlexPLM(asyncEvent);
605
+ const result = await this.sendAndRecordPublishEvent(flexPLMConnect, outboundPublishEvent);
606
+ const isResultObject = typeof result === 'object' && result !== null;
607
+ return isResultObject
608
+ ? { ...result, outboundPublishEvent }
609
+ : { result, outboundPublishEvent };
610
+ }
611
+ async sendAndRecordPublishEvent(flexPLMConnect, outboundPublishEvent) {
612
+ let sendResult;
613
+ try {
614
+ sendResult = await flexPLMConnect.sendToFlexPLM(outboundPublishEvent);
615
+ }
616
+ catch (e) {
617
+ const error = e instanceof Error ? e : new Error(String(e));
618
+ error.outboundPublishEvent = outboundPublishEvent;
619
+ error.sendToFlexPLMResult = { error: e?.message ?? String(e) };
620
+ throw error;
621
+ }
622
+ try {
623
+ await this.sendPublishPayloadEvent(outboundPublishEvent);
624
+ }
625
+ catch (e) {
626
+ const error = e instanceof Error ? e : new Error(String(e));
627
+ error.outboundPublishEvent = outboundPublishEvent;
628
+ error.sendToFlexPLMResult = sendResult;
629
+ error.sendPublishPayloadEventResult = { error: e?.message ?? String(e) };
630
+ throw error;
631
+ }
632
+ return sendResult;
606
633
  }
607
634
  async saveToLocalFile(events, eventType) {
608
635
  let results = {};
@@ -614,15 +641,16 @@ class BaseProcessPublishAssortment {
614
641
  const eventDirName = 'events-output';
615
642
  const fileName = `${path.sep}${eventDirName}${path.sep}events-${dateString}.json`;
616
643
  fileHandle = await fsPromise.open('.' + fileName, 'a');
617
- const asyncEvent = {
644
+ const outboundPublishEvent = {
618
645
  taskId: this.config.taskId,
619
646
  eventType,
620
647
  objectClass: 'LCSSeason',
621
648
  events
622
649
  };
623
- await fileHandle.writeFile(JSON.stringify(asyncEvent));
650
+ await fileHandle.writeFile(JSON.stringify(outboundPublishEvent));
624
651
  results = {
625
652
  message: 'Successfully Saved File',
653
+ outboundPublishEvent,
626
654
  fileLocation: dirName + fileName
627
655
  };
628
656
  }
@@ -638,7 +666,7 @@ class BaseProcessPublishAssortment {
638
666
  const eventBuffer = Buffer.from(JSON.stringify(events), 'utf-8');
639
667
  const fileName = 'ASYNC_PUBLISH_SEASON-events.json';
640
668
  const uploadFile = await new sdk_1.Files().createAndUploadFileFromBuffer(eventBuffer, 'application/json', fileName, null, this.TTL);
641
- const asyncEvent = {
669
+ const outboundPublishEvent = {
642
670
  taskId: this.config.taskId,
643
671
  eventType,
644
672
  objectClass: 'LCSSeason',
@@ -648,15 +676,39 @@ class BaseProcessPublishAssortment {
648
676
  };
649
677
  if (sendMode === 'vibeiqfile') {
650
678
  const flexPLMConnect = new flexplm_connect_1.FlexPLMConnect(this.config);
651
- return await flexPLMConnect.sendToFlexPLM(asyncEvent);
679
+ const result = await this.sendAndRecordPublishEvent(flexPLMConnect, outboundPublishEvent);
680
+ const isResultObject = typeof result === 'object' && result !== null;
681
+ return isResultObject
682
+ ? { ...result, outboundPublishEvent }
683
+ : { result, outboundPublishEvent };
652
684
  }
653
685
  else {
686
+ await this.sendPublishPayloadEvent(outboundPublishEvent);
654
687
  return {
655
688
  message: 'Successfully Uploaded File.',
656
- asyncEvent
689
+ outboundPublishEvent
657
690
  };
658
691
  }
659
692
  }
693
+ async sendPublishPayloadEvent(outboundPublishEvent) {
694
+ console.info('sendPublishPayloadEvent()');
695
+ let initialEvent = {};
696
+ if (this.config?.event) {
697
+ initialEvent = (typeof this.config?.event === 'string')
698
+ ? JSON.parse(this.config?.event)
699
+ : this.config?.event;
700
+ }
701
+ const eventBody = {
702
+ originSystemType: 'VibeIQ',
703
+ objectClass: 'AssortmentPublishedToFlexPLM',
704
+ outboundPublishEvent,
705
+ initialEvent
706
+ };
707
+ await new sdk_1.Entities().create({
708
+ entityName: 'external-event',
709
+ object: eventBody
710
+ });
711
+ }
660
712
  getCurrentDateString() {
661
713
  const d = new Date();
662
714
  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 {
@@ -31,6 +32,13 @@ jest.mock('../util/federation', () => {
31
32
  });
32
33
  let entityObject = {};
33
34
  let getOptionsObject = {};
35
+ let createCallArg = undefined;
36
+ let fileUploadCalls = [];
37
+ let fileUploadResult = {
38
+ id: 'file-123',
39
+ downloadUrl: 'https://download.url',
40
+ adminDownloadUrl: 'https://admin.download.url'
41
+ };
34
42
  jest.mock('@contrail/sdk', () => {
35
43
  return {
36
44
  Entities: class {
@@ -38,6 +46,18 @@ jest.mock('@contrail/sdk', () => {
38
46
  getOptionsObject = _getOtionsObject;
39
47
  return entityObject;
40
48
  }
49
+ create(arg) {
50
+ createCallArg = arg;
51
+ return Promise.resolve({});
52
+ }
53
+ },
54
+ Files: class {
55
+ createAndUploadFileFromBuffer(buffer, mimeType, fileName, _x, ttl) {
56
+ fileUploadCalls.push({ buffer, mimeType, fileName, ttl });
57
+ return Promise.resolve(fileUploadResult);
58
+ }
59
+ },
60
+ Request: class {
41
61
  }
42
62
  };
43
63
  });
@@ -1686,3 +1706,171 @@ describe('getEventsForItemFamilyChanges - conditional eventType', () => {
1686
1706
  expect(type_conversion_utils_1.TypeConversionUtils.isOutboundCreatableFromEntity).toHaveBeenNthCalledWith(2, undefined, mapFileUtil, colorProjectItem, { item: itemData, assortment });
1687
1707
  });
1688
1708
  });
1709
+ describe('sendToFlexPLM / handleVibeIQFile / sendPublishPayloadEvent', () => {
1710
+ const config = {
1711
+ taskId: 'task-abc',
1712
+ event: '{"sourceEventId":"src-1"}'
1713
+ };
1714
+ const mapFileUtil = new transform_data_1.MapFileUtil(new sdk_1.Entities());
1715
+ const dc = new data_converter_1.DataConverter(config, mapFileUtil);
1716
+ const events = [{ objectClass: 'LCSProductSeasonLink' }];
1717
+ const eventType = 'ASYNC_PUBLISH_SEASON';
1718
+ beforeEach(() => {
1719
+ createCallArg = undefined;
1720
+ fileUploadCalls = [];
1721
+ fileUploadResult = {
1722
+ id: 'file-123',
1723
+ downloadUrl: 'https://download.url',
1724
+ adminDownloadUrl: 'https://admin.download.url'
1725
+ };
1726
+ });
1727
+ afterEach(() => {
1728
+ jest.restoreAllMocks();
1729
+ });
1730
+ it('should merge outboundPublishEvent into result and call sendPublishPayloadEvent after sendToFlexPLM succeeds', async () => {
1731
+ const bppa = new base_process_publish_assortment_1.BaseProcessPublishAssortment(config, dc, mapFileUtil);
1732
+ const flexResponse = { status: 200, data: { ok: true } };
1733
+ const callOrder = [];
1734
+ const spyFlex = jest.spyOn(flexplm_connect_1.FlexPLMConnect.prototype, 'sendToFlexPLM')
1735
+ .mockImplementation(async () => {
1736
+ await new Promise(r => setTimeout(r, 5));
1737
+ callOrder.push('flex');
1738
+ return flexResponse;
1739
+ });
1740
+ const spyEvent = jest.spyOn(bppa, 'sendPublishPayloadEvent')
1741
+ .mockImplementation(async () => {
1742
+ callOrder.push('event');
1743
+ });
1744
+ const result = await bppa.sendToFlexPLM(events, eventType);
1745
+ expect(spyFlex).toHaveBeenCalledTimes(1);
1746
+ expect(spyEvent).toHaveBeenCalledTimes(1);
1747
+ expect(callOrder).toEqual(['flex', 'event']);
1748
+ const passedOutboundPublishEvent = spyFlex.mock.calls[0][0];
1749
+ expect(passedOutboundPublishEvent.taskId).toBe('task-abc');
1750
+ expect(passedOutboundPublishEvent.eventType).toBe(eventType);
1751
+ expect(passedOutboundPublishEvent.objectClass).toBe('LCSSeason');
1752
+ expect(passedOutboundPublishEvent.events).toBe(events);
1753
+ expect(spyEvent).toHaveBeenCalledWith(passedOutboundPublishEvent);
1754
+ expect(result).toEqual({ ...flexResponse, outboundPublishEvent: passedOutboundPublishEvent });
1755
+ });
1756
+ it('should have sendToFlexPLM throw when flexPLMConnect.sendToFlexPLM fails, without calling sendPublishPayloadEvent', async () => {
1757
+ const bppa = new base_process_publish_assortment_1.BaseProcessPublishAssortment(config, dc, mapFileUtil);
1758
+ const flexError = new Error('flex failed');
1759
+ jest.spyOn(flexplm_connect_1.FlexPLMConnect.prototype, 'sendToFlexPLM').mockRejectedValue(flexError);
1760
+ const spyEvent = jest.spyOn(bppa, 'sendPublishPayloadEvent').mockResolvedValue(undefined);
1761
+ await expect(bppa.sendToFlexPLM(events, eventType)).rejects.toMatchObject({
1762
+ message: 'flex failed',
1763
+ outboundPublishEvent: expect.objectContaining({ taskId: 'task-abc', eventType, objectClass: 'LCSSeason', events }),
1764
+ sendToFlexPLMResult: { error: 'flex failed' }
1765
+ });
1766
+ expect(spyEvent).not.toHaveBeenCalled();
1767
+ expect(flexError.sendPublishPayloadEventResult).toBeUndefined();
1768
+ });
1769
+ it('should have sendToFlexPLM throw when sendPublishPayloadEvent fails, with flex result attached', async () => {
1770
+ const bppa = new base_process_publish_assortment_1.BaseProcessPublishAssortment(config, dc, mapFileUtil);
1771
+ const flexResponse = { status: 200 };
1772
+ const eventError = new Error('event failed');
1773
+ jest.spyOn(flexplm_connect_1.FlexPLMConnect.prototype, 'sendToFlexPLM').mockResolvedValue(flexResponse);
1774
+ jest.spyOn(bppa, 'sendPublishPayloadEvent').mockRejectedValue(eventError);
1775
+ await expect(bppa.sendToFlexPLM(events, eventType)).rejects.toMatchObject({
1776
+ message: 'event failed',
1777
+ outboundPublishEvent: expect.objectContaining({ taskId: 'task-abc', eventType }),
1778
+ sendToFlexPLMResult: flexResponse,
1779
+ sendPublishPayloadEventResult: { error: 'event failed' }
1780
+ });
1781
+ });
1782
+ it('should have handleVibeIQFile (vibeiqfile) throw when flexPLMConnect.sendToFlexPLM fails, without calling sendPublishPayloadEvent', async () => {
1783
+ const bppa = new base_process_publish_assortment_1.BaseProcessPublishAssortment(config, dc, mapFileUtil);
1784
+ const flexError = new Error('flex failed');
1785
+ jest.spyOn(flexplm_connect_1.FlexPLMConnect.prototype, 'sendToFlexPLM').mockRejectedValue(flexError);
1786
+ const spyEvent = jest.spyOn(bppa, 'sendPublishPayloadEvent').mockResolvedValue(undefined);
1787
+ await expect(bppa.handleVibeIQFile(events, eventType, 'vibeiqfile')).rejects.toMatchObject({
1788
+ message: 'flex failed',
1789
+ outboundPublishEvent: expect.objectContaining({ eventsFileId: 'file-123' }),
1790
+ sendToFlexPLMResult: { error: 'flex failed' }
1791
+ });
1792
+ expect(spyEvent).not.toHaveBeenCalled();
1793
+ expect(flexError.sendPublishPayloadEventResult).toBeUndefined();
1794
+ });
1795
+ it('should have handleVibeIQFile (vibeiqfile) throw when sendPublishPayloadEvent fails, with flex result attached', async () => {
1796
+ const bppa = new base_process_publish_assortment_1.BaseProcessPublishAssortment(config, dc, mapFileUtil);
1797
+ const flexResponse = { status: 200 };
1798
+ const eventError = new Error('event failed');
1799
+ jest.spyOn(flexplm_connect_1.FlexPLMConnect.prototype, 'sendToFlexPLM').mockResolvedValue(flexResponse);
1800
+ jest.spyOn(bppa, 'sendPublishPayloadEvent').mockRejectedValue(eventError);
1801
+ await expect(bppa.handleVibeIQFile(events, eventType, 'vibeiqfile')).rejects.toMatchObject({
1802
+ message: 'event failed',
1803
+ outboundPublishEvent: expect.objectContaining({ eventsFileId: 'file-123' }),
1804
+ sendToFlexPLMResult: flexResponse,
1805
+ sendPublishPayloadEventResult: { error: 'event failed' }
1806
+ });
1807
+ });
1808
+ it('should merge outboundPublishEvent into FlexPLM result and call sendPublishPayloadEvent when mode is vibeiqfile', async () => {
1809
+ const bppa = new base_process_publish_assortment_1.BaseProcessPublishAssortment(config, dc, mapFileUtil);
1810
+ const flexResponse = { status: 200, data: { ok: true } };
1811
+ const spyFlex = jest.spyOn(flexplm_connect_1.FlexPLMConnect.prototype, 'sendToFlexPLM')
1812
+ .mockResolvedValue(flexResponse);
1813
+ const spyEvent = jest.spyOn(bppa, 'sendPublishPayloadEvent')
1814
+ .mockResolvedValue(undefined);
1815
+ const result = await bppa.handleVibeIQFile(events, eventType, 'vibeiqfile');
1816
+ expect(fileUploadCalls).toHaveLength(1);
1817
+ expect(fileUploadCalls[0].fileName).toBe('ASYNC_PUBLISH_SEASON-events.json');
1818
+ expect(fileUploadCalls[0].mimeType).toBe('application/json');
1819
+ const passedOutboundPublishEvent = spyFlex.mock.calls[0][0];
1820
+ expect(passedOutboundPublishEvent.taskId).toBe('task-abc');
1821
+ expect(passedOutboundPublishEvent.eventType).toBe(eventType);
1822
+ expect(passedOutboundPublishEvent.objectClass).toBe('LCSSeason');
1823
+ expect(passedOutboundPublishEvent.eventsFileId).toBe('file-123');
1824
+ expect(passedOutboundPublishEvent.eventsDownloadLink).toBe('https://download.url');
1825
+ expect(passedOutboundPublishEvent.eventsAdminDownloadLink).toBe('https://admin.download.url');
1826
+ expect(spyFlex).toHaveBeenCalledTimes(1);
1827
+ expect(spyEvent).toHaveBeenCalledTimes(1);
1828
+ expect(spyEvent).toHaveBeenCalledWith(passedOutboundPublishEvent);
1829
+ expect(result).toEqual({ ...flexResponse, outboundPublishEvent: passedOutboundPublishEvent });
1830
+ });
1831
+ it('should skip FlexPLM but still call sendPublishPayloadEvent when mode is vibeiqfile-dontsendtoflexplm', async () => {
1832
+ const bppa = new base_process_publish_assortment_1.BaseProcessPublishAssortment(config, dc, mapFileUtil);
1833
+ const spyFlex = jest.spyOn(flexplm_connect_1.FlexPLMConnect.prototype, 'sendToFlexPLM')
1834
+ .mockResolvedValue({});
1835
+ const spyEvent = jest.spyOn(bppa, 'sendPublishPayloadEvent')
1836
+ .mockResolvedValue(undefined);
1837
+ const result = await bppa.handleVibeIQFile(events, eventType, 'vibeiqfile-dontsendtoflexplm');
1838
+ expect(spyFlex).not.toHaveBeenCalled();
1839
+ expect(spyEvent).toHaveBeenCalledTimes(1);
1840
+ const passedOutboundPublishEvent = spyEvent.mock.calls[0][0];
1841
+ expect(passedOutboundPublishEvent.eventsFileId).toBe('file-123');
1842
+ expect(result).toEqual({
1843
+ message: 'Successfully Uploaded File.',
1844
+ outboundPublishEvent: passedOutboundPublishEvent
1845
+ });
1846
+ });
1847
+ it('should create an external-event with initialEvent parsed from config', async () => {
1848
+ const bppa = new base_process_publish_assortment_1.BaseProcessPublishAssortment(config, dc, mapFileUtil);
1849
+ const outboundPublishEvent = { foo: 'bar' };
1850
+ await bppa.sendPublishPayloadEvent(outboundPublishEvent);
1851
+ expect(createCallArg).toEqual({
1852
+ entityName: 'external-event',
1853
+ object: {
1854
+ originSystemType: 'VibeIQ',
1855
+ objectClass: 'AssortmentPublishedToFlexPLM',
1856
+ outboundPublishEvent,
1857
+ initialEvent: { sourceEventId: 'src-1' }
1858
+ }
1859
+ });
1860
+ });
1861
+ it('should default initialEvent to {} when config has no event', async () => {
1862
+ const bareConfig = { taskId: 'task-x' };
1863
+ const bppa = new base_process_publish_assortment_1.BaseProcessPublishAssortment(bareConfig, dc, mapFileUtil);
1864
+ const outboundPublishEvent = { hello: 'world' };
1865
+ await bppa.sendPublishPayloadEvent(outboundPublishEvent);
1866
+ expect(createCallArg).toEqual({
1867
+ entityName: 'external-event',
1868
+ object: {
1869
+ originSystemType: 'VibeIQ',
1870
+ objectClass: 'AssortmentPublishedToFlexPLM',
1871
+ outboundPublishEvent,
1872
+ initialEvent: {}
1873
+ }
1874
+ });
1875
+ });
1876
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrail/flexplm",
3
- "version": "1.6.1-alpha.75cfe7f",
3
+ "version": "1.6.1-alpha.cc44824",
4
4
  "description": "Library used for integration with flexplm.",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",