@contrail/flexplm 1.7.8-alpha.0024b30 → 1.7.8-alpha.30ce497

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.
@@ -348,6 +348,10 @@ class BaseProcessPublishAssortment {
348
348
  const apcDeletes = await this.downloadDeleteChanges(assortmentPublishChange);
349
349
  let previousApcDate = Date.parse(apcCreatedOn);
350
350
  const sinceDateMilliseconds = sinceDate.getTime();
351
+ // getSinceDate() always resolves its config (default, date, numberOfDays, numberOfPublishes) to a publish
352
+ // timestamp, so the walk below stops on an exact match. A caller that supplies a raw date matching no publish
353
+ // (e.g. a subclass overriding getSinceDate) would never match, so the walk stops on the date instead.
354
+ const hasPublishAtSinceDate = apcHistory.some(apc => Date.parse(apc?.createdOn) === sinceDateMilliseconds);
351
355
  console.info('getDeleteChanges(): apc: ' + assortmentPublishChange?.id
352
356
  + ', index: ' + currentAPCIndex + ', previousApcCreatedOn: ' + apcCreatedOn);
353
357
  //if only 1 apc, no processing needed
@@ -361,7 +365,11 @@ class BaseProcessPublishAssortment {
361
365
  for (let i = currentAPCIndex - 1; i > 0; i--) {
362
366
  const workingAPC = apcHistory[i];
363
367
  previousApcDate = Date.parse(workingAPC?.createdOn);
364
- if (sinceDateMilliseconds === previousApcDate) {
368
+ const reachedSinceDate = hasPublishAtSinceDate
369
+ ? previousApcDate === sinceDateMilliseconds
370
+ : previousApcDate < sinceDateMilliseconds;
371
+ if (reachedSinceDate) {
372
+ console.info('getDeleteChanges(): reached sinceDate at apc: ' + workingAPC?.id);
365
373
  break;
366
374
  }
367
375
  if (workingAPC?.deletes > 0) {
@@ -922,6 +922,71 @@ describe('getDeleteChanges', () => {
922
922
  spy.mockRestore();
923
923
  });
924
924
  });
925
+ describe('getDeleteChanges - how far back the history walk goes', () => {
926
+ const config = {};
927
+ const mapFileUtil = new transform_data_1.MapFileUtil(new sdk_1.Entities());
928
+ const dc = new data_converter_1.DataConverter(config, mapFileUtil);
929
+ // Oldest -> newest. 'current' is the publish being processed.
930
+ const history = [
931
+ { id: 'apc0', createdOn: '2025-09-30T00:00:00.000Z', deletes: 5 },
932
+ { id: 'apc1', createdOn: '2026-09-28T05:48:26.357Z', deletes: 1 },
933
+ { id: 'apc2', createdOn: '2026-10-08T07:16:43.363Z', deletes: 2 },
934
+ { id: 'apc3', createdOn: '2026-10-09T02:37:30.292Z', deletes: 3 },
935
+ { id: 'apc4', createdOn: '2026-10-14T04:23:10.416Z', deletes: 0 },
936
+ { id: 'current', createdOn: '2026-10-16T15:25:51.965Z', deletes: 0 }
937
+ ];
938
+ const current = history[history.length - 1];
939
+ const baseline = { assortmentItems: [{ itemId: 'stillInPlan' }] };
940
+ let ppa;
941
+ let buildSpy;
942
+ beforeEach(() => {
943
+ ppa = new base_process_publish_assortment_1.BaseProcessPublishAssortment(config, dc, mapFileUtil);
944
+ jest.spyOn(ppa, 'downloadDeleteChanges').mockResolvedValue([]);
945
+ buildSpy = jest.spyOn(ppa, 'buildDeleteChanges')
946
+ .mockImplementation(async (apc) => [{ itemId: 'deleted-in-' + apc['id'] }]);
947
+ });
948
+ afterEach(() => {
949
+ jest.restoreAllMocks();
950
+ });
951
+ const walkedApcIds = () => buildSpy.mock.calls.map(call => call[0]['id']);
952
+ it('does not walk older publishes when sinceDate is the previous publish date (default)', async () => {
953
+ const result = await ppa.getDeleteChanges(current, history, baseline, new Date(history[4].createdOn));
954
+ expect(walkedApcIds()).toEqual([]);
955
+ expect(result).toEqual([]);
956
+ });
957
+ it('stops at the first publish before a date-only sinceDate that matches no publish timestamp', async () => {
958
+ const result = await ppa.getDeleteChanges(current, history, baseline, new Date('2026-10-08T00:00:00.000Z'));
959
+ // apc3 and apc2 are after the cutoff; apc1 (Sep 28) is before it, so the walk ends there and apc0 is never read.
960
+ expect(walkedApcIds()).toEqual(['apc3', 'apc2']);
961
+ expect(result.map(item => item['itemId'])).toEqual(['deleted-in-apc3', 'deleted-in-apc2']);
962
+ });
963
+ it('stops at a publish whose timestamp equals sinceDate without reading it (numberOfPublishes)', async () => {
964
+ await ppa.getDeleteChanges(current, history, baseline, new Date(history[1].createdOn));
965
+ expect(walkedApcIds()).toEqual(['apc3', 'apc2']);
966
+ });
967
+ it('does not walk any publish when a date-only sinceDate is later than the previous publish', async () => {
968
+ await ppa.getDeleteChanges(current, history, baseline, new Date('2026-10-15T00:00:00.000Z'));
969
+ expect(walkedApcIds()).toEqual([]);
970
+ });
971
+ it('resolves a date-only sinceDate config to a publish timestamp, so the walk stops on an exact match', async () => {
972
+ const configured = new base_process_publish_assortment_1.BaseProcessPublishAssortment({ sinceDate: '2026-10-08' }, dc, mapFileUtil);
973
+ jest.spyOn(configured, 'downloadDeleteChanges').mockResolvedValue([]);
974
+ const configuredBuildSpy = jest.spyOn(configured, 'buildDeleteChanges')
975
+ .mockImplementation(async (apc) => [{ itemId: 'deleted-in-' + apc['id'] }]);
976
+ const sinceDate = await configured.getSinceDate('assortment1', current.id, history);
977
+ await configured.getDeleteChanges(current, history, baseline, sinceDate);
978
+ // The latest publish before 2026-10-08 (apc1, Sep 28), not the raw midnight date.
979
+ expect(sinceDate.toISOString()).toEqual(history[1].createdOn);
980
+ expect(configuredBuildSpy.mock.calls.map(call => call[0]['id'])).toEqual(['apc3', 'apc2']);
981
+ });
982
+ it('skips deleted items still in the plan and de-duplicates across publishes', async () => {
983
+ buildSpy.mockImplementation(async (apc) => apc['id'] === 'apc3'
984
+ ? [{ itemId: 'stillInPlan' }, { itemId: 'sameItem' }]
985
+ : [{ itemId: 'sameItem' }, { itemId: 'deleted-in-' + apc['id'] }]);
986
+ const result = await ppa.getDeleteChanges(current, history, baseline, new Date('2026-10-08T00:00:00.000Z'));
987
+ expect(result.map(item => item['itemId'])).toEqual(['sameItem', 'deleted-in-apc2']);
988
+ });
989
+ });
925
990
  describe('buildDeleteChanges', () => {
926
991
  const config = {};
927
992
  const mapFileUtil = new transform_data_1.MapFileUtil(new sdk_1.Entities());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrail/flexplm",
3
- "version": "1.7.8-alpha.0024b30",
3
+ "version": "1.7.8-alpha.30ce497",
4
4
  "description": "Library used for integration with flexplm.",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",