@nzz/q-cli 1.6.0 → 1.8.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 +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/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/package-lock.json +3969 -0
- package/skeletons/et-utils-package-skeleton/package.json +34 -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 +19 -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
|
+
};
|