@adobe/aio-cli-plugin-api-mesh 2.1.0 → 2.2.0-beta.2

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 (31) hide show
  1. package/oclif.manifest.json +1 -1
  2. package/package.json +4 -2
  3. package/src/commands/__fixtures__/env_invalid +8 -0
  4. package/src/commands/__fixtures__/env_valid +3 -0
  5. package/src/commands/__fixtures__/files/requestParams.json +3 -0
  6. package/src/commands/__fixtures__/openapi-schema.json +4 -0
  7. package/src/commands/__fixtures__/requestParams.json +3 -0
  8. package/src/commands/__fixtures__/sample_fully_qualified_mesh.json +29 -0
  9. package/src/commands/__fixtures__/sample_invalid_mesh.txt +17 -0
  10. package/src/commands/__fixtures__/sample_mesh_files.json +23 -0
  11. package/src/commands/__fixtures__/sample_mesh_invalid_file_content.json +14 -0
  12. package/src/commands/__fixtures__/sample_mesh_invalid_file_name.json +27 -0
  13. package/src/commands/__fixtures__/sample_mesh_invalid_paths.json +23 -0
  14. package/src/commands/__fixtures__/sample_mesh_invalid_type.json +27 -0
  15. package/src/commands/__fixtures__/sample_mesh_mismatching_path.json +29 -0
  16. package/src/commands/__fixtures__/sample_mesh_outside_workspace_dir.json +23 -0
  17. package/src/commands/__fixtures__/sample_mesh_path_from_home.json +14 -0
  18. package/src/commands/__fixtures__/sample_mesh_subdirectory.json +23 -0
  19. package/src/commands/__fixtures__/sample_mesh_with_files_array.json +29 -0
  20. package/src/commands/__fixtures__/sample_mesh_with_placeholder +17 -0
  21. package/src/commands/api-mesh/__tests__/create.test.js +1202 -1
  22. package/src/commands/api-mesh/__tests__/init.test.js +390 -0
  23. package/src/commands/api-mesh/__tests__/update.test.js +419 -4
  24. package/src/commands/api-mesh/create.js +47 -14
  25. package/src/commands/api-mesh/init.js +168 -0
  26. package/src/commands/api-mesh/update.js +49 -17
  27. package/src/helpers.js +254 -3
  28. package/src/lib/devConsole.js +1 -2
  29. package/src/templates/gitignore +1 -0
  30. package/src/templates/package.json +38 -0
  31. package/src/utils.js +337 -33
@@ -14,7 +14,13 @@ const mockConsoleCLIInstance = {};
14
14
 
15
15
  const CreateCommand = require('../create');
16
16
  const sampleCreateMeshConfig = require('../../__fixtures__/sample_mesh.json');
17
- const { initSdk, initRequestId, promptConfirm } = require('../../../helpers');
17
+ const {
18
+ initSdk,
19
+ initRequestId,
20
+ promptConfirm,
21
+ interpolateMesh,
22
+ importFiles,
23
+ } = require('../../../helpers');
18
24
  const {
19
25
  createMesh,
20
26
  createAPIMeshCredentials,
@@ -40,6 +46,8 @@ jest.mock('../../../helpers', () => ({
40
46
  initSdk: jest.fn().mockResolvedValue({}),
41
47
  initRequestId: jest.fn().mockResolvedValue({}),
42
48
  promptConfirm: jest.fn().mockResolvedValue(true),
49
+ interpolateMesh: jest.fn().mockResolvedValue({}),
50
+ importFiles: jest.fn().mockResolvedValue(),
43
51
  }));
44
52
  jest.mock('../../../lib/devConsole');
45
53
 
@@ -111,6 +119,7 @@ describe('create command tests', () => {
111
119
  },
112
120
  ]
113
121
  `);
122
+
114
123
  expect(CreateCommand.flags).toMatchInlineSnapshot(`
115
124
  {
116
125
  "autoConfirmAction": {
@@ -121,6 +130,15 @@ describe('create command tests', () => {
121
130
  "parse": [Function],
122
131
  "type": "boolean",
123
132
  },
133
+ "env": {
134
+ "char": "e",
135
+ "default": ".env",
136
+ "description": "Path to env file",
137
+ "input": [],
138
+ "multiple": false,
139
+ "parse": [Function],
140
+ "type": "option",
141
+ },
124
142
  "ignoreCache": {
125
143
  "allowNo": false,
126
144
  "char": "i",
@@ -481,4 +499,1187 @@ describe('create command tests', () => {
481
499
  `);
482
500
  expect(errorLogSpy.mock.calls).toMatchInlineSnapshot(`[]`);
483
501
  });
502
+
503
+ test('must return proper object structure used by adobe/generator-app-api-mesh', async () => {
504
+ parseSpy.mockResolvedValueOnce({
505
+ args: { file: 'src/commands/__fixtures__/sample_mesh.json' },
506
+ flags: {
507
+ json: Promise.resolve(true),
508
+ },
509
+ });
510
+ const output = await CreateCommand.run();
511
+ expect(output).toHaveProperty('mesh');
512
+ expect(output).toHaveProperty('adobeIdIntegrationsForWorkspace');
513
+ expect(output.mesh).toEqual(expect.objectContaining({ meshId: 'dummy_mesh_id' }));
514
+ expect(output.adobeIdIntegrationsForWorkspace).toEqual(
515
+ expect.objectContaining({ apiKey: 'dummy_api_key' }),
516
+ );
517
+ });
518
+
519
+ test('should return error if the mesh has placeholders and env file provided using --env flag is not found', async () => {
520
+ parseSpy.mockResolvedValueOnce({
521
+ args: { file: 'src/commands/__fixtures__/sample_mesh_with_placeholder' },
522
+ flags: {
523
+ ignoreCache: mockIgnoreCacheFlag,
524
+ autoConfirmAction: Promise.resolve(true),
525
+ env: 'src/commands/__fixtures__/.env_nonExisting',
526
+ },
527
+ });
528
+ const runResult = CreateCommand.run();
529
+
530
+ await expect(runResult).rejects.toEqual(
531
+ new Error(
532
+ 'Unable to read the file src/commands/__fixtures__/.env_nonExisting. Please check the file and try again.',
533
+ ),
534
+ );
535
+
536
+ expect(logSpy.mock.calls).toMatchInlineSnapshot(`
537
+ [
538
+ [
539
+ "The provided mesh contains placeholders. Starting mesh interpolation process.",
540
+ ],
541
+ [
542
+ "ENOENT: no such file or directory, open 'src/commands/__fixtures__/.env_nonExisting'",
543
+ ],
544
+ ]
545
+ `);
546
+ expect(errorLogSpy.mock.calls).toMatchInlineSnapshot(`
547
+ [
548
+ [
549
+ "Unable to read the file src/commands/__fixtures__/.env_nonExisting. Please check the file and try again.",
550
+ ],
551
+ ]
552
+ `);
553
+ });
554
+
555
+ test('should return error if mesh has placeholders and the provided env file is invalid', async () => {
556
+ parseSpy.mockResolvedValueOnce({
557
+ args: { file: 'src/commands/__fixtures__/sample_mesh_with_placeholder' },
558
+ flags: {
559
+ ignoreCache: mockIgnoreCacheFlag,
560
+ autoConfirmAction: Promise.resolve(true),
561
+ env: 'src/commands/__fixtures__/env_invalid',
562
+ },
563
+ });
564
+
565
+ const runResult = CreateCommand.run();
566
+
567
+ await expect(runResult).rejects.toEqual(
568
+ new Error(
569
+ "Issue in src/commands/__fixtures__/env_invalid file - Duplicate key << key1 >> on line 3,Invalid format for key/value << key2=='value3' >> on line 5,Invalid format << key3 >> on line 6,Invalid format for key/value << key4='value4 >> on line 7",
570
+ ),
571
+ );
572
+
573
+ expect(errorLogSpy.mock.calls).toMatchInlineSnapshot(`
574
+ [
575
+ [
576
+ "Issue in src/commands/__fixtures__/env_invalid file - Duplicate key << key1 >> on line 3,Invalid format for key/value << key2=='value3' >> on line 5,Invalid format << key3 >> on line 6,Invalid format for key/value << key4='value4 >> on line 7",
577
+ ],
578
+ ]
579
+ `);
580
+ });
581
+
582
+ test('should return error if the mesh has placeholders and the provided env file is valid but there are missing keys found in mesh interpolation', async () => {
583
+ parseSpy.mockResolvedValueOnce({
584
+ args: { file: 'src/commands/__fixtures__/sample_mesh_with_placeholder' },
585
+ flags: {
586
+ ignoreCache: mockIgnoreCacheFlag,
587
+ autoConfirmAction: Promise.resolve(true),
588
+ env: 'src/commands/__fixtures__/env_valid',
589
+ },
590
+ });
591
+
592
+ interpolateMesh.mockResolvedValueOnce({
593
+ interpolationStatus: 'failed',
594
+ missingKeys: ['newKey1', 'newKey2'],
595
+ interpolatedMesh: '',
596
+ });
597
+
598
+ const runResult = CreateCommand.run();
599
+ await expect(runResult).rejects.toEqual(
600
+ new Error('The mesh file cannot be interpolated due to missing keys : newKey1 , newKey2'),
601
+ );
602
+
603
+ await expect(errorLogSpy.mock.calls).toMatchInlineSnapshot(`
604
+ [
605
+ [
606
+ "The mesh file cannot be interpolated due to missing keys : newKey1 , newKey2",
607
+ ],
608
+ ]
609
+ `);
610
+ });
611
+
612
+ test('should return error if the provided env file is valid and mesh interpolation is successful but interpolated mesh is not a valid JSON', async () => {
613
+ parseSpy.mockResolvedValueOnce({
614
+ args: { file: 'src/commands/__fixtures__/sample_mesh_with_placeholder' },
615
+ flags: {
616
+ ignoreCache: mockIgnoreCacheFlag,
617
+ autoConfirmAction: Promise.resolve(true),
618
+ env: 'src/commands/__fixtures__/env_valid',
619
+ },
620
+ });
621
+
622
+ //sampleInterpolated mesh where value of responseConfig.includeHTTPDetails is invalid i.e. non-boolean
623
+ const sampleInterpolatedMesh =
624
+ '{"meshConfig":{"sources":[{"name":"<api-name>","handler":{"graphql":{"endpoint":"<api-url>"}}}],"responseConfig":{"includeHTTPDetails":sample}}}';
625
+
626
+ interpolateMesh.mockResolvedValueOnce({
627
+ interpolationStatus: 'success',
628
+ missingKeys: [],
629
+ interpolatedMeshData: sampleInterpolatedMesh,
630
+ });
631
+
632
+ const runResult = CreateCommand.run();
633
+ await expect(runResult).rejects.toEqual(
634
+ new Error('Interpolated mesh is not a valid JSON. Please check the generated json file.'),
635
+ );
636
+
637
+ await expect(errorLogSpy.mock.calls).toMatchInlineSnapshot(`
638
+ [
639
+ [
640
+ "Interpolated mesh is not a valid JSON. Please check the generated json file.",
641
+ ],
642
+ ]
643
+ `);
644
+ });
645
+
646
+ test('should successfully create a mesh if provided env file is valid, mesh interpolation is successful and interpolated mesh is a valid JSON', async () => {
647
+ parseSpy.mockResolvedValue({
648
+ args: { file: 'src/commands/__fixtures__/sample_mesh_with_placeholder' },
649
+ flags: {
650
+ ignoreCache: mockIgnoreCacheFlag,
651
+ autoConfirmAction: mockAutoApproveAction,
652
+ env: 'src/commands/__fixtures__/env_valid',
653
+ },
654
+ });
655
+
656
+ //sampleInterpolated mesh where the mesh string is a valid JSON
657
+ const sampleInterpolatedMesh =
658
+ '{"meshConfig":{"sources":[{"name":"<api-name>","handler":{"graphql":{"endpoint":"<api-url>"}}}],"responseConfig":{"includeHTTPDetails":true}}}';
659
+
660
+ interpolateMesh.mockResolvedValueOnce({
661
+ interpolationStatus: 'success',
662
+ missingKeys: [],
663
+ interpolatedMeshData: sampleInterpolatedMesh,
664
+ });
665
+
666
+ const runResult = await CreateCommand.run();
667
+
668
+ expect(promptConfirm).toHaveBeenCalledWith('Are you sure you want to create a mesh?');
669
+ expect(runResult).toMatchInlineSnapshot(`
670
+ {
671
+ "adobeIdIntegrationsForWorkspace": {
672
+ "apiKey": "dummy_api_key",
673
+ "id": "dummy_id",
674
+ },
675
+ "mesh": {
676
+ "meshConfig": {
677
+ "sources": [
678
+ {
679
+ "handler": {
680
+ "graphql": {
681
+ "endpoint": "<gql_endpoint>",
682
+ },
683
+ },
684
+ "name": "<api_name>",
685
+ },
686
+ ],
687
+ },
688
+ "meshId": "dummy_mesh_id",
689
+ },
690
+ "sdkList": [
691
+ "dummy_service",
692
+ ],
693
+ }
694
+ `);
695
+ });
696
+
697
+ test('should return error if inputMesh is not a valid JSON', async () => {
698
+ parseSpy.mockResolvedValue({
699
+ args: { file: 'src/commands/__fixtures__/sample_invalid_mesh.txt' },
700
+ flags: {
701
+ ignoreCache: mockIgnoreCacheFlag,
702
+ autoConfirmAction: mockAutoApproveAction,
703
+ },
704
+ });
705
+
706
+ const runResult = CreateCommand.run();
707
+ await expect(runResult).rejects.toEqual(
708
+ new Error('Input mesh file is not a valid JSON. Please check the file provided.'),
709
+ );
710
+
711
+ await expect(errorLogSpy.mock.calls).toMatchInlineSnapshot(`
712
+ [
713
+ [
714
+ "Input mesh file is not a valid JSON. Please check the file provided.",
715
+ ],
716
+ ]
717
+ `);
718
+ });
719
+
720
+ test('should pass if there are local files in meshConfig i.e., the file is appended in files array', async () => {
721
+ let meshConfig = {
722
+ sources: [
723
+ {
724
+ name: '<json_source_name>',
725
+ handler: {
726
+ JsonSchema: {
727
+ baseUrl: '<json_source__baseurl>',
728
+ operations: [
729
+ {
730
+ type: 'Query',
731
+ field: '<query>',
732
+ path: '<query_path>',
733
+ method: 'POST',
734
+ requestSchema: './requestParams.json',
735
+ },
736
+ ],
737
+ },
738
+ },
739
+ },
740
+ ],
741
+ files: [
742
+ {
743
+ path: './requestParams.json',
744
+ content: '{"type":"updatedContent"}',
745
+ },
746
+ ],
747
+ };
748
+
749
+ createMesh.mockResolvedValue({
750
+ meshId: 'dummy_mesh_id',
751
+ meshConfig: meshConfig,
752
+ });
753
+
754
+ parseSpy.mockResolvedValue({
755
+ args: { file: 'src/commands/__fixtures__/sample_mesh_files.json' },
756
+ flags: {
757
+ autoConfirmAction: Promise.resolve(false),
758
+ },
759
+ });
760
+
761
+ importFiles.mockResolvedValueOnce({
762
+ meshConfig,
763
+ });
764
+
765
+ const output = await CreateCommand.run();
766
+
767
+ expect(initRequestId).toHaveBeenCalled();
768
+ expect(createMesh.mock.calls[0]).toMatchInlineSnapshot(`
769
+ [
770
+ "1234",
771
+ "5678",
772
+ "123456789",
773
+ {
774
+ "meshConfig": {
775
+ "files": [
776
+ {
777
+ "content": "{"type":"updatedContent"}",
778
+ "path": "./requestParams.json",
779
+ },
780
+ ],
781
+ "sources": [
782
+ {
783
+ "handler": {
784
+ "JsonSchema": {
785
+ "baseUrl": "<json_source__baseurl>",
786
+ "operations": [
787
+ {
788
+ "field": "<query>",
789
+ "method": "POST",
790
+ "path": "<query_path>",
791
+ "requestSchema": "./requestParams.json",
792
+ "type": "Query",
793
+ },
794
+ ],
795
+ },
796
+ },
797
+ "name": "<json_source_name>",
798
+ },
799
+ ],
800
+ },
801
+ },
802
+ ]
803
+ `);
804
+ expect(createAPIMeshCredentials.mock.calls[0]).toMatchInlineSnapshot(`
805
+ [
806
+ "1234",
807
+ "5678",
808
+ "123456789",
809
+ ]
810
+ `);
811
+
812
+ expect(subscribeCredentialToMeshService.mock.calls[0]).toMatchInlineSnapshot(`
813
+ [
814
+ "1234",
815
+ "5678",
816
+ "123456789",
817
+ "dummy_id",
818
+ ]
819
+ `);
820
+ expect(output).toMatchInlineSnapshot(`
821
+ {
822
+ "adobeIdIntegrationsForWorkspace": {
823
+ "apiKey": "dummy_api_key",
824
+ "id": "dummy_id",
825
+ },
826
+ "mesh": {
827
+ "meshConfig": {
828
+ "files": [
829
+ {
830
+ "content": "{"type":"updatedContent"}",
831
+ "path": "./requestParams.json",
832
+ },
833
+ ],
834
+ "sources": [
835
+ {
836
+ "handler": {
837
+ "JsonSchema": {
838
+ "baseUrl": "<json_source__baseurl>",
839
+ "operations": [
840
+ {
841
+ "field": "<query>",
842
+ "method": "POST",
843
+ "path": "<query_path>",
844
+ "requestSchema": "./requestParams.json",
845
+ "type": "Query",
846
+ },
847
+ ],
848
+ },
849
+ },
850
+ "name": "<json_source_name>",
851
+ },
852
+ ],
853
+ },
854
+ "meshId": "dummy_mesh_id",
855
+ },
856
+ "sdkList": [
857
+ "dummy_service",
858
+ ],
859
+ }
860
+ `);
861
+ });
862
+
863
+ test('should fail if the file name is more than 25 characters', async () => {
864
+ parseSpy.mockResolvedValue({
865
+ args: { file: 'src/commands/__fixtures__/sample_mesh_invalid_file_name.json' },
866
+ flags: {
867
+ autoConfirmAction: Promise.resolve(false),
868
+ },
869
+ });
870
+
871
+ const output = CreateCommand.run();
872
+
873
+ await expect(output).rejects.toEqual(new Error('Input mesh config is not valid.'));
874
+
875
+ expect(logSpy.mock.calls).toMatchInlineSnapshot(`
876
+ [
877
+ [
878
+ "Mesh file names must be less than 25 characters. The following file(s) are invalid: requestJSONParameters.json.",
879
+ ],
880
+ ]
881
+ `);
882
+
883
+ expect(errorLogSpy.mock.calls).toMatchInlineSnapshot(`
884
+ [
885
+ [
886
+ "Input mesh config is not valid.",
887
+ ],
888
+ ]
889
+ `);
890
+ });
891
+
892
+ test('should fail if the file paths in files array and filenames in sources, transforms, additionalResolvers do not match in mesh config', async () => {
893
+ parseSpy.mockResolvedValue({
894
+ args: { file: 'src/commands/__fixtures__/sample_mesh_mismatching_path.json' },
895
+ flags: {
896
+ autoConfirmAction: Promise.resolve(false),
897
+ },
898
+ });
899
+
900
+ const output = CreateCommand.run();
901
+
902
+ await expect(output).rejects.toEqual(new Error('Input mesh config is not valid.'));
903
+
904
+ expect(logSpy.mock.calls).toMatchInlineSnapshot(`
905
+ [
906
+ [
907
+ "Please make sure the file names are matching in meshConfig.",
908
+ ],
909
+ ]
910
+ `);
911
+ expect(errorLogSpy.mock.calls).toMatchInlineSnapshot(`
912
+ [
913
+ [
914
+ "Input mesh config is not valid.",
915
+ ],
916
+ ]
917
+ `);
918
+ });
919
+
920
+ test('should fail if the file is of type other than js, json extension', async () => {
921
+ parseSpy.mockResolvedValue({
922
+ args: { file: 'src/commands/__fixtures__/sample_mesh_invalid_type.json' },
923
+ flags: {
924
+ autoConfirmAction: Promise.resolve(false),
925
+ },
926
+ });
927
+
928
+ const output = CreateCommand.run();
929
+
930
+ await expect(output).rejects.toEqual(new Error('Input mesh config is not valid.'));
931
+
932
+ expect(logSpy.mock.calls).toMatchInlineSnapshot(`
933
+ [
934
+ [
935
+ "Mesh files must be JavaScript or JSON. Other file types are not supported. The following file(s) are invalid: requestParams.txt.",
936
+ ],
937
+ ]
938
+ `);
939
+ expect(errorLogSpy.mock.calls).toMatchInlineSnapshot(`
940
+ [
941
+ [
942
+ "Input mesh config is not valid.",
943
+ ],
944
+ ]
945
+ `);
946
+ });
947
+
948
+ test('should fail if the files do not exist in the mesh directory or subdirectory', async () => {
949
+ parseSpy.mockResolvedValue({
950
+ args: { file: 'src/commands/__fixtures__/sample_mesh_invalid_paths.json' },
951
+ flags: {
952
+ autoConfirmAction: Promise.resolve(false),
953
+ },
954
+ });
955
+
956
+ importFiles.mockImplementation(() => {
957
+ throw new Error(
958
+ 'Please make sure the files: schemaBody.json and sample_mesh_invalid_paths.json are in the same directory/subdirectory.',
959
+ );
960
+ });
961
+
962
+ const output = CreateCommand.run();
963
+ await expect(output).rejects.toEqual(
964
+ new Error(
965
+ 'Unable to import the files in the mesh config. Please check the file and try again.',
966
+ ),
967
+ );
968
+
969
+ expect(logSpy.mock.calls).toMatchInlineSnapshot(`
970
+ [
971
+ [
972
+ "Please make sure the files: schemaBody.json and sample_mesh_invalid_paths.json are in the same directory/subdirectory.",
973
+ ],
974
+ ]
975
+ `);
976
+
977
+ expect(errorLogSpy.mock.calls).toMatchInlineSnapshot(`
978
+ [
979
+ [
980
+ "Unable to import the files in the mesh config. Please check the file and try again.",
981
+ ],
982
+ ]
983
+ `);
984
+ });
985
+
986
+ test('should fail if import files function fails', async () => {
987
+ parseSpy.mockResolvedValue({
988
+ args: { file: 'src/commands/__fixtures__/sample_mesh_files.json' },
989
+ flags: {
990
+ autoConfirmAction: Promise.resolve(false),
991
+ },
992
+ });
993
+
994
+ importFiles.mockImplementation(() => {
995
+ throw new Error('Error reading the file');
996
+ });
997
+
998
+ const output = CreateCommand.run();
999
+
1000
+ await expect(output).rejects.toEqual(
1001
+ new Error(
1002
+ 'Unable to import the files in the mesh config. Please check the file and try again.',
1003
+ ),
1004
+ );
1005
+
1006
+ expect(logSpy.mock.calls).toMatchInlineSnapshot(`
1007
+ [
1008
+ [
1009
+ "Error reading the file",
1010
+ ],
1011
+ ]
1012
+ `);
1013
+
1014
+ expect(errorLogSpy.mock.calls).toMatchInlineSnapshot(`
1015
+ [
1016
+ [
1017
+ "Unable to import the files in the mesh config. Please check the file and try again.",
1018
+ ],
1019
+ ]
1020
+ `);
1021
+ });
1022
+
1023
+ test('should not override if prompt returns No, if there is files array', async () => {
1024
+ let meshConfig = {
1025
+ sources: [
1026
+ {
1027
+ name: '<json_source_name>',
1028
+ handler: {
1029
+ JsonSchema: {
1030
+ baseUrl: '<json_source__baseurl>',
1031
+ operations: [
1032
+ {
1033
+ type: 'Query',
1034
+ field: '<query>',
1035
+ path: '<query_path>',
1036
+ method: 'POST',
1037
+ requestSchema: './requestParams.json',
1038
+ },
1039
+ ],
1040
+ },
1041
+ },
1042
+ },
1043
+ ],
1044
+ files: [
1045
+ {
1046
+ path: './requestParams.json',
1047
+ content: '{"type":"dummyContent"}',
1048
+ },
1049
+ ],
1050
+ };
1051
+
1052
+ promptConfirm.mockResolvedValue(false).mockResolvedValue(true);
1053
+
1054
+ importFiles.mockResolvedValue(meshConfig);
1055
+
1056
+ createMesh.mockResolvedValue({
1057
+ meshId: 'dummy_mesh_id',
1058
+ meshConfig: meshConfig,
1059
+ });
1060
+
1061
+ parseSpy.mockResolvedValue({
1062
+ args: { file: 'src/commands/__fixtures__/sample_mesh_with_files_array.json' },
1063
+ flags: {
1064
+ autoConfirmAction: Promise.resolve(false),
1065
+ },
1066
+ });
1067
+
1068
+ const output = await CreateCommand.run();
1069
+
1070
+ expect(initRequestId).toHaveBeenCalled();
1071
+ expect(createMesh.mock.calls[0]).toMatchInlineSnapshot(`
1072
+ [
1073
+ "1234",
1074
+ "5678",
1075
+ "123456789",
1076
+ {
1077
+ "files": [
1078
+ {
1079
+ "content": "{"type":"dummyContent"}",
1080
+ "path": "./requestParams.json",
1081
+ },
1082
+ ],
1083
+ "sources": [
1084
+ {
1085
+ "handler": {
1086
+ "JsonSchema": {
1087
+ "baseUrl": "<json_source__baseurl>",
1088
+ "operations": [
1089
+ {
1090
+ "field": "<query>",
1091
+ "method": "POST",
1092
+ "path": "<query_path>",
1093
+ "requestSchema": "./requestParams.json",
1094
+ "type": "Query",
1095
+ },
1096
+ ],
1097
+ },
1098
+ },
1099
+ "name": "<json_source_name>",
1100
+ },
1101
+ ],
1102
+ },
1103
+ ]
1104
+ `);
1105
+ expect(createAPIMeshCredentials.mock.calls[0]).toMatchInlineSnapshot(`
1106
+ [
1107
+ "1234",
1108
+ "5678",
1109
+ "123456789",
1110
+ ]
1111
+ `);
1112
+ expect(subscribeCredentialToMeshService.mock.calls[0]).toMatchInlineSnapshot(`
1113
+ [
1114
+ "1234",
1115
+ "5678",
1116
+ "123456789",
1117
+ "dummy_id",
1118
+ ]
1119
+ `);
1120
+ expect(output).toMatchInlineSnapshot(`
1121
+ {
1122
+ "adobeIdIntegrationsForWorkspace": {
1123
+ "apiKey": "dummy_api_key",
1124
+ "id": "dummy_id",
1125
+ },
1126
+ "mesh": {
1127
+ "meshConfig": {
1128
+ "files": [
1129
+ {
1130
+ "content": "{"type":"dummyContent"}",
1131
+ "path": "./requestParams.json",
1132
+ },
1133
+ ],
1134
+ "sources": [
1135
+ {
1136
+ "handler": {
1137
+ "JsonSchema": {
1138
+ "baseUrl": "<json_source__baseurl>",
1139
+ "operations": [
1140
+ {
1141
+ "field": "<query>",
1142
+ "method": "POST",
1143
+ "path": "<query_path>",
1144
+ "requestSchema": "./requestParams.json",
1145
+ "type": "Query",
1146
+ },
1147
+ ],
1148
+ },
1149
+ },
1150
+ "name": "<json_source_name>",
1151
+ },
1152
+ ],
1153
+ },
1154
+ "meshId": "dummy_mesh_id",
1155
+ },
1156
+ "sdkList": [
1157
+ "dummy_service",
1158
+ ],
1159
+ }
1160
+ `);
1161
+ });
1162
+
1163
+ test('should override if prompt returns Yes, if there is files array', async () => {
1164
+ let meshConfig = {
1165
+ sources: [
1166
+ {
1167
+ name: '<json_source_name>',
1168
+ handler: {
1169
+ JsonSchema: {
1170
+ baseUrl: '<json_source__baseurl>',
1171
+ operations: [
1172
+ {
1173
+ type: 'Query',
1174
+ field: '<query>',
1175
+ path: '<query_path>',
1176
+ method: 'POST',
1177
+ requestSchema: './requestParams.json',
1178
+ },
1179
+ ],
1180
+ },
1181
+ },
1182
+ },
1183
+ ],
1184
+ files: [
1185
+ {
1186
+ path: './requestParams.json',
1187
+ content: '{"type":"updatedContent"}',
1188
+ },
1189
+ ],
1190
+ };
1191
+
1192
+ promptConfirm.mockResolvedValue(true).mockResolvedValue(true);
1193
+
1194
+ parseSpy.mockResolvedValue({
1195
+ args: { file: 'src/commands/__fixtures__/sample_mesh_with_files_array.json' },
1196
+ flags: {
1197
+ autoConfirmAction: Promise.resolve(false),
1198
+ },
1199
+ });
1200
+
1201
+ importFiles.mockResolvedValueOnce({
1202
+ meshConfig,
1203
+ });
1204
+
1205
+ createMesh.mockResolvedValue({
1206
+ meshId: 'dummy_mesh_id',
1207
+ meshConfig: meshConfig,
1208
+ });
1209
+
1210
+ const output = await CreateCommand.run();
1211
+
1212
+ expect(initRequestId).toHaveBeenCalled();
1213
+ expect(createMesh.mock.calls[0]).toMatchInlineSnapshot(`
1214
+ [
1215
+ "1234",
1216
+ "5678",
1217
+ "123456789",
1218
+ {
1219
+ "meshConfig": {
1220
+ "files": [
1221
+ {
1222
+ "content": "{"type":"updatedContent"}",
1223
+ "path": "./requestParams.json",
1224
+ },
1225
+ ],
1226
+ "sources": [
1227
+ {
1228
+ "handler": {
1229
+ "JsonSchema": {
1230
+ "baseUrl": "<json_source__baseurl>",
1231
+ "operations": [
1232
+ {
1233
+ "field": "<query>",
1234
+ "method": "POST",
1235
+ "path": "<query_path>",
1236
+ "requestSchema": "./requestParams.json",
1237
+ "type": "Query",
1238
+ },
1239
+ ],
1240
+ },
1241
+ },
1242
+ "name": "<json_source_name>",
1243
+ },
1244
+ ],
1245
+ },
1246
+ },
1247
+ ]
1248
+ `);
1249
+ expect(createAPIMeshCredentials.mock.calls[0]).toMatchInlineSnapshot(`
1250
+ [
1251
+ "1234",
1252
+ "5678",
1253
+ "123456789",
1254
+ ]
1255
+ `);
1256
+
1257
+ expect(subscribeCredentialToMeshService.mock.calls[0]).toMatchInlineSnapshot(`
1258
+ [
1259
+ "1234",
1260
+ "5678",
1261
+ "123456789",
1262
+ "dummy_id",
1263
+ ]
1264
+ `);
1265
+ expect(output).toMatchInlineSnapshot(`
1266
+ {
1267
+ "adobeIdIntegrationsForWorkspace": {
1268
+ "apiKey": "dummy_api_key",
1269
+ "id": "dummy_id",
1270
+ },
1271
+ "mesh": {
1272
+ "meshConfig": {
1273
+ "files": [
1274
+ {
1275
+ "content": "{"type":"updatedContent"}",
1276
+ "path": "./requestParams.json",
1277
+ },
1278
+ ],
1279
+ "sources": [
1280
+ {
1281
+ "handler": {
1282
+ "JsonSchema": {
1283
+ "baseUrl": "<json_source__baseurl>",
1284
+ "operations": [
1285
+ {
1286
+ "field": "<query>",
1287
+ "method": "POST",
1288
+ "path": "<query_path>",
1289
+ "requestSchema": "./requestParams.json",
1290
+ "type": "Query",
1291
+ },
1292
+ ],
1293
+ },
1294
+ },
1295
+ "name": "<json_source_name>",
1296
+ },
1297
+ ],
1298
+ },
1299
+ "meshId": "dummy_mesh_id",
1300
+ },
1301
+ "sdkList": [
1302
+ "dummy_service",
1303
+ ],
1304
+ }
1305
+ `);
1306
+ });
1307
+
1308
+ test('should pass for a fully-qualified meshConfig even if the file does not exist in fileSystem', async () => {
1309
+ let meshConfig = {
1310
+ sources: [
1311
+ {
1312
+ name: '<json_source_name>',
1313
+ handler: {
1314
+ JsonSchema: {
1315
+ baseUrl: '<json_source__baseurl>',
1316
+ operations: [
1317
+ {
1318
+ type: 'Query',
1319
+ field: '<query>',
1320
+ path: '<query_path>',
1321
+ method: 'POST',
1322
+ requestSchema: './schemaBody.json',
1323
+ },
1324
+ ],
1325
+ },
1326
+ },
1327
+ },
1328
+ ],
1329
+ files: [
1330
+ {
1331
+ path: './schemaBody.json',
1332
+ content: '{"type":"dummyContent"}',
1333
+ },
1334
+ ],
1335
+ };
1336
+
1337
+ parseSpy.mockResolvedValue({
1338
+ args: { file: 'src/commands/__fixtures__/sample_fully_qualified_mesh.json' },
1339
+ flags: {
1340
+ autoConfirmAction: Promise.resolve(false),
1341
+ },
1342
+ });
1343
+
1344
+ promptConfirm.mockResolvedValue(true);
1345
+
1346
+ importFiles.mockResolvedValueOnce({
1347
+ meshConfig,
1348
+ });
1349
+
1350
+ createMesh.mockResolvedValue({
1351
+ meshId: 'dummy_mesh_id',
1352
+ meshConfig: meshConfig,
1353
+ });
1354
+
1355
+ const output = await CreateCommand.run();
1356
+
1357
+ expect(initRequestId).toHaveBeenCalled();
1358
+ expect(createMesh.mock.calls[0]).toMatchInlineSnapshot(`
1359
+ [
1360
+ "1234",
1361
+ "5678",
1362
+ "123456789",
1363
+ {
1364
+ "meshConfig": {
1365
+ "files": [
1366
+ {
1367
+ "content": "{"type":"dummyContent"}",
1368
+ "path": "./schemaBody.json",
1369
+ },
1370
+ ],
1371
+ "sources": [
1372
+ {
1373
+ "handler": {
1374
+ "JsonSchema": {
1375
+ "baseUrl": "<json_source__baseurl>",
1376
+ "operations": [
1377
+ {
1378
+ "field": "<query>",
1379
+ "method": "POST",
1380
+ "path": "<query_path>",
1381
+ "requestSchema": "./schemaBody.json",
1382
+ "type": "Query",
1383
+ },
1384
+ ],
1385
+ },
1386
+ },
1387
+ "name": "<json_source_name>",
1388
+ },
1389
+ ],
1390
+ },
1391
+ },
1392
+ ]
1393
+ `);
1394
+ expect(createAPIMeshCredentials.mock.calls[0]).toMatchInlineSnapshot(`
1395
+ [
1396
+ "1234",
1397
+ "5678",
1398
+ "123456789",
1399
+ ]
1400
+ `);
1401
+ expect(subscribeCredentialToMeshService.mock.calls[0]).toMatchInlineSnapshot(`
1402
+ [
1403
+ "1234",
1404
+ "5678",
1405
+ "123456789",
1406
+ "dummy_id",
1407
+ ]
1408
+ `);
1409
+ expect(output).toMatchInlineSnapshot(`
1410
+ {
1411
+ "adobeIdIntegrationsForWorkspace": {
1412
+ "apiKey": "dummy_api_key",
1413
+ "id": "dummy_id",
1414
+ },
1415
+ "mesh": {
1416
+ "meshConfig": {
1417
+ "files": [
1418
+ {
1419
+ "content": "{"type":"dummyContent"}",
1420
+ "path": "./schemaBody.json",
1421
+ },
1422
+ ],
1423
+ "sources": [
1424
+ {
1425
+ "handler": {
1426
+ "JsonSchema": {
1427
+ "baseUrl": "<json_source__baseurl>",
1428
+ "operations": [
1429
+ {
1430
+ "field": "<query>",
1431
+ "method": "POST",
1432
+ "path": "<query_path>",
1433
+ "requestSchema": "./schemaBody.json",
1434
+ "type": "Query",
1435
+ },
1436
+ ],
1437
+ },
1438
+ },
1439
+ "name": "<json_source_name>",
1440
+ },
1441
+ ],
1442
+ },
1443
+ "meshId": "dummy_mesh_id",
1444
+ },
1445
+ "sdkList": [
1446
+ "dummy_service",
1447
+ ],
1448
+ }
1449
+ `);
1450
+ });
1451
+
1452
+ test('should pass if the file is located in subdirectory of mesh directory', async () => {
1453
+ let meshConfig = {
1454
+ sources: [
1455
+ {
1456
+ name: '<json_source_name>',
1457
+ handler: {
1458
+ JsonSchema: {
1459
+ baseUrl: '<json_source__baseurl>',
1460
+ operations: [
1461
+ {
1462
+ type: 'Query',
1463
+ field: '<query>',
1464
+ path: '<query_path>',
1465
+ method: 'POST',
1466
+ requestSchema: './files/requestParams.json',
1467
+ },
1468
+ ],
1469
+ },
1470
+ },
1471
+ },
1472
+ ],
1473
+ files: [
1474
+ {
1475
+ path: './files/requestParams.json',
1476
+ content: '{"type":"updatedContent"}',
1477
+ },
1478
+ ],
1479
+ };
1480
+
1481
+ createMesh.mockResolvedValue({
1482
+ meshId: 'dummy_mesh_id',
1483
+ meshConfig: meshConfig,
1484
+ });
1485
+
1486
+ parseSpy.mockResolvedValue({
1487
+ args: { file: 'src/commands/__fixtures__/sample_mesh_subdirectory.json' },
1488
+ flags: {
1489
+ autoConfirmAction: Promise.resolve(false),
1490
+ },
1491
+ });
1492
+
1493
+ importFiles.mockResolvedValueOnce({
1494
+ meshConfig,
1495
+ });
1496
+
1497
+ const output = await CreateCommand.run();
1498
+
1499
+ expect(initRequestId).toHaveBeenCalled();
1500
+ expect(createMesh.mock.calls[0]).toMatchInlineSnapshot(`
1501
+ [
1502
+ "1234",
1503
+ "5678",
1504
+ "123456789",
1505
+ {
1506
+ "meshConfig": {
1507
+ "files": [
1508
+ {
1509
+ "content": "{"type":"updatedContent"}",
1510
+ "path": "./files/requestParams.json",
1511
+ },
1512
+ ],
1513
+ "sources": [
1514
+ {
1515
+ "handler": {
1516
+ "JsonSchema": {
1517
+ "baseUrl": "<json_source__baseurl>",
1518
+ "operations": [
1519
+ {
1520
+ "field": "<query>",
1521
+ "method": "POST",
1522
+ "path": "<query_path>",
1523
+ "requestSchema": "./files/requestParams.json",
1524
+ "type": "Query",
1525
+ },
1526
+ ],
1527
+ },
1528
+ },
1529
+ "name": "<json_source_name>",
1530
+ },
1531
+ ],
1532
+ },
1533
+ },
1534
+ ]
1535
+ `);
1536
+ expect(createAPIMeshCredentials.mock.calls[0]).toMatchInlineSnapshot(`
1537
+ [
1538
+ "1234",
1539
+ "5678",
1540
+ "123456789",
1541
+ ]
1542
+ `);
1543
+
1544
+ expect(subscribeCredentialToMeshService.mock.calls[0]).toMatchInlineSnapshot(`
1545
+ [
1546
+ "1234",
1547
+ "5678",
1548
+ "123456789",
1549
+ "dummy_id",
1550
+ ]
1551
+ `);
1552
+ expect(output).toMatchInlineSnapshot(`
1553
+ {
1554
+ "adobeIdIntegrationsForWorkspace": {
1555
+ "apiKey": "dummy_api_key",
1556
+ "id": "dummy_id",
1557
+ },
1558
+ "mesh": {
1559
+ "meshConfig": {
1560
+ "files": [
1561
+ {
1562
+ "content": "{"type":"updatedContent"}",
1563
+ "path": "./files/requestParams.json",
1564
+ },
1565
+ ],
1566
+ "sources": [
1567
+ {
1568
+ "handler": {
1569
+ "JsonSchema": {
1570
+ "baseUrl": "<json_source__baseurl>",
1571
+ "operations": [
1572
+ {
1573
+ "field": "<query>",
1574
+ "method": "POST",
1575
+ "path": "<query_path>",
1576
+ "requestSchema": "./files/requestParams.json",
1577
+ "type": "Query",
1578
+ },
1579
+ ],
1580
+ },
1581
+ },
1582
+ "name": "<json_source_name>",
1583
+ },
1584
+ ],
1585
+ },
1586
+ "meshId": "dummy_mesh_id",
1587
+ },
1588
+ "sdkList": [
1589
+ "dummy_service",
1590
+ ],
1591
+ }
1592
+ `);
1593
+ });
1594
+
1595
+ test('should fail if the file is outside the workspace directory', async () => {
1596
+ parseSpy.mockResolvedValue({
1597
+ args: { file: 'src/commands/__fixtures__/sample_mesh_outside_workspace_dir.json' },
1598
+ flags: {
1599
+ autoConfirmAction: Promise.resolve(false),
1600
+ },
1601
+ });
1602
+
1603
+ const output = CreateCommand.run();
1604
+
1605
+ await expect(output).rejects.toEqual(new Error('Input mesh config is not valid.'));
1606
+ expect(logSpy.mock.calls).toMatchInlineSnapshot(`
1607
+ [
1608
+ [
1609
+ "File(s): requestParams.json is outside the mesh directory.",
1610
+ ],
1611
+ ]
1612
+ `);
1613
+ expect(errorLogSpy.mock.calls).toMatchInlineSnapshot(`
1614
+ [
1615
+ [
1616
+ "Input mesh config is not valid.",
1617
+ ],
1618
+ ]
1619
+ `);
1620
+ });
1621
+
1622
+ test('should fail if the file has invalid JSON content', async () => {
1623
+ parseSpy.mockResolvedValue({
1624
+ args: { file: 'src/commands/__fixtures__/sample_mesh_invalid_file_content.json' },
1625
+ flags: {
1626
+ autoConfirmAction: Promise.resolve(false),
1627
+ },
1628
+ });
1629
+
1630
+ importFiles.mockImplementation(() => {
1631
+ throw new Error('Invalid JSON content in openapi-schema.json');
1632
+ });
1633
+
1634
+ const output = CreateCommand.run();
1635
+
1636
+ await expect(output).rejects.toEqual(
1637
+ new Error(
1638
+ 'Unable to import the files in the mesh config. Please check the file and try again.',
1639
+ ),
1640
+ );
1641
+
1642
+ expect(logSpy.mock.calls).toMatchInlineSnapshot(`
1643
+ [
1644
+ [
1645
+ "Invalid JSON content in openapi-schema.json",
1646
+ ],
1647
+ ]
1648
+ `);
1649
+
1650
+ expect(errorLogSpy.mock.calls).toMatchInlineSnapshot(`
1651
+ [
1652
+ [
1653
+ "Unable to import the files in the mesh config. Please check the file and try again.",
1654
+ ],
1655
+ ]
1656
+ `);
1657
+ });
1658
+
1659
+ test('should fail if the file path starts from home directory i.e., path starts with ~/', async () => {
1660
+ parseSpy.mockResolvedValue({
1661
+ args: { file: 'src/commands/__fixtures__/sample_mesh_path_from_home.json' },
1662
+ flags: {
1663
+ autoConfirmAction: Promise.resolve(false),
1664
+ },
1665
+ });
1666
+
1667
+ const output = CreateCommand.run();
1668
+
1669
+ await expect(output).rejects.toEqual(new Error('Input mesh config is not valid.'));
1670
+ expect(logSpy.mock.calls).toMatchInlineSnapshot(`
1671
+ [
1672
+ [
1673
+ "File(s): venia-openapi-schema.json is outside the mesh directory.",
1674
+ ],
1675
+ ]
1676
+ `);
1677
+ expect(errorLogSpy.mock.calls).toMatchInlineSnapshot(`
1678
+ [
1679
+ [
1680
+ "Input mesh config is not valid.",
1681
+ ],
1682
+ ]
1683
+ `);
1684
+ });
484
1685
  });