@contrail/flexplm 1.4.0 → 1.5.0-alpha.14a4f1b

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.
Files changed (112) hide show
  1. package/lib/cli/commands/compile.d.ts +4 -0
  2. package/lib/cli/commands/compile.js +73 -0
  3. package/lib/cli/commands/compile.spec.d.ts +1 -0
  4. package/lib/cli/commands/compile.spec.js +80 -0
  5. package/lib/cli/commands/create.d.ts +5 -0
  6. package/lib/cli/commands/create.js +77 -0
  7. package/lib/cli/commands/create.spec.d.ts +1 -0
  8. package/lib/cli/commands/create.spec.js +78 -0
  9. package/lib/cli/commands/upload.d.ts +17 -0
  10. package/lib/cli/commands/upload.js +228 -0
  11. package/lib/cli/commands/upload.spec.d.ts +1 -0
  12. package/lib/cli/commands/upload.spec.js +88 -0
  13. package/lib/cli/index.d.ts +5 -0
  14. package/lib/cli/index.js +70 -0
  15. package/lib/cli/index.spec.d.ts +1 -0
  16. package/lib/cli/index.spec.js +85 -0
  17. package/lib/cli/template/mapping-template.ts.template +62 -0
  18. package/lib/entity-processor/base-entity-processor.d.ts +65 -0
  19. package/lib/entity-processor/base-entity-processor.js +71 -0
  20. package/lib/entity-processor/base-entity-processor.spec.js +1 -0
  21. package/lib/index.d.ts +1 -0
  22. package/lib/index.js +1 -0
  23. package/lib/interfaces/mapping-file.d.ts +460 -0
  24. package/lib/interfaces/mapping-file.js +2 -0
  25. package/lib/publish/base-process-publish-assortment.d.ts +25 -0
  26. package/lib/publish/base-process-publish-assortment.js +60 -6
  27. package/lib/publish/base-process-publish-assortment.spec.js +22 -4
  28. package/lib/publish/mockData.js +5 -0
  29. package/lib/transform/identifier-conversion-spec-mockData.js +34 -6
  30. package/lib/transform/identifier-conversion.d.ts +36 -0
  31. package/lib/transform/identifier-conversion.js +37 -1
  32. package/lib/transform/identifier-conversion.spec.js +35 -0
  33. package/lib/util/config-defaults.d.ts +18 -0
  34. package/lib/util/config-defaults.js +25 -15
  35. package/lib/util/config-defaults.spec.js +56 -0
  36. package/lib/util/data-converter-spec-mockData.js +17 -3
  37. package/lib/util/data-converter.d.ts +102 -0
  38. package/lib/util/data-converter.js +195 -34
  39. package/lib/util/data-converter.spec.js +430 -0
  40. package/lib/util/error-response-object.d.ts +5 -0
  41. package/lib/util/error-response-object.js +7 -0
  42. package/lib/util/event-short-message-status.js +1 -0
  43. package/lib/util/federation.js +8 -0
  44. package/lib/util/flexplm-connect.d.ts +7 -0
  45. package/lib/util/flexplm-connect.js +14 -0
  46. package/lib/util/logger-config.js +1 -0
  47. package/lib/util/map-util-spec-mockData.js +17 -3
  48. package/lib/util/map-utils.d.ts +27 -0
  49. package/lib/util/map-utils.js +27 -0
  50. package/lib/util/thumbnail-util.d.ts +21 -0
  51. package/lib/util/thumbnail-util.js +28 -1
  52. package/lib/util/thumbnail-util.spec.js +6 -0
  53. package/lib/util/type-conversion-utils-spec-mockData.js +3 -3
  54. package/lib/util/type-conversion-utils.d.ts +151 -0
  55. package/lib/util/type-conversion-utils.js +154 -0
  56. package/lib/util/type-defaults.d.ts +69 -0
  57. package/lib/util/type-defaults.js +98 -4
  58. package/lib/util/type-defaults.spec.js +114 -4
  59. package/lib/util/type-utils.d.ts +21 -0
  60. package/lib/util/type-utils.js +23 -0
  61. package/lib/util/type-utils.spec.js +2 -0
  62. package/package.json +21 -6
  63. package/scripts/copy-template.js +10 -0
  64. package/.github/pull_request_template.md +0 -31
  65. package/.github/workflows/flexplm-lib.yml +0 -27
  66. package/.github/workflows/publish-to-npm.yml +0 -121
  67. package/CHANGELOG.md +0 -40
  68. package/publish.bat +0 -5
  69. package/publish.sh +0 -5
  70. package/src/entity-processor/base-entity-processor.spec.ts +0 -689
  71. package/src/entity-processor/base-entity-processor.ts +0 -583
  72. package/src/flexplm-request.ts +0 -28
  73. package/src/flexplm-utils.spec.ts +0 -27
  74. package/src/flexplm-utils.ts +0 -29
  75. package/src/index.ts +0 -22
  76. package/src/interfaces/interfaces.ts +0 -122
  77. package/src/interfaces/item-family-changes.ts +0 -67
  78. package/src/interfaces/publish-change-data.ts +0 -43
  79. package/src/publish/base-process-publish-assortment-callback.ts +0 -50
  80. package/src/publish/base-process-publish-assortment.spec.ts +0 -1992
  81. package/src/publish/base-process-publish-assortment.ts +0 -1134
  82. package/src/publish/mockData.ts +0 -4561
  83. package/src/transform/identifier-conversion-spec-mockData.ts +0 -496
  84. package/src/transform/identifier-conversion.spec.ts +0 -354
  85. package/src/transform/identifier-conversion.ts +0 -282
  86. package/src/util/config-defaults.spec.ts +0 -392
  87. package/src/util/config-defaults.ts +0 -97
  88. package/src/util/data-converter-spec-mockData.ts +0 -231
  89. package/src/util/data-converter.spec.ts +0 -1120
  90. package/src/util/data-converter.ts +0 -766
  91. package/src/util/error-response-object.spec.ts +0 -116
  92. package/src/util/error-response-object.ts +0 -50
  93. package/src/util/event-short-message-status.ts +0 -22
  94. package/src/util/federation.ts +0 -172
  95. package/src/util/flexplm-connect.spec.ts +0 -132
  96. package/src/util/flexplm-connect.ts +0 -208
  97. package/src/util/logger-config.ts +0 -20
  98. package/src/util/map-util-spec-mockData.ts +0 -231
  99. package/src/util/map-utils.spec.ts +0 -103
  100. package/src/util/map-utils.ts +0 -41
  101. package/src/util/mockData.ts +0 -101
  102. package/src/util/thumbnail-util.spec.ts +0 -508
  103. package/src/util/thumbnail-util.ts +0 -272
  104. package/src/util/type-conversion-utils-spec-mockData.ts +0 -272
  105. package/src/util/type-conversion-utils.spec.ts +0 -1031
  106. package/src/util/type-conversion-utils.ts +0 -490
  107. package/src/util/type-defaults.spec.ts +0 -669
  108. package/src/util/type-defaults.ts +0 -281
  109. package/src/util/type-utils.spec.ts +0 -227
  110. package/src/util/type-utils.ts +0 -144
  111. package/tsconfig.json +0 -24
  112. package/tslint.json +0 -57
@@ -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',
@@ -609,6 +1037,7 @@ describe('checkKeysAndValues', () => {
609
1037
  { name: 'Group 4', typePath: 'custom-entity:grouping:sub' },
610
1038
  { name: 'Group 5', typePath: 'custom-entity:grouping:sub' }
611
1039
  ];
1040
+ //item:product:newBalance:accessories, item:product:newBalance:apparel
612
1041
  it('Group 1', () => {
613
1042
  const criteria = {
614
1043
  name: 'Group 1'
@@ -830,6 +1259,7 @@ describe('getUserListValue', () => {
830
1259
  ];
831
1260
  const mapFileUtil = new transform_data_1.MapFileUtil(new sdk_1.Entities());
832
1261
  const dc = new data_converter_1.DataConverter(config, mapFileUtil);
1262
+ //getUserById
833
1263
  let spyGetUserById = jest.spyOn(dc, 'getUserById')
834
1264
  .mockImplementation(async (nd) => {
835
1265
  return userEmailMapping.find((user) => user.id === nd);
@@ -1,4 +1,9 @@
1
1
  import { AppActionCallBack } from "@contrail/app-framework";
2
2
  export declare class ErrorResponseObject {
3
+ /** Returns a response, checking for multiple possible error details
4
+ *
5
+ * @param e
6
+ * @returns
7
+ */
3
8
  static getResponse(e: any): AppActionCallBack;
4
9
  }
@@ -3,6 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ErrorResponseObject = void 0;
4
4
  const app_framework_1 = require("@contrail/app-framework");
5
5
  class ErrorResponseObject {
6
+ /** Returns a response, checking for multiple possible error details
7
+ *
8
+ * @param e
9
+ * @returns
10
+ */
6
11
  static getResponse(e) {
7
12
  if (!e) {
8
13
  const response = {
@@ -20,6 +25,7 @@ class ErrorResponseObject {
20
25
  }
21
26
  };
22
27
  const output = response.output;
28
+ //Errors when persisting in VibeIQ
23
29
  if (e.details)
24
30
  output.errorDetails = e.details;
25
31
  if (e.code)
@@ -30,6 +36,7 @@ class ErrorResponseObject {
30
36
  output.type = e.type;
31
37
  if (e.name)
32
38
  output.errorName = e.name;
39
+ //Has original cause
33
40
  if (e.cause) {
34
41
  output.cause = {};
35
42
  const cause = output.cause;
@@ -16,6 +16,7 @@ var EventShortMessageStatus;
16
16
  EventShortMessageStatus["PRIMARY_CONTENT_UPDATED"] = "Primary_Content_Updated";
17
17
  EventShortMessageStatus["TOO_MANY_ENTITIES_FOUND"] = "Too_Many_Entities_Found";
18
18
  EventShortMessageStatus["UPDATED"] = "Updated";
19
+ //Publish
19
20
  EventShortMessageStatus["NOT_PUBLISHABLE"] = "Not_Publishable";
20
21
  EventShortMessageStatus["NO_FEDERATION_INFO"] = "No_Federation_Information";
21
22
  EventShortMessageStatus["NO_EVENTS_TO_SEND"] = "No_Events_to_Send";
@@ -43,9 +43,11 @@ class Federation {
43
43
  mappedReference: itemResults[i].federatedId,
44
44
  federationSchema: FED_CONFIG.federationSchema
45
45
  };
46
+ // console.log('createFederatedRecord: ' + JSON.stringify(payload));
46
47
  try {
47
48
  const results = await new sdk_1.Entities().create({ entityName: 'federation', object: payload });
48
49
  return results;
50
+ // console.log(JSON.stringify(results));
49
51
  }
50
52
  catch (error) {
51
53
  console.log('createFederatedRecord-error: ', error);
@@ -79,13 +81,16 @@ class Federation {
79
81
  return { entityType, entityId };
80
82
  }
81
83
  async getEntityFromMappedRefId(mappedRefId) {
84
+ // console.log('!---getEntityFromMappedRefId: ' + mappedRefId);
82
85
  const fedRecord = await this.getFederationRecordFromMappedRefId(mappedRefId);
83
86
  console.log('fedRecord: ' + JSON.stringify(fedRecord));
84
87
  if (!fedRecord) {
88
+ //Not creating from FlexPLM at this time.
85
89
  console.log('Federation Record doesnt exist. Cant get entity!');
86
90
  return;
87
91
  }
88
92
  const { entityType, entityId } = Federation.getEntityId(fedRecord);
93
+ // console.log(entityType + ':' + entityId);
89
94
  const criteria = {
90
95
  id: entityId
91
96
  };
@@ -94,6 +99,7 @@ class Federation {
94
99
  criteria
95
100
  });
96
101
  const entity = (entities && entities[0]) ? entities[0] : undefined;
102
+ // console.log(' entities: ' +JSON.stringify(entities));
97
103
  return entity;
98
104
  }
99
105
  async getFederationRecordsFromIds(ids) {
@@ -104,6 +110,7 @@ class Federation {
104
110
  appIdentifier: FED_CONFIG.appIdentifier,
105
111
  federationSchema: FED_CONFIG.federationSchema
106
112
  };
113
+ // this.logger.log('getFederatedMappedRefId: ' + JSON.stringify(criteria));
107
114
  const recs = await new sdk_1.Entities().get({
108
115
  entityName: 'federation',
109
116
  criteria
@@ -126,6 +133,7 @@ class Federation {
126
133
  appIdentifier: FED_CONFIG.appIdentifier,
127
134
  federationSchema: FED_CONFIG.federationSchema
128
135
  };
136
+ // this.logger.log('getFederatedMappedRefId: ' + JSON.stringify(criteria));
129
137
  const records = await entities.get({
130
138
  entityName: 'federation',
131
139
  criteria
@@ -14,6 +14,13 @@ export declare class FlexPLMConnect {
14
14
  protected processRequest(payload: any): Promise<FlexPLMResponseData>;
15
15
  sendToFlexPLM(payload: PayloadType): Promise<FlexPLMResponseData>;
16
16
  sendMultipleToFlexPLM(payload: PayloadType[]): Promise<FlexPLMResponseData>;
17
+ /** Runs a GET request to FlexPLM.
18
+ * @param params - Optional configuration for the request.
19
+ * @param params.urlPath - Custom URL path to use instead of the default `/servlet/rest` + endpoint.
20
+ * @param params.includeUrlContext - Whether to include the urlContext in the URL. Defaults to `true`.
21
+ * @param params.returnFullResponse - If `true`, returns the raw `Response` object instead of parsed JSON. Defaults to `false`.
22
+ * @returns The parsed JSON response body, or the raw `Response` object if `returnFullResponse` is `true`.
23
+ */
17
24
  getRequest(params?: {
18
25
  urlPath?: string;
19
26
  includeUrlContext?: boolean;
@@ -18,6 +18,9 @@ class FlexPLMConnect {
18
18
  this.staticHeaders = this.config?.flexplmConnect?.staticHeaders;
19
19
  }
20
20
  }
21
+ /////////////////////////////////////////////////////////////////////////////
22
+ ///////// Custom getRequestOptions: start
23
+ /////////////////////////////////////////////////////////////////////////////
21
24
  getRequestOptions(method) {
22
25
  const csrfOptions = {
23
26
  method,
@@ -37,6 +40,9 @@ class FlexPLMConnect {
37
40
  }
38
41
  return csrfOptions;
39
42
  }
43
+ /////////////////////////////////////////////////////////////////////////////
44
+ ///////// Custom getRequestOptions: end
45
+ /////////////////////////////////////////////////////////////////////////////
40
46
  async getCSRF() {
41
47
  const urlContext = this.config.urlContext;
42
48
  const csrfEndpoint = this.config.csrfEndpoint;
@@ -130,6 +136,7 @@ class FlexPLMConnect {
130
136
  }
131
137
  }
132
138
  console.log('eventResponse.status: ' + status);
139
+ // console.log('eventBody: ', JSON.stringify(data));
133
140
  return res;
134
141
  }
135
142
  catch (e) {
@@ -145,6 +152,13 @@ class FlexPLMConnect {
145
152
  async sendMultipleToFlexPLM(payload) {
146
153
  return await this.processRequest(payload);
147
154
  }
155
+ /** Runs a GET request to FlexPLM.
156
+ * @param params - Optional configuration for the request.
157
+ * @param params.urlPath - Custom URL path to use instead of the default `/servlet/rest` + endpoint.
158
+ * @param params.includeUrlContext - Whether to include the urlContext in the URL. Defaults to `true`.
159
+ * @param params.returnFullResponse - If `true`, returns the raw `Response` object instead of parsed JSON. Defaults to `false`.
160
+ * @returns The parsed JSON response body, or the raw `Response` object if `returnFullResponse` is `true`.
161
+ */
148
162
  async getRequest(params) {
149
163
  const { urlPath, includeUrlContext = true, returnFullResponse = false } = params || {};
150
164
  const urlContext = includeUrlContext ? this.config.urlContext : '';
@@ -5,6 +5,7 @@ const app_framework_1 = require("@contrail/app-framework");
5
5
  async function setLoggerConfig(appConfig) {
6
6
  let logLevel = app_framework_1.LogLevel.INFO;
7
7
  if (!appConfig.logLevel) {
8
+ //pass
8
9
  }
9
10
  else if (appConfig.logLevel === 'error') {
10
11
  logLevel = app_framework_1.LogLevel.ERROR;