@contrail/flexplm 1.7.4-alpha.72422a6 → 1.7.4-alpha.879820c

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.
@@ -12,6 +12,7 @@ export declare class ItemFamilyChanges {
12
12
  colorDeletes: string[];
13
13
  colorUpdates: string[];
14
14
  colorUnchanged: string[];
15
+ colorSuppressed: string[];
15
16
  assortmentItemFullChangeMap: Map<string, any>;
16
17
  assortmentItemDeleteMap: Map<string, any>;
17
18
  itemToFederatedIdMapping: Map<string, string>;
@@ -14,6 +14,7 @@ class ItemFamilyChanges {
14
14
  this.colorDeletes = [];
15
15
  this.colorUpdates = [];
16
16
  this.colorUnchanged = [];
17
+ this.colorSuppressed = []; // changed this window, but excluded by the lifecycle filter
17
18
  this.assortmentItemFullChangeMap = new Map();
18
19
  this.assortmentItemDeleteMap = new Map();
19
20
  this.itemToFederatedIdMapping = new Map();
@@ -32,6 +33,7 @@ class ItemFamilyChanges {
32
33
  s += ' colorDeletes: [' + this.colorDeletes + ']\n';
33
34
  s += ' colorUpdates: [' + this.colorUpdates + ']\n';
34
35
  s += ' colorUnchanged: [' + this.colorUnchanged + ']\n';
36
+ s += ' colorSuppressed: [' + this.colorSuppressed + ']\n';
35
37
  s += 'assortmentItemFullChangeMap:\n';
36
38
  s += 'size: ' + this.assortmentItemFullChangeMap.size + '\n[';
37
39
  for (const key of this.assortmentItemFullChangeMap.keys()) {
@@ -9,6 +9,7 @@ const map_utils_1 = require("../util/map-utils");
9
9
  const type_conversion_utils_1 = require("../util/type-conversion-utils");
10
10
  const fsPromise = require("fs/promises");
11
11
  const path = require("path");
12
+ const app_framework_1 = require("@contrail/app-framework");
12
13
  const event_short_message_status_1 = require("../util/event-short-message-status");
13
14
  class BaseProcessPublishAssortment {
14
15
  constructor(_config, _dc, _mapFileUtil) {
@@ -28,8 +29,7 @@ class BaseProcessPublishAssortment {
28
29
  let publisher;
29
30
  const assortmentId = event.assortmentId;
30
31
  try {
31
- console.info('process-start: assortmentId: ' + assortmentId
32
- + ', assortmentPublishChangeId: ' + assortmentPublishChangeId);
32
+ console.info('process-start!');
33
33
  let seasonFed;
34
34
  try {
35
35
  seasonFed = await this.getSeasonFederation(assortmentId);
@@ -74,9 +74,9 @@ class BaseProcessPublishAssortment {
74
74
  e.publishInfo = publishInfo;
75
75
  }
76
76
  catch (e2) {
77
- console.error('getPublishInfo failed: ' + e2.message);
77
+ console.log('catch e2: ' + e2.message);
78
78
  }
79
- console.error('process failed: assortmentId: ' + assortmentId + ': ' + e.message);
79
+ console.log('catch e: ' + e.message);
80
80
  throw e;
81
81
  }
82
82
  }
@@ -315,17 +315,19 @@ class BaseProcessPublishAssortment {
315
315
  }
316
316
  async downloadHydratedChangeDetail(assortmentPublishChange) {
317
317
  try {
318
+ console.info('downloadHydratedChangeDetail-start');
318
319
  const link = assortmentPublishChange?.hydratedDetailDownloadLink;
319
320
  const response = await fetch(link);
320
321
  const data = await response.json();
321
322
  return data;
322
323
  }
323
324
  catch (e) {
324
- console.error('Error hydratedDetailDownloadLink: ' + e.message);
325
+ console.log('Error hydratedDetailDownloadLink: ' + e.message);
325
326
  }
326
327
  return undefined;
327
328
  }
328
329
  async downloadAssortmentBaseline(assortmentPublishChange) {
330
+ console.info('downloadAssortmentBaseline-start');
329
331
  try {
330
332
  const link = assortmentPublishChange?.assortmentBaselineDownloadLink;
331
333
  const response = await fetch(link);
@@ -333,12 +335,15 @@ class BaseProcessPublishAssortment {
333
335
  return data;
334
336
  }
335
337
  catch (e) {
336
- console.error('Error assortmentBaselineDownloadLink: ' + e.message);
338
+ console.log('Error assortmentBaselineDownloadLink: ' + e.message);
337
339
  }
338
340
  return undefined;
339
341
  }
340
342
  async getDeleteChanges(assortmentPublishChange, apcHistory, assortmentBaseline, sinceDate) {
343
+ console.info('getDeleteChanges(): ' + assortmentPublishChange?.id);
344
+ console.info(sinceDate);
341
345
  const currentAPCIndex = apcHistory.findIndex(pc => pc?.id === assortmentPublishChange?.id);
346
+ console.info(' currentAPCIndex: ' + currentAPCIndex);
342
347
  if (currentAPCIndex == 0) {
343
348
  return [];
344
349
  }
@@ -347,13 +352,16 @@ class BaseProcessPublishAssortment {
347
352
  const apcDeletes = await this.downloadDeleteChanges(assortmentPublishChange);
348
353
  let previousApcDate = Date.parse(apcCreatedOn);
349
354
  const sinceDateMilliseconds = sinceDate.getTime();
350
- console.info('getDeleteChanges(): apc: ' + assortmentPublishChange?.id
351
- + ', index: ' + currentAPCIndex + ', previousApcCreatedOn: ' + apcCreatedOn);
355
+ if (app_framework_1.Logger.isInfoOn()) {
356
+ console.info('apcCreatedOn: ' + apcCreatedOn);
357
+ console.info('sinceDate: ' + sinceDate);
358
+ console.info('sinceDateMilliseconds: ' + sinceDateMilliseconds);
359
+ console.info('apcDateMilliseconds: ' + previousApcDate);
360
+ }
352
361
  //if only 1 apc, no processing needed
353
362
  if (sinceDateMilliseconds !== previousApcDate) {
363
+ console.info('sinceDateMilliseconds !== apcDateMilliseconds');
354
364
  const currentAssortmentItemIds = this.getBaselineItemIds(assortmentBaseline);
355
- let scannedCount = 0;
356
- let addedCount = 0;
357
365
  const deleteIds = (apcDeletes.length === 0)
358
366
  ? []
359
367
  : apcDeletes.map(item => item?.itemId);
@@ -368,20 +376,19 @@ class BaseProcessPublishAssortment {
368
376
  ? apcHistory[i - 1]
369
377
  : undefined;
370
378
  const deleteChanges = await this.buildDeleteChanges(workingAPC, previousApc);
379
+ console.info('checking deleteChanges');
371
380
  for (const dItem of deleteChanges) {
372
381
  const dItemId = dItem?.itemId;
373
- scannedCount++;
382
+ console.info(dItemId);
374
383
  if (!currentAssortmentItemIds.includes(dItemId) && !deleteIds.includes(dItemId)) {
375
- addedCount++;
384
+ console.info('adding');
376
385
  deleteIds.push(dItemId);
377
386
  apcDeletes.push(dItem);
378
387
  }
379
388
  }
380
389
  }
381
390
  }
382
- console.info('getDeleteChanges(): baselineItems: ' + currentAssortmentItemIds.length
383
- + ', scanned ' + scannedCount + ' historical delete changes, added ' + addedCount
384
- + ', deleteIds: ' + deleteIds.length);
391
+ console.info('getDeleteChanges()-currentAssortmentItemIds: ' + currentAssortmentItemIds);
385
392
  console.info('getDeleteChanges()-deleteIds: ' + deleteIds);
386
393
  }
387
394
  return apcDeletes;
@@ -395,6 +402,7 @@ class BaseProcessPublishAssortment {
395
402
  return itemIds;
396
403
  }
397
404
  async downloadDeleteChanges(apc) {
405
+ console.info('deleteDataDownloadLink-start');
398
406
  try {
399
407
  const link = apc?.deleteDataDownloadLink;
400
408
  const response = await fetch(link);
@@ -402,17 +410,19 @@ class BaseProcessPublishAssortment {
402
410
  return data;
403
411
  }
404
412
  catch (e) {
405
- console.error('Error deleteDataDownloadLink: ' + e.message);
413
+ console.log('Error deleteDataDownloadLink: ' + e.message);
406
414
  }
407
415
  return undefined;
408
416
  }
409
417
  async buildDeleteChanges(apc, previousApc) {
418
+ console.info('buildDeleteChanges()');
410
419
  if (apc?.deleteDataDownloadLink) {
411
420
  const deleteChanges = await this.downloadDeleteChanges(apc);
412
421
  if (deleteChanges) {
413
422
  return deleteChanges;
414
423
  }
415
424
  }
425
+ console.info('pulling down full APC');
416
426
  apc = await this.downloadAssortmentPublishChange(apc?.assortmentId, apc?.id);
417
427
  if (apc?.deleteDataDownloadLink) {
418
428
  const deleteChanges = await this.downloadDeleteChanges(apc);
@@ -424,18 +434,19 @@ class BaseProcessPublishAssortment {
424
434
  throw new Error(BaseProcessPublishAssortment.NOT_ABLE_TO_PROCESS_DELETE_CHANGES);
425
435
  }
426
436
  let previousBaseline;
437
+ console.info('check previousApc');
427
438
  if (previousApc?.assortmentBaselineDownloadLink) {
428
439
  previousBaseline = await this.downloadAssortmentBaseline(previousApc);
429
440
  }
430
441
  else {
442
+ console.info('previousApc pulling down full APC');
431
443
  previousApc = await this.downloadAssortmentPublishChange(previousApc?.assortmentId, previousApc?.id);
432
444
  previousBaseline = await this.downloadAssortmentBaseline(previousApc);
433
445
  }
434
446
  const deleteIds = apc?.detail?.deletes.map(dItem => dItem?.id);
435
447
  //building deletes based on previous baseline; because some APCs don't have delete data
436
448
  const deleteArray = previousBaseline?.assortmentItems.filter(aItem => deleteIds.includes(aItem?.itemId));
437
- console.info('buildDeleteChanges(): no deleteDataDownloadLink on apc ' + apc?.id
438
- + '; rebuilding ' + deleteArray.length + ' deletes from previous baseline');
449
+ console.info('deleteArray.length: ' + deleteArray.length);
439
450
  return deleteArray;
440
451
  }
441
452
  async getItemFederatedIds( /*itemIds*/) {
@@ -473,6 +484,7 @@ class BaseProcessPublishAssortment {
473
484
  return deleteItems;
474
485
  }
475
486
  getReleasedForDevelopmentItemAndFamilyIds(fullChange, deleteChanges) {
487
+ console.info('getReleasedForDevelopmentItemAndFamilyIds');
476
488
  const releasedForDevelopmentItemIds = [];
477
489
  const itemFamilySet = new Set();
478
490
  const assortmentItemsArray = fullChange?.assortmentItems;
@@ -524,6 +536,7 @@ class BaseProcessPublishAssortment {
524
536
  return !!lifecycleStage && !this.config.itemPreDevelopmentLifecycleStages.includes(lifecycleStage);
525
537
  }
526
538
  async processPublish(pcd, changeDetail, fullChange, deleteChanges) {
539
+ console.info('processPublish-start');
527
540
  const event = {
528
541
  assortmentId: pcd.assortmentId,
529
542
  assortmentPublishChangeId: pcd.assortmentPublishChangeId
@@ -707,6 +720,7 @@ class BaseProcessPublishAssortment {
707
720
  + '-' + d.getUTCMilliseconds();
708
721
  }
709
722
  getItemFamilyChanges(pcd, changeDetail, assortmentItemFullChangeMap, assortmentItemDeleteMap) {
723
+ console.info('getItemFamilyChanges-start');
710
724
  const itemFamilyChanges = new Map();
711
725
  const { adds, deletes, updates, familyItemsRemoved } = changeDetail;
712
726
  const addIds = adds.map(item => item.id);
@@ -716,7 +730,7 @@ class BaseProcessPublishAssortment {
716
730
  for (const [itemId, aItem] of assortmentItemFullChangeMap) {
717
731
  const projectItem = aItem?.projectItem;
718
732
  const itemFamilyId = aItem?.item?.itemFamilyId;
719
- if (!pcd.releasedForDevelopmentItemIds.includes(itemFamilyId) || !pcd.releasedForDevelopmentItemIds.includes(itemId)) {
733
+ if (!pcd.releasedForDevelopmentItemIds.includes(itemFamilyId)) {
720
734
  continue;
721
735
  }
722
736
  const ifc = itemFamilyChanges.get(itemFamilyId) || new item_family_changes_1.ItemFamilyChanges(itemFamilyId, pcd.sinceDate);
@@ -728,6 +742,20 @@ class BaseProcessPublishAssortment {
728
742
  if (pcd.itemToFederatedIdMapping.has(itemId)) {
729
743
  ifc.itemToFederatedIdMapping.set(itemId, pcd.itemToFederatedIdMapping.get(itemId));
730
744
  }
745
+ // ---- NEW Step to enable ItemFamily publish ET-243: option is pre-development. It must not produce an
746
+ // LCSSKUSeasonLink, but a change to it still makes the family publishable. ----
747
+ if (!pcd.releasedForDevelopmentItemIds.includes(itemId)) {
748
+ const changedInWindow = addIds.includes(itemId)
749
+ || deleteIds.includes(itemId)
750
+ || updateIds.includes(itemId)
751
+ || this.updatedSinceDate(projectItem, pcd.sinceDate)
752
+ || this.updatedSinceDate(aItem?.item, pcd.sinceDate);
753
+ if (changedInWindow) {
754
+ ifc.colorSuppressed.push(itemId);
755
+ }
756
+ continue;
757
+ }
758
+ // NEW Step - ends
731
759
  if (itemId === itemFamilyId) {
732
760
  if (addIds.includes(itemId))
733
761
  ifc.familyAdd = true;
@@ -743,6 +771,12 @@ class BaseProcessPublishAssortment {
743
771
  ifc.familyUpdate = true;
744
772
  }
745
773
  }
774
+ // NEW Step to enable ItemFamily publish ET-243: item-level edits (incl. lifecycleStage) must publish
775
+ // the family. Guarded so the "first option added" flow stays suppressed. ----
776
+ if (!ifc.familyItemRemoved && this.updatedSinceDate(aItem?.item, pcd.sinceDate)) {
777
+ ifc.familyUpdate = true;
778
+ }
779
+ // NEW Step - ends
746
780
  if (projectItem && !projectItem?.roles && aItem?.item?.roles) {
747
781
  projectItem.roles = aItem?.item?.roles;
748
782
  }
@@ -771,7 +805,8 @@ class BaseProcessPublishAssortment {
771
805
  }
772
806
  else {
773
807
  if (!item) {
774
- console.error('Failed to find deleted item entity: itemFamilyId: ' + itemFamilyId + ', itemId: ' + itemId);
808
+ console.error('Failed to find deleted item entity');
809
+ console.error(' itemFamilyId: ' + itemFamilyId + ' -itemId: ' + itemId);
775
810
  continue;
776
811
  }
777
812
  }
@@ -799,7 +834,7 @@ class BaseProcessPublishAssortment {
799
834
  ifc.colorDeletes.push(itemId);
800
835
  }
801
836
  } //End deletes for loop
802
- if (Logger.isDebugOn()) {
837
+ if (app_framework_1.Logger.isDebugOn()) {
803
838
  console.debug('returning size: ' + itemFamilyChanges.size);
804
839
  for (const [key, value] of itemFamilyChanges) {
805
840
  console.debug(key + ' => ' + value);
@@ -808,6 +843,7 @@ class BaseProcessPublishAssortment {
808
843
  return itemFamilyChanges;
809
844
  }
810
845
  async getEventsForPublishChangeData(publishChangeData) {
846
+ console.info('getEventsForPublishChangeData-start');
811
847
  const seasonalPayloads = [];
812
848
  for (const itemFamilyChange of publishChangeData.itemFamilyChanges.values()) {
813
849
  const events = await this.getEventsForItemFamilyChanges(itemFamilyChange, publishChangeData.assortmentId, publishChangeData.seasonFed, publishChangeData.itemToFederatedIdMapping);
@@ -815,8 +851,6 @@ class BaseProcessPublishAssortment {
815
851
  seasonalPayloads.push(...events);
816
852
  }
817
853
  }
818
- console.info('getEventsForPublishChangeData: itemFamilies: '
819
- + publishChangeData.itemFamilyChanges.size + ', payloads: ' + seasonalPayloads.length);
820
854
  return seasonalPayloads;
821
855
  }
822
856
  /**Returns the events for a given ItemFamilyChanges object
@@ -836,12 +870,15 @@ class BaseProcessPublishAssortment {
836
870
  * @returns
837
871
  */
838
872
  async getEventsForItemFamilyChanges(itemFamilyChanges, assortmentId, seasonFed, itemToFederatedIdMapping) {
873
+ console.info('getEventsForItemFamilyChanges()');
839
874
  const events = [];
840
875
  const LCSSeason = Object.assign({}, seasonFed);
841
876
  const assortment = await this.getAssortment(assortmentId);
842
877
  const projectItem = this.getProjectItem(itemFamilyChanges, itemFamilyChanges.itemFamilyId);
878
+ // ---- NEW Step to enable ItemFamily publish ET-243: added color suppressed length to existing condition, no other changes on familyAssortmentItem boolean
843
879
  const familyAssortmentItem = itemFamilyChanges.familyAdd || itemFamilyChanges.familyUpdate || itemFamilyChanges.familyDelete
844
880
  || itemFamilyChanges.colorAdds.length > 0 || itemFamilyChanges.colorUpdates.length > 0 || itemFamilyChanges.colorDeletes.length > 0
881
+ || itemFamilyChanges.colorSuppressed.length > 0
845
882
  || (projectItem && this.updatedSinceDate(projectItem, itemFamilyChanges.sinceDate));
846
883
  //familyItemRemoved is used when adding the first option to an assortment
847
884
  //and will have updates for the family item.
@@ -1337,6 +1337,150 @@ describe('getItemFamilyChanges', () => {
1337
1337
  expect(ifc.colorDeletes.length).toEqual(1);
1338
1338
  });
1339
1339
  });
1340
+ describe('getItemFamilyChanges - lifecycle-suppressed options', () => {
1341
+ const familyId = 'AERfdvdAt1HHEVvQ';
1342
+ const maroonId = 'YwWKVoL_vGN8khLy';
1343
+ const limeId = 'rsmWhWiXRNv_XBJG';
1344
+ const sinceDate = new Date('2026-08-01T00:00:00.000Z');
1345
+ const afterSince = '2026-08-26T00:00:00.000Z';
1346
+ const beforeSince = '2026-05-11T00:00:00.000Z';
1347
+ const noDetailChanges = () => ({ adds: [], deletes: [], updates: [], familyItemsRemoved: [] });
1348
+ const buildPpa = () => {
1349
+ const config = {};
1350
+ const mapFileUtil = new transform_data_1.MapFileUtil(new sdk_1.Entities());
1351
+ const dc = new data_converter_1.DataConverter(config, mapFileUtil);
1352
+ return new base_process_publish_assortment_1.BaseProcessPublishAssortment(config, dc, mapFileUtil);
1353
+ };
1354
+ const buildPcd = (releasedIds) => {
1355
+ const seasonFed = {
1356
+ entityReference: 'assortment:11EZML5dMn3tREuT',
1357
+ objectClass: 'LCSSeason'
1358
+ };
1359
+ const pcd = new publish_change_data_1.PublishChangeData('11EZML5dMn3tREuT', seasonFed, 'Pg3y2x5IclYxk3hM', sinceDate);
1360
+ pcd.itemToFederatedIdMapping = new Map();
1361
+ releasedIds.forEach(id => pcd.releasedForDevelopmentItemIds.push(id));
1362
+ return pcd;
1363
+ };
1364
+ const familyItem = (updatedOn = beforeSince, lifecycleStage = 'released') => ({
1365
+ id: familyId, itemFamilyId: familyId, lifecycleStage, name: 'Sweatpant', updatedOn
1366
+ });
1367
+ const familyRow = (updatedOn = beforeSince, lifecycleStage = 'released') => ({
1368
+ itemId: familyId,
1369
+ item: familyItem(updatedOn, lifecycleStage),
1370
+ projectItem: { id: 'pi-' + familyId, updatedOn: beforeSince }
1371
+ });
1372
+ const optionRow = (id, updatedOn, lifecycleStage = 'concept') => ({
1373
+ itemId: id,
1374
+ item: { id, itemFamilyId: familyId, lifecycleStage, updatedOn, itemFamily: familyItem() },
1375
+ projectItem: { id: 'pi-' + id, updatedOn: beforeSince },
1376
+ familyProjectItem: { id: 'pi-' + familyId, updatedOn: beforeSince }
1377
+ });
1378
+ it('records changed concept options as suppressed, not as colour updates', () => {
1379
+ const ppa = buildPpa();
1380
+ const fullChangeMap = ppa.getFullChangeAssortmentMap({
1381
+ assortmentItems: [familyRow(), optionRow(maroonId, afterSince), optionRow(limeId, afterSince)]
1382
+ });
1383
+ const pcd = buildPcd([familyId]);
1384
+ pcd.itemFamilyChanges = ppa.getItemFamilyChanges(pcd, noDetailChanges(), fullChangeMap, new Map());
1385
+ expect(pcd.itemFamilyChanges.size).toEqual(1);
1386
+ const ifc = pcd.itemFamilyChanges.get(familyId);
1387
+ expect(ifc.colorSuppressed).toEqual([maroonId, limeId]);
1388
+ expect(ifc.colorAdds.length).toEqual(0);
1389
+ expect(ifc.colorUpdates.length).toEqual(0);
1390
+ expect(ifc.colorDeletes.length).toEqual(0);
1391
+ expect(ifc.familyUpdate).toBe(false); // family itself unchanged
1392
+ });
1393
+ it('keeps the family reachable when only concept option rows are in the baseline', () => {
1394
+ const ppa = buildPpa();
1395
+ const fullChangeMap = ppa.getFullChangeAssortmentMap({
1396
+ assortmentItems: [optionRow(maroonId, afterSince), optionRow(limeId, afterSince)]
1397
+ });
1398
+ const pcd = buildPcd([familyId]);
1399
+ pcd.itemFamilyChanges = ppa.getItemFamilyChanges(pcd, noDetailChanges(), fullChangeMap, new Map());
1400
+ expect(pcd.itemFamilyChanges.size).toEqual(1);
1401
+ const ifc = pcd.itemFamilyChanges.get(familyId);
1402
+ expect(ifc.itemFamilyObject.id).toEqual(familyId);
1403
+ expect(ifc.colorSuppressed.length).toEqual(2);
1404
+ // the suppressed option must stay in the map so the family's project item is reachable
1405
+ expect(ppa.getProjectItem(ifc, familyId)).toEqual({ id: 'pi-' + familyId, updatedOn: beforeSince });
1406
+ });
1407
+ it('does not suppress-record an unchanged concept option', () => {
1408
+ const ppa = buildPpa();
1409
+ const fullChangeMap = ppa.getFullChangeAssortmentMap({
1410
+ assortmentItems: [familyRow(), optionRow(maroonId, beforeSince)]
1411
+ });
1412
+ const pcd = buildPcd([familyId]);
1413
+ pcd.itemFamilyChanges = ppa.getItemFamilyChanges(pcd, noDetailChanges(), fullChangeMap, new Map());
1414
+ const ifc = pcd.itemFamilyChanges.get(familyId);
1415
+ expect(ifc.colorSuppressed.length).toEqual(0);
1416
+ });
1417
+ it('picks up a concept option listed in the change detail even when timestamps are stale', () => {
1418
+ const ppa = buildPpa();
1419
+ const fullChangeMap = ppa.getFullChangeAssortmentMap({
1420
+ assortmentItems: [familyRow(), optionRow(maroonId, beforeSince)]
1421
+ });
1422
+ const details = noDetailChanges();
1423
+ details.updates = [{ id: maroonId }];
1424
+ const pcd = buildPcd([familyId]);
1425
+ pcd.itemFamilyChanges = ppa.getItemFamilyChanges(pcd, details, fullChangeMap, new Map());
1426
+ expect(pcd.itemFamilyChanges.get(familyId).colorSuppressed).toEqual([maroonId]);
1427
+ });
1428
+ it('skips everything when the family itself is pre-development', () => {
1429
+ const ppa = buildPpa();
1430
+ const fullChangeMap = ppa.getFullChangeAssortmentMap({
1431
+ assortmentItems: [familyRow(afterSince, 'concept'), optionRow(maroonId, afterSince)]
1432
+ });
1433
+ const pcd = buildPcd([]); // nothing released
1434
+ pcd.itemFamilyChanges = ppa.getItemFamilyChanges(pcd, noDetailChanges(), fullChangeMap, new Map());
1435
+ expect(pcd.itemFamilyChanges.size).toEqual(0);
1436
+ });
1437
+ it('still classifies released options normally', () => {
1438
+ const ppa = buildPpa();
1439
+ const fullChangeMap = ppa.getFullChangeAssortmentMap({
1440
+ assortmentItems: [familyRow(), optionRow(maroonId, afterSince, 'released')]
1441
+ });
1442
+ const details = noDetailChanges();
1443
+ details.adds = [{ id: maroonId }];
1444
+ const pcd = buildPcd([familyId, maroonId]);
1445
+ pcd.itemFamilyChanges = ppa.getItemFamilyChanges(pcd, details, fullChangeMap, new Map());
1446
+ const ifc = pcd.itemFamilyChanges.get(familyId);
1447
+ expect(ifc.colorAdds).toEqual([maroonId]);
1448
+ expect(ifc.colorSuppressed.length).toEqual(0);
1449
+ });
1450
+ it('publishes a family-only row whose lifecycle just flipped (item-level edit)', () => {
1451
+ const ppa = buildPpa();
1452
+ const fullChangeMap = ppa.getFullChangeAssortmentMap({
1453
+ assortmentItems: [familyRow(afterSince)] // item.updatedOn moved, projectItem did not
1454
+ });
1455
+ const pcd = buildPcd([familyId]);
1456
+ pcd.itemFamilyChanges = ppa.getItemFamilyChanges(pcd, noDetailChanges(), fullChangeMap, new Map());
1457
+ const ifc = pcd.itemFamilyChanges.get(familyId);
1458
+ expect(ifc.familyUpdate).toBe(true); // fails without Step 3
1459
+ expect(ifc.colorSuppressed.length).toEqual(0);
1460
+ });
1461
+ it('leaves an unchanged family-only row with no flags', () => {
1462
+ const ppa = buildPpa();
1463
+ const fullChangeMap = ppa.getFullChangeAssortmentMap({ assortmentItems: [familyRow()] });
1464
+ const pcd = buildPcd([familyId]);
1465
+ pcd.itemFamilyChanges = ppa.getItemFamilyChanges(pcd, noDetailChanges(), fullChangeMap, new Map());
1466
+ const ifc = pcd.itemFamilyChanges.get(familyId);
1467
+ expect(ifc.familyAdd).toBe(false);
1468
+ expect(ifc.familyUpdate).toBe(false);
1469
+ expect(ifc.familyDelete).toBe(false);
1470
+ expect(ifc.colorSuppressed.length).toEqual(0);
1471
+ });
1472
+ it('does not set familyUpdate when the family item was removed', () => {
1473
+ const ppa = buildPpa();
1474
+ const fullChangeMap = ppa.getFullChangeAssortmentMap({ assortmentItems: [familyRow(afterSince)] });
1475
+ const details = noDetailChanges();
1476
+ details.familyItemsRemoved = [{ itemId: familyId }]; // note: itemId, not id
1477
+ const pcd = buildPcd([familyId]);
1478
+ pcd.itemFamilyChanges = ppa.getItemFamilyChanges(pcd, details, fullChangeMap, new Map());
1479
+ const ifc = pcd.itemFamilyChanges.get(familyId);
1480
+ expect(ifc.familyItemRemoved).toBe(true);
1481
+ expect(ifc.familyUpdate).toBe(false); // fails if the !familyItemRemoved guard is dropped
1482
+ });
1483
+ });
1340
1484
  describe('getProjectItem', () => {
1341
1485
  const config = {
1342
1486
  identifierAtts: {
@@ -175,12 +175,18 @@ class DataConverter {
175
175
  }
176
176
  async getObjectReferenceValue(prop, newData, inflateObjRef = false) {
177
177
  const slug = prop['slug'];
178
+ if (app_framework_1.Logger.isDebugOn()) {
179
+ console.debug('getObjectReferenceValue-prop: ' + slug);
180
+ }
178
181
  let value = newData[slug];
179
182
  const entityType = prop['referencedTypeRootSlug'];
180
183
  const entityId = newData[slug + 'Id'];
181
184
  if ((!value || typeof value === 'string') && inflateObjRef) {
182
185
  if (entityId) {
183
186
  if (this.objRefCache[entityId]) {
187
+ if (app_framework_1.Logger.isDebugOn()) {
188
+ console.debug('cache hit: ' + entityId);
189
+ }
184
190
  return this.objRefCache[entityId];
185
191
  }
186
192
  const criteria = {
@@ -426,6 +432,9 @@ class DataConverter {
426
432
  return "";
427
433
  }
428
434
  if (this.objRefCache[ctx.cacheKey]) {
435
+ if (app_framework_1.Logger.isDebugOn()) {
436
+ console.debug(`object reference cache hit: ${ctx.cacheKey}`);
437
+ }
429
438
  return this.objRefCache[ctx.cacheKey];
430
439
  }
431
440
  const objectReferenceId = ctx.useIdentityService
@@ -625,6 +634,9 @@ class DataConverter {
625
634
  }
626
635
  let cacheUser = DataConverter.getFromStaticCache(nd.email);
627
636
  if (cacheUser) {
637
+ if (app_framework_1.Logger.isDebugOn()) {
638
+ console.debug('user cache hit: ' + nd.email);
639
+ }
628
640
  await this.processGroupMemberCheck(prop, nd.email);
629
641
  return cacheUser;
630
642
  }
@@ -694,12 +706,18 @@ class DataConverter {
694
706
  */
695
707
  async getUserListValue(prop, newData) {
696
708
  const slug = prop['slug'];
709
+ if (app_framework_1.Logger.isDebugOn()) {
710
+ console.debug('getUserListValue-prop: ' + slug);
711
+ }
697
712
  const entityId = newData[slug + 'Id'];
698
713
  if (!entityId) {
699
714
  return {};
700
715
  }
701
716
  const cacheUser = DataConverter.getFromStaticCache(entityId);
702
717
  if (cacheUser) {
718
+ if (app_framework_1.Logger.isDebugOn()) {
719
+ console.debug('user cache hit: ' + entityId);
720
+ }
703
721
  return Object.assign({}, cacheUser);
704
722
  }
705
723
  const user = await this.getUserById(entityId);
package/package.json CHANGED
@@ -1,66 +1,66 @@
1
- {
2
- "name": "@contrail/flexplm",
3
- "version": "1.7.4-alpha.72422a6",
4
- "description": "Library used for integration with flexplm.",
5
- "main": "lib/index.js",
6
- "types": "lib/index.d.ts",
7
- "bin": {
8
- "flexplm-mapping": "lib/cli/index.js"
9
- },
10
- "files": [
11
- "lib/**/*",
12
- "scripts/copy-template.js"
13
- ],
14
- "scripts": {
15
- "build": "tsc; node scripts/copy-template.js",
16
- "build:win": "tsc && node scripts/copy-template.js",
17
- "format": "prettier --write \"src/**/*.ts\" \"src/**/*.js\"",
18
- "lint": "tslint -p tsconfig.json",
19
- "test": "jest",
20
- "test-watch": "jest --watch",
21
- "test-debug": "jest --runInBand",
22
- "test-coverage": "jest --coverage"
23
- },
24
- "keywords": [],
25
- "author": "VibeIQ",
26
- "license": "ISC",
27
- "devDependencies": {
28
- "@types/jest": "^29.5.2",
29
- "jest": "^29.5.0",
30
- "prettier": "^1.19.1",
31
- "ts-jest": "^29.1.1",
32
- "tslint": "^5.11.0",
33
- "tslint-config-prettier": "^1.18.0",
34
- "typescript": "^4.0.0"
35
- },
36
- "peerDependencies": {
37
- "typescript": ">=4.0.0"
38
- },
39
- "peerDependenciesMeta": {
40
- "typescript": {
41
- "optional": true
42
- }
43
- },
44
- "jest": {
45
- "moduleFileExtensions": [
46
- "js",
47
- "json",
48
- "ts"
49
- ],
50
- "rootDir": "src",
51
- "testRegex": ".spec.ts$",
52
- "transform": {
53
- "^.+\\.(t|j)s$": "ts-jest"
54
- },
55
- "coverageDirectory": "../coverage",
56
- "testEnvironment": "node"
57
- },
58
- "dependencies": {
59
- "@contrail/app-framework": "^1.4.3",
60
- "@contrail/sdk": "^1.5.10",
61
- "@contrail/transform-data": "^1.3.2",
62
- "@contrail/util": "^1.3.1",
63
- "axios": "^1.4.0",
64
- "p-limit": "^3.1.0"
65
- }
66
- }
1
+ {
2
+ "name": "@contrail/flexplm",
3
+ "version": "1.7.4-alpha.879820c",
4
+ "description": "Library used for integration with flexplm.",
5
+ "main": "lib/index.js",
6
+ "types": "lib/index.d.ts",
7
+ "bin": {
8
+ "flexplm-mapping": "lib/cli/index.js"
9
+ },
10
+ "files": [
11
+ "lib/**/*",
12
+ "scripts/copy-template.js"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc; node scripts/copy-template.js",
16
+ "build:win": "tsc && node scripts/copy-template.js",
17
+ "format": "prettier --write \"src/**/*.ts\" \"src/**/*.js\"",
18
+ "lint": "tslint -p tsconfig.json",
19
+ "test": "jest",
20
+ "test-watch": "jest --watch",
21
+ "test-debug": "jest --runInBand",
22
+ "test-coverage": "jest --coverage"
23
+ },
24
+ "keywords": [],
25
+ "author": "VibeIQ",
26
+ "license": "ISC",
27
+ "devDependencies": {
28
+ "@types/jest": "^29.5.2",
29
+ "jest": "^29.5.0",
30
+ "prettier": "^1.19.1",
31
+ "ts-jest": "^29.1.1",
32
+ "tslint": "^5.11.0",
33
+ "tslint-config-prettier": "^1.18.0",
34
+ "typescript": "^4.0.0"
35
+ },
36
+ "peerDependencies": {
37
+ "typescript": ">=4.0.0"
38
+ },
39
+ "peerDependenciesMeta": {
40
+ "typescript": {
41
+ "optional": true
42
+ }
43
+ },
44
+ "jest": {
45
+ "moduleFileExtensions": [
46
+ "js",
47
+ "json",
48
+ "ts"
49
+ ],
50
+ "rootDir": "src",
51
+ "testRegex": ".spec.ts$",
52
+ "transform": {
53
+ "^.+\\.(t|j)s$": "ts-jest"
54
+ },
55
+ "coverageDirectory": "../coverage",
56
+ "testEnvironment": "node"
57
+ },
58
+ "dependencies": {
59
+ "@contrail/app-framework": "^1.4.3",
60
+ "@contrail/sdk": "^1.5.10",
61
+ "@contrail/transform-data": "^1.3.2",
62
+ "@contrail/util": "^1.3.1",
63
+ "axios": "^1.4.0",
64
+ "p-limit": "^3.1.0"
65
+ }
66
+ }