@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.
Files changed (26) hide show
  1. package/README.md +93 -20
  2. package/bin/commands/bootstrap.js +34 -14
  3. package/bin/commands/qItem/configStore.js +142 -0
  4. package/bin/commands/qItem/copyItem/copyItem.js +103 -0
  5. package/bin/commands/qItem/copyItem/copySchema.json +37 -0
  6. package/bin/commands/qItem/helpers.js +102 -0
  7. package/bin/commands/qItem/itemService.js +302 -0
  8. package/bin/commands/{updateItem/resourcesHelpers.js → qItem/resourcesService.js} +0 -102
  9. package/bin/commands/qItem/schemaService.js +64 -0
  10. package/bin/commands/{updateItem → qItem/updateItem}/updateItem.js +10 -5
  11. package/bin/commands/{updateItem/schema.json → qItem/updateItem/updateSchema.json} +0 -0
  12. package/bin/q.js +65 -4
  13. package/package.json +1 -1
  14. package/skeletons/custom-code-skeleton/tsconfig.json +12 -12
  15. package/skeletons/et-utils-package-skeleton/.nvmrc +1 -0
  16. package/skeletons/et-utils-package-skeleton/README.md +7 -0
  17. package/skeletons/et-utils-package-skeleton/jest.config.ts +17 -0
  18. package/skeletons/et-utils-package-skeleton/package-lock.json +3969 -0
  19. package/skeletons/et-utils-package-skeleton/package.json +37 -0
  20. package/skeletons/et-utils-package-skeleton/rollup.config.js +17 -0
  21. package/skeletons/et-utils-package-skeleton/src/Service.ts +8 -0
  22. package/skeletons/et-utils-package-skeleton/src/index.ts +4 -0
  23. package/skeletons/et-utils-package-skeleton/test/Service.spec.ts +10 -0
  24. package/skeletons/et-utils-package-skeleton/test/tsconfig.json +5 -0
  25. package/skeletons/et-utils-package-skeleton/tsconfig.json +9 -0
  26. package/bin/commands/updateItem/helpers.js +0 -440
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@nzz/et-utils-<package-name>",
3
+ "version": "1.0.0",
4
+ "description": "<package-description>",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/nzzdev/ed-tech-utilities.git",
8
+ "directory": "<package-name>"
9
+ },
10
+ "scripts": {
11
+ "prepublishOnly": "npm run test && npm run clean && npm run build && rollup -c",
12
+ "clean": "rimraf dist && rimraf index.js && rimraf index.d.ts",
13
+ "build": "tsc -p .",
14
+ "start": "npm run build -- -w",
15
+ "test": "jest --config jest.config.ts"
16
+ },
17
+ "files": [
18
+ "index.js",
19
+ "index.d.ts"
20
+ ],
21
+ "publishConfig": {
22
+ "access": "public"
23
+ },
24
+ "devDependencies": {
25
+ "@types/jest": "^27.5.1",
26
+ "@types/node": "^16.11.36",
27
+ "jest": "^28.1.0",
28
+ "rimraf": "^3.0.2",
29
+ "rollup": "^2.75.0",
30
+ "rollup-plugin-dts": "^4.2.2",
31
+ "ts-jest": "^28.0.3",
32
+ "ts-node": "^10.8.0",
33
+ "typescript": "^4.6.4"
34
+ },
35
+ "author": "<author-name>",
36
+ "license": "MIT"
37
+ }
@@ -0,0 +1,17 @@
1
+ import dts from "rollup-plugin-dts";
2
+
3
+ export default [
4
+ {
5
+ input: "dist/index.js",
6
+ output: {
7
+ file: "index.js",
8
+ },
9
+ },
10
+ {
11
+ input: "dist/index.d.ts",
12
+ output: {
13
+ file: "index.d.ts",
14
+ },
15
+ plugins: [dts()],
16
+ },
17
+ ];
@@ -0,0 +1,8 @@
1
+ // Note: This is an example file
2
+ function someFunction() {
3
+ const value = "Up, Up, Down, Down, Left, Right, Left, Right, B, A";
4
+
5
+ return value;
6
+ }
7
+
8
+ export { someFunction };
@@ -0,0 +1,4 @@
1
+ // Note: Export all your code in here, that should be exposed to the consumer
2
+
3
+ // e.g. Exports all exports of the Service.ts file
4
+ export * from "./Service";
@@ -0,0 +1,10 @@
1
+ import { someFunction } from "../src";
2
+
3
+ describe("Service", () => {
4
+ it("should return the konami cheatcode", () => {
5
+ const result = someFunction();
6
+ const expectedResult = "Up, Up, Down, Down, Left, Right, Left, Right, B, A";
7
+
8
+ expect(result).toEqual(expectedResult);
9
+ });
10
+ });
@@ -0,0 +1,5 @@
1
+ {
2
+ "compilerOptions": {
3
+ "types": ["jest", "node"]
4
+ }
5
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "include": ["src/**/*"],
3
+ "exclude": ["test/**/*"],
4
+ "compilerOptions": {
5
+ "target": "es6",
6
+ "outDir": "dist",
7
+ "declaration": true
8
+ }
9
+ }
@@ -1,440 +0,0 @@
1
- const fetch = require("node-fetch");
2
- const deepmerge = require("deepmerge");
3
- const Ajv = require("ajv");
4
- // Remove additional properties which are not defined by the json schema
5
- // See https://ajv.js.org/options.html#removeadditional for details
6
- const ajv = new Ajv({ schemaId: "auto", removeAdditional: true });
7
- ajv.addMetaSchema(require("ajv/lib/refs/json-schema-draft-04.json"));
8
- const promptly = require("promptly");
9
- const chalk = require("chalk");
10
- const errorColor = chalk.red;
11
- const Configstore = require("configstore");
12
- const package = require("../../../package.json");
13
- const config = new Configstore(package.name, {});
14
- const resourcesHelpers = require("./resourcesHelpers.js");
15
-
16
- async function updateItem(item, environment, config, qConfigPath) {
17
- const qServer = config.get(`${environment.name}.qServer`);
18
- const accessToken = config.get(`${environment.name}.accessToken`);
19
- const cookie = config.get(`${environment.name}.cookie`);
20
- const existingItem = await getItem(qServer, environment, accessToken, cookie);
21
- const updatedItem = await getUpdatedItem(
22
- qServer,
23
- accessToken,
24
- cookie,
25
- existingItem,
26
- item,
27
- environment,
28
- qConfigPath
29
- );
30
- return await saveItem(qServer, environment, accessToken, cookie, updatedItem);
31
- }
32
-
33
- async function getItem(qServer, environment, accessToken, cookie) {
34
- try {
35
- const response = await fetch(`${qServer}item/${environment.id}`, {
36
- headers: {
37
- "user-agent": "Q Command-line Tool",
38
- Authorization: `Bearer ${accessToken}`,
39
- Cookie: cookie ? cookie : "",
40
- },
41
- });
42
- if (response.ok) {
43
- return await response.json();
44
- } else {
45
- throw new Error(
46
- `A problem occured while getting item with id ${environment.id} on ${environment.name} environment. Please make sure that the id is correct, you have an internet connection and try again.`
47
- );
48
- }
49
- } catch (error) {
50
- console.error(errorColor(error.message));
51
- process.exit(1);
52
- }
53
- }
54
-
55
- async function getUpdatedItem(
56
- qServer,
57
- accessToken,
58
- cookie,
59
- existingItem,
60
- item,
61
- environment,
62
- qConfigPath
63
- ) {
64
- try {
65
- const toolSchema = await resourcesHelpers.getToolSchema(
66
- qServer,
67
- existingItem.tool
68
- );
69
- // Removes additional properties not defined in the schema on the top level object of the item
70
- toolSchema.additionalProperties = false;
71
- // If options object is available additional properties not defined in the schema are removed
72
- if (toolSchema.properties && toolSchema.properties.options) {
73
- toolSchema.properties.options.additionalProperties = false;
74
- }
75
- const defaultItem = resourcesHelpers.getDefaultItem(toolSchema);
76
- item = JSON.parse(JSON.stringify(item));
77
- item = await resourcesHelpers.handleResources(
78
- qServer,
79
- accessToken,
80
- cookie,
81
- item,
82
- defaultItem,
83
- qConfigPath,
84
- environment
85
- );
86
-
87
- // Merge options:
88
- // File of files property will be updated (if file exists on destination)
89
- // If it doesn't exist it is appended to the files array
90
- // All other properties are overwritten from source config
91
- const options = {
92
- arrayMerge: (destArr, srcArr) => srcArr,
93
- customMerge: (key) => {
94
- if (key === "files") {
95
- return (destArr, srcArr) => {
96
- if (destArr.length <= 0) {
97
- return srcArr;
98
- }
99
-
100
- srcArr.forEach((fileObj) => {
101
- let destIndex = destArr.findIndex(
102
- (destFileObj) =>
103
- destFileObj.file.originalName === fileObj.file.originalName
104
- );
105
-
106
- if (destIndex !== -1) {
107
- destArr[destIndex] = fileObj;
108
- } else {
109
- destArr.push(fileObj);
110
- }
111
- });
112
- return destArr;
113
- };
114
- }
115
- },
116
- };
117
-
118
- // merges existing item with the item defined in q.config.json
119
- const updatedItem = deepmerge(existingItem, item, options);
120
- // normalizes the item which removes additional properties not defined in the schema
121
- // and validates the item against the schema
122
- const normalizedItem = getNormalizedItem(
123
- toolSchema,
124
- updatedItem,
125
- environment
126
- );
127
- // the normalized item is merged with the existing item. This is done because properties such as _id and _rev
128
- // defined in the existing item are removed during normalization, because they are not defined in the schema
129
- return deepmerge(existingItem, normalizedItem, options);
130
- } catch (error) {
131
- console.error(errorColor(error.message));
132
- process.exit(1);
133
- }
134
- }
135
-
136
- async function saveItem(
137
- qServer,
138
- environment,
139
- accessToken,
140
- cookie,
141
- updatedItem
142
- ) {
143
- try {
144
- delete updatedItem.updatedDate;
145
- const response = await fetch(`${qServer}item`, {
146
- method: "PUT",
147
- body: JSON.stringify(updatedItem),
148
- headers: {
149
- "user-agent": "Q Command-line Tool",
150
- Authorization: `Bearer ${accessToken}`,
151
- "Content-Type": "application/json",
152
- Cookie: cookie ? cookie : "",
153
- },
154
- });
155
- if (response.ok) {
156
- return await response.json();
157
- } else {
158
- throw new Error(
159
- `A problem occured while saving item with id ${environment.id} on ${environment.name} environment. Please check your connection and try again.`
160
- );
161
- }
162
- } catch (error) {
163
- console.error(errorColor(error.message));
164
- process.exit(1);
165
- }
166
- }
167
-
168
- function getItems(qConfig, environmentFilter) {
169
- const items = qConfig.items
170
- .filter((item) => {
171
- if (environmentFilter) {
172
- return item.environments.some(
173
- (environment) => environment.name === environmentFilter
174
- );
175
- }
176
-
177
- return true;
178
- })
179
- .map((item) => {
180
- if (environmentFilter) {
181
- item.environments = item.environments.filter(
182
- (environment) => environment.name === environmentFilter
183
- );
184
- }
185
-
186
- return item;
187
- });
188
-
189
- return items;
190
- }
191
-
192
- function validateConfig(config) {
193
- const isValid = ajv.validate(require("./schema.json"), config);
194
- return {
195
- isValid: isValid,
196
- errorsText: ajv.errorsText(),
197
- };
198
- }
199
-
200
- function getNormalizedItem(schema, item, environment) {
201
- const isValid = ajv.validate(schema, item);
202
- if (!isValid) {
203
- throw new Error(
204
- `A problem occured while validating item with id ${environment.id} on ${
205
- environment.name
206
- } environment: ${ajv.errorsText()}`
207
- );
208
- }
209
-
210
- return item;
211
- }
212
-
213
- function getEnvironments(qConfig, environmentFilter) {
214
- try {
215
- const environments = new Set();
216
- for (const item of qConfig.items) {
217
- for (const environment of item.environments) {
218
- if (environmentFilter) {
219
- if (environmentFilter === environment.name) {
220
- environments.add(environment.name);
221
- }
222
- } else {
223
- environments.add(environment.name);
224
- }
225
- }
226
- }
227
-
228
- if (environments.size > 0) {
229
- return Array.from(environments);
230
- } else {
231
- throw new Error(
232
- `No items with environment ${environmentFilter} found. Please check your configuration and try again.`
233
- );
234
- }
235
- } catch (error) {
236
- console.error(errorColor(error.message));
237
- process.exit(1);
238
- }
239
- }
240
-
241
- async function setAuthenticationConfig(environment, qServer) {
242
- const result = await authenticate(environment, qServer);
243
- config.set(`${environment}.accessToken`, result.accessToken);
244
- config.set(`${environment}.cookie`, result.cookie);
245
- }
246
-
247
- async function setupConfig(qConfig, environmentFilter, reset) {
248
- if (reset) {
249
- config.clear();
250
- }
251
- for (const environment of getEnvironments(qConfig, environmentFilter)) {
252
- await setupConfigFromEnvVars(environment);
253
-
254
- if (!config.get(`${environment}.qServer`)) {
255
- const qServer = await promptly.prompt(
256
- `Enter the Q-Server url for ${environment} environment: `,
257
- {
258
- validator: (qServer) => {
259
- return new URL(qServer).toString();
260
- },
261
- retry: true,
262
- }
263
- );
264
- config.set(`${environment}.qServer`, qServer);
265
- }
266
-
267
- const qServer = config.get(`${environment}.qServer`);
268
- if (!config.get(`${environment}.accessToken`)) {
269
- await setAuthenticationConfig(environment, qServer);
270
- }
271
-
272
- const accessToken = config.get(`${environment}.accessToken`);
273
- const cookie = config.get(`${environment}.cookie`);
274
- const isAccessTokenValid = await checkValidityOfAccessToken(
275
- environment,
276
- qServer,
277
- accessToken,
278
- cookie
279
- );
280
-
281
- // Get a new access token in case its not valid anymore
282
- if (!isAccessTokenValid) {
283
- await setAuthenticationConfig(environment, qServer);
284
- }
285
- }
286
-
287
- return config;
288
- }
289
-
290
- async function setupConfigFromEnvVars(environment) {
291
- const environmentPrefix = environment.toUpperCase();
292
-
293
- const qServer = process.env[`Q_${environmentPrefix}_SERVER`];
294
- if (qServer) {
295
- config.set(`${environment}.qServer`, qServer);
296
- }
297
- const accessToken = process.env[`Q_${environmentPrefix}_ACCESSTOKEN`];
298
- const username = process.env[`Q_${environmentPrefix}_USERNAME`];
299
- const password = process.env[`Q_${environmentPrefix}_PASSWORD`];
300
- if (qServer && accessToken) {
301
- config.set(`${environment}.accessToken`, accessToken);
302
- } else if (qServer && username && password) {
303
- const cookie = config.get(`${environment}.cookie`);
304
- const result = await getAccessToken(
305
- environment,
306
- qServer,
307
- username,
308
- password,
309
- cookie
310
- );
311
-
312
- if (!result) {
313
- console.error(
314
- errorColor(
315
- `A problem occured while authenticating to the ${environment} environment using environment variables. Please check your credentials and try again.`
316
- )
317
- );
318
- process.exit(1);
319
- }
320
-
321
- config.set(`${environment}.accessToken`, result.accessToken);
322
- config.set(`${environment}.cookie`, result.cookie);
323
- }
324
- }
325
-
326
- async function authenticate(environment, qServer) {
327
- let username = config.get(`${environment}.username`);
328
- if (!username) {
329
- username = await promptly.prompt(
330
- `Enter your username on ${environment} environment: `,
331
- { validator: (username) => username.trim() }
332
- );
333
- config.set(`${environment}.username`, username);
334
- }
335
-
336
- const password = await promptly.password(
337
- `Enter your password on ${environment} environment: `,
338
- {
339
- validator: async (password) => password.trim(),
340
- replace: "*",
341
- }
342
- );
343
-
344
- const cookie = config.get(`${environment}.cookie`);
345
- let result = await getAccessToken(
346
- environment,
347
- qServer,
348
- username,
349
- password,
350
- cookie
351
- );
352
-
353
- while (!result) {
354
- console.error(
355
- errorColor(
356
- "A problem occured while authenticating. Please check your credentials and try again."
357
- )
358
- );
359
-
360
- result = await authenticate(environment, qServer);
361
-
362
- if (result.accessToken) {
363
- break;
364
- }
365
- }
366
-
367
- return result;
368
- }
369
-
370
- async function getAccessToken(
371
- environment,
372
- qServer,
373
- username,
374
- password,
375
- cookie
376
- ) {
377
- try {
378
- const response = await fetch(`${qServer}authenticate`, {
379
- method: "POST",
380
- headers: {
381
- "user-agent": "Q Command-line Tool",
382
- origin: qServer,
383
- Cookie: cookie ? cookie : "",
384
- },
385
- body: JSON.stringify({
386
- username: username,
387
- password: password,
388
- }),
389
- });
390
-
391
- if (response.ok) {
392
- const body = await response.json();
393
- return {
394
- accessToken: body.access_token,
395
- cookie: response.headers.get("set-cookie"),
396
- };
397
- }
398
-
399
- return false;
400
- } catch (error) {
401
- console.error(
402
- errorColor(
403
- `A problem occured while authenticating on ${environment} environment. Please check your connection and try again.`
404
- )
405
- );
406
- process.exit(1);
407
- }
408
- }
409
-
410
- async function checkValidityOfAccessToken(
411
- environment,
412
- qServer,
413
- accessToken,
414
- cookie
415
- ) {
416
- try {
417
- const response = await fetch(`${qServer}user`, {
418
- headers: {
419
- "user-agent": "Q Command-line Tool",
420
- Authorization: `Bearer ${accessToken}`,
421
- Cookie: cookie ? cookie : "",
422
- },
423
- });
424
- return response.ok;
425
- } catch (error) {
426
- console.error(
427
- errorColor(
428
- `A problem occured while checking the validity of your access token on ${environment} environment. Please check your connection and try again.`
429
- )
430
- );
431
- process.exit(1);
432
- }
433
- }
434
-
435
- module.exports = {
436
- updateItem: updateItem,
437
- setupConfig: setupConfig,
438
- getItems: getItems,
439
- validateConfig: validateConfig,
440
- };