@adobe/aio-cli-plugin-api-mesh 1.0.4-beta → 1.2.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.
- package/README.md +34 -2
- package/oclif.manifest.json +1 -1
- package/package.json +4 -2
- package/src/commands/api-mesh/__tests__/describe.test.js +3 -17
- package/src/commands/api-mesh/describe.js +6 -4
- package/src/commands/api-mesh/source/__fixtures__/0.0.1-test-01.json +44 -0
- package/src/commands/api-mesh/source/__fixtures__/0.0.1-test-02.json +44 -0
- package/src/commands/api-mesh/source/__fixtures__/connectors-metadata.json +16 -0
- package/src/commands/api-mesh/source/__tests__/discover.test.js +58 -0
- package/src/commands/api-mesh/source/__tests__/get.test.js +123 -0
- package/src/commands/api-mesh/source/discover.js +70 -0
- package/src/commands/api-mesh/source/get.js +138 -0
- package/src/helpers.js +137 -32
package/README.md
CHANGED
|
@@ -52,14 +52,17 @@ If you want to have custom configuration instead, please follow the steps below:
|
|
|
52
52
|
```
|
|
53
53
|
{
|
|
54
54
|
"baseUrl": "<base_url>",
|
|
55
|
-
"apiKey": "<api_key>"
|
|
55
|
+
"apiKey": "<api_key>",
|
|
56
|
+
"sourceRegistry: {
|
|
57
|
+
"path": "<path_to_the_source_registry_storage>"
|
|
58
|
+
}
|
|
56
59
|
}
|
|
57
60
|
```
|
|
58
61
|
|
|
59
62
|
2. Perform the following command to update the configuration
|
|
60
63
|
|
|
61
64
|
```
|
|
62
|
-
aio config:set api-mesh.
|
|
65
|
+
aio config:set api-mesh.cliConfig <path_to_json_file>
|
|
63
66
|
```
|
|
64
67
|
|
|
65
68
|
# Commands
|
|
@@ -76,3 +79,32 @@ aio api-mesh:delete
|
|
|
76
79
|
All commands support `-i` or `--ignoreCache` flag that will force the CLI to ignore the cached Org, Project and Workspace details and prompt the user to select new options just for that action.
|
|
77
80
|
|
|
78
81
|
Create, Update and Delete support `-c` or `--autoConfirmAction` flag that will not prompt the user for action confirmation mostly used for testing or scaffolding where user prompt can not be handled. This flag is only to be used in certain situations.
|
|
82
|
+
|
|
83
|
+
# Sources Registry
|
|
84
|
+
|
|
85
|
+
Source registry is a collection of predefined sources (API mesh source configurations) that are created to solve specific use cases. The source can be installed for customer-specific API mesh configuration.
|
|
86
|
+
|
|
87
|
+
To submit a new source, please follow the instructions provided in the [Source Registry](https://github.com/adobe/api-mesh-sources/) repository.
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
## Commands
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
aio api-mesh:source:get
|
|
94
|
+
NAME_OF_THE_SOURCE
|
|
95
|
+
aio api-mesh:source:get NAME_OF_THE_SOURCE@VERSION_OF_THE_SOURCE
|
|
96
|
+
aio api-mesh:source:get -m
|
|
97
|
+
aio api-mesh:source:discover
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
The "source:get" command accept multiple sources per one call.
|
|
101
|
+
|
|
102
|
+
Example:
|
|
103
|
+
```
|
|
104
|
+
aio api-mesh:source:get -m <NAME_OF_THE_SOURCE>@<VERSION_OF_THE_SOURC><NAME_OF_THE_SECOND_SOURCE>@<VERSION_OF_THE_SOURC>
|
|
105
|
+
<NAME_OF_THE_THIRD_SOURCE>@<VERSION_OF_THE_SOURC>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
All commands support `-i` or `--ignoreCache` flag that will force the CLI to ignore the cached Org, Project and Workspace details and prompt the user to select new options just for that action.
|
|
109
|
+
|
|
110
|
+
Create, Update and Delete support `-c` or `--autoConfirmAction` flag that will not prompt the user for action confirmation mostly used for testing or scaffolding where user prompt can not be handled. This flag is only to be used in certain situations.
|
package/oclif.manifest.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"1.0
|
|
1
|
+
{"version":"1.2.0","commands":{"PLUGINNAME":{"id":"PLUGINNAME","description":"Your description here","pluginName":"@adobe/aio-cli-plugin-api-mesh","pluginType":"core","aliases":[],"examples":["$ aio PLUGINNAME:some_command"],"flags":{"someflag":{"name":"someflag","type":"option","char":"f","description":"this is some flag"}},"args":[]},"api-mesh:create":{"id":"api-mesh:create","description":"Create a mesh with the given config.","pluginName":"@adobe/aio-cli-plugin-api-mesh","pluginType":"core","aliases":[],"flags":{"ignoreCache":{"name":"ignoreCache","type":"boolean","char":"i","description":"Ignore cache and force manual org -> project -> workspace selection","allowNo":false},"autoConfirmAction":{"name":"autoConfirmAction","type":"boolean","char":"c","description":"Auto confirm action prompt. CLI will not check for user approval before executing the action.","allowNo":false}},"args":[{"name":"file"}]},"api-mesh:delete":{"id":"api-mesh:delete","description":"Delete the config of a given mesh","pluginName":"@adobe/aio-cli-plugin-api-mesh","pluginType":"core","aliases":[],"flags":{"ignoreCache":{"name":"ignoreCache","type":"boolean","char":"i","description":"Ignore cache and force manual org -> project -> workspace selection","allowNo":false},"autoConfirmAction":{"name":"autoConfirmAction","type":"boolean","char":"c","description":"Auto confirm action prompt. CLI will not check for user approval before executing the action.","allowNo":false}},"args":[]},"api-mesh:describe":{"id":"api-mesh:describe","description":"Get details of a mesh","pluginName":"@adobe/aio-cli-plugin-api-mesh","pluginType":"core","aliases":[],"flags":{"ignoreCache":{"name":"ignoreCache","type":"boolean","char":"i","description":"Ignore cache and force manual org -> project -> workspace selection","allowNo":false}},"args":[]},"api-mesh:get":{"id":"api-mesh:get","description":"Get the config of a given mesh","pluginName":"@adobe/aio-cli-plugin-api-mesh","pluginType":"core","aliases":[],"flags":{"ignoreCache":{"name":"ignoreCache","type":"boolean","char":"i","description":"Ignore cache and force manual org -> project -> workspace selection","allowNo":false}},"args":[{"name":"file"}]},"api-mesh:update":{"id":"api-mesh:update","description":"Update a mesh with the given config.","pluginName":"@adobe/aio-cli-plugin-api-mesh","pluginType":"core","aliases":[],"flags":{"ignoreCache":{"name":"ignoreCache","type":"boolean","char":"i","description":"Ignore cache and force manual org -> project -> workspace selection","allowNo":false},"autoConfirmAction":{"name":"autoConfirmAction","type":"boolean","char":"c","description":"Auto confirm action prompt. CLI will not check for user approval before executing the action.","allowNo":false}},"args":[{"name":"file"}]},"api-mesh:source:discover":{"id":"api-mesh:source:discover","description":"Return the list of avaliable sources","pluginName":"@adobe/aio-cli-plugin-api-mesh","pluginType":"core","aliases":[],"flags":{},"args":[]},"api-mesh:source:get":{"id":"api-mesh:source:get","description":"Command returns the content of a specific source.","pluginName":"@adobe/aio-cli-plugin-api-mesh","pluginType":"core","aliases":[],"examples":["$ aio api-mesh:source:get <version>@<source_name>","$ aio api-mesh:source:get <source_name>","$ aio api-mesh:source:get -m"],"flags":{"source":{"name":"source","type":"option","char":"s","description":"Source name"},"multiple":{"name":"multiple","type":"boolean","char":"m","description":"Select multiple sources","allowNo":false}},"args":[]}}}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adobe/aio-cli-plugin-api-mesh",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -15,10 +15,13 @@
|
|
|
15
15
|
"@oclif/core": "^1.14.1",
|
|
16
16
|
"@oclif/errors": "^1.1.2",
|
|
17
17
|
"axios": "^0.23.0",
|
|
18
|
+
"chalk": "^4.1.0",
|
|
18
19
|
"dotenv": "^16.0.1",
|
|
19
20
|
"inquirer": "^8.2.4",
|
|
21
|
+
"node-clipboardy": "^1.0.3",
|
|
20
22
|
"pino": "^7.9.2",
|
|
21
23
|
"pino-pretty": "^7.6.0",
|
|
24
|
+
"source-registry-storage-adapter": "github:devx-services/source-registry-storage-adapter#main",
|
|
22
25
|
"uuid": "^8.3.2"
|
|
23
26
|
},
|
|
24
27
|
"devDependencies": {
|
|
@@ -28,7 +31,6 @@
|
|
|
28
31
|
"@oclif/plugin-help": "2.2.3",
|
|
29
32
|
"@trivago/prettier-plugin-sort-imports": "^3.1.1",
|
|
30
33
|
"acorn": "7.4.1",
|
|
31
|
-
"chalk": "4.1.2",
|
|
32
34
|
"eslint": "7.28.0",
|
|
33
35
|
"eslint-config-oclif": "^3.1.0",
|
|
34
36
|
"eslint-config-prettier": "^8.3.0",
|
|
@@ -89,26 +89,19 @@ describe('describe command tests', () => {
|
|
|
89
89
|
});
|
|
90
90
|
|
|
91
91
|
test('should error if describe api has failed', async () => {
|
|
92
|
-
describeMesh.mockRejectedValueOnce(new Error('
|
|
92
|
+
describeMesh.mockRejectedValueOnce(new Error('Describe api failed'));
|
|
93
93
|
|
|
94
94
|
const runResult = DescribeCommand.run();
|
|
95
95
|
|
|
96
96
|
await expect(runResult).rejects.toEqual(
|
|
97
97
|
new Error(
|
|
98
|
-
'
|
|
98
|
+
'Describe api failed Please check the details and try again. If the error persists please contact support. RequestId: dummy_request_id',
|
|
99
99
|
),
|
|
100
100
|
);
|
|
101
|
-
expect(logSpy.mock.calls).toMatchInlineSnapshot(`
|
|
102
|
-
Array [
|
|
103
|
-
Array [
|
|
104
|
-
"describe api failed",
|
|
105
|
-
],
|
|
106
|
-
]
|
|
107
|
-
`);
|
|
108
101
|
expect(errorLogSpy.mock.calls).toMatchInlineSnapshot(`
|
|
109
102
|
Array [
|
|
110
103
|
Array [
|
|
111
|
-
"
|
|
104
|
+
"Describe api failed Please check the details and try again. If the error persists please contact support. RequestId: dummy_request_id",
|
|
112
105
|
],
|
|
113
106
|
]
|
|
114
107
|
`);
|
|
@@ -124,13 +117,6 @@ describe('describe command tests', () => {
|
|
|
124
117
|
'Unable to get mesh details. Please check the details and try again. If the error persists please contact support. RequestId: dummy_request_id',
|
|
125
118
|
),
|
|
126
119
|
);
|
|
127
|
-
expect(logSpy.mock.calls).toMatchInlineSnapshot(`
|
|
128
|
-
Array [
|
|
129
|
-
Array [
|
|
130
|
-
"Unable to get mesh details",
|
|
131
|
-
],
|
|
132
|
-
]
|
|
133
|
-
`);
|
|
134
120
|
expect(errorLogSpy.mock.calls).toMatchInlineSnapshot(`
|
|
135
121
|
Array [
|
|
136
122
|
Array [
|
|
@@ -68,13 +68,15 @@ class DescribeCommand extends Command {
|
|
|
68
68
|
);
|
|
69
69
|
}
|
|
70
70
|
} else {
|
|
71
|
-
throw new Error(`Unable to get mesh details
|
|
71
|
+
throw new Error(`Unable to get mesh details.`);
|
|
72
72
|
}
|
|
73
73
|
} catch (error) {
|
|
74
|
-
this.log(error.message);
|
|
75
|
-
|
|
76
74
|
this.error(
|
|
77
|
-
|
|
75
|
+
`${
|
|
76
|
+
error.message || 'Unable to get mesh details.'
|
|
77
|
+
} Please check the details and try again. If the error persists please contact support. RequestId: ${
|
|
78
|
+
global.requestId
|
|
79
|
+
}`,
|
|
78
80
|
);
|
|
79
81
|
}
|
|
80
82
|
}
|
|
@@ -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;
|
package/src/helpers.js
CHANGED
|
@@ -26,49 +26,103 @@ const { objToString } = require('./utils');
|
|
|
26
26
|
|
|
27
27
|
const { DEV_CONSOLE_BASE_URL, DEV_CONSOLE_API_KEY, AIO_CLI_API_KEY } = CONSTANTS;
|
|
28
28
|
|
|
29
|
+
async function getDevConsoleConfigFromFile(configFilePath) {
|
|
30
|
+
try {
|
|
31
|
+
if (!fs.existsSync(configFilePath)) {
|
|
32
|
+
throw new Error(
|
|
33
|
+
`Config file does not exist. Please run the command: aio config:set api-mesh.configPath <path_to_json_file> with a valid file.`,
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const data = JSON.parse(fs.readFileSync(configFilePath, { encoding: 'utf8', flag: 'r' }));
|
|
38
|
+
|
|
39
|
+
if (!data.baseUrl || !data.apiKey) {
|
|
40
|
+
throw new Error(
|
|
41
|
+
'Invalid config file. Please validate the file contents and try again. Config file must contain baseUrl and apiKey.',
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const baseUrl = data.baseUrl.endsWith('/')
|
|
46
|
+
? data.baseUrl.slice(0, data.baseUrl.length - 1)
|
|
47
|
+
: data.baseUrl;
|
|
48
|
+
|
|
49
|
+
const config = {
|
|
50
|
+
baseUrl: baseUrl,
|
|
51
|
+
accessToken: (await getLibConsoleCLI()).accessToken,
|
|
52
|
+
apiKey: data.apiKey,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
logger.debug(`Using cli config from ${configFilePath}: ${objToString(config)}`);
|
|
56
|
+
|
|
57
|
+
return config;
|
|
58
|
+
} catch (error) {
|
|
59
|
+
logger.error(
|
|
60
|
+
'Please run the command: aio config:set api-mesh.configPath <path_to_json_file> with a valid config file.',
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
throw new Error(error);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function getDevConsoleConfigFromObject(configObject) {
|
|
68
|
+
const { baseUrl, apiKey } = configObject;
|
|
69
|
+
const config = {
|
|
70
|
+
baseUrl,
|
|
71
|
+
accessToken: (await getLibConsoleCLI()).accessToken,
|
|
72
|
+
apiKey,
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
logger.debug(`Using cli config: ${objToString(config)}`);
|
|
76
|
+
|
|
77
|
+
return config;
|
|
78
|
+
}
|
|
79
|
+
|
|
29
80
|
/**
|
|
30
81
|
* @returns {any} Returns a config object or null
|
|
31
82
|
*/
|
|
32
83
|
async function getDevConsoleConfig() {
|
|
33
|
-
const
|
|
84
|
+
const configFileOrObject = Config.get('api-mesh.cliConfig');
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Old legacy option, needs to be deprecated
|
|
88
|
+
*/
|
|
89
|
+
const configPath = Config.get('api-mesh.configPath');
|
|
90
|
+
if (configPath) {
|
|
91
|
+
if (configFileOrObject) {
|
|
92
|
+
throw new Error(
|
|
93
|
+
'Found both cliConfig and configPath in api-mesh config. Please consider using only cliConfig.',
|
|
94
|
+
);
|
|
95
|
+
} else {
|
|
96
|
+
console.warn(
|
|
97
|
+
'Please consider using cliConfig instead of configPath on api-mesh config. configPath will be deprecated soon.',
|
|
98
|
+
);
|
|
99
|
+
logger.warn(
|
|
100
|
+
'Please consider using cliConfig instead of configPath on api-mesh config. configPath will be deprecated soon.',
|
|
101
|
+
);
|
|
34
102
|
|
|
35
|
-
|
|
36
|
-
|
|
103
|
+
return await getDevConsoleConfigFromFile(configPath);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (!configFileOrObject) {
|
|
108
|
+
const config = {
|
|
37
109
|
baseUrl: DEV_CONSOLE_BASE_URL,
|
|
38
110
|
accessToken: (await getLibConsoleCLI()).accessToken,
|
|
39
111
|
apiKey: DEV_CONSOLE_API_KEY,
|
|
40
112
|
};
|
|
41
|
-
} else {
|
|
42
|
-
try {
|
|
43
|
-
if (!fs.existsSync(configFile)) {
|
|
44
|
-
throw new Error(
|
|
45
|
-
`Config file does not exist. Please run the command: aio config:set api-mesh.configPath <path_to_json_file> with a valid file.`,
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const data = JSON.parse(fs.readFileSync(configFile, { encoding: 'utf8', flag: 'r' }));
|
|
50
|
-
|
|
51
|
-
if (!data.baseUrl || !data.apiKey) {
|
|
52
|
-
throw new Error(
|
|
53
|
-
'Invalid config file. Please validate the file contents and try again. Config file must contain baseUrl and apiKey.',
|
|
54
|
-
);
|
|
55
|
-
}
|
|
56
113
|
|
|
57
|
-
|
|
58
|
-
? data.baseUrl.slice(0, data.baseUrl.length - 1)
|
|
59
|
-
: data.baseUrl;
|
|
114
|
+
logger.debug(`No cli config found. Using defaults: ${objToString(config)}`);
|
|
60
115
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
116
|
+
return config;
|
|
117
|
+
} else {
|
|
118
|
+
if (typeof configFileOrObject === 'object') {
|
|
119
|
+
return getDevConsoleConfigFromObject(configFileOrObject);
|
|
120
|
+
} else if (typeof configFileOrObject === 'string') {
|
|
121
|
+
return await getDevConsoleConfigFromFile(configFileOrObject);
|
|
122
|
+
} else {
|
|
123
|
+
throw new Error(
|
|
124
|
+
'Invalid config. Please validate and try again. Config should be a JSON object or a JSON file with baseUrl and apiKey.',
|
|
69
125
|
);
|
|
70
|
-
|
|
71
|
-
throw new Error(error);
|
|
72
126
|
}
|
|
73
127
|
}
|
|
74
128
|
}
|
|
@@ -100,7 +154,6 @@ async function getAuthorizedOrganization() {
|
|
|
100
154
|
// remove selected project and workspace from config and let the user select a new one
|
|
101
155
|
Config.delete('console.project');
|
|
102
156
|
Config.delete('console.workspace');
|
|
103
|
-
|
|
104
157
|
return Object.assign({}, selectedOrg);
|
|
105
158
|
} else {
|
|
106
159
|
logger.error(`No organizations found`);
|
|
@@ -113,6 +166,10 @@ async function getAuthorizedOrganization() {
|
|
|
113
166
|
}
|
|
114
167
|
}
|
|
115
168
|
|
|
169
|
+
/**
|
|
170
|
+
* @param imsOrgId
|
|
171
|
+
* @param imsOrgTitle
|
|
172
|
+
*/
|
|
116
173
|
async function getProject(imsOrgId, imsOrgTitle) {
|
|
117
174
|
logger.info(`Initializing project selection for ${imsOrgId}`);
|
|
118
175
|
|
|
@@ -153,6 +210,12 @@ async function getProject(imsOrgId, imsOrgTitle) {
|
|
|
153
210
|
}
|
|
154
211
|
}
|
|
155
212
|
|
|
213
|
+
/**
|
|
214
|
+
* @param orgId
|
|
215
|
+
* @param projectId
|
|
216
|
+
* @param imsOrgTitle
|
|
217
|
+
* @param projectTitle
|
|
218
|
+
*/
|
|
156
219
|
async function getWorkspace(orgId, projectId, imsOrgTitle, projectTitle) {
|
|
157
220
|
logger.info(`Initializing workspace selection for ${orgId} -> ${projectId}`);
|
|
158
221
|
|
|
@@ -331,10 +394,52 @@ async function promptConfirm(message) {
|
|
|
331
394
|
return confirm.res;
|
|
332
395
|
}
|
|
333
396
|
|
|
397
|
+
/**
|
|
398
|
+
* Function to run the CLI selectable list
|
|
399
|
+
*
|
|
400
|
+
* @param {string} message - prompt message
|
|
401
|
+
* @param {object[]} choices - list of options
|
|
402
|
+
* @returns {object[]} - selected options
|
|
403
|
+
*/
|
|
404
|
+
async function promptMultiselect(message, choices) {
|
|
405
|
+
const selected = await inquirer.prompt([
|
|
406
|
+
{
|
|
407
|
+
name: 'items',
|
|
408
|
+
message: message,
|
|
409
|
+
type: 'checkbox',
|
|
410
|
+
choices: choices,
|
|
411
|
+
},
|
|
412
|
+
]);
|
|
413
|
+
|
|
414
|
+
return selected.items;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Function to run the CLI selectable list
|
|
419
|
+
*
|
|
420
|
+
* @param {string} message - prompt message
|
|
421
|
+
* @param {object[]} choices - list of options
|
|
422
|
+
* @returns {object[]} - selected options
|
|
423
|
+
*/
|
|
424
|
+
async function promptSelect(message, choices) {
|
|
425
|
+
const selected = await inquirer.prompt([
|
|
426
|
+
{
|
|
427
|
+
name: 'item',
|
|
428
|
+
message: message,
|
|
429
|
+
type: 'list',
|
|
430
|
+
choices: choices,
|
|
431
|
+
},
|
|
432
|
+
]);
|
|
433
|
+
|
|
434
|
+
return selected.item;
|
|
435
|
+
}
|
|
436
|
+
|
|
334
437
|
module.exports = {
|
|
335
438
|
promptConfirm,
|
|
336
439
|
getLibConsoleCLI,
|
|
337
440
|
getDevConsoleConfig,
|
|
338
441
|
initSdk,
|
|
339
442
|
initRequestId,
|
|
443
|
+
promptSelect,
|
|
444
|
+
promptMultiselect,
|
|
340
445
|
};
|