@contrail/flexplm 1.7.8-alpha.69dcae1 → 1.7.8-alpha.6f9b7d7

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,9 @@ class BaseProcessPublishAssortment {
348
348
  const apcDeletes = await this.downloadDeleteChanges(assortmentPublishChange);
349
349
  let previousApcDate = Date.parse(apcCreatedOn);
350
350
  const sinceDateMilliseconds = sinceDate.getTime();
351
+ // sinceDate matches a publish timestamp when it is the default (previous publish) or 'numberOfPublishes:N'.
352
+ // A date-only sinceDate (e.g. 2026-10-08) matches none, so the walk below must stop on the date instead.
353
+ const hasPublishAtSinceDate = apcHistory.some(apc => Date.parse(apc?.createdOn) === sinceDateMilliseconds);
351
354
  console.info('getDeleteChanges(): apc: ' + assortmentPublishChange?.id
352
355
  + ', index: ' + currentAPCIndex + ', previousApcCreatedOn: ' + apcCreatedOn);
353
356
  //if only 1 apc, no processing needed
@@ -361,7 +364,11 @@ class BaseProcessPublishAssortment {
361
364
  for (let i = currentAPCIndex - 1; i > 0; i--) {
362
365
  const workingAPC = apcHistory[i];
363
366
  previousApcDate = Date.parse(workingAPC?.createdOn);
364
- if (sinceDateMilliseconds === previousApcDate) {
367
+ const reachedSinceDate = hasPublishAtSinceDate
368
+ ? previousApcDate === sinceDateMilliseconds
369
+ : previousApcDate < sinceDateMilliseconds;
370
+ if (reachedSinceDate) {
371
+ console.info('getDeleteChanges(): reached sinceDate at apc: ' + workingAPC?.id);
365
372
  break;
366
373
  }
367
374
  if (workingAPC?.deletes > 0) {
@@ -922,6 +922,60 @@ 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('skips deleted items still in the plan and de-duplicates across publishes', async () => {
972
+ buildSpy.mockImplementation(async (apc) => apc['id'] === 'apc3'
973
+ ? [{ itemId: 'stillInPlan' }, { itemId: 'sameItem' }]
974
+ : [{ itemId: 'sameItem' }, { itemId: 'deleted-in-' + apc['id'] }]);
975
+ const result = await ppa.getDeleteChanges(current, history, baseline, new Date('2026-10-08T00:00:00.000Z'));
976
+ expect(result.map(item => item['itemId'])).toEqual(['sameItem', 'deleted-in-apc2']);
977
+ });
978
+ });
925
979
  describe('buildDeleteChanges', () => {
926
980
  const config = {};
927
981
  const mapFileUtil = new transform_data_1.MapFileUtil(new sdk_1.Entities());
@@ -191,15 +191,15 @@ describe('filterProperties-color', () => {
191
191
  });
192
192
  });
193
193
  describe('TypeUtils.getTypeById() tests', () => {
194
- it('throws a descriptive error when the type does not exist', async () => {
195
- const tu = new type_utils_1.TypeUtils();
196
- tu['typesObj'] = { getType: jest.fn().mockResolvedValue(null) };
197
- await expect(tu.getTypeById('missingTypeId')).rejects.toThrow('type not found for typeId: missingTypeId');
194
+ it('should throw an error containing the missing type id when the type does not exist', async () => {
195
+ const typeUtils = new type_utils_1.TypeUtils();
196
+ typeUtils['typesObj'] = { getType: jest.fn().mockResolvedValue(null) };
197
+ await expect(typeUtils.getTypeById('missingTypeId')).rejects.toThrow('type not found for typeId: missingTypeId');
198
198
  });
199
- it('returns the type when it exists', async () => {
200
- const tu = new type_utils_1.TypeUtils();
199
+ it('should return the same type object when the type exists', async () => {
200
+ const typeUtils = new type_utils_1.TypeUtils();
201
201
  const type = { id: 'typeId', typeProperties: [] };
202
- tu['typesObj'] = { getType: jest.fn().mockResolvedValue(type) };
203
- expect(await tu.getTypeById('typeId')).toBe(type);
202
+ typeUtils['typesObj'] = { getType: jest.fn().mockResolvedValue(type) };
203
+ expect(await typeUtils.getTypeById('typeId')).toBe(type);
204
204
  });
205
205
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrail/flexplm",
3
- "version": "1.7.8-alpha.69dcae1",
3
+ "version": "1.7.8-alpha.6f9b7d7",
4
4
  "description": "Library used for integration with flexplm.",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",