@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
|
-
|
|
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());
|