@contrail/flexplm 1.4.0 → 1.5.0-alpha.0d65410
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.
- package/CHANGELOG.md +5 -0
- package/lib/transform/identifier-conversion.js +1 -1
- package/lib/transform/identifier-conversion.spec.js +31 -0
- package/lib/util/config-defaults.d.ts +18 -0
- package/lib/util/config-defaults.js +22 -15
- package/lib/util/config-defaults.spec.js +47 -0
- package/lib/util/data-converter.d.ts +5 -0
- package/lib/util/data-converter.js +68 -33
- package/lib/util/data-converter.spec.js +428 -0
- package/lib/util/type-defaults.d.ts +3 -0
- package/lib/util/type-defaults.js +32 -4
- package/lib/util/type-defaults.spec.js +110 -0
- package/package.json +1 -1
- package/src/transform/identifier-conversion.spec.ts +32 -0
- package/src/transform/identifier-conversion.ts +1 -1
- package/src/util/config-defaults.spec.ts +53 -0
- package/src/util/config-defaults.ts +25 -16
- package/src/util/data-converter.spec.ts +502 -0
- package/src/util/data-converter.ts +90 -37
- package/src/util/type-defaults.spec.ts +128 -0
- package/src/util/type-defaults.ts +45 -6
|
@@ -454,6 +454,393 @@ describe('getObjectReferenceValue bad value', () => {
|
|
|
454
454
|
}
|
|
455
455
|
});
|
|
456
456
|
});
|
|
457
|
+
describe('setObjectReferenceValue - identity service', () => {
|
|
458
|
+
const baseConfig = () => ({
|
|
459
|
+
apiHost: 'host',
|
|
460
|
+
userName: () => 'user',
|
|
461
|
+
password: () => 'pass',
|
|
462
|
+
urlContext: 'xxx',
|
|
463
|
+
vibeEventEndpoint: '/rfa/vibeiq/vibeEvents',
|
|
464
|
+
csrfEndpoint: '/servlet/rest/security/csrf',
|
|
465
|
+
itemPreDevelopmentLifecycleStages: ['concept']
|
|
466
|
+
});
|
|
467
|
+
const refProp = {
|
|
468
|
+
id: 'cJoZQvoj7dkfCBJq',
|
|
469
|
+
propertyType: 'object_reference',
|
|
470
|
+
slug: 'material',
|
|
471
|
+
label: 'Material',
|
|
472
|
+
referencedTypeRootSlug: 'item',
|
|
473
|
+
referencedTypePath: 'item:material'
|
|
474
|
+
};
|
|
475
|
+
let identifierSpy;
|
|
476
|
+
let poolKeySpy;
|
|
477
|
+
let getByRootAndPathSpy;
|
|
478
|
+
let filterTypePropertiesSpy;
|
|
479
|
+
const setupTypeUtilsSpies = (dc, rootPropSlugs = ['itemNumber']) => {
|
|
480
|
+
getByRootAndPathSpy = jest.spyOn(dc['typeUtils'], 'getByRootAndPath')
|
|
481
|
+
.mockImplementation(async () => ({ typePath: 'item', typeProperties: [] }));
|
|
482
|
+
filterTypePropertiesSpy = jest.spyOn(dc['typeUtils'], 'filterTypeProperties')
|
|
483
|
+
.mockImplementation(() => rootPropSlugs.map(slug => ({ slug })));
|
|
484
|
+
};
|
|
485
|
+
afterEach(() => {
|
|
486
|
+
identifierSpy?.mockRestore();
|
|
487
|
+
poolKeySpy?.mockRestore();
|
|
488
|
+
getByRootAndPathSpy?.mockRestore();
|
|
489
|
+
filterTypePropertiesSpy?.mockRestore();
|
|
490
|
+
mockGetFunction.mockReset();
|
|
491
|
+
mockGetFunction.mockImplementation((options) => ((options?.criteria?.id === 1234) ? [mockObj1234] : [mockObj2222]));
|
|
492
|
+
});
|
|
493
|
+
it('flag off - uses getAllObjectReferences path', async () => {
|
|
494
|
+
const dc = new data_converter_1.DataConverter(baseConfig(), new transform_data_1.MapFileUtil(new sdk_1.Entities()));
|
|
495
|
+
setupTypeUtilsSpies(dc, ['itemNumber', 'roles']);
|
|
496
|
+
identifierSpy = jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'getIdentifierPropertiesFromObject')
|
|
497
|
+
.mockImplementation(async () => ['itemNumber', 'roles']);
|
|
498
|
+
poolKeySpy = jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'getUniquenessPoolKeyFromObject')
|
|
499
|
+
.mockImplementation(async () => 'item:material');
|
|
500
|
+
mockGetFunction.mockClear();
|
|
501
|
+
mockGetFunction.mockImplementation(() => [{ id: 'abc123', typePath: 'item:material' }]);
|
|
502
|
+
const nd = { itemNumber: 'MAT-100', roles: ['family'] };
|
|
503
|
+
const result = await dc.setObjectReferenceValue(refProp, nd);
|
|
504
|
+
expect(mockGetFunction).toHaveBeenCalled();
|
|
505
|
+
const callArg = mockGetFunction.mock.calls[0][0];
|
|
506
|
+
expect(callArg.entityName).toEqual('item');
|
|
507
|
+
expect(callArg.entityName).not.toEqual('identity');
|
|
508
|
+
expect(result).toEqual('abc123');
|
|
509
|
+
expect(poolKeySpy).not.toHaveBeenCalled();
|
|
510
|
+
});
|
|
511
|
+
it('flag on, single identity match - returns parsed id', async () => {
|
|
512
|
+
const config = baseConfig();
|
|
513
|
+
config.search = { item: { useIdentityServiceForInboundData: true } };
|
|
514
|
+
const dc = new data_converter_1.DataConverter(config, new transform_data_1.MapFileUtil(new sdk_1.Entities()));
|
|
515
|
+
setupTypeUtilsSpies(dc);
|
|
516
|
+
identifierSpy = jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'getIdentifierPropertiesFromObject')
|
|
517
|
+
.mockImplementation(async () => ['itemNumber', 'roles']);
|
|
518
|
+
poolKeySpy = jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'getUniquenessPoolKeyFromObject')
|
|
519
|
+
.mockImplementation(async () => 'item:material');
|
|
520
|
+
mockGetFunction.mockClear();
|
|
521
|
+
mockGetFunction.mockImplementation(() => [{ entityReference: 'item:abc123' }]);
|
|
522
|
+
const nd = { itemNumber: 'MAT-100', roles: ['family'] };
|
|
523
|
+
const result = await dc.setObjectReferenceValue(refProp, nd);
|
|
524
|
+
expect(mockGetFunction).toHaveBeenCalledTimes(1);
|
|
525
|
+
const callArg = mockGetFunction.mock.calls[0][0];
|
|
526
|
+
expect(callArg.entityName).toEqual('identity');
|
|
527
|
+
expect(callArg.criteria).toEqual({
|
|
528
|
+
poolKey: 'item:material',
|
|
529
|
+
propertyName: 'itemNumber',
|
|
530
|
+
propertyValue: 'MAT-100'
|
|
531
|
+
});
|
|
532
|
+
expect(result).toEqual('abc123');
|
|
533
|
+
});
|
|
534
|
+
it('flag on, no identity match - returns empty and warns', async () => {
|
|
535
|
+
const config = baseConfig();
|
|
536
|
+
config.search = { item: { useIdentityServiceForInboundData: true } };
|
|
537
|
+
const dc = new data_converter_1.DataConverter(config, new transform_data_1.MapFileUtil(new sdk_1.Entities()));
|
|
538
|
+
setupTypeUtilsSpies(dc);
|
|
539
|
+
identifierSpy = jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'getIdentifierPropertiesFromObject')
|
|
540
|
+
.mockImplementation(async () => ['itemNumber', 'roles']);
|
|
541
|
+
poolKeySpy = jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'getUniquenessPoolKeyFromObject')
|
|
542
|
+
.mockImplementation(async () => 'item:material');
|
|
543
|
+
mockGetFunction.mockClear();
|
|
544
|
+
mockGetFunction.mockImplementation(() => []);
|
|
545
|
+
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => { });
|
|
546
|
+
const getAllSpy = jest.spyOn(dc, 'getAllObjectReferences');
|
|
547
|
+
try {
|
|
548
|
+
const result = await dc.setObjectReferenceValue(refProp, { itemNumber: 'MAT-100', roles: ['family'] });
|
|
549
|
+
expect(result).toEqual('');
|
|
550
|
+
expect(warnSpy).toHaveBeenCalled();
|
|
551
|
+
expect(getAllSpy).not.toHaveBeenCalled();
|
|
552
|
+
}
|
|
553
|
+
finally {
|
|
554
|
+
warnSpy.mockRestore();
|
|
555
|
+
getAllSpy.mockRestore();
|
|
556
|
+
}
|
|
557
|
+
});
|
|
558
|
+
it('flag on, multiple identity matches - returns empty and does not throw', async () => {
|
|
559
|
+
const config = baseConfig();
|
|
560
|
+
config.search = { item: { useIdentityServiceForInboundData: true } };
|
|
561
|
+
const dc = new data_converter_1.DataConverter(config, new transform_data_1.MapFileUtil(new sdk_1.Entities()));
|
|
562
|
+
setupTypeUtilsSpies(dc);
|
|
563
|
+
identifierSpy = jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'getIdentifierPropertiesFromObject')
|
|
564
|
+
.mockImplementation(async () => ['itemNumber', 'roles']);
|
|
565
|
+
poolKeySpy = jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'getUniquenessPoolKeyFromObject')
|
|
566
|
+
.mockImplementation(async () => 'item:material');
|
|
567
|
+
mockGetFunction.mockClear();
|
|
568
|
+
mockGetFunction.mockImplementation(() => [
|
|
569
|
+
{ entityReference: 'item:abc123' },
|
|
570
|
+
{ entityReference: 'item:def456' }
|
|
571
|
+
]);
|
|
572
|
+
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => { });
|
|
573
|
+
try {
|
|
574
|
+
const result = await dc.setObjectReferenceValue(refProp, { itemNumber: 'MAT-100', roles: ['family'] });
|
|
575
|
+
expect(result).toEqual('');
|
|
576
|
+
expect(warnSpy).toHaveBeenCalled();
|
|
577
|
+
}
|
|
578
|
+
finally {
|
|
579
|
+
warnSpy.mockRestore();
|
|
580
|
+
}
|
|
581
|
+
});
|
|
582
|
+
it('flag on, ambiguous identifier set - falls back to getAllObjectReferences', async () => {
|
|
583
|
+
const config = baseConfig();
|
|
584
|
+
config.search = { item: { useIdentityServiceForInboundData: true } };
|
|
585
|
+
const dc = new data_converter_1.DataConverter(config, new transform_data_1.MapFileUtil(new sdk_1.Entities()));
|
|
586
|
+
setupTypeUtilsSpies(dc, ['itemNumber', 'season', 'roles']);
|
|
587
|
+
identifierSpy = jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'getIdentifierPropertiesFromObject')
|
|
588
|
+
.mockImplementation(async () => ['itemNumber', 'season', 'roles']);
|
|
589
|
+
poolKeySpy = jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'getUniquenessPoolKeyFromObject')
|
|
590
|
+
.mockImplementation(async () => 'item:material');
|
|
591
|
+
mockGetFunction.mockClear();
|
|
592
|
+
mockGetFunction.mockImplementation(() => [{ id: 'abc123', typePath: 'item:material' }]);
|
|
593
|
+
const nd = { itemNumber: 'MAT-100', season: 'SS24', roles: ['family'] };
|
|
594
|
+
const result = await dc.setObjectReferenceValue(refProp, nd);
|
|
595
|
+
expect(mockGetFunction).toHaveBeenCalled();
|
|
596
|
+
const callArg = mockGetFunction.mock.calls[0][0];
|
|
597
|
+
expect(callArg.entityName).toEqual('item');
|
|
598
|
+
expect(callArg.entityName).not.toEqual('identity');
|
|
599
|
+
expect(result).toEqual('abc123');
|
|
600
|
+
expect(poolKeySpy).not.toHaveBeenCalled();
|
|
601
|
+
});
|
|
602
|
+
it('flag on, cache hit - second call short-circuits', async () => {
|
|
603
|
+
const config = baseConfig();
|
|
604
|
+
config.search = { item: { useIdentityServiceForInboundData: true } };
|
|
605
|
+
const dc = new data_converter_1.DataConverter(config, new transform_data_1.MapFileUtil(new sdk_1.Entities()));
|
|
606
|
+
setupTypeUtilsSpies(dc);
|
|
607
|
+
identifierSpy = jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'getIdentifierPropertiesFromObject')
|
|
608
|
+
.mockImplementation(async () => ['itemNumber', 'roles']);
|
|
609
|
+
poolKeySpy = jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'getUniquenessPoolKeyFromObject')
|
|
610
|
+
.mockImplementation(async () => 'item:material');
|
|
611
|
+
mockGetFunction.mockClear();
|
|
612
|
+
mockGetFunction.mockImplementation(() => [{ entityReference: 'item:abc123' }]);
|
|
613
|
+
const nd = { itemNumber: 'MAT-100', roles: ['family'] };
|
|
614
|
+
const r1 = await dc.setObjectReferenceValue(refProp, nd);
|
|
615
|
+
const r2 = await dc.setObjectReferenceValue(refProp, nd);
|
|
616
|
+
expect(r1).toEqual('abc123');
|
|
617
|
+
expect(r2).toEqual('abc123');
|
|
618
|
+
expect(mockGetFunction).toHaveBeenCalledTimes(1);
|
|
619
|
+
});
|
|
620
|
+
it('flag on, project-item entityType - roles is filtered, identity path used', async () => {
|
|
621
|
+
const projectItemProp = {
|
|
622
|
+
...refProp,
|
|
623
|
+
referencedTypeRootSlug: 'project-item',
|
|
624
|
+
referencedTypePath: 'project-item'
|
|
625
|
+
};
|
|
626
|
+
const config = baseConfig();
|
|
627
|
+
config.search = { 'project-item': { useIdentityServiceForInboundData: true } };
|
|
628
|
+
const dc = new data_converter_1.DataConverter(config, new transform_data_1.MapFileUtil(new sdk_1.Entities()));
|
|
629
|
+
setupTypeUtilsSpies(dc, ['itemNumber', 'roles']);
|
|
630
|
+
identifierSpy = jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'getIdentifierPropertiesFromObject')
|
|
631
|
+
.mockImplementation(async () => ['itemNumber', 'roles']);
|
|
632
|
+
poolKeySpy = jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'getUniquenessPoolKeyFromObject')
|
|
633
|
+
.mockImplementation(async () => 'project-item');
|
|
634
|
+
mockGetFunction.mockClear();
|
|
635
|
+
mockGetFunction.mockImplementation(() => [{ entityReference: 'project-item:pi-789' }]);
|
|
636
|
+
const result = await dc.setObjectReferenceValue(projectItemProp, { itemNumber: 'X1', roles: ['family'] });
|
|
637
|
+
expect(mockGetFunction).toHaveBeenCalledTimes(1);
|
|
638
|
+
const callArg = mockGetFunction.mock.calls[0][0];
|
|
639
|
+
expect(callArg.entityName).toEqual('identity');
|
|
640
|
+
expect(callArg.criteria.propertyName).toEqual('itemNumber');
|
|
641
|
+
expect(result).toEqual('pi-789');
|
|
642
|
+
});
|
|
643
|
+
it('flag on, non-item entityType - roles is NOT filtered, falls back to query path', async () => {
|
|
644
|
+
const colorProp = {
|
|
645
|
+
...refProp,
|
|
646
|
+
referencedTypeRootSlug: 'color',
|
|
647
|
+
referencedTypePath: 'color'
|
|
648
|
+
};
|
|
649
|
+
const config = baseConfig();
|
|
650
|
+
config.search = { color: { useIdentityServiceForInboundData: true } };
|
|
651
|
+
const dc = new data_converter_1.DataConverter(config, new transform_data_1.MapFileUtil(new sdk_1.Entities()));
|
|
652
|
+
setupTypeUtilsSpies(dc, ['colorNumber', 'roles']);
|
|
653
|
+
identifierSpy = jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'getIdentifierPropertiesFromObject')
|
|
654
|
+
.mockImplementation(async () => ['colorNumber', 'roles']);
|
|
655
|
+
poolKeySpy = jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'getUniquenessPoolKeyFromObject')
|
|
656
|
+
.mockImplementation(async () => 'color');
|
|
657
|
+
mockGetFunction.mockClear();
|
|
658
|
+
mockGetFunction.mockImplementation(() => [{ id: 'col-1', typePath: 'color' }]);
|
|
659
|
+
const result = await dc.setObjectReferenceValue(colorProp, { colorNumber: 'C1', roles: ['family'] });
|
|
660
|
+
expect(mockGetFunction).toHaveBeenCalled();
|
|
661
|
+
const callArg = mockGetFunction.mock.calls[0][0];
|
|
662
|
+
expect(callArg.entityName).toEqual('color');
|
|
663
|
+
expect(callArg.entityName).not.toEqual('identity');
|
|
664
|
+
expect(result).toEqual('col-1');
|
|
665
|
+
expect(poolKeySpy).not.toHaveBeenCalled();
|
|
666
|
+
});
|
|
667
|
+
it('query path, multiple matches - returns empty with single warn (no double warn)', async () => {
|
|
668
|
+
const dc = new data_converter_1.DataConverter(baseConfig(), new transform_data_1.MapFileUtil(new sdk_1.Entities()));
|
|
669
|
+
setupTypeUtilsSpies(dc, ['itemNumber']);
|
|
670
|
+
identifierSpy = jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'getIdentifierPropertiesFromObject')
|
|
671
|
+
.mockImplementation(async () => ['itemNumber']);
|
|
672
|
+
mockGetFunction.mockClear();
|
|
673
|
+
mockGetFunction.mockImplementation(() => [
|
|
674
|
+
{ id: 'a1', typePath: 'item:material' },
|
|
675
|
+
{ id: 'a2', typePath: 'item:material' }
|
|
676
|
+
]);
|
|
677
|
+
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => { });
|
|
678
|
+
try {
|
|
679
|
+
const result = await dc.setObjectReferenceValue(refProp, { itemNumber: 'MAT-100' });
|
|
680
|
+
expect(result).toEqual('');
|
|
681
|
+
expect(warnSpy).toHaveBeenCalledTimes(1);
|
|
682
|
+
expect(warnSpy.mock.calls[0][0]).toMatch(/duplicate records/);
|
|
683
|
+
}
|
|
684
|
+
finally {
|
|
685
|
+
warnSpy.mockRestore();
|
|
686
|
+
}
|
|
687
|
+
});
|
|
688
|
+
it('flag on, identity returns null - normalized to empty array, returns empty', async () => {
|
|
689
|
+
const config = baseConfig();
|
|
690
|
+
config.search = { item: { useIdentityServiceForInboundData: true } };
|
|
691
|
+
const dc = new data_converter_1.DataConverter(config, new transform_data_1.MapFileUtil(new sdk_1.Entities()));
|
|
692
|
+
setupTypeUtilsSpies(dc);
|
|
693
|
+
identifierSpy = jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'getIdentifierPropertiesFromObject')
|
|
694
|
+
.mockImplementation(async () => ['itemNumber', 'roles']);
|
|
695
|
+
poolKeySpy = jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'getUniquenessPoolKeyFromObject')
|
|
696
|
+
.mockImplementation(async () => 'item:material');
|
|
697
|
+
mockGetFunction.mockClear();
|
|
698
|
+
mockGetFunction.mockImplementation(() => null);
|
|
699
|
+
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => { });
|
|
700
|
+
try {
|
|
701
|
+
const result = await dc.setObjectReferenceValue(refProp, { itemNumber: 'MAT-100', roles: ['family'] });
|
|
702
|
+
expect(result).toEqual('');
|
|
703
|
+
expect(warnSpy).toHaveBeenCalled();
|
|
704
|
+
}
|
|
705
|
+
finally {
|
|
706
|
+
warnSpy.mockRestore();
|
|
707
|
+
}
|
|
708
|
+
});
|
|
709
|
+
it('null nd - returns empty string and does not query', async () => {
|
|
710
|
+
const dc = new data_converter_1.DataConverter(baseConfig(), new transform_data_1.MapFileUtil(new sdk_1.Entities()));
|
|
711
|
+
mockGetFunction.mockClear();
|
|
712
|
+
const result = await dc.setObjectReferenceValue(refProp, null);
|
|
713
|
+
expect(result).toEqual('');
|
|
714
|
+
expect(mockGetFunction).not.toHaveBeenCalled();
|
|
715
|
+
});
|
|
716
|
+
it('zero identifier keys - warns and returns empty string', async () => {
|
|
717
|
+
const dc = new data_converter_1.DataConverter(baseConfig(), new transform_data_1.MapFileUtil(new sdk_1.Entities()));
|
|
718
|
+
setupTypeUtilsSpies(dc);
|
|
719
|
+
identifierSpy = jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'getIdentifierPropertiesFromObject')
|
|
720
|
+
.mockImplementation(async () => []);
|
|
721
|
+
mockGetFunction.mockClear();
|
|
722
|
+
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => { });
|
|
723
|
+
try {
|
|
724
|
+
const result = await dc.setObjectReferenceValue(refProp, { itemNumber: 'MAT-100' });
|
|
725
|
+
expect(result).toEqual('');
|
|
726
|
+
expect(warnSpy).toHaveBeenCalledWith(expect.stringMatching(/doesnt have all "identifier" properties/));
|
|
727
|
+
expect(mockGetFunction).not.toHaveBeenCalled();
|
|
728
|
+
}
|
|
729
|
+
finally {
|
|
730
|
+
warnSpy.mockRestore();
|
|
731
|
+
}
|
|
732
|
+
});
|
|
733
|
+
it('missing identifier keys on nd - warns and returns empty string', async () => {
|
|
734
|
+
const dc = new data_converter_1.DataConverter(baseConfig(), new transform_data_1.MapFileUtil(new sdk_1.Entities()));
|
|
735
|
+
setupTypeUtilsSpies(dc);
|
|
736
|
+
identifierSpy = jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'getIdentifierPropertiesFromObject')
|
|
737
|
+
.mockImplementation(async () => ['itemNumber', 'season']);
|
|
738
|
+
mockGetFunction.mockClear();
|
|
739
|
+
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => { });
|
|
740
|
+
try {
|
|
741
|
+
const result = await dc.setObjectReferenceValue(refProp, { itemNumber: 'MAT-100' });
|
|
742
|
+
expect(result).toEqual('');
|
|
743
|
+
expect(warnSpy).toHaveBeenCalledWith(expect.stringMatching(/doesnt have all "identifier" properties/));
|
|
744
|
+
expect(mockGetFunction).not.toHaveBeenCalled();
|
|
745
|
+
}
|
|
746
|
+
finally {
|
|
747
|
+
warnSpy.mockRestore();
|
|
748
|
+
}
|
|
749
|
+
});
|
|
750
|
+
it('transformMapFile set - applyInboundTransformMap is invoked before context build', async () => {
|
|
751
|
+
const config = baseConfig();
|
|
752
|
+
config['transformMapFile'] = 'file1';
|
|
753
|
+
const dc = new data_converter_1.DataConverter(config, new transform_data_1.MapFileUtil(new sdk_1.Entities()));
|
|
754
|
+
setupTypeUtilsSpies(dc, ['itemNumber', 'roles']);
|
|
755
|
+
identifierSpy = jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'getIdentifierPropertiesFromObject')
|
|
756
|
+
.mockImplementation(async () => ['itemNumber', 'roles']);
|
|
757
|
+
const mapKeySpy = jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'getMapKeyFromObject')
|
|
758
|
+
.mockImplementation(async () => 'LCSMaterial');
|
|
759
|
+
const applyMapSpy = jest.spyOn(map_utils_1.MapUtil, 'applyTransformMap')
|
|
760
|
+
.mockImplementation(async (...args) => args[2]);
|
|
761
|
+
mockGetFunction.mockClear();
|
|
762
|
+
mockGetFunction.mockImplementation(() => [{ id: 'q-1', typePath: 'item:material' }]);
|
|
763
|
+
try {
|
|
764
|
+
const result = await dc.setObjectReferenceValue(refProp, { itemNumber: 'MAT-100', roles: ['family'] });
|
|
765
|
+
expect(mapKeySpy).toHaveBeenCalledTimes(1);
|
|
766
|
+
expect(applyMapSpy).toHaveBeenCalledTimes(1);
|
|
767
|
+
expect(applyMapSpy.mock.calls[0][3]).toEqual('LCSMaterial');
|
|
768
|
+
expect(result).toEqual('q-1');
|
|
769
|
+
}
|
|
770
|
+
finally {
|
|
771
|
+
mapKeySpy.mockRestore();
|
|
772
|
+
applyMapSpy.mockRestore();
|
|
773
|
+
}
|
|
774
|
+
});
|
|
775
|
+
it('transformMapFile unset - applyInboundTransformMap does not invoke map utilities', async () => {
|
|
776
|
+
const dc = new data_converter_1.DataConverter(baseConfig(), new transform_data_1.MapFileUtil(new sdk_1.Entities()));
|
|
777
|
+
setupTypeUtilsSpies(dc, ['itemNumber', 'roles']);
|
|
778
|
+
identifierSpy = jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'getIdentifierPropertiesFromObject')
|
|
779
|
+
.mockImplementation(async () => ['itemNumber', 'roles']);
|
|
780
|
+
const mapKeySpy = jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'getMapKeyFromObject');
|
|
781
|
+
const applyMapSpy = jest.spyOn(map_utils_1.MapUtil, 'applyTransformMap');
|
|
782
|
+
mockGetFunction.mockClear();
|
|
783
|
+
mockGetFunction.mockImplementation(() => [{ id: 'q-2', typePath: 'item:material' }]);
|
|
784
|
+
try {
|
|
785
|
+
const result = await dc.setObjectReferenceValue(refProp, { itemNumber: 'MAT-100', roles: ['family'] });
|
|
786
|
+
expect(mapKeySpy).not.toHaveBeenCalled();
|
|
787
|
+
expect(applyMapSpy).not.toHaveBeenCalled();
|
|
788
|
+
expect(result).toEqual('q-2');
|
|
789
|
+
}
|
|
790
|
+
finally {
|
|
791
|
+
mapKeySpy.mockRestore();
|
|
792
|
+
applyMapSpy.mockRestore();
|
|
793
|
+
}
|
|
794
|
+
});
|
|
795
|
+
it('query path success - writes id to cache (second call short-circuits)', async () => {
|
|
796
|
+
const dc = new data_converter_1.DataConverter(baseConfig(), new transform_data_1.MapFileUtil(new sdk_1.Entities()));
|
|
797
|
+
setupTypeUtilsSpies(dc, ['itemNumber', 'roles']);
|
|
798
|
+
identifierSpy = jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'getIdentifierPropertiesFromObject')
|
|
799
|
+
.mockImplementation(async () => ['itemNumber', 'roles']);
|
|
800
|
+
mockGetFunction.mockClear();
|
|
801
|
+
mockGetFunction.mockImplementation(() => [{ id: 'cached-q', typePath: 'item:material' }]);
|
|
802
|
+
const nd = { itemNumber: 'MAT-100', roles: ['family'] };
|
|
803
|
+
const r1 = await dc.setObjectReferenceValue(refProp, nd);
|
|
804
|
+
const callsAfterFirst = mockGetFunction.mock.calls.length;
|
|
805
|
+
const r2 = await dc.setObjectReferenceValue(refProp, nd);
|
|
806
|
+
expect(r1).toEqual('cached-q');
|
|
807
|
+
expect(r2).toEqual('cached-q');
|
|
808
|
+
expect(mockGetFunction.mock.calls.length).toEqual(callsAfterFirst);
|
|
809
|
+
});
|
|
810
|
+
it('query path with subtype filter - applies checkKeysAndValues when entityType !== entityTypePath', async () => {
|
|
811
|
+
const dc = new data_converter_1.DataConverter(baseConfig(), new transform_data_1.MapFileUtil(new sdk_1.Entities()));
|
|
812
|
+
setupTypeUtilsSpies(dc, ['itemNumber']);
|
|
813
|
+
identifierSpy = jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'getIdentifierPropertiesFromObject')
|
|
814
|
+
.mockImplementation(async () => ['itemNumber', 'season']);
|
|
815
|
+
const checkSpy = jest.spyOn(dc, 'checkKeysAndValues')
|
|
816
|
+
.mockImplementation((_criteria, arr) => arr);
|
|
817
|
+
mockGetFunction.mockClear();
|
|
818
|
+
mockGetFunction.mockImplementation(() => [{ id: 'sub-1', typePath: 'item:material' }]);
|
|
819
|
+
try {
|
|
820
|
+
const result = await dc.setObjectReferenceValue(refProp, { itemNumber: 'MAT-100', season: 'SS24' });
|
|
821
|
+
expect(checkSpy).toHaveBeenCalledTimes(1);
|
|
822
|
+
expect(checkSpy.mock.calls[0][2]).toEqual('item:material');
|
|
823
|
+
expect(result).toEqual('sub-1');
|
|
824
|
+
}
|
|
825
|
+
finally {
|
|
826
|
+
checkSpy.mockRestore();
|
|
827
|
+
}
|
|
828
|
+
});
|
|
829
|
+
it('pickSingleResult single result - identity branch parses id from entityReference', async () => {
|
|
830
|
+
const config = baseConfig();
|
|
831
|
+
config.search = { item: { useIdentityServiceForInboundData: true } };
|
|
832
|
+
const dc = new data_converter_1.DataConverter(config, new transform_data_1.MapFileUtil(new sdk_1.Entities()));
|
|
833
|
+
setupTypeUtilsSpies(dc);
|
|
834
|
+
identifierSpy = jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'getIdentifierPropertiesFromObject')
|
|
835
|
+
.mockImplementation(async () => ['itemNumber', 'roles']);
|
|
836
|
+
poolKeySpy = jest.spyOn(type_conversion_utils_1.TypeConversionUtils, 'getUniquenessPoolKeyFromObject')
|
|
837
|
+
.mockImplementation(async () => 'item:material');
|
|
838
|
+
mockGetFunction.mockClear();
|
|
839
|
+
mockGetFunction.mockImplementation(() => [{ entityReference: 'item:parsed-id-from-ref' }]);
|
|
840
|
+
const result = await dc.setObjectReferenceValue(refProp, { itemNumber: 'MAT-100', roles: ['family'] });
|
|
841
|
+
expect(result).toEqual('parsed-id-from-ref');
|
|
842
|
+
});
|
|
843
|
+
});
|
|
457
844
|
describe('getObjectReferenceValue - use mapping', () => {
|
|
458
845
|
const maps = require('./data-converter-spec-mockData');
|
|
459
846
|
const mapping = maps['mapping'];
|
|
@@ -525,6 +912,47 @@ describe('getObjectReferenceValue - use mapping', () => {
|
|
|
525
912
|
}
|
|
526
913
|
});
|
|
527
914
|
});
|
|
915
|
+
describe('getEntityValues', () => {
|
|
916
|
+
const config = {
|
|
917
|
+
apiHost: 'host',
|
|
918
|
+
userName: () => 'user',
|
|
919
|
+
password: () => 'pass',
|
|
920
|
+
urlContext: 'xxx',
|
|
921
|
+
vibeEventEndpoint: '/rfa/vibeiq/vibeEvents',
|
|
922
|
+
csrfEndpoint: '/servlet/rest/security/csrf',
|
|
923
|
+
itemPreDevelopmentLifecycleStages: ['concept']
|
|
924
|
+
};
|
|
925
|
+
const mapFileUtil = new transform_data_1.MapFileUtil(new sdk_1.Entities());
|
|
926
|
+
const dc = new data_converter_1.DataConverter(config, mapFileUtil);
|
|
927
|
+
const runWithStubs = async (typePath, objectClass, data) => {
|
|
928
|
+
const tcoSpy = jest.spyOn(dc['typeUtils'], 'getEntityTypeClientOptionsUsingMapping')
|
|
929
|
+
.mockImplementation(async () => ({ root: 'item' }));
|
|
930
|
+
const typeSpy = jest.spyOn(dc['typeUtils'], 'getByRootAndPath')
|
|
931
|
+
.mockImplementation(async () => ({ typePath, typeProperties: [] }));
|
|
932
|
+
const filterSpy = jest.spyOn(dc['typeUtils'], 'filterTypeProperties')
|
|
933
|
+
.mockImplementation(() => []);
|
|
934
|
+
try {
|
|
935
|
+
return await dc.getEntityValues(objectClass, data);
|
|
936
|
+
}
|
|
937
|
+
finally {
|
|
938
|
+
tcoSpy.mockRestore();
|
|
939
|
+
typeSpy.mockRestore();
|
|
940
|
+
filterSpy.mockRestore();
|
|
941
|
+
}
|
|
942
|
+
};
|
|
943
|
+
it('LCSProduct -> roles family', async () => {
|
|
944
|
+
const result = await runWithStubs('item', 'LCSProduct', { itemNumber: 'X1' });
|
|
945
|
+
expect(result['roles']).toEqual(['family']);
|
|
946
|
+
});
|
|
947
|
+
it('LCSSKU -> roles color, option', async () => {
|
|
948
|
+
const result = await runWithStubs('item', 'LCSSKU', { itemNumber: 'X1' });
|
|
949
|
+
expect(result['roles']).toEqual(['color', 'option']);
|
|
950
|
+
});
|
|
951
|
+
it('LCSMaterial -> roles family', async () => {
|
|
952
|
+
const result = await runWithStubs('item:material', 'LCSMaterial', { itemNumber: 'MAT-100' });
|
|
953
|
+
expect(result['roles']).toEqual(['family']);
|
|
954
|
+
});
|
|
955
|
+
});
|
|
528
956
|
describe('setEnumerationKeys', () => {
|
|
529
957
|
const config = {
|
|
530
958
|
apiHost: 'host',
|
|
@@ -2,7 +2,10 @@ export declare class TypeDefaults {
|
|
|
2
2
|
static NO_ENTITY_TYPE: string;
|
|
3
3
|
static NO_OBJECT_CLASS: string;
|
|
4
4
|
static NO_TYPE_PATH: string;
|
|
5
|
+
static processLCSMaterialAsItem: boolean;
|
|
5
6
|
constructor();
|
|
7
|
+
static applyConfig(config: any): void;
|
|
8
|
+
static isPropertyTrue(value: any): boolean;
|
|
6
9
|
static getDefaultObjectClass(entity: any): string;
|
|
7
10
|
static getDefaultObjectTypePath(entity: any): string;
|
|
8
11
|
static getDefaultIdentifierProperties(entity: any): string[];
|
|
@@ -4,6 +4,12 @@ exports.TypeDefaults = void 0;
|
|
|
4
4
|
class TypeDefaults {
|
|
5
5
|
constructor() {
|
|
6
6
|
}
|
|
7
|
+
static applyConfig(config) {
|
|
8
|
+
TypeDefaults.processLCSMaterialAsItem = TypeDefaults.isPropertyTrue(config?.LCSMaterial?.processAsItem);
|
|
9
|
+
}
|
|
10
|
+
static isPropertyTrue(value) {
|
|
11
|
+
return value === true || (typeof value === 'string' && value.toLowerCase() === 'true');
|
|
12
|
+
}
|
|
7
13
|
static getDefaultObjectClass(entity) {
|
|
8
14
|
const entityType = this.getEntityType(entity);
|
|
9
15
|
let objectClass = '';
|
|
@@ -130,7 +136,13 @@ class TypeDefaults {
|
|
|
130
136
|
static getDefaultEntityClass(object) {
|
|
131
137
|
let entityClass = '';
|
|
132
138
|
let objectClass = TypeDefaults.getObjectClass(object);
|
|
133
|
-
|
|
139
|
+
const itemClasses = TypeDefaults.processLCSMaterialAsItem
|
|
140
|
+
? ['LCSProduct', 'LCSSKU', 'LCSMaterial']
|
|
141
|
+
: ['LCSProduct', 'LCSSKU'];
|
|
142
|
+
const customEntityClasses = TypeDefaults.processLCSMaterialAsItem
|
|
143
|
+
? ['LCSRevisableEntity', 'LCSLifecycleManaged', 'LCSLast']
|
|
144
|
+
: ['LCSRevisableEntity', 'LCSLifecycleManaged', 'LCSLast', 'LCSMaterial'];
|
|
145
|
+
if (itemClasses.includes(objectClass)) {
|
|
134
146
|
entityClass = 'item';
|
|
135
147
|
}
|
|
136
148
|
else if (['LCSProductSeasonLink', 'LCSSKUSeasonLink'].includes(objectClass)) {
|
|
@@ -142,7 +154,7 @@ class TypeDefaults {
|
|
|
142
154
|
else if (['LCSSeason', 'SeasonGroup'].includes(objectClass)) {
|
|
143
155
|
entityClass = 'assortment';
|
|
144
156
|
}
|
|
145
|
-
else if (
|
|
157
|
+
else if (customEntityClasses.includes(objectClass)) {
|
|
146
158
|
entityClass = 'custom-entity';
|
|
147
159
|
}
|
|
148
160
|
if (entityClass === '')
|
|
@@ -157,6 +169,11 @@ class TypeDefaults {
|
|
|
157
169
|
case 'LCSSKU':
|
|
158
170
|
typePath = 'item';
|
|
159
171
|
break;
|
|
172
|
+
case 'LCSMaterial':
|
|
173
|
+
if (TypeDefaults.processLCSMaterialAsItem) {
|
|
174
|
+
typePath = 'item:material';
|
|
175
|
+
}
|
|
176
|
+
break;
|
|
160
177
|
case 'LCSProductSeasonLink':
|
|
161
178
|
case 'LCSSKUSeasonLink':
|
|
162
179
|
typePath = 'project-item';
|
|
@@ -181,6 +198,14 @@ class TypeDefaults {
|
|
|
181
198
|
case 'LCSSKU':
|
|
182
199
|
identifierProps.push('itemNumber');
|
|
183
200
|
break;
|
|
201
|
+
case 'LCSMaterial':
|
|
202
|
+
if (TypeDefaults.processLCSMaterialAsItem) {
|
|
203
|
+
identifierProps.push('itemNumber');
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
identifierProps.push('name');
|
|
207
|
+
}
|
|
208
|
+
break;
|
|
184
209
|
case 'LCSSeason':
|
|
185
210
|
identifierProps.push('flexPLMSeasonName');
|
|
186
211
|
break;
|
|
@@ -191,7 +216,6 @@ class TypeDefaults {
|
|
|
191
216
|
case 'LCSRevisableEntity':
|
|
192
217
|
case 'LCSLifecycleManaged':
|
|
193
218
|
case 'LCSLast':
|
|
194
|
-
case 'LCSMaterial':
|
|
195
219
|
identifierProps.push('name');
|
|
196
220
|
break;
|
|
197
221
|
}
|
|
@@ -200,7 +224,10 @@ class TypeDefaults {
|
|
|
200
224
|
static getDefaultInformationalPropertiesFromObject(object) {
|
|
201
225
|
const objectClass = TypeDefaults.getObjectClass(object);
|
|
202
226
|
let properties = [];
|
|
203
|
-
|
|
227
|
+
const itemClasses = TypeDefaults.processLCSMaterialAsItem
|
|
228
|
+
? ['LCSProduct', 'LCSMaterial']
|
|
229
|
+
: ['LCSProduct'];
|
|
230
|
+
if (itemClasses.includes(objectClass)) {
|
|
204
231
|
properties.push('name');
|
|
205
232
|
}
|
|
206
233
|
else if ('LCSSKU' === objectClass) {
|
|
@@ -219,3 +246,4 @@ exports.TypeDefaults = TypeDefaults;
|
|
|
219
246
|
TypeDefaults.NO_ENTITY_TYPE = 'Not able to determine the entity type of the entity object';
|
|
220
247
|
TypeDefaults.NO_OBJECT_CLASS = 'Please ensure that the flexPLMObjectClass property is provided.';
|
|
221
248
|
TypeDefaults.NO_TYPE_PATH = 'Please ensure that the flexPLMTypePath property is provided.';
|
|
249
|
+
TypeDefaults.processLCSMaterialAsItem = false;
|