@contrail/flexplm 1.5.0-alpha.6fc44c4 → 1.5.0-alpha.98b8b06

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 (111) 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 +36 -0
  32. package/lib/transform/identifier-conversion.spec.js +7 -0
  33. package/lib/util/config-defaults.js +5 -0
  34. package/lib/util/config-defaults.spec.js +23 -0
  35. package/lib/util/data-converter-spec-mockData.js +17 -3
  36. package/lib/util/data-converter.d.ts +102 -0
  37. package/lib/util/data-converter.js +194 -33
  38. package/lib/util/data-converter.spec.js +389 -0
  39. package/lib/util/error-response-object.d.ts +5 -0
  40. package/lib/util/error-response-object.js +7 -0
  41. package/lib/util/event-short-message-status.js +1 -0
  42. package/lib/util/federation.js +8 -0
  43. package/lib/util/flexplm-connect.d.ts +7 -0
  44. package/lib/util/flexplm-connect.js +14 -0
  45. package/lib/util/logger-config.js +1 -0
  46. package/lib/util/map-util-spec-mockData.js +17 -3
  47. package/lib/util/map-utils.d.ts +27 -0
  48. package/lib/util/map-utils.js +27 -0
  49. package/lib/util/thumbnail-util.d.ts +21 -0
  50. package/lib/util/thumbnail-util.js +28 -1
  51. package/lib/util/thumbnail-util.spec.js +6 -0
  52. package/lib/util/type-conversion-utils-spec-mockData.js +3 -3
  53. package/lib/util/type-conversion-utils.d.ts +152 -1
  54. package/lib/util/type-conversion-utils.js +155 -1
  55. package/lib/util/type-defaults.d.ts +69 -0
  56. package/lib/util/type-defaults.js +96 -5
  57. package/lib/util/type-defaults.spec.js +94 -14
  58. package/lib/util/type-utils.d.ts +21 -0
  59. package/lib/util/type-utils.js +23 -0
  60. package/lib/util/type-utils.spec.js +2 -0
  61. package/package.json +21 -6
  62. package/scripts/copy-template.js +10 -0
  63. package/.github/pull_request_template.md +0 -31
  64. package/.github/workflows/flexplm-lib.yml +0 -27
  65. package/.github/workflows/publish-to-npm.yml +0 -121
  66. package/CHANGELOG.md +0 -42
  67. package/publish.bat +0 -5
  68. package/publish.sh +0 -5
  69. package/src/entity-processor/base-entity-processor.spec.ts +0 -689
  70. package/src/entity-processor/base-entity-processor.ts +0 -583
  71. package/src/flexplm-request.ts +0 -28
  72. package/src/flexplm-utils.spec.ts +0 -27
  73. package/src/flexplm-utils.ts +0 -29
  74. package/src/index.ts +0 -22
  75. package/src/interfaces/interfaces.ts +0 -122
  76. package/src/interfaces/item-family-changes.ts +0 -67
  77. package/src/interfaces/publish-change-data.ts +0 -43
  78. package/src/publish/base-process-publish-assortment-callback.ts +0 -50
  79. package/src/publish/base-process-publish-assortment.spec.ts +0 -1992
  80. package/src/publish/base-process-publish-assortment.ts +0 -1134
  81. package/src/publish/mockData.ts +0 -4561
  82. package/src/transform/identifier-conversion-spec-mockData.ts +0 -496
  83. package/src/transform/identifier-conversion.spec.ts +0 -383
  84. package/src/transform/identifier-conversion.ts +0 -282
  85. package/src/util/config-defaults.spec.ts +0 -430
  86. package/src/util/config-defaults.ts +0 -103
  87. package/src/util/data-converter-spec-mockData.ts +0 -231
  88. package/src/util/data-converter.spec.ts +0 -1165
  89. package/src/util/data-converter.ts +0 -766
  90. package/src/util/error-response-object.spec.ts +0 -116
  91. package/src/util/error-response-object.ts +0 -50
  92. package/src/util/event-short-message-status.ts +0 -22
  93. package/src/util/federation.ts +0 -172
  94. package/src/util/flexplm-connect.spec.ts +0 -132
  95. package/src/util/flexplm-connect.ts +0 -208
  96. package/src/util/logger-config.ts +0 -20
  97. package/src/util/map-util-spec-mockData.ts +0 -231
  98. package/src/util/map-utils.spec.ts +0 -103
  99. package/src/util/map-utils.ts +0 -41
  100. package/src/util/mockData.ts +0 -101
  101. package/src/util/thumbnail-util.spec.ts +0 -508
  102. package/src/util/thumbnail-util.ts +0 -272
  103. package/src/util/type-conversion-utils-spec-mockData.ts +0 -272
  104. package/src/util/type-conversion-utils.spec.ts +0 -1031
  105. package/src/util/type-conversion-utils.ts +0 -490
  106. package/src/util/type-defaults.spec.ts +0 -708
  107. package/src/util/type-defaults.ts +0 -284
  108. package/src/util/type-utils.spec.ts +0 -227
  109. package/src/util/type-utils.ts +0 -144
  110. package/tsconfig.json +0 -24
  111. 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'];
@@ -650,6 +1037,7 @@ describe('checkKeysAndValues', () => {
650
1037
  { name: 'Group 4', typePath: 'custom-entity:grouping:sub' },
651
1038
  { name: 'Group 5', typePath: 'custom-entity:grouping:sub' }
652
1039
  ];
1040
+ //item:product:newBalance:accessories, item:product:newBalance:apparel
653
1041
  it('Group 1', () => {
654
1042
  const criteria = {
655
1043
  name: 'Group 1'
@@ -871,6 +1259,7 @@ describe('getUserListValue', () => {
871
1259
  ];
872
1260
  const mapFileUtil = new transform_data_1.MapFileUtil(new sdk_1.Entities());
873
1261
  const dc = new data_converter_1.DataConverter(config, mapFileUtil);
1262
+ //getUserById
874
1263
  let spyGetUserById = jest.spyOn(dc, 'getUserById')
875
1264
  .mockImplementation(async (nd) => {
876
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;
@@ -31,12 +31,26 @@ exports.mapping = {
31
31
  }
32
32
  }
33
33
  }
34
+ // flex2vibe: {
35
+ // LCSRevisableEntity: {
36
+ // getMapKey: (object) =>{ return object;}
37
+ // },
38
+ // LCSLast: {
39
+ // getMapKey: (object) => {return object;}
40
+ // },
41
+ // LCSMaterial: {
42
+ // getMapKey: (object) =>{ return object;}
43
+ // },
44
+ // LCSBusinessObject: {
45
+ // getMapKey: (object) => {return object;}
46
+ // }
47
+ // },
34
48
  },
35
49
  LCSProduct: {
36
50
  vibeOwningKeys: ['itemNumber', 'lifecycleStage'],
37
51
  vibe2flex: {
38
52
  getClass: () => 'LCSProduct',
39
- getSoftType: (entity) => {
53
+ getSoftType: (entity /*, dependencies*/) => {
40
54
  const prodType = entity['prodType'];
41
55
  let val = '';
42
56
  switch (prodType) {
@@ -61,7 +75,7 @@ exports.mapping = {
61
75
  vibeIQIdentifier: 'itemNumber'
62
76
  },
63
77
  valueTransform: {
64
- transformEx: (row) => {
78
+ transformEx: (row /*, dependencies*/) => {
65
79
  return row['otherProp'] + 'xxx';
66
80
  }
67
81
  }
@@ -79,7 +93,7 @@ exports.mapping = {
79
93
  vibeOwningKeys: ['itemNumber', 'lifecycleStage'],
80
94
  vibe2flex: {
81
95
  getClass: () => 'LCSSKU',
82
- getSoftType: (entity) => {
96
+ getSoftType: (entity /*, dependencies*/) => {
83
97
  const prodType = entity['prodType'];
84
98
  let val = '';
85
99
  switch (prodType) {
@@ -1,6 +1,33 @@
1
1
  import { MapFileUtil } from '@contrail/transform-data';
2
2
  export declare class MapUtil {
3
+ /** Transforms the data, assumes mapSectionKey has been correctly determined.
4
+ *
5
+ * @param transformMapFile id of map file
6
+ * @param mapFileUtil
7
+ * @param data
8
+ * @param mapSectionKey key for section of map file
9
+ * @param direction vibe2flex or flex2vibe
10
+ * @param transformTaskOrderKey key for the list of tasks to be processed. default is 'transformOrder'
11
+ * @returns The converted data
12
+ */
3
13
  static applyTransformMap(transformMapFile: any, mapFileUtil: any, data: any, mapSectionKey: string, direction: string, transformTaskOrderKey?: string): Promise<any>;
14
+ /** Returns the mapKey based on the 'typeConversion' section of the map file.
15
+ * If the map file doesn't have a section for the type in question, 'undefined' is returned.
16
+ *
17
+ * @param fileId
18
+ * @param mapFileUtil
19
+ * @param data The full object
20
+ * @param type The entity or object class of the object. The functions are keyed based on this.
21
+ * @param direction
22
+ * @returns
23
+ */
4
24
  static getMapKey(transformMapFile: any, mapFileUtil: MapFileUtil, data: any, type: string, direction: string): Promise<string>;
25
+ /** Returns the full map section with both directions; identifier and informational properties; etc.
26
+ *
27
+ * @param transformMapFile id if map file
28
+ * @param mapFileUtil
29
+ * @param mapSectionKey key of the map section
30
+ * @returns
31
+ */
5
32
  static getFullMapSection(transformMapFile: string, mapFileUtil: MapFileUtil, mapSectionKey: string): Promise<any>;
6
33
  }