@contrail/flexplm 1.1.37 → 1.1.40

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.
@@ -31,7 +31,4 @@ export declare abstract class BaseEntityProcessor {
31
31
  getOutboundEntityUpdates(event: any, flexResponse: any): Promise<any>;
32
32
  handleOutgoingDelete(entityType: any, event: any): Promise<void>;
33
33
  protected abstract getOutgoingUpsertPayload(entityType: any, event: any): Promise<EntityPayloadType>;
34
- protected triggerSendEvent(triggerKey: string, event: any): Promise<any>;
35
- protected sendUpsertToFlexPLM(event: any): Promise<any>;
36
- protected getEntityUpsertPayload(event: any): Promise<EntityPayloadType>;
37
34
  }
@@ -133,8 +133,6 @@ class BaseEntityProcessor {
133
133
  return await this.handleOutgoingUpsert(entityType, event);
134
134
  case 'delete':
135
135
  return await this.handleOutgoingDelete(entityType, event);
136
- case 'sendUpsertToFlexPLM':
137
- return await this.sendUpsertToFlexPLM(event);
138
136
  default:
139
137
  console.log(UNSUPPORTED_TYPE);
140
138
  return {
@@ -173,38 +171,5 @@ class BaseEntityProcessor {
173
171
  async handleOutgoingDelete(entityType, event) {
174
172
  console.warn('delete is not configured', entityType, event.oldData);
175
173
  }
176
- async triggerSendEvent(triggerKey, event) {
177
- const newEvent = {
178
- entityName: 'event-workflow-request',
179
- object: {
180
- triggerKey,
181
- event
182
- }
183
- };
184
- const response = await this.entities.create(newEvent);
185
- return response;
186
- }
187
- async sendUpsertToFlexPLM(event) {
188
- const payload = await this.getEntityUpsertPayload(event);
189
- if (!payload) {
190
- const message = 'No payload to send to FlexPLM';
191
- console.log(message);
192
- return {
193
- status: 500,
194
- data: { message }
195
- };
196
- }
197
- ;
198
- const flexResponse = await new flexplm_connect_1.FlexPLMConnect(this.config).sendToFlexPLM(payload);
199
- const outboundEntityUpdates = await this.getOutboundEntityUpdates(event, flexResponse);
200
- if (outboundEntityUpdates) {
201
- flexResponse['outboundEntityUpdates'] = outboundEntityUpdates;
202
- }
203
- return flexResponse;
204
- }
205
- async getEntityUpsertPayload(event) {
206
- console.warn('getEntityUpsertPayload is not configured', JSON.stringify(event));
207
- return undefined;
208
- }
209
174
  }
210
175
  exports.BaseEntityProcessor = BaseEntityProcessor;
@@ -13,6 +13,7 @@ export declare class BaseProcessPublishAssortment {
13
13
  private mapFileUtil;
14
14
  private transformMapFile;
15
15
  private cache;
16
+ private assortment;
16
17
  constructor(_config: FCConfig, _dc: DataConverter, _mapFileUtil: MapFileUtil);
17
18
  process(event: any): Promise<{
18
19
  results: {
@@ -23,7 +24,9 @@ export declare class BaseProcessPublishAssortment {
23
24
  results: any;
24
25
  skip_await?: undefined;
25
26
  }>;
27
+ getPublishInfo(assortmentId: string, assortmentPublishChangeId: string, apcHistory: any, publisher: any): Promise<any>;
26
28
  getSeasonFederation(assortmentId: any): Promise<SeasonFederation>;
29
+ getAssortment(assortmentId: any): Promise<any>;
27
30
  getSinceDate(assortmentId: any, assortmentPublishChangeId: any, apcHistory: object[]): Promise<Date>;
28
31
  getApcHistory(assortmentId: any): Promise<any>;
29
32
  protected updatedSinceDate(entity: any, sinceDate: Date): boolean;
@@ -22,9 +22,12 @@ class BaseProcessPublishAssortment {
22
22
  this.transformMapFile = this.config['transformMapFile'];
23
23
  }
24
24
  async process(event) {
25
+ const assortmentPublishChangeId = event.assortmentPublishChangeId;
26
+ let apcHistory;
27
+ let publisher;
28
+ const assortmentId = event.assortmentId;
25
29
  try {
26
30
  console.info('process-start!');
27
- const assortmentId = event.assortmentId;
28
31
  let seasonFed;
29
32
  try {
30
33
  seasonFed = await this.getSeasonFederation(assortmentId);
@@ -38,40 +41,50 @@ class BaseProcessPublishAssortment {
38
41
  };
39
42
  return output;
40
43
  }
41
- const assortmentPublishChangeId = event.assortmentPublishChangeId;
42
- const apcHistory = await this.getApcHistory(assortmentId);
44
+ apcHistory = await this.getApcHistory(assortmentId);
43
45
  const sinceDate = await this.getSinceDate(assortmentId, assortmentPublishChangeId, apcHistory);
44
46
  const assortmentPublishChange = await this.downloadAssortmentPublishChange(assortmentId, assortmentPublishChangeId);
47
+ publisher = this.getPublisher(assortmentPublishChange);
45
48
  const changeDetail = await this.downloadHydratedChangeDetail(assortmentPublishChange);
46
49
  const assortmentBaseline = await this.downloadAssortmentBaseline(assortmentPublishChange);
47
50
  const deleteChanges = await this.getDeleteChanges(assortmentPublishChange, apcHistory, assortmentBaseline, sinceDate);
48
51
  const releasedForDevelopmentItemIds = this.getReleasedForDevelopmentItemAndFamilyIds(assortmentBaseline, deleteChanges);
49
52
  const itemToFederatedIdMapping = await this.getItemFederatedIds();
50
- const publisher = this.getPublisher(assortmentPublishChange);
51
53
  const pcd = new publish_change_data_1.PublishChangeData(assortmentId, seasonFed, assortmentPublishChangeId, sinceDate, publisher);
52
54
  pcd.itemToFederatedIdMapping = itemToFederatedIdMapping;
53
55
  pcd.releasedForDevelopmentItemIds = releasedForDevelopmentItemIds;
54
56
  const output = await this.processPublish(pcd, changeDetail, assortmentBaseline, deleteChanges);
57
+ output['publishInfo'] = await this.getPublishInfo(assortmentId, assortmentPublishChangeId, apcHistory, publisher);
55
58
  console.info('process-end');
56
59
  return output;
57
60
  }
58
61
  catch (e) {
62
+ try {
63
+ const publishInfo = await this.getPublishInfo(assortmentId, assortmentPublishChangeId, apcHistory, publisher);
64
+ e.publishInfo = publishInfo;
65
+ }
66
+ catch (e2) {
67
+ console.log('catch e2: ' + e2.message);
68
+ }
59
69
  console.log('catch e: ' + e.message);
60
70
  throw e;
61
71
  }
62
72
  }
73
+ async getPublishInfo(assortmentId, assortmentPublishChangeId, apcHistory, publisher) {
74
+ const apc = apcHistory?.find(apc => apc?.id === assortmentPublishChangeId) || {};
75
+ const assortmentName = (await this.getAssortment(assortmentId))?.name;
76
+ const publishInfo = {
77
+ assortmentName,
78
+ versionName: apc?.versionName,
79
+ versionComments: apc?.versionComments,
80
+ publishDate: apc?.createdOn,
81
+ publisher
82
+ };
83
+ return publishInfo;
84
+ }
63
85
  async getSeasonFederation(assortmentId) {
64
- let assortment;
65
- try {
66
- assortment = await new sdk_1.Entities().get({
67
- entityName: 'assortment',
68
- id: assortmentId
69
- });
70
- console.info('assortment-name: ' + assortment['name']);
71
- }
72
- catch (e) {
73
- console.warn(`No Assortment found for id: ${assortmentId}`);
74
- }
86
+ const assortment = await this.getAssortment(assortmentId);
87
+ console.error('assortment-name: ' + assortment?.name);
75
88
  const publishToFlexPLM = assortment?.publishToFlexPLM;
76
89
  if (!publishToFlexPLM) {
77
90
  throw new Error(BaseProcessPublishAssortment.ASSORTMENT_NOT_PUBLISHABLE);
@@ -95,6 +108,16 @@ class BaseProcessPublishAssortment {
95
108
  seasonObj = await map_utils_1.MapUtil.applyTransformMap(this.transformMapFile, this.mapFileUtil, seasonObj, 'LCSSeason', 'vibe2flex');
96
109
  return seasonObj;
97
110
  }
111
+ async getAssortment(assortmentId) {
112
+ if (this.assortment != undefined) {
113
+ return this.assortment;
114
+ }
115
+ this.assortment = await new sdk_1.Entities().get({
116
+ entityName: 'assortment',
117
+ id: assortmentId
118
+ });
119
+ return this.assortment;
120
+ }
98
121
  async getSinceDate(assortmentId, assortmentPublishChangeId, apcHistory) {
99
122
  let sinceDate = null;
100
123
  let pastPublishCount = 1;
@@ -226,7 +249,7 @@ class BaseProcessPublishAssortment {
226
249
  return publisher;
227
250
  }
228
251
  async downloadAssortmentPublishChange(assortmentId, assortmentPublishChangeId) {
229
- const resourceString = '/assortments/' + assortmentId + '/history/' + assortmentPublishChangeId;
252
+ const resourceString = '/assortments/' + assortmentId + '/history/' + assortmentPublishChangeId + '?relations=createdBy';
230
253
  return await sdk_1.Request.request(resourceString, {
231
254
  method: 'GET',
232
255
  Headers: {
@@ -57,6 +57,160 @@ describe('process publish assortment', () => {
57
57
  expect(results['results']['message']).toEqual(base_process_publish_assortment_1.BaseProcessPublishAssortment.ASSORTMENT_NO_FED_INFO + config.identifierAtts.LCSSeason);
58
58
  });
59
59
  });
60
+ describe('getPublishInfo', () => {
61
+ const config = {};
62
+ const mapFileUtil = new transform_data_1.MapFileUtil(new sdk_1.Entities());
63
+ const dc = new data_converter_1.DataConverter(config, mapFileUtil);
64
+ const assortmentId = 'oqIFX3ELRy8sFRd0';
65
+ const versionName = 'Version 1';
66
+ const versionComments = 'Some Comments';
67
+ const createdOn = '2023-01-15T19:13:58.902Z';
68
+ const plan_history = [
69
+ {
70
+ id: 'sJbGx1Fpq1v2MmcI',
71
+ versionName,
72
+ versionComments,
73
+ createdOn
74
+ }
75
+ ];
76
+ it('All Publish Info', async () => {
77
+ const ppa = new base_process_publish_assortment_1.BaseProcessPublishAssortment(config, dc, mapFileUtil);
78
+ const assortmentPublishChangeId = 'sJbGx1Fpq1v2MmcI';
79
+ const publisher = {
80
+ email: 'chris@vibeiq.com'
81
+ };
82
+ const assortmentName = 'VibeIQ Fall 2023';
83
+ const spy = jest.spyOn(ppa, 'getAssortment')
84
+ .mockImplementation(async (assortmentId) => {
85
+ return { id: assortmentId, name: assortmentName };
86
+ });
87
+ const publishInfo = await ppa.getPublishInfo(assortmentId, assortmentPublishChangeId, plan_history, publisher);
88
+ expect(publishInfo.assortmentName).toEqual(assortmentName);
89
+ expect(publishInfo.versionName).toEqual(versionName);
90
+ expect(publishInfo.versionComments).toEqual(versionComments);
91
+ expect(publishInfo.publishDate).toEqual(createdOn);
92
+ expect(publishInfo.publisher).toEqual(publisher);
93
+ spy.mockRestore();
94
+ });
95
+ it('No Assortment Name', async () => {
96
+ const ppa = new base_process_publish_assortment_1.BaseProcessPublishAssortment(config, dc, mapFileUtil);
97
+ const assortmentPublishChangeId = 'sJbGx1Fpq1v2MmcI';
98
+ const publisher = {
99
+ email: 'chris@vibeiq.com'
100
+ };
101
+ const spy = jest.spyOn(ppa, 'getAssortment')
102
+ .mockImplementation(async (assortmentId) => {
103
+ return { id: assortmentId, name: undefined };
104
+ });
105
+ const publishInfo = await ppa.getPublishInfo(assortmentId, assortmentPublishChangeId, plan_history, publisher);
106
+ expect(publishInfo.assortmentName).toBeUndefined;
107
+ expect(publishInfo.versionName).toEqual(versionName);
108
+ expect(publishInfo.versionComments).toEqual(versionComments);
109
+ expect(publishInfo.publishDate).toEqual(createdOn);
110
+ expect(publishInfo.publisher).toEqual(publisher);
111
+ spy.mockRestore();
112
+ });
113
+ it('No Assortment', async () => {
114
+ const ppa = new base_process_publish_assortment_1.BaseProcessPublishAssortment(config, dc, mapFileUtil);
115
+ const assortmentPublishChangeId = 'sJbGx1Fpq1v2MmcI';
116
+ const publisher = {
117
+ email: 'chris@vibeiq.com'
118
+ };
119
+ const spy = jest.spyOn(ppa, 'getAssortment')
120
+ .mockImplementation(async (assortmentId) => {
121
+ return undefined;
122
+ });
123
+ const publishInfo = await ppa.getPublishInfo(assortmentId, assortmentPublishChangeId, plan_history, publisher);
124
+ expect(publishInfo.assortmentName).toBeUndefined();
125
+ expect(publishInfo.versionName).toEqual(versionName);
126
+ expect(publishInfo.versionComments).toEqual(versionComments);
127
+ expect(publishInfo.publishDate).toEqual(createdOn);
128
+ expect(publishInfo.publisher).toEqual(publisher);
129
+ spy.mockRestore();
130
+ });
131
+ it('Empty Version Name & Comments', async () => {
132
+ const ppa = new base_process_publish_assortment_1.BaseProcessPublishAssortment(config, dc, mapFileUtil);
133
+ const history = [
134
+ {
135
+ id: 'sJbGx1Fpq1v2MmcI',
136
+ versionName: '',
137
+ versionComments: '',
138
+ createdOn
139
+ }
140
+ ];
141
+ const assortmentPublishChangeId = 'sJbGx1Fpq1v2MmcI';
142
+ const publisher = {
143
+ email: 'chris@vibeiq.com'
144
+ };
145
+ const assortmentName = 'VibeIQ Fall 2023';
146
+ const spy = jest.spyOn(ppa, 'getAssortment')
147
+ .mockImplementation(async (assortmentId) => {
148
+ return { id: assortmentId, name: assortmentName };
149
+ });
150
+ const publishInfo = await ppa.getPublishInfo(assortmentId, assortmentPublishChangeId, history, publisher);
151
+ expect(publishInfo.assortmentName).toEqual(assortmentName);
152
+ expect(publishInfo.versionName).toEqual('');
153
+ expect(publishInfo.versionComments).toEqual('');
154
+ expect(publishInfo.publishDate).toEqual(createdOn);
155
+ expect(publishInfo.publisher).toEqual(publisher);
156
+ spy.mockRestore();
157
+ });
158
+ it('Undefined Publisher', async () => {
159
+ const ppa = new base_process_publish_assortment_1.BaseProcessPublishAssortment(config, dc, mapFileUtil);
160
+ const assortmentPublishChangeId = 'sJbGx1Fpq1v2MmcI';
161
+ const publisher = undefined;
162
+ const assortmentName = 'VibeIQ Fall 2023';
163
+ const spy = jest.spyOn(ppa, 'getAssortment')
164
+ .mockImplementation(async (assortmentId) => {
165
+ return { id: assortmentId, name: assortmentName };
166
+ });
167
+ const publishInfo = await ppa.getPublishInfo(assortmentId, assortmentPublishChangeId, plan_history, publisher);
168
+ expect(publishInfo.assortmentName).toEqual(assortmentName);
169
+ expect(publishInfo.versionName).toEqual(versionName);
170
+ expect(publishInfo.versionComments).toEqual(versionComments);
171
+ expect(publishInfo.publishDate).toEqual(createdOn);
172
+ expect(publishInfo.publisher).toBeUndefined();
173
+ spy.mockRestore();
174
+ });
175
+ it('No APC', async () => {
176
+ const ppa = new base_process_publish_assortment_1.BaseProcessPublishAssortment(config, dc, mapFileUtil);
177
+ const assortmentPublishChangeId = 'bad-id';
178
+ const publisher = {
179
+ email: 'chris@vibeiq.com'
180
+ };
181
+ const assortmentName = 'VibeIQ Fall 2023';
182
+ const spy = jest.spyOn(ppa, 'getAssortment')
183
+ .mockImplementation(async (assortmentId) => {
184
+ return { id: assortmentId, name: assortmentName };
185
+ });
186
+ const publishInfo = await ppa.getPublishInfo(assortmentId, assortmentPublishChangeId, plan_history, publisher);
187
+ expect(publishInfo.assortmentName).toEqual(assortmentName);
188
+ expect(publishInfo.versionName).toBeUndefined();
189
+ expect(publishInfo.versionComments).toBeUndefined();
190
+ expect(publishInfo.publishDate).toBeUndefined();
191
+ expect(publishInfo.publisher).toEqual(publisher);
192
+ spy.mockRestore();
193
+ });
194
+ it('No APC history', async () => {
195
+ const ppa = new base_process_publish_assortment_1.BaseProcessPublishAssortment(config, dc, mapFileUtil);
196
+ const assortmentPublishChangeId = 'bad-id';
197
+ const publisher = {
198
+ email: 'chris@vibeiq.com'
199
+ };
200
+ const assortmentName = 'VibeIQ Fall 2023';
201
+ const spy = jest.spyOn(ppa, 'getAssortment')
202
+ .mockImplementation(async (assortmentId) => {
203
+ return { id: assortmentId, name: assortmentName };
204
+ });
205
+ const publishInfo = await ppa.getPublishInfo(assortmentId, assortmentPublishChangeId, undefined, publisher);
206
+ expect(publishInfo.assortmentName).toEqual(assortmentName);
207
+ expect(publishInfo.versionName).toBeUndefined();
208
+ expect(publishInfo.versionComments).toBeUndefined();
209
+ expect(publishInfo.publishDate).toBeUndefined();
210
+ expect(publishInfo.publisher).toEqual(publisher);
211
+ spy.mockRestore();
212
+ });
213
+ });
60
214
  describe('getSeasonFederation', () => {
61
215
  const config = {
62
216
  identifierAtts: {
@@ -11,6 +11,9 @@ class ConfigDefaults {
11
11
  if (config?.itemPreDevelopmentLifecycleStages && !(config?.itemPreDevelopmentLifecycleStages instanceof Array)) {
12
12
  config.itemPreDevelopmentLifecycleStages = config.itemPreDevelopmentLifecycleStages.split(',');
13
13
  }
14
+ else if (Object.keys(config).includes('itemPreDevelopmentLifecycleStages')) {
15
+ delete config['itemPreDevelopmentLifecycleStages'];
16
+ }
14
17
  const defaultConfig = {
15
18
  urlContext: '/Windchill',
16
19
  sendMode: {
@@ -159,6 +159,32 @@ describe('all tests', () => {
159
159
  expect(itemPreDevelopmentLifecycleStages.includes(concept));
160
160
  expect(itemPreDevelopmentLifecycleStages.includes(development));
161
161
  });
162
+ it('itemPreDevelopmentLifecycleStages as empty string', async () => {
163
+ console.log('itemPreDevelopmentLifecycleStages as empty string');
164
+ const concept = 'concept';
165
+ const startConfig = Object.assign({}, config);
166
+ startConfig['itemPreDevelopmentLifecycleStages'] = '';
167
+ console.log('startConfig: ' + JSON.stringify(startConfig));
168
+ const fcConfig = await config_defaults_1.ConfigDefaults.setConfigDefaults(startConfig);
169
+ console.log('fcConfig: ' + JSON.stringify(fcConfig));
170
+ const itemPreDevelopmentLifecycleStages = fcConfig['itemPreDevelopmentLifecycleStages'];
171
+ expect(itemPreDevelopmentLifecycleStages instanceof Array).toBeTruthy();
172
+ expect(itemPreDevelopmentLifecycleStages.length).toBe(1);
173
+ expect(itemPreDevelopmentLifecycleStages.includes(concept));
174
+ });
175
+ it('itemPreDevelopmentLifecycleStages as empty array', async () => {
176
+ console.log('itemPreDevelopmentLifecycleStages as empty string');
177
+ const concept = 'concept';
178
+ const startConfig = Object.assign({}, config);
179
+ startConfig['itemPreDevelopmentLifecycleStages'] = [];
180
+ console.log('startConfig: ' + JSON.stringify(startConfig));
181
+ const fcConfig = await config_defaults_1.ConfigDefaults.setConfigDefaults(startConfig);
182
+ console.log('fcConfig: ' + JSON.stringify(fcConfig));
183
+ const itemPreDevelopmentLifecycleStages = fcConfig['itemPreDevelopmentLifecycleStages'];
184
+ expect(itemPreDevelopmentLifecycleStages instanceof Array).toBeTruthy();
185
+ expect(itemPreDevelopmentLifecycleStages.length).toBe(1);
186
+ expect(itemPreDevelopmentLifecycleStages.includes(concept));
187
+ });
162
188
  it('identifierAtts-LCSProduct-get default', async () => {
163
189
  const startConfig = Object.assign({}, config);
164
190
  const objectClass = 'LCSProduct';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrail/flexplm",
3
- "version": "1.1.37",
3
+ "version": "1.1.40",
4
4
  "description": "Library used for integration with flexplm.",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -9,7 +9,8 @@
9
9
  "format": "prettier --write \"src/**/*.ts\" \"src/**/*.js\"",
10
10
  "lint": "tslint -p tsconfig.json",
11
11
  "test": "jest",
12
- "test-watch": "jest --watch"
12
+ "test-watch": "jest --watch",
13
+ "test-debug": "jest --runInBand"
13
14
  },
14
15
  "keywords": [],
15
16
  "author": "",
@@ -176,8 +176,6 @@ export abstract class BaseEntityProcessor {
176
176
  return await this.handleOutgoingUpsert(entityType, event);
177
177
  case 'delete':
178
178
  return await this.handleOutgoingDelete(entityType, event);
179
- case 'sendUpsertToFlexPLM':
180
- return await this.sendUpsertToFlexPLM(event);
181
179
  default:
182
180
  console.log(UNSUPPORTED_TYPE);
183
181
  return {
@@ -225,57 +223,4 @@ export abstract class BaseEntityProcessor {
225
223
 
226
224
  // This method must be implemented by derived classes
227
225
  protected abstract getOutgoingUpsertPayload(entityType, event): Promise<EntityPayloadType>;
228
-
229
- /** Create a new event-workflow-request to rerun sending the entity to FlexPLM
230
- * The event must contain any information needed to ensure it is put in the correct queue for the entity
231
- *
232
- * @param triggerKey Ex: event.entityType + '|sendUpsertToFlexPLM'
233
- * @param event
234
- * @returns
235
- */
236
-
237
- protected async triggerSendEvent(triggerKey: string, event: any) {
238
- const newEvent = {
239
- entityName: 'event-workflow-request',
240
- object: {
241
- triggerKey,
242
- event
243
- }
244
- };
245
- const response = await this.entities.create(newEvent);
246
-
247
- return response;
248
- }
249
-
250
- /** Sends the current state of the entity to FlexPLM.
251
- * So any changes made in Vibe between the event being generated and the event being processed are sent to FlexPLM.
252
- *
253
- * @param event must contain entityType, id; which are used to query for the entity
254
- * @returns results of sending the entity to FlexPLM
255
- */
256
- protected async sendUpsertToFlexPLM(event) {
257
- const payload = await this.getEntityUpsertPayload(event);
258
- if(!payload){
259
- const message = 'No payload to send to FlexPLM';
260
- console.log(message);
261
- return {
262
- status: 500,
263
- data: {message}
264
- };
265
- };
266
- const flexResponse: any = await new FlexPLMConnect(this.config).sendToFlexPLM(payload);
267
-
268
- const outboundEntityUpdates = await this.getOutboundEntityUpdates(event, flexResponse);
269
- if(outboundEntityUpdates){
270
- flexResponse['outboundEntityUpdates'] = outboundEntityUpdates;
271
- }
272
-
273
- return flexResponse;
274
-
275
- }
276
-
277
- protected async getEntityUpsertPayload(event: any): Promise<EntityPayloadType> {
278
- console.warn('getEntityUpsertPayload is not configured', JSON.stringify(event));
279
- return undefined;
280
- }
281
226
  }
@@ -60,6 +60,175 @@ describe('process publish assortment', () => {
60
60
  });
61
61
  });
62
62
 
63
+ describe('getPublishInfo', () =>{
64
+ const config = {} as FCConfig;
65
+ const mapFileUtil = new MapFileUtil(new Entities());
66
+ const dc = new DataConverter(config, mapFileUtil);
67
+
68
+ const assortmentId = 'oqIFX3ELRy8sFRd0';
69
+ const versionName = 'Version 1';
70
+ const versionComments = 'Some Comments';
71
+ const createdOn = '2023-01-15T19:13:58.902Z';
72
+ const plan_history = [
73
+ {
74
+ id: 'sJbGx1Fpq1v2MmcI',
75
+ versionName,
76
+ versionComments,
77
+ createdOn
78
+
79
+ }
80
+ ];
81
+ it('All Publish Info', async () =>{
82
+ const ppa = new BaseProcessPublishAssortment(config, dc, mapFileUtil);
83
+
84
+ const assortmentPublishChangeId = 'sJbGx1Fpq1v2MmcI';
85
+ const publisher = {
86
+ email: 'chris@vibeiq.com'
87
+ };
88
+ const assortmentName = 'VibeIQ Fall 2023';
89
+ const spy = jest.spyOn(ppa, 'getAssortment')
90
+ .mockImplementation(async (assortmentId) => {
91
+ return {id: assortmentId, name: assortmentName};
92
+ });
93
+ const publishInfo = await ppa.getPublishInfo(assortmentId, assortmentPublishChangeId, plan_history, publisher);
94
+ expect(publishInfo.assortmentName).toEqual(assortmentName);
95
+ expect(publishInfo.versionName).toEqual(versionName);
96
+ expect(publishInfo.versionComments).toEqual(versionComments);
97
+ expect(publishInfo.publishDate).toEqual(createdOn);
98
+ expect(publishInfo.publisher).toEqual(publisher);
99
+ spy.mockRestore();
100
+ });
101
+
102
+ it('No Assortment Name', async () =>{
103
+ const ppa = new BaseProcessPublishAssortment(config, dc, mapFileUtil);
104
+
105
+ const assortmentPublishChangeId = 'sJbGx1Fpq1v2MmcI';
106
+ const publisher = {
107
+ email: 'chris@vibeiq.com'
108
+ };
109
+ const spy = jest.spyOn(ppa, 'getAssortment')
110
+ .mockImplementation(async (assortmentId) => {
111
+ return {id: assortmentId, name: undefined};
112
+ });
113
+ const publishInfo = await ppa.getPublishInfo(assortmentId, assortmentPublishChangeId, plan_history, publisher);
114
+ expect(publishInfo.assortmentName).toBeUndefined;
115
+ expect(publishInfo.versionName).toEqual(versionName);
116
+ expect(publishInfo.versionComments).toEqual(versionComments);
117
+ expect(publishInfo.publishDate).toEqual(createdOn);
118
+ expect(publishInfo.publisher).toEqual(publisher);
119
+ spy.mockRestore();
120
+ });
121
+
122
+ it('No Assortment', async () =>{
123
+ const ppa = new BaseProcessPublishAssortment(config, dc, mapFileUtil);
124
+
125
+ const assortmentPublishChangeId = 'sJbGx1Fpq1v2MmcI';
126
+ const publisher = {
127
+ email: 'chris@vibeiq.com'
128
+ };
129
+ const spy = jest.spyOn(ppa, 'getAssortment')
130
+ .mockImplementation(async (assortmentId) => {
131
+ return undefined;
132
+ });
133
+ const publishInfo = await ppa.getPublishInfo(assortmentId, assortmentPublishChangeId, plan_history, publisher);
134
+ expect(publishInfo.assortmentName).toBeUndefined();
135
+ expect(publishInfo.versionName).toEqual(versionName);
136
+ expect(publishInfo.versionComments).toEqual(versionComments);
137
+ expect(publishInfo.publishDate).toEqual(createdOn);
138
+ expect(publishInfo.publisher).toEqual(publisher);
139
+ spy.mockRestore();
140
+ });
141
+
142
+ it('Empty Version Name & Comments', async () =>{
143
+ const ppa = new BaseProcessPublishAssortment(config, dc, mapFileUtil);
144
+ const history = [
145
+ {
146
+ id: 'sJbGx1Fpq1v2MmcI',
147
+ versionName: '',
148
+ versionComments: '',
149
+ createdOn
150
+ }
151
+ ];
152
+ const assortmentPublishChangeId = 'sJbGx1Fpq1v2MmcI';
153
+ const publisher = {
154
+ email: 'chris@vibeiq.com'
155
+ };
156
+ const assortmentName = 'VibeIQ Fall 2023';
157
+ const spy = jest.spyOn(ppa, 'getAssortment')
158
+ .mockImplementation(async (assortmentId) => {
159
+ return {id: assortmentId, name: assortmentName};
160
+ });
161
+ const publishInfo = await ppa.getPublishInfo(assortmentId, assortmentPublishChangeId, history, publisher);
162
+ expect(publishInfo.assortmentName).toEqual(assortmentName);
163
+ expect(publishInfo.versionName).toEqual('');
164
+ expect(publishInfo.versionComments).toEqual('');
165
+ expect(publishInfo.publishDate).toEqual(createdOn);
166
+ expect(publishInfo.publisher).toEqual(publisher);
167
+ spy.mockRestore();
168
+ });
169
+
170
+ it('Undefined Publisher', async () =>{
171
+ const ppa = new BaseProcessPublishAssortment(config, dc, mapFileUtil);
172
+
173
+ const assortmentPublishChangeId = 'sJbGx1Fpq1v2MmcI';
174
+ const publisher = undefined;
175
+ const assortmentName = 'VibeIQ Fall 2023';
176
+ const spy = jest.spyOn(ppa, 'getAssortment')
177
+ .mockImplementation(async (assortmentId) => {
178
+ return {id: assortmentId, name: assortmentName};
179
+ });
180
+ const publishInfo = await ppa.getPublishInfo(assortmentId, assortmentPublishChangeId, plan_history, publisher);
181
+ expect(publishInfo.assortmentName).toEqual(assortmentName);
182
+ expect(publishInfo.versionName).toEqual(versionName);
183
+ expect(publishInfo.versionComments).toEqual(versionComments);
184
+ expect(publishInfo.publishDate).toEqual(createdOn);
185
+ expect(publishInfo.publisher).toBeUndefined();
186
+ spy.mockRestore();
187
+ });
188
+
189
+ it('No APC', async () =>{
190
+ const ppa = new BaseProcessPublishAssortment(config, dc, mapFileUtil);
191
+
192
+ const assortmentPublishChangeId = 'bad-id';
193
+ const publisher = {
194
+ email: 'chris@vibeiq.com'
195
+ };
196
+ const assortmentName = 'VibeIQ Fall 2023';
197
+ const spy = jest.spyOn(ppa, 'getAssortment')
198
+ .mockImplementation(async (assortmentId) => {
199
+ return {id: assortmentId, name: assortmentName};
200
+ });
201
+ const publishInfo = await ppa.getPublishInfo(assortmentId, assortmentPublishChangeId, plan_history, publisher);
202
+ expect(publishInfo.assortmentName).toEqual(assortmentName);
203
+ expect(publishInfo.versionName).toBeUndefined();
204
+ expect(publishInfo.versionComments).toBeUndefined();
205
+ expect(publishInfo.publishDate).toBeUndefined();
206
+ expect(publishInfo.publisher).toEqual(publisher);
207
+ spy.mockRestore();
208
+ });
209
+
210
+ it('No APC history', async () =>{
211
+ const ppa = new BaseProcessPublishAssortment(config, dc, mapFileUtil);
212
+
213
+ const assortmentPublishChangeId = 'bad-id';
214
+ const publisher = {
215
+ email: 'chris@vibeiq.com'
216
+ };
217
+ const assortmentName = 'VibeIQ Fall 2023';
218
+ const spy = jest.spyOn(ppa, 'getAssortment')
219
+ .mockImplementation(async (assortmentId) => {
220
+ return {id: assortmentId, name: assortmentName};
221
+ });
222
+ const publishInfo = await ppa.getPublishInfo(assortmentId, assortmentPublishChangeId, undefined, publisher);
223
+ expect(publishInfo.assortmentName).toEqual(assortmentName);
224
+ expect(publishInfo.versionName).toBeUndefined();
225
+ expect(publishInfo.versionComments).toBeUndefined();
226
+ expect(publishInfo.publishDate).toBeUndefined();
227
+ expect(publishInfo.publisher).toEqual(publisher);
228
+ spy.mockRestore();
229
+ });
230
+
231
+ });
63
232
  describe('getSeasonFederation', () => {
64
233
  const config = {
65
234
  identifierAtts: {
@@ -25,6 +25,8 @@ export class BaseProcessPublishAssortment {
25
25
  private cache = {
26
26
  carriedFromSeason: {}
27
27
  };
28
+ private assortment: any;
29
+
28
30
  constructor(_config: FCConfig, _dc: DataConverter, _mapFileUtil: MapFileUtil) {
29
31
  this.config = _config;
30
32
  this.dc = _dc;
@@ -33,9 +35,13 @@ export class BaseProcessPublishAssortment {
33
35
  }
34
36
 
35
37
  public async process(event) {
38
+
39
+ const assortmentPublishChangeId = event.assortmentPublishChangeId;
40
+ let apcHistory;
41
+ let publisher;
42
+ const assortmentId = event.assortmentId;
36
43
  try {
37
44
  console.info('process-start!');
38
- const assortmentId = event.assortmentId;
39
45
  let seasonFed: SeasonFederation;
40
46
  try {
41
47
  seasonFed = await this.getSeasonFederation(assortmentId);
@@ -49,12 +55,13 @@ export class BaseProcessPublishAssortment {
49
55
  return output;
50
56
  }
51
57
 
52
- const assortmentPublishChangeId = event.assortmentPublishChangeId;
53
- const apcHistory = await this.getApcHistory(assortmentId);
58
+ apcHistory = await this.getApcHistory(assortmentId);
59
+
54
60
  const sinceDate = await this.getSinceDate(assortmentId, assortmentPublishChangeId, apcHistory);
55
61
 
56
62
  //Get detail information
57
63
  const assortmentPublishChange = await this.downloadAssortmentPublishChange(assortmentId, assortmentPublishChangeId);
64
+ publisher = this.getPublisher(assortmentPublishChange);
58
65
 
59
66
  const changeDetail = await this.downloadHydratedChangeDetail(assortmentPublishChange);
60
67
 
@@ -65,33 +72,45 @@ export class BaseProcessPublishAssortment {
65
72
  const releasedForDevelopmentItemIds = this.getReleasedForDevelopmentItemAndFamilyIds(assortmentBaseline, deleteChanges);
66
73
  const itemToFederatedIdMapping = await this.getItemFederatedIds(/*allItemIds*/);
67
74
 
68
- const publisher = this.getPublisher(assortmentPublishChange);
69
75
  const pcd = new PublishChangeData(assortmentId, seasonFed, assortmentPublishChangeId, sinceDate, publisher);
70
76
  pcd.itemToFederatedIdMapping = itemToFederatedIdMapping;
71
77
 
72
78
  pcd.releasedForDevelopmentItemIds = releasedForDevelopmentItemIds;
73
79
 
74
80
  const output = await this.processPublish(pcd, changeDetail, assortmentBaseline, deleteChanges);
81
+ output ['publishInfo'] = await this.getPublishInfo(assortmentId, assortmentPublishChangeId, apcHistory, publisher);
75
82
  console.info('process-end');
76
83
 
77
84
  return output;
78
85
  } catch (e) {
86
+ try {
87
+ const publishInfo = await this.getPublishInfo(assortmentId, assortmentPublishChangeId, apcHistory, publisher);
88
+ e.publishInfo = publishInfo;
89
+ }catch(e2){
90
+ console.log('catch e2: ' + e2.message);
91
+ }
79
92
  console.log('catch e: ' + e.message);
80
93
  throw e;
81
94
  }
82
95
  }
96
+ async getPublishInfo(assortmentId: string, assortmentPublishChangeId: string, apcHistory: any, publisher: any): Promise<any> {
97
+ const apc = apcHistory?.find(apc => apc?.id === assortmentPublishChangeId) || {};
98
+ const assortmentName = (await this.getAssortment(assortmentId))?.name;
99
+ const publishInfo = {
100
+ assortmentName,
101
+ versionName: apc?.versionName,
102
+ versionComments: apc?.versionComments,
103
+ publishDate: apc?.createdOn,
104
+ publisher
105
+ };
106
+
107
+ return publishInfo;
108
+ }
83
109
 
84
110
  async getSeasonFederation(assortmentId): Promise<SeasonFederation> {
85
- let assortment;
86
- try {
87
- assortment = await new Entities().get({
88
- entityName: 'assortment',
89
- id: assortmentId
90
- });
91
- console.info('assortment-name: ' + assortment['name']);
92
- } catch (e) {
93
- console.warn(`No Assortment found for id: ${assortmentId}`);
94
- }
111
+ const assortment = await this.getAssortment(assortmentId);
112
+ console.error('assortment-name: ' + assortment?.name);
113
+
95
114
  const publishToFlexPLM = assortment?.publishToFlexPLM;
96
115
  if (!publishToFlexPLM) {
97
116
  throw new Error(BaseProcessPublishAssortment.ASSORTMENT_NOT_PUBLISHABLE);
@@ -120,6 +139,18 @@ export class BaseProcessPublishAssortment {
120
139
  return seasonObj as SeasonFederation;
121
140
  }
122
141
 
142
+ async getAssortment(assortmentId: any) {
143
+ if(this.assortment != undefined){
144
+ return this.assortment;
145
+ }
146
+
147
+ this.assortment = await new Entities().get({
148
+ entityName: 'assortment',
149
+ id: assortmentId
150
+ });
151
+ return this.assortment;
152
+ }
153
+
123
154
  public async getSinceDate(assortmentId: any, assortmentPublishChangeId: any, apcHistory: object[]): Promise<Date> {
124
155
 
125
156
  let sinceDate = null;
@@ -265,7 +296,7 @@ export class BaseProcessPublishAssortment {
265
296
  }
266
297
 
267
298
  async downloadAssortmentPublishChange(assortmentId: string, assortmentPublishChangeId: string) {
268
- const resourceString = '/assortments/' + assortmentId + '/history/' + assortmentPublishChangeId;
299
+ const resourceString = '/assortments/' + assortmentId + '/history/' + assortmentPublishChangeId + '?relations=createdBy';
269
300
  return await Request.request(
270
301
  resourceString,
271
302
  {
@@ -190,6 +190,37 @@ describe('all tests', () => {
190
190
  expect(itemPreDevelopmentLifecycleStages.includes(development));
191
191
  });
192
192
 
193
+ it('itemPreDevelopmentLifecycleStages as empty string', async () => {
194
+ console.log('itemPreDevelopmentLifecycleStages as empty string');
195
+ const concept = 'concept';
196
+ const startConfig = Object.assign({}, config);
197
+ startConfig['itemPreDevelopmentLifecycleStages'] = '';
198
+ console.log('startConfig: ' + JSON.stringify(startConfig));
199
+
200
+ const fcConfig: FCConfig = await ConfigDefaults.setConfigDefaults(startConfig);
201
+ console.log('fcConfig: ' + JSON.stringify(fcConfig));
202
+ const itemPreDevelopmentLifecycleStages = fcConfig['itemPreDevelopmentLifecycleStages'];
203
+ expect(itemPreDevelopmentLifecycleStages instanceof Array).toBeTruthy();
204
+ expect(itemPreDevelopmentLifecycleStages.length).toBe(1);
205
+ expect(itemPreDevelopmentLifecycleStages.includes(concept));
206
+
207
+ });
208
+
209
+ it('itemPreDevelopmentLifecycleStages as empty array', async () => {
210
+ console.log('itemPreDevelopmentLifecycleStages as empty string');
211
+ const concept = 'concept';
212
+ const startConfig = Object.assign({}, config);
213
+ startConfig['itemPreDevelopmentLifecycleStages'] = [];
214
+ console.log('startConfig: ' + JSON.stringify(startConfig));
215
+
216
+ const fcConfig: FCConfig = await ConfigDefaults.setConfigDefaults(startConfig);
217
+ console.log('fcConfig: ' + JSON.stringify(fcConfig));
218
+ const itemPreDevelopmentLifecycleStages = fcConfig['itemPreDevelopmentLifecycleStages'];
219
+ expect(itemPreDevelopmentLifecycleStages instanceof Array).toBeTruthy();
220
+ expect(itemPreDevelopmentLifecycleStages.length).toBe(1);
221
+ expect(itemPreDevelopmentLifecycleStages.includes(concept));
222
+
223
+ });
193
224
  it('identifierAtts-LCSProduct-get default', async () => {
194
225
  const startConfig = Object.assign({}, config);
195
226
  const objectClass = 'LCSProduct';
@@ -13,6 +13,8 @@ export class ConfigDefaults {
13
13
  //List will be comma separated list in UI, so convert to array
14
14
  if(config?.itemPreDevelopmentLifecycleStages && !(config?.itemPreDevelopmentLifecycleStages instanceof Array)){
15
15
  config.itemPreDevelopmentLifecycleStages = config.itemPreDevelopmentLifecycleStages.split(',');
16
+ } else if(Object.keys(config).includes('itemPreDevelopmentLifecycleStages')){
17
+ delete config['itemPreDevelopmentLifecycleStages'];
16
18
  }
17
19
 
18
20
  const defaultConfig = {
@@ -37,10 +37,6 @@ export class ThumbnailUtil {
37
37
  }
38
38
  /** Determines if a new image has been generated. If any viewable is new, there might be a better
39
39
  * sized image. So send the fileId as new.
40
- *
41
- * @param event
42
- * @param customSizes
43
- * @returns boolean
44
40
  */
45
41
  public isThumbnailNew(event, customSizes: any[]): boolean {
46
42
  const propertyDiffs = event?.propertyDiffs;