@adobe/aio-cli-plugin-api-mesh 1.0.3-beta → 1.1.0

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.
@@ -11,60 +11,80 @@ governing permissions and limitations under the License.
11
11
 
12
12
  const { Command } = require('@oclif/command');
13
13
  const { writeFile } = require('fs/promises');
14
+
14
15
  const logger = require('../../classes/logger');
15
16
  const { initSdk, initRequestId } = require('../../helpers');
17
+ const { ignoreCacheFlag } = require('../../utils');
18
+ const { getMeshId, getMesh } = require('../../lib/devConsole');
16
19
 
17
20
  require('dotenv').config();
18
21
 
19
22
  class GetCommand extends Command {
20
- static args = [{ name: 'meshId' }, { name: 'file' }];
23
+ static args = [{ name: 'file' }];
24
+ static flags = {
25
+ ignoreCache: ignoreCacheFlag,
26
+ };
21
27
 
22
28
  async run() {
23
29
  await initRequestId();
24
30
 
25
31
  logger.info(`RequestId: ${global.requestId}`);
26
32
 
27
- const { args } = this.parse(GetCommand);
33
+ const { args, flags } = await this.parse(GetCommand);
28
34
 
29
- if (!args.meshId) {
30
- this.error('Missing Mesh ID. Run aio api-mesh get --help for more info.');
35
+ const ignoreCache = await flags.ignoreCache;
31
36
 
32
- return;
33
- }
37
+ const { imsOrgId, projectId, workspaceId } = await initSdk({
38
+ ignoreCache,
39
+ });
34
40
 
35
- const { schemaServiceClient, imsOrgId, projectId, workspaceId } = await initSdk();
41
+ let meshId = null;
36
42
 
37
43
  try {
38
- const mesh = await schemaServiceClient.getMesh(imsOrgId, projectId, workspaceId, args.meshId);
44
+ meshId = await getMeshId(imsOrgId, projectId, workspaceId);
45
+ } catch (err) {
46
+ this.error(
47
+ `Unable to get mesh ID. Please check the details and try again. RequestId: ${global.requestId}`,
48
+ );
49
+ }
50
+
51
+ if (meshId) {
52
+ try {
53
+ const mesh = await getMesh(imsOrgId, projectId, workspaceId, meshId);
39
54
 
40
- if (mesh) {
41
- this.log('Successfully retrieved mesh %s', JSON.stringify(mesh, null, 2));
55
+ if (mesh) {
56
+ this.log('Successfully retrieved mesh %s', JSON.stringify(mesh, null, 2));
42
57
 
43
- if (args.file) {
44
- try {
45
- const { meshConfig } = mesh;
46
- await writeFile(args.file, JSON.stringify({ meshConfig }, null, 2));
58
+ if (args.file) {
59
+ try {
60
+ const { meshConfig } = mesh;
61
+ await writeFile(args.file, JSON.stringify({ meshConfig }, null, 2));
47
62
 
48
- this.log('Successfully wrote mesh to file %s', args.file);
49
- } catch (error) {
50
- this.log('Unable to write mesh to file %s', args.file);
63
+ this.log('Successfully wrote mesh to file %s', args.file);
64
+ } catch (error) {
65
+ this.log('Unable to write mesh to file %s', args.file);
51
66
 
52
- logger.error(error);
67
+ logger.error(error);
68
+ }
53
69
  }
70
+
71
+ return mesh;
72
+ } else {
73
+ this.error(
74
+ `Unable to get mesh with the ID ${meshId}. Please check the mesh ID and try again. RequestId: ${global.requestId}`,
75
+ { exit: false },
76
+ );
54
77
  }
78
+ } catch (error) {
79
+ this.log(error.message);
55
80
 
56
- return mesh;
57
- } else {
58
81
  this.error(
59
- `Unable to get mesh with the ID ${args.meshId}. Please check the mesh ID and try again. RequestId: ${global.requestId}`,
60
- { exit: false },
82
+ `Unable to get mesh. Please check the details and try again. If the error persists please contact support. RequestId: ${global.requestId}`,
61
83
  );
62
84
  }
63
- } catch (error) {
64
- this.log(error.message);
65
-
85
+ } else {
66
86
  this.error(
67
- `Unable to get mesh. Please check the details and try again. If the error persists please contact support. RequestId: ${global.requestId}`,
87
+ `Unable to get mesh config. No mesh found for Org(${imsOrgId}) -> Project(${projectId}) -> Workspace(${workspaceId}). Please check the details and try again.`,
68
88
  );
69
89
  }
70
90
  }
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "Test 01",
3
+ "version": "0.0.1",
4
+ "description": "Mock for valid connector",
5
+ "author": "VZ",
6
+ "provider": {
7
+ "name": "Commerce",
8
+ "handler": {
9
+ "graphql": {
10
+ "endpoint": "https://venia.magento.com/graphql/"
11
+ }
12
+ },
13
+ "transforms": [
14
+ {
15
+ "rename": {
16
+ "mode": "bare | wrap",
17
+ "renames": [
18
+ {
19
+ "from": {
20
+ "type": "Query",
21
+ "field": "compareList"
22
+ },
23
+ "to": {
24
+ "type": "Query",
25
+ "field": "productCompareList"
26
+ }
27
+ }
28
+ ]
29
+ }
30
+ },
31
+ {
32
+ "filterSchema": {
33
+ "mode": "bare | wrap",
34
+ "filters": [
35
+ "Query.!category",
36
+ "Query.!customerOrders",
37
+ "Query.!urlResolver",
38
+ "Query.!wishlist"
39
+ ]
40
+ }
41
+ }
42
+ ]
43
+ }
44
+ }
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "Test 02",
3
+ "version": "0.0.1",
4
+ "description": "Mock for valid connector",
5
+ "author": "VZ",
6
+ "provider": {
7
+ "name": "Commerce",
8
+ "handler": {
9
+ "graphql": {
10
+ "endpoint": "https://venia.magento.com/graphql/"
11
+ }
12
+ },
13
+ "transforms": [
14
+ {
15
+ "rename": {
16
+ "mode": "bare | wrap",
17
+ "renames": [
18
+ {
19
+ "from": {
20
+ "type": "Query",
21
+ "field": "compareList"
22
+ },
23
+ "to": {
24
+ "type": "Query",
25
+ "field": "productCompareList"
26
+ }
27
+ }
28
+ ]
29
+ }
30
+ },
31
+ {
32
+ "filterSchema": {
33
+ "mode": "bare | wrap",
34
+ "filters": [
35
+ "Query.!category",
36
+ "Query.!customerOrders",
37
+ "Query.!urlResolver",
38
+ "Query.!wishlist"
39
+ ]
40
+ }
41
+ }
42
+ ]
43
+ }
44
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "test-01": {
3
+ "name": "test-01",
4
+ "description": "Mock for valid connector",
5
+ "author": "VZ",
6
+ "latest": "0.0.2",
7
+ "versions": ["0.0.1", "0.0.2"]
8
+ },
9
+ "test-02": {
10
+ "name": "test-02",
11
+ "description": "Mock for valid connector",
12
+ "author": "VZ",
13
+ "latest": "0.0.1",
14
+ "versions": ["0.0.1"]
15
+ }
16
+ }
@@ -0,0 +1,58 @@
1
+ /*
2
+ Copyright 2021 Adobe. All rights reserved.
3
+ This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License. You may obtain a copy
5
+ of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+
7
+ Unless required by applicable law or agreed to in writing, software distributed under
8
+ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ OF ANY KIND, either express or implied. See the License for the specific language
10
+ governing permissions and limitations under the License.
11
+ */
12
+
13
+ const mockMetadataFixture = require('../__fixtures__/connectors-metadata.json');
14
+ const mockAdapter = require('source-registry-storage-adapter');
15
+ const { promptConfirm } = require('../../../../helpers');
16
+ const GetCommand = require('../get');
17
+ const { CliUx } = require('@oclif/core');
18
+ const DiscoverCommand = require('../discover');
19
+ jest.mock('source-registry-storage-adapter');
20
+ jest.mock('../../../../helpers');
21
+ jest.mock('../get');
22
+ jest.mock('@oclif/core', () => ({
23
+ ...jest.requireActual('@oclif/core'),
24
+ CliUx: {
25
+ Table: {
26
+ table: jest.fn().mockImplementation(data => data),
27
+ },
28
+ },
29
+ }));
30
+
31
+ mockAdapter.mockImplementation(() => ({
32
+ getList: jest.fn().mockImplementation(() => mockMetadataFixture),
33
+ }));
34
+
35
+ describe('source:discover command tests', () => {
36
+ test('Snapshot create command description', () => {
37
+ expect(DiscoverCommand.description).toMatchInlineSnapshot(
38
+ `"Return the list of avaliable sources"`,
39
+ );
40
+ expect(DiscoverCommand.aliases).toMatchInlineSnapshot(`Array []`);
41
+ });
42
+ test('Check table render is executed', async () => {
43
+ await DiscoverCommand.run([]);
44
+ expect(CliUx.Table.table).toHaveBeenCalledTimes(1);
45
+ });
46
+ test('Check that "source:get -m" command is called', async () => {
47
+ GetCommand.run = jest.fn().mockImplementation(() => 'source:get -m');
48
+ promptConfirm.mockResolvedValue(true);
49
+ await DiscoverCommand.run([]);
50
+ expect(GetCommand.run).toHaveBeenCalledTimes(1);
51
+ });
52
+ test('Check that "source:get -m" command is not called', async () => {
53
+ GetCommand.run = jest.fn().mockImplementation(() => 'source:get -m');
54
+ promptConfirm.mockResolvedValue(false);
55
+ await DiscoverCommand.run([]);
56
+ expect(GetCommand.run).toHaveBeenCalledTimes(0);
57
+ });
58
+ });
@@ -0,0 +1,123 @@
1
+ /*
2
+ Copyright 2021 Adobe. All rights reserved.
3
+ This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License. You may obtain a copy
5
+ of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+
7
+ Unless required by applicable law or agreed to in writing, software distributed under
8
+ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ OF ANY KIND, either express or implied. See the License for the specific language
10
+ governing permissions and limitations under the License.
11
+ */
12
+
13
+ const mockMetadataFixture = require('../__fixtures__/connectors-metadata.json');
14
+ const mockSourceTest01v1Fixture = require('../__fixtures__/0.0.1-test-01.json');
15
+ const mockSourceTest02v1Fixture = require('../__fixtures__/0.0.1-test-02.json');
16
+ const mockAdapter = require('source-registry-storage-adapter');
17
+ const chalk = require('chalk');
18
+ const ncp = require('node-clipboardy');
19
+ const { promptMultiselect, promptSelect, promptConfirm } = require('../../../../helpers');
20
+ const mockSources = {
21
+ '0.0.1-test-01': mockSourceTest01v1Fixture,
22
+ '0.0.1-test-02': mockSourceTest02v1Fixture,
23
+ };
24
+ jest.mock('source-registry-storage-adapter');
25
+ jest.mock('../../../../helpers');
26
+ const GetCommand = require('../get');
27
+ const logSpy = jest.spyOn(GetCommand.prototype, 'log');
28
+ const expectedResultForSuccessScenarios = JSON.stringify(
29
+ [mockSourceTest01v1Fixture.provider, mockSourceTest02v1Fixture.provider],
30
+ null,
31
+ 4,
32
+ );
33
+ mockAdapter.mockImplementation(() => ({
34
+ get: jest
35
+ .fn()
36
+ .mockResolvedValueOnce(mockSources[`0.0.1-test-01`])
37
+ .mockResolvedValueOnce(mockSources[`0.0.1-test-02`]),
38
+ getList: jest.fn().mockImplementation(() => mockMetadataFixture),
39
+ }));
40
+ promptMultiselect.mockResolvedValue(Object.values(mockMetadataFixture));
41
+ promptConfirm.mockResolvedValue(true);
42
+ promptSelect
43
+ .mockResolvedValueOnce(
44
+ `${mockMetadataFixture['test-01'].name}@${mockMetadataFixture['test-01'].latest}`,
45
+ )
46
+ .mockResolvedValueOnce(
47
+ `${mockMetadataFixture['test-02'].name}@${mockMetadataFixture['test-02'].latest}`,
48
+ );
49
+
50
+ describe('source:get command tests', () => {
51
+ test('Snapshot create command description', () => {
52
+ expect(GetCommand.description).toMatchInlineSnapshot(
53
+ `"Command returns the content of a specific source."`,
54
+ );
55
+ expect(GetCommand.flags).toMatchInlineSnapshot(`
56
+ Object {
57
+ "multiple": Object {
58
+ "allowNo": false,
59
+ "char": "m",
60
+ "description": "Select multiple sources",
61
+ "exclusive": Array [
62
+ "name",
63
+ ],
64
+ "parse": [Function],
65
+ "type": "boolean",
66
+ },
67
+ "source": Object {
68
+ "char": "s",
69
+ "description": "Source name",
70
+ "input": Array [],
71
+ "multiple": true,
72
+ "parse": [Function],
73
+ "type": "option",
74
+ },
75
+ }
76
+ `);
77
+ expect(GetCommand.aliases).toMatchInlineSnapshot(`Array []`);
78
+ });
79
+ test('Check executing without parameters', async () => {
80
+ await GetCommand.run([]).catch(err => {
81
+ expect(err.message).toEqual(`The "aio api-mesh:source:get" command requires additional parameters` +
82
+ `\nUse "aio api-mesh:source:get --help" to see parameters information.`)
83
+ });
84
+ });
85
+ test('Check executing success with multiple, copied to clipboard and logged to console', async () => {
86
+ await GetCommand.run(['-m']);
87
+ expect(ncp.readSync()).toEqual(expectedResultForSuccessScenarios);
88
+ expect(logSpy.mock.calls.pop()[0]).toEqual(expectedResultForSuccessScenarios);
89
+ });
90
+ test('Check executing success with provided name and version, copied to clipboard and logged to console', async () => {
91
+ await GetCommand.run(['-s=test-01@0.0.1', '-s=test-02@0.0.1']);
92
+ expect(ncp.readSync()).toEqual(expectedResultForSuccessScenarios);
93
+ expect(logSpy.mock.calls.pop()[0]).toEqual(expectedResultForSuccessScenarios);
94
+ });
95
+ test('Check executing success with provided name and without version, copied to clipboard and logged to console', async () => {
96
+ await GetCommand.run(['-s=test-01', '-s=test-02']);
97
+ expect(ncp.readSync()).toEqual(expectedResultForSuccessScenarios);
98
+ expect(logSpy.mock.calls.pop()[0]).toEqual(expectedResultForSuccessScenarios);
99
+ });
100
+ test('Check executing failed due to requested source does not exist', async () => {
101
+ const name = 'test-99';
102
+ await GetCommand.run([`-s=${name}`]).catch(err => {
103
+ expect(err.message).toEqual(
104
+ chalk.red(
105
+ `The source with the name "${name}" doesn't exist.` +
106
+ `\nUse "aio api-mesh:source:discover" command to see avaliable sources.`,
107
+ ),
108
+ );
109
+ });
110
+ });
111
+ test('Check executing failed due to requested version does not exist', async () => {
112
+ const name = 'test-01';
113
+ const version = '1.1.1';
114
+ await GetCommand.run([`-s=${name}@${version}`]).catch(err => {
115
+ expect(err.message).toEqual(
116
+ chalk.red(
117
+ `The version "${version}" for source name "${name}" doesn't exist.` +
118
+ `\nUse "aio api-mesh:source:discover" command to see avaliable source versions.`,
119
+ ),
120
+ );
121
+ });
122
+ });
123
+ });
@@ -0,0 +1,70 @@
1
+ /*
2
+ Copyright 2021 Adobe. All rights reserved.
3
+ This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License. You may obtain a copy
5
+ of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ Unless required by applicable law or agreed to in writing, software distributed under
7
+ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
8
+ OF ANY KIND, either express or implied. See the License for the specific language
9
+ governing permissions and limitations under the License.
10
+ */
11
+
12
+ const { Command, CliUx } = require('@oclif/core');
13
+ const { promptConfirm, initRequestId } = require('../../../helpers');
14
+ const GetCommand = require('./get');
15
+ const SourceRegistryStorage = require('source-registry-storage-adapter');
16
+ const config = require('@adobe/aio-lib-core-config');
17
+ const logger = require('../../../classes/logger');
18
+
19
+
20
+ class DiscoverCommand extends Command {
21
+ async run() {
22
+ try {
23
+ await initRequestId();
24
+
25
+ logger.info(`RequestId: ${global.requestId}`);
26
+ const srs = new SourceRegistryStorage(config.get('api-mesh.sourceRegistry.path'));
27
+ let list;
28
+ try {
29
+ list = await srs.getList();
30
+ } catch (error) {
31
+ logger.error(error);
32
+ this.error(`Cannot get the list of sources: ${error}`);
33
+ }
34
+
35
+ this.generateSourcesTable(list);
36
+ const needInstall = await promptConfirm(`Are you want to install sources?`);
37
+ if (needInstall) {
38
+ GetCommand.run(['-m']);
39
+ }
40
+ } catch (error) {
41
+ logger.error(error);
42
+ this.error(`
43
+ Something went wrong with "discover" command. Please try again later.
44
+ ${error}
45
+ `);
46
+ }
47
+ }
48
+
49
+ async generateSourcesTable(data) {
50
+ const columns = {
51
+ name: {
52
+ get: row => `${row.name}`,
53
+ },
54
+ current: {
55
+ get: row => `${row.latest}`,
56
+ },
57
+ description: {
58
+ get: row => `${row.description}`,
59
+ },
60
+ versions: {
61
+ get: row => row.versions.reduce((prev, next) => `${prev}, ${next}`),
62
+ },
63
+ };
64
+ CliUx.Table.table(Object.values(data), columns);
65
+ }
66
+ }
67
+
68
+ DiscoverCommand.description = 'Return the list of avaliable sources';
69
+
70
+ module.exports = DiscoverCommand;
@@ -0,0 +1,138 @@
1
+ /*
2
+ Copyright 2020 Adobe. All rights reserved.
3
+ This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License. You may obtain a copy
5
+ of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+
7
+ Unless required by applicable law or agreed to in writing, software distributed under
8
+ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ OF ANY KIND, either express or implied. See the License for the specific language
10
+ governing permissions and limitations under the License.
11
+ */
12
+
13
+ const { Command, Flags } = require('@oclif/core');
14
+ const SourceRegistryStorage = require('source-registry-storage-adapter');
15
+ const { promptMultiselect, promptSelect, promptConfirm, initRequestId } = require('../../../helpers');
16
+ const ncp = require('node-clipboardy');
17
+ const chalk = require('chalk');
18
+ const config = require('@adobe/aio-lib-core-config');
19
+ const logger = require('../../../classes/logger');
20
+
21
+ class GetCommand extends Command {
22
+ constructor() {
23
+ super(...arguments);
24
+ this.sourceRegistryStorage = new SourceRegistryStorage(
25
+ config.get('api-mesh.sourceRegistry.path'),
26
+ );
27
+ }
28
+
29
+ async run() {
30
+ try {
31
+ await initRequestId();
32
+
33
+ logger.info(`RequestId: ${global.requestId}`);
34
+ let list
35
+ try {
36
+ list = await this.sourceRegistryStorage.getList();
37
+ } catch (err) {
38
+ this.log(err)
39
+ this.error(`Cannot get the list of sources: ${err}`)
40
+ }
41
+ const { flags } = await this.parse(GetCommand);
42
+ if (!flags.source && !flags.multiple) {
43
+ this.error(
44
+ `The "aio api-mesh:source:get" command requires additional parameters` +
45
+ `\nUse "aio api-mesh:source:get --help" to see parameters information.`,
46
+ );
47
+ }
48
+ const sources = flags.multiple ? await this.handleMultiple(list) : flags.source;
49
+ const sourceConfigs = [];
50
+ for (const source of sources) {
51
+ let [name, version] = source.split('@');
52
+ const normalizedName = this.normalizeName(name);
53
+ if (!list[normalizedName]) {
54
+ this.error(
55
+ chalk.red(
56
+ `The source with the name "${name}" doesn't exist.` +
57
+ `\nUse "aio api-mesh:source:discover" command to see avaliable sources.`,
58
+ ),
59
+ );
60
+ }
61
+ version = version || list[normalizedName].latest;
62
+ if (!list[normalizedName].versions.includes(version)) {
63
+ this.error(
64
+ chalk.red(
65
+ `The version "${version}" for source name "${name}" doesn't exist.` +
66
+ `\nUse "aio api-mesh:source:discover" command to see avaliable source versions.`,
67
+ ),
68
+ );
69
+ }
70
+ const sourceConfig = await this.sourceRegistryStorage.get(name, version);
71
+ sourceConfigs.push(sourceConfig.provider);
72
+ }
73
+ const sourceConfigsString = JSON.stringify(sourceConfigs, null, 4);
74
+ await ncp.writeSync(sourceConfigsString);
75
+ this.log(
76
+ chalk.green.bold.underline(
77
+ 'The sources are copied to the clipboard, please paste them to your API Mesh configuration',
78
+ ),
79
+ );
80
+ const print = await promptConfirm(`Do you want to print Source configurations in console?`);
81
+ if (print) {
82
+ this.log(sourceConfigsString);
83
+ }
84
+ } catch (error) {
85
+ logger.error(error);
86
+ this.error(`
87
+ Something went wrong with "get" command. Please try again later.
88
+ ${error}
89
+ `);
90
+ }
91
+ }
92
+
93
+ async handleMultiple(data) {
94
+ const result = [];
95
+ const selectedList = await promptMultiselect(
96
+ 'Select sources to install',
97
+ Object.values(data).map(elem => ({ name: elem.name, value: elem })),
98
+ );
99
+ for (const selected of selectedList) {
100
+ if (selected.versions.length > 1) {
101
+ const version = await promptSelect(
102
+ `Please choose the version of "${selected.name}" source`,
103
+ selected.versions.map(v => ({ name: `v${v}`, value: `${selected.name}@${v}` })),
104
+ );
105
+ result.push(version);
106
+ } else {
107
+ result.push(`${selected.name}@${selected.latest}`);
108
+ }
109
+ }
110
+ return result;
111
+ }
112
+
113
+ normalizeName(name) {
114
+ return name.toLowerCase().split(' ').join('-');
115
+ }
116
+ }
117
+
118
+ GetCommand.flags = {
119
+ source: Flags.string({
120
+ char: 's',
121
+ description: 'Source name',
122
+ multiple: true,
123
+ }),
124
+ multiple: Flags.boolean({
125
+ char: 'm',
126
+ description: 'Select multiple sources',
127
+ exclusive: ['name'],
128
+ }),
129
+ };
130
+
131
+ GetCommand.description = 'Command returns the content of a specific source.';
132
+ GetCommand.examples = [
133
+ '$ aio api-mesh:source:get <version>@<source_name>',
134
+ '$ aio api-mesh:source:get <source_name>',
135
+ '$ aio api-mesh:source:get -m',
136
+ ];
137
+
138
+ module.exports = GetCommand;