@nzz/q-cli 1.6.1 → 1.8.1
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +93 -20
- package/bin/commands/bootstrap.js +34 -14
- package/bin/commands/qItem/configStore.js +142 -0
- package/bin/commands/qItem/copyItem/copyItem.js +103 -0
- package/bin/commands/qItem/copyItem/copySchema.json +37 -0
- package/bin/commands/qItem/helpers.js +102 -0
- package/bin/commands/qItem/itemService.js +302 -0
- package/bin/commands/{updateItem/resourcesHelpers.js → qItem/resourcesService.js} +0 -102
- package/bin/commands/qItem/schemaService.js +64 -0
- package/bin/commands/{updateItem → qItem/updateItem}/updateItem.js +10 -5
- package/bin/commands/{updateItem/schema.json → qItem/updateItem/updateSchema.json} +0 -0
- package/bin/q.js +65 -4
- package/package.json +1 -1
- package/skeletons/custom-code-skeleton/tsconfig.json +12 -12
- package/skeletons/et-utils-package-skeleton/.nvmrc +1 -0
- package/skeletons/et-utils-package-skeleton/README.md +7 -0
- package/skeletons/et-utils-package-skeleton/jest.config.ts +17 -0
- package/skeletons/et-utils-package-skeleton/package-lock.json +3969 -0
- package/skeletons/et-utils-package-skeleton/package.json +37 -0
- package/skeletons/et-utils-package-skeleton/rollup.config.js +17 -0
- package/skeletons/et-utils-package-skeleton/src/Service.ts +8 -0
- package/skeletons/et-utils-package-skeleton/src/index.ts +4 -0
- package/skeletons/et-utils-package-skeleton/test/Service.spec.ts +10 -0
- package/skeletons/et-utils-package-skeleton/test/tsconfig.json +5 -0
- package/skeletons/et-utils-package-skeleton/tsconfig.json +9 -0
- package/bin/commands/updateItem/helpers.js +0 -440
package/README.md
CHANGED
@@ -164,7 +164,29 @@ Q new-custom-code my-project-name
|
|
164
164
|
Q new-custom-code my-project-name -d my-project-directory
|
165
165
|
```
|
166
166
|
|
167
|
-
###
|
167
|
+
### Creating new ed-tech utility package project
|
168
|
+
|
169
|
+
Once `Q` cli is installed one can create the skeleton of a new ed-tech utility package project by executing
|
170
|
+
|
171
|
+
```bash
|
172
|
+
Q new-et-utils-package package-name package-author package-description
|
173
|
+
```
|
174
|
+
|
175
|
+
- The directory name where the new ed-tech utility package project is being created defaults to the project name and can be overwritten by using option `-d` or `--dir`
|
176
|
+
|
177
|
+
```bash
|
178
|
+
Q new-et-utils-package package-name -d my-project-directory
|
179
|
+
```
|
180
|
+
|
181
|
+
#### Notes
|
182
|
+
|
183
|
+
New utility package projects should only be created inside the [ed-tech-utilities](https://github.com/nzzdev/ed-tech-utilities) repository.
|
184
|
+
|
185
|
+
### Q item actions
|
186
|
+
|
187
|
+
The `Q` cli can copy and/or update existing Q items.
|
188
|
+
|
189
|
+
#### Updating existing Q items
|
168
190
|
|
169
191
|
Once `Q` cli installed one can update one or many Q items by executing:
|
170
192
|
|
@@ -193,63 +215,116 @@ Q update-item -r
|
|
193
215
|
- Credentials can be provided as environment variables to avoid user prompts. The variable names are `Q_ENV_SERVER`, `Q_ENV_USERNAME`, `Q_ENV_PASSWORD`, `Q_ENV_ACCESSTOKEN`, where `ENV` is the uppercase version of the environment name.
|
194
216
|
|
195
217
|
```bash
|
196
|
-
Q_TEST_SERVER=
|
218
|
+
Q_TEST_SERVER=[server_route] Q_TEST_USERNAME=[username] Q_TEST_PASSWORD=[password] Q update-item
|
197
219
|
```
|
198
220
|
|
199
221
|
or
|
200
222
|
|
201
223
|
```bash
|
202
|
-
Q_TEST_SERVER=
|
224
|
+
Q_TEST_SERVER=[server_route] Q_TEST_ACCESSTOKEN=[accessToken] Q update-item
|
203
225
|
```
|
204
226
|
|
205
|
-
|
206
|
-
|
207
|
-
The config file has to follow [this json-schema](./bin/commands/updateItem/schema.json). Here an example:
|
227
|
+
The config file has to follow [this json-schema](./bin/commands/qItem/updateItem/updateSchema.json). This schema will be extended by the respective tool schema of your Q item.
|
228
|
+
Here's an example:
|
208
229
|
|
209
230
|
```json
|
210
231
|
{
|
211
232
|
"items": [
|
212
233
|
{
|
213
234
|
"environments": [
|
235
|
+
// "environments" references the desired q items to be updated, at least 1 environment is required
|
214
236
|
{
|
215
237
|
"name": "production",
|
216
|
-
"id": "6dcf203a5c5f74b61aeea0cb0eef7e0b"
|
238
|
+
"id": "6dcf203a5c5f74b61aeea0cb0eef7e0b" // Id of your q item in the production environment
|
217
239
|
},
|
218
240
|
{
|
219
241
|
"name": "staging",
|
220
|
-
"id": "6dcf203a5c5f74b61aeea0cb0ef2ca9f"
|
242
|
+
"id": "6dcf203a5c5f74b61aeea0cb0ef2ca9f" // Id of your q item in the staging environment
|
221
243
|
}
|
222
244
|
],
|
223
245
|
"item": {
|
246
|
+
// The actual content you want to update for your referenced q items listed in "environments"
|
224
247
|
"title": "Der Konsum in der Schweiz springt wieder an",
|
225
248
|
"subtitle": "Wöchentliche Ausgaben mittels Bankkarten in Mio. Fr. im Jahr 2020, zum Vergleich 2019",
|
226
249
|
"data": [
|
250
|
+
// "data" represents the data table of your q item inside the q-editor
|
227
251
|
["Datum", "2020", "2019"],
|
228
252
|
["2020-01-06", "690004302", "641528028"],
|
229
253
|
["2020-01-13", "662122373", "617653790"],
|
230
254
|
["2020-01-20", "688208667", "654303249"]
|
231
255
|
]
|
232
256
|
}
|
233
|
-
}
|
257
|
+
}
|
258
|
+
]
|
259
|
+
}
|
260
|
+
```
|
261
|
+
|
262
|
+
#### Copy existing Q items
|
263
|
+
|
264
|
+
Once `Q` cli installed one can copy one or many Q items by executing:
|
265
|
+
|
266
|
+
```bash
|
267
|
+
Q copy-item
|
268
|
+
```
|
269
|
+
|
270
|
+
- The path to the config file can be set by using option `-c` or `--config`. By default the `copy-item` command will look for a config file called `q.config.json` in the current directory
|
271
|
+
|
272
|
+
```bash
|
273
|
+
Q copy-item -c [path]
|
274
|
+
```
|
275
|
+
|
276
|
+
- Items of a specified environment can be updated by using the option `-e` or `--environment`. By default the `copy-item` command updates all item specified in the config file
|
277
|
+
|
278
|
+
```bash
|
279
|
+
Q copy-item -e [env]
|
280
|
+
```
|
281
|
+
|
282
|
+
- Stored configuration properties like Q-Server url or access tokens can be reset by using option `-r` or `--reset`
|
283
|
+
|
284
|
+
```bash
|
285
|
+
Q copy-item -r
|
286
|
+
```
|
287
|
+
|
288
|
+
- Credentials can be provided as environment variables to avoid user prompts. The variable names are `Q_ENV_SERVER`, `Q_ENV_USERNAME`, `Q_ENV_PASSWORD`, `Q_ENV_ACCESSTOKEN`, where `ENV` is the uppercase version of the environment name.
|
289
|
+
|
290
|
+
```bash
|
291
|
+
Q_TEST_SERVER=[server_route] Q_TEST_USERNAME=[username] Q_TEST_PASSWORD=[password] Q update-item
|
292
|
+
```
|
293
|
+
|
294
|
+
or
|
295
|
+
|
296
|
+
```bash
|
297
|
+
Q_TEST_SERVER=[server_route] Q_TEST_ACCESSTOKEN=[accessToken] Q update-item
|
298
|
+
```
|
299
|
+
|
300
|
+
The config file has to follow [this json-schema](./bin/commands/qItem/updateItem/updateSchema.json). This schema will be extended by the respective tool schema of your Q item.
|
301
|
+
Here's an example:
|
302
|
+
|
303
|
+
```json
|
304
|
+
{
|
305
|
+
"items": [
|
234
306
|
{
|
235
307
|
"environments": [
|
236
308
|
{
|
237
309
|
"name": "production",
|
238
|
-
"id": "
|
310
|
+
"id": "6dcf203a5c5f74b61aeea0cb0eef7e0b" // Id of your q item in the production environment
|
239
311
|
},
|
240
312
|
{
|
241
313
|
"name": "staging",
|
242
|
-
"id": "
|
314
|
+
"id": "6dcf203a5c5f74b61aeea0cb0ef2ca9f" // Id of your q item in the staging environment
|
243
315
|
}
|
244
316
|
],
|
245
317
|
"item": {
|
246
|
-
"title": "
|
247
|
-
"subtitle": "
|
248
|
-
"
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
318
|
+
"title": "Russische Angriffe auf die Ukraine",
|
319
|
+
"subtitle": "Verzeichnete Angriffe in der ganzen Ukraine",
|
320
|
+
"files": [
|
321
|
+
// Adds or overwrites the listed files in your q item
|
322
|
+
{
|
323
|
+
"loadSyncBeforeInit": false, // Has to be set for the file upload to work
|
324
|
+
"file": {
|
325
|
+
"path": "./angriffsFlaechen.json" // Your local path to your file. The path is relative to where you execute the command.
|
326
|
+
}
|
327
|
+
}
|
253
328
|
]
|
254
329
|
}
|
255
330
|
}
|
@@ -257,8 +332,6 @@ The config file has to follow [this json-schema](./bin/commands/updateItem/schem
|
|
257
332
|
}
|
258
333
|
```
|
259
334
|
|
260
|
-
The configuration object has a property `items` which contains an object for each Q item. A Q item has a property `environments` and `item`. The `environments` array contains an objects with properties `name` and `id` for each environment the item is deployed on. The `item` contains the data of the Q item. The structure of the item can vary between each graphic type (chart, map, table ect.).
|
261
|
-
|
262
335
|
[to the top](#table-of-contents)
|
263
336
|
|
264
337
|
## License
|
@@ -2,11 +2,19 @@ const fs = require("fs-extra");
|
|
2
2
|
const path = require("path");
|
3
3
|
const replaceInFile = require("replace-in-file");
|
4
4
|
const chalk = require("chalk");
|
5
|
+
const { replace } = require("nunjucks/src/filters");
|
5
6
|
const errorColor = chalk.red;
|
6
7
|
const successColor = chalk.green;
|
7
8
|
const warningColor = chalk.yellow;
|
8
9
|
|
9
|
-
|
10
|
+
/**
|
11
|
+
*
|
12
|
+
* @param {string} type - Skeleton type
|
13
|
+
* @param {string} name - Name of the project
|
14
|
+
* @param {string} basedir - Base directory name to be created
|
15
|
+
* @param {Array.<{regex: RegExp, replaceWith: string}>} textReplacements
|
16
|
+
*/
|
17
|
+
module.exports = async function (type, basedir, textReplacements) {
|
10
18
|
if (fs.existsSync(basedir)) {
|
11
19
|
console.error(
|
12
20
|
errorColor(`directory ${basedir} already exists or is not writable`)
|
@@ -16,34 +24,46 @@ module.exports = async function (type, name, basedir) {
|
|
16
24
|
fs.mkdirSync(basedir);
|
17
25
|
}
|
18
26
|
|
19
|
-
const replaceOptions = {
|
20
|
-
files: `${basedir}/**`,
|
21
|
-
from: new RegExp(`${type}-skeleton`, "g"),
|
22
|
-
to: name,
|
23
|
-
glob: {
|
24
|
-
dot: true, // Include file names starting with a dot
|
25
|
-
},
|
26
|
-
};
|
27
|
-
|
28
27
|
try {
|
29
28
|
await fs.copySync(
|
30
29
|
path.join(__dirname, `../../skeletons/${type}-skeleton`),
|
31
30
|
basedir
|
32
31
|
);
|
33
|
-
|
32
|
+
|
33
|
+
if (textReplacements) {
|
34
|
+
for (const txtRe of textReplacements) {
|
35
|
+
await replaceText(txtRe.regex, txtRe.replaceWith, basedir);
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
34
39
|
console.log(successColor(`Q ${type} is now bootstrapped in ${basedir}`));
|
35
40
|
|
36
|
-
if (type === "tool")
|
41
|
+
if (type === "tool" || type === "et-utils-package")
|
37
42
|
console.log(
|
38
|
-
warningColor(
|
43
|
+
warningColor(
|
44
|
+
"Search for 'TODO' inside the new tool/package to get started!"
|
45
|
+
)
|
39
46
|
);
|
40
47
|
} catch (error) {
|
41
48
|
console.error(
|
42
49
|
errorColor(
|
43
|
-
`An unexpected error
|
50
|
+
`An unexpected error occurred. Please check the entered information and try again. ${JSON.stringify(
|
44
51
|
error
|
45
52
|
)}`
|
46
53
|
)
|
47
54
|
);
|
48
55
|
}
|
49
56
|
};
|
57
|
+
|
58
|
+
async function replaceText(regex, replaceWith, basedir) {
|
59
|
+
const replaceOptions = {
|
60
|
+
files: `${basedir}/**`, // Replace in all files
|
61
|
+
from: regex,
|
62
|
+
to: replaceWith,
|
63
|
+
glob: {
|
64
|
+
dot: true, // Include file names starting with a dot
|
65
|
+
},
|
66
|
+
};
|
67
|
+
|
68
|
+
return await replaceInFile(replaceOptions);
|
69
|
+
}
|
@@ -0,0 +1,142 @@
|
|
1
|
+
const helpers = require("./helpers.js");
|
2
|
+
const promptly = require("promptly");
|
3
|
+
const Configstore = require("configstore");
|
4
|
+
const package = require("./../../../package.json");
|
5
|
+
const configStore = new Configstore(package.name, {});
|
6
|
+
|
7
|
+
async function setAuthenticationConfig(environment, qServer) {
|
8
|
+
const result = await authenticate(environment, qServer);
|
9
|
+
configStore.set(`${environment}.accessToken`, result.accessToken);
|
10
|
+
configStore.set(`${environment}.cookie`, result.cookie);
|
11
|
+
}
|
12
|
+
|
13
|
+
async function setupStore(qConfig, environmentFilter, reset) {
|
14
|
+
if (reset) {
|
15
|
+
configStore.clear();
|
16
|
+
}
|
17
|
+
for (const environment of helpers.getEnvironments(
|
18
|
+
qConfig,
|
19
|
+
environmentFilter
|
20
|
+
)) {
|
21
|
+
await setupConfigFromEnvVars(environment);
|
22
|
+
|
23
|
+
if (!configStore.get(`${environment}.qServer`)) {
|
24
|
+
const qServer = await promptly.prompt(
|
25
|
+
`Enter the Q-Server url for ${environment} environment: `,
|
26
|
+
{
|
27
|
+
validator: (qServer) => {
|
28
|
+
return new URL(qServer).toString();
|
29
|
+
},
|
30
|
+
retry: true,
|
31
|
+
}
|
32
|
+
);
|
33
|
+
configStore.set(`${environment}.qServer`, qServer);
|
34
|
+
}
|
35
|
+
|
36
|
+
const qServer = configStore.get(`${environment}.qServer`);
|
37
|
+
if (!configStore.get(`${environment}.accessToken`)) {
|
38
|
+
await setAuthenticationConfig(environment, qServer);
|
39
|
+
}
|
40
|
+
|
41
|
+
const accessToken = configStore.get(`${environment}.accessToken`);
|
42
|
+
const cookie = configStore.get(`${environment}.cookie`);
|
43
|
+
const isAccessTokenValid = await helpers.checkValidityOfAccessToken(
|
44
|
+
environment,
|
45
|
+
qServer,
|
46
|
+
accessToken,
|
47
|
+
cookie
|
48
|
+
);
|
49
|
+
|
50
|
+
// Get a new access token in case its not valid anymore
|
51
|
+
if (!isAccessTokenValid) {
|
52
|
+
await setAuthenticationConfig(environment, qServer);
|
53
|
+
}
|
54
|
+
}
|
55
|
+
|
56
|
+
return configStore;
|
57
|
+
}
|
58
|
+
|
59
|
+
async function setupConfigFromEnvVars(environment) {
|
60
|
+
const environmentPrefix = environment.toUpperCase();
|
61
|
+
|
62
|
+
const qServer = process.env[`Q_${environmentPrefix}_SERVER`];
|
63
|
+
if (qServer) {
|
64
|
+
configStore.set(`${environment}.qServer`, qServer);
|
65
|
+
}
|
66
|
+
const accessToken = process.env[`Q_${environmentPrefix}_ACCESSTOKEN`];
|
67
|
+
const username = process.env[`Q_${environmentPrefix}_USERNAME`];
|
68
|
+
const password = process.env[`Q_${environmentPrefix}_PASSWORD`];
|
69
|
+
if (qServer && accessToken) {
|
70
|
+
configStore.set(`${environment}.accessToken`, accessToken);
|
71
|
+
} else if (qServer && username && password) {
|
72
|
+
const cookie = configStore.get(`${environment}.cookie`);
|
73
|
+
const result = await helpers.getAccessToken(
|
74
|
+
environment,
|
75
|
+
qServer,
|
76
|
+
username,
|
77
|
+
password,
|
78
|
+
cookie
|
79
|
+
);
|
80
|
+
|
81
|
+
if (!result) {
|
82
|
+
console.error(
|
83
|
+
errorColor(
|
84
|
+
`A problem occured while authenticating to the ${environment} environment using environment variables. Please check your credentials and try again.`
|
85
|
+
)
|
86
|
+
);
|
87
|
+
process.exit(1);
|
88
|
+
}
|
89
|
+
|
90
|
+
configStore.set(`${environment}.accessToken`, result.accessToken);
|
91
|
+
configStore.set(`${environment}.cookie`, result.cookie);
|
92
|
+
}
|
93
|
+
}
|
94
|
+
|
95
|
+
async function authenticate(environment, qServer) {
|
96
|
+
let username = configStore.get(`${environment}.username`);
|
97
|
+
if (!username) {
|
98
|
+
username = await promptly.prompt(
|
99
|
+
`Enter your username on ${environment} environment: `,
|
100
|
+
{ validator: (username) => username.trim() }
|
101
|
+
);
|
102
|
+
configStore.set(`${environment}.username`, username);
|
103
|
+
}
|
104
|
+
|
105
|
+
const password = await promptly.password(
|
106
|
+
`Enter your password on ${environment} environment: `,
|
107
|
+
{
|
108
|
+
validator: async (password) => password.trim(),
|
109
|
+
replace: "*",
|
110
|
+
}
|
111
|
+
);
|
112
|
+
|
113
|
+
const cookie = configStore.get(`${environment}.cookie`);
|
114
|
+
let result = await helpers.getAccessToken(
|
115
|
+
environment,
|
116
|
+
qServer,
|
117
|
+
username,
|
118
|
+
password,
|
119
|
+
cookie
|
120
|
+
);
|
121
|
+
|
122
|
+
while (!result) {
|
123
|
+
console.error(
|
124
|
+
errorColor(
|
125
|
+
"A problem occured while authenticating. Please check your credentials and try again."
|
126
|
+
)
|
127
|
+
);
|
128
|
+
|
129
|
+
result = await authenticate(environment, qServer);
|
130
|
+
|
131
|
+
if (result.accessToken) {
|
132
|
+
break;
|
133
|
+
}
|
134
|
+
}
|
135
|
+
|
136
|
+
return result;
|
137
|
+
}
|
138
|
+
|
139
|
+
module.exports = {
|
140
|
+
store: configStore,
|
141
|
+
setupStore: setupStore,
|
142
|
+
};
|
@@ -0,0 +1,103 @@
|
|
1
|
+
const schemaService = require("./../schemaService.js");
|
2
|
+
const configStore = require("./../configStore.js");
|
3
|
+
const itemService = require("./../itemService.js");
|
4
|
+
const fs = require("fs");
|
5
|
+
const path = require("path");
|
6
|
+
const chalk = require("chalk");
|
7
|
+
const errorColor = chalk.red;
|
8
|
+
const successColor = chalk.green;
|
9
|
+
|
10
|
+
module.exports = async function (command) {
|
11
|
+
try {
|
12
|
+
const qConfigPath = path.resolve(command.config);
|
13
|
+
|
14
|
+
if (fs.existsSync(qConfigPath)) {
|
15
|
+
const qConfig = JSON.parse(fs.readFileSync(qConfigPath));
|
16
|
+
const validationResult = schemaService.validateConfig(
|
17
|
+
qConfig,
|
18
|
+
"copyItem"
|
19
|
+
);
|
20
|
+
|
21
|
+
if (validationResult.isValid) {
|
22
|
+
const config = await configStore.setupStore(
|
23
|
+
qConfig,
|
24
|
+
command.environment,
|
25
|
+
command.reset
|
26
|
+
);
|
27
|
+
|
28
|
+
for (const item of itemService.getItems(qConfig, command.environment)) {
|
29
|
+
for (const environment of item.environments) {
|
30
|
+
const qServer = config.get(`${environment.name}.qServer`);
|
31
|
+
const accessToken = config.get(`${environment.name}.accessToken`);
|
32
|
+
const cookie = config.get(`${environment.name}.cookie`);
|
33
|
+
|
34
|
+
const existingItem = await itemService.getItem(
|
35
|
+
qServer,
|
36
|
+
environment,
|
37
|
+
accessToken,
|
38
|
+
cookie
|
39
|
+
);
|
40
|
+
|
41
|
+
delete existingItem.updatedBy;
|
42
|
+
delete existingItem.createdBy;
|
43
|
+
delete existingItem.createdDate;
|
44
|
+
delete existingItem._id;
|
45
|
+
delete existingItem._rev;
|
46
|
+
|
47
|
+
let newItem = await itemService.createItem(
|
48
|
+
existingItem,
|
49
|
+
environment,
|
50
|
+
config
|
51
|
+
);
|
52
|
+
// Save for success message
|
53
|
+
const newItemId = newItem._id;
|
54
|
+
const existingItemId = environment.id;
|
55
|
+
|
56
|
+
const hasOverwrites =
|
57
|
+
item.item &&
|
58
|
+
Object.keys(item.item).length > 0 &&
|
59
|
+
Object.getPrototypeOf(item.item) === Object.prototype;
|
60
|
+
|
61
|
+
if (hasOverwrites) {
|
62
|
+
environment.id = newItemId;
|
63
|
+
|
64
|
+
newItem = await itemService.updateItem(
|
65
|
+
item.item,
|
66
|
+
environment,
|
67
|
+
config,
|
68
|
+
qConfigPath
|
69
|
+
);
|
70
|
+
}
|
71
|
+
|
72
|
+
if (newItem) {
|
73
|
+
console.log(
|
74
|
+
successColor(
|
75
|
+
`Successfully copied item with id ${existingItemId} on ${environment.name} environment. Copied item id ${newItemId}`
|
76
|
+
)
|
77
|
+
);
|
78
|
+
}
|
79
|
+
}
|
80
|
+
}
|
81
|
+
} else {
|
82
|
+
console.error(
|
83
|
+
errorColor(
|
84
|
+
`A problem occured while validating the config file: ${validationResult.errorsText}`
|
85
|
+
)
|
86
|
+
);
|
87
|
+
process.exit(1);
|
88
|
+
}
|
89
|
+
} else {
|
90
|
+
console.error(
|
91
|
+
errorColor(
|
92
|
+
"Couldn't find config file named q.config.json in the current directory. Create a config file in the current directory or pass the path to the config file with the option -c <path>"
|
93
|
+
)
|
94
|
+
);
|
95
|
+
}
|
96
|
+
} catch (error) {
|
97
|
+
console.error(
|
98
|
+
errorColor(
|
99
|
+
`A problem occured while parsing the config file at ${command.config}. Please make sure it is valid JSON.`
|
100
|
+
)
|
101
|
+
);
|
102
|
+
}
|
103
|
+
};
|
@@ -0,0 +1,37 @@
|
|
1
|
+
{
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
3
|
+
"title": "Q Config",
|
4
|
+
"description": "Config used by the Q CLI to copy items",
|
5
|
+
"type": "object",
|
6
|
+
"properties": {
|
7
|
+
"items": {
|
8
|
+
"description": "Array of Q items",
|
9
|
+
"type": "array",
|
10
|
+
"minItems": 1,
|
11
|
+
"items": {
|
12
|
+
"type": "object",
|
13
|
+
"properties": {
|
14
|
+
"environments": {
|
15
|
+
"type": "array",
|
16
|
+
"minItems": 1,
|
17
|
+
"items": {
|
18
|
+
"type": "object",
|
19
|
+
"properties": {
|
20
|
+
"id": {
|
21
|
+
"type": "string",
|
22
|
+
"description": "Id of Q item"
|
23
|
+
}
|
24
|
+
},
|
25
|
+
"required": ["id"]
|
26
|
+
}
|
27
|
+
},
|
28
|
+
"item": {
|
29
|
+
"type": "object"
|
30
|
+
}
|
31
|
+
},
|
32
|
+
"required": ["environments", "item"]
|
33
|
+
}
|
34
|
+
}
|
35
|
+
},
|
36
|
+
"required": ["items"]
|
37
|
+
}
|
@@ -0,0 +1,102 @@
|
|
1
|
+
const fetch = require("node-fetch");
|
2
|
+
const chalk = require("chalk");
|
3
|
+
const errorColor = chalk.red;
|
4
|
+
|
5
|
+
function getEnvironments(qConfig, environmentFilter) {
|
6
|
+
try {
|
7
|
+
const environments = new Set();
|
8
|
+
for (const item of qConfig.items) {
|
9
|
+
for (const environment of item.environments) {
|
10
|
+
if (environmentFilter) {
|
11
|
+
if (environmentFilter === environment.name) {
|
12
|
+
environments.add(environment.name);
|
13
|
+
}
|
14
|
+
} else {
|
15
|
+
environments.add(environment.name);
|
16
|
+
}
|
17
|
+
}
|
18
|
+
}
|
19
|
+
|
20
|
+
if (environments.size > 0) {
|
21
|
+
return Array.from(environments);
|
22
|
+
} else {
|
23
|
+
throw new Error(
|
24
|
+
`No items with environment ${environmentFilter} found. Please check your configuration and try again.`
|
25
|
+
);
|
26
|
+
}
|
27
|
+
} catch (error) {
|
28
|
+
console.error(errorColor(error.message));
|
29
|
+
process.exit(1);
|
30
|
+
}
|
31
|
+
}
|
32
|
+
|
33
|
+
async function getAccessToken(
|
34
|
+
environment,
|
35
|
+
qServer,
|
36
|
+
username,
|
37
|
+
password,
|
38
|
+
cookie
|
39
|
+
) {
|
40
|
+
try {
|
41
|
+
const response = await fetch(`${qServer}authenticate`, {
|
42
|
+
method: "POST",
|
43
|
+
headers: {
|
44
|
+
"user-agent": "Q Command-line Tool",
|
45
|
+
origin: qServer,
|
46
|
+
Cookie: cookie ? cookie : "",
|
47
|
+
},
|
48
|
+
body: JSON.stringify({
|
49
|
+
username: username,
|
50
|
+
password: password,
|
51
|
+
}),
|
52
|
+
});
|
53
|
+
|
54
|
+
if (response.ok) {
|
55
|
+
const body = await response.json();
|
56
|
+
return {
|
57
|
+
accessToken: body.access_token,
|
58
|
+
cookie: response.headers.get("set-cookie"),
|
59
|
+
};
|
60
|
+
}
|
61
|
+
|
62
|
+
return false;
|
63
|
+
} catch (error) {
|
64
|
+
console.error(
|
65
|
+
errorColor(
|
66
|
+
`A problem occured while authenticating on ${environment} environment. Please check your connection and try again.`
|
67
|
+
)
|
68
|
+
);
|
69
|
+
process.exit(1);
|
70
|
+
}
|
71
|
+
}
|
72
|
+
|
73
|
+
async function checkValidityOfAccessToken(
|
74
|
+
environment,
|
75
|
+
qServer,
|
76
|
+
accessToken,
|
77
|
+
cookie
|
78
|
+
) {
|
79
|
+
try {
|
80
|
+
const response = await fetch(`${qServer}user`, {
|
81
|
+
headers: {
|
82
|
+
"user-agent": "Q Command-line Tool",
|
83
|
+
Authorization: `Bearer ${accessToken}`,
|
84
|
+
Cookie: cookie ? cookie : "",
|
85
|
+
},
|
86
|
+
});
|
87
|
+
return response.ok;
|
88
|
+
} catch (error) {
|
89
|
+
console.error(
|
90
|
+
errorColor(
|
91
|
+
`A problem occured while checking the validity of your access token on ${environment} environment. Please check your connection and try again.`
|
92
|
+
)
|
93
|
+
);
|
94
|
+
process.exit(1);
|
95
|
+
}
|
96
|
+
}
|
97
|
+
|
98
|
+
module.exports = {
|
99
|
+
getEnvironments: getEnvironments,
|
100
|
+
getAccessToken,
|
101
|
+
checkValidityOfAccessToken,
|
102
|
+
};
|