@nzz/q-cli 2.1.2 → 2.2.1

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.
@@ -7,10 +7,21 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
- import { logSuccess, logError, updateItem, getAccessToken, getQServerUrl } from './utils.js';
10
+ import { logSuccess, logError, updateItem, getAccessToken, getQServerUrl } from '../utils.js';
11
11
  import { readFileSync, existsSync, writeFileSync } from 'fs';
12
12
  import { resolve } from 'path';
13
- export default function (command) {
13
+ export function createCreateCustomCodeItemCommand(program) {
14
+ return program
15
+ .command('create-custom-code-item')
16
+ .description('creates a new q custom code item in the db and adds it to the q config file')
17
+ .option('-c, --config [path]', 'set config path to q.config.json. defaults to ./q.config.json', `${process.cwd()}/q.config.json`)
18
+ .option('-e, --environment [env]', 'set environment where the new q custom code item should be created in')
19
+ .option('-t, --title [title]', 'set title of the new q custom code item')
20
+ .action((command) => __awaiter(this, void 0, void 0, function* () {
21
+ yield createCustomCodeItem(command);
22
+ }));
23
+ }
24
+ function createCustomCodeItem(command) {
14
25
  return __awaiter(this, void 0, void 0, function* () {
15
26
  const qConfigPath = resolve(command.config);
16
27
  if (!existsSync(qConfigPath)) {
@@ -35,6 +46,7 @@ export default function (command) {
35
46
  files: [],
36
47
  options: {
37
48
  previewDisabled: true,
49
+ qEditorPreviewDisabled: false,
38
50
  },
39
51
  // @ts-ignore
40
52
  publication: 'nzz',
@@ -1,8 +1,18 @@
1
- import * as fs from 'fs';
2
1
  import * as path from 'path';
2
+ import * as fs from 'fs';
3
3
  import { execSync } from 'child_process';
4
- import { logError, logSuccess } from './utils.js';
5
- export default function (command) {
4
+ import { logError, logSuccess } from '../utils.js';
5
+ export function createNewCustomCodeCommand(program) {
6
+ return program
7
+ .command('new-custom-code')
8
+ .argument('<path>', 'the base directory to bootstrap the new custom code project in')
9
+ .description('bootstrap a new custom code project')
10
+ .action(() => {
11
+ const dir = program.args[1];
12
+ newCustomCode({ dir });
13
+ });
14
+ }
15
+ function newCustomCode(command) {
6
16
  const { dir } = command;
7
17
  if (!dir) {
8
18
  logError('No custom-code project name/directory given');
@@ -12,9 +12,19 @@ import { readFileSync, existsSync, createReadStream } from 'fs';
12
12
  import { resolve } from 'path';
13
13
  import FormData from 'form-data';
14
14
  import fetch from 'node-fetch'; // Use node-fetch instead of native fetch, because native fetch seems to have issues with FormData/uploading files
15
- import { getAccessToken, getQServerUrl, logError, logSuccess, updateItem } from './utils.js';
16
- import updateSchemaJson from './assets/updateSchema.json' with { type: 'json' };
17
- import { Environment } from './enums.js';
15
+ import { getAccessToken, getQServerUrl, logError, logSuccess, updateItem as updateItemUtils } from '../utils.js';
16
+ import updateSchemaJson from '../assets/updateSchema.json' with { type: 'json' };
17
+ import { Environment } from '../enums.js';
18
+ export function createUpdateItemCommand(program) {
19
+ return program
20
+ .command('update-item')
21
+ .description('update q item')
22
+ .option('-c, --config [path]', 'set config path which defines the q items to be updated. defaults to ./q.config.json', `${process.cwd()}/q.config.json`)
23
+ .option('-e, --environment [env]', 'set environment which should be updated, defaults to update all items of all environments defined in config')
24
+ .action((command) => __awaiter(this, void 0, void 0, function* () {
25
+ yield updateItem(command);
26
+ }));
27
+ }
18
28
  const envCredentials = {
19
29
  [Environment.LOCAL]: {
20
30
  qServerUrl: '',
@@ -84,6 +94,28 @@ function uploadFile(path, qServerUrl, accessToken) {
84
94
  body: form,
85
95
  headers: Object.assign(Object.assign({}, form.getHeaders()), { 'user-agent': 'Q Command-line Tool', Authorization: `Bearer ${accessToken}` }),
86
96
  });
97
+ // Handle HTTP errors before parsing JSON
98
+ if (!response.ok) {
99
+ let errorMessage;
100
+ switch (response.status) {
101
+ case 401:
102
+ errorMessage = 'Authentication error: Invalid or expired access token';
103
+ break;
104
+ case 403:
105
+ errorMessage = 'Authorization error: Insufficient permissions to upload files';
106
+ break;
107
+ case 413:
108
+ errorMessage = 'File too large: The file exceeds the maximum allowed size';
109
+ break;
110
+ case 415:
111
+ errorMessage = 'Unsupported file type: The file type is not allowed';
112
+ break;
113
+ default:
114
+ errorMessage = createGenericFileUploadErrorMsg(response);
115
+ }
116
+ logError(errorMessage);
117
+ return undefined;
118
+ }
87
119
  const data = (yield response.json());
88
120
  if (!data.apiResponseStatus.success) {
89
121
  logError(`Failed to upload file: ${data.apiResponseStatus.msg}`);
@@ -97,6 +129,9 @@ function uploadFile(path, qServerUrl, accessToken) {
97
129
  }
98
130
  });
99
131
  }
132
+ function createGenericFileUploadErrorMsg(response) {
133
+ return `HTTP ${response.status}: File upload failed`;
134
+ }
100
135
  /**
101
136
  * Recursively processes a document structure, looking for 'path' properties
102
137
  * and processing them with the processResource function.
@@ -127,7 +162,7 @@ function processPathProperties(item, processResource) {
127
162
  return result;
128
163
  });
129
164
  }
130
- export default function (command) {
165
+ function updateItem(command) {
131
166
  return __awaiter(this, void 0, void 0, function* () {
132
167
  const qConfigPath = resolve(command.config);
133
168
  if (!existsSync(qConfigPath)) {
@@ -170,7 +205,7 @@ export default function (command) {
170
205
  return yield uploadFile(formattedPath, qServerUrl, accessToken);
171
206
  }));
172
207
  // Update the item on the Q server
173
- const result = yield updateItem(qDoc, qServerUrl, accessToken);
208
+ const result = yield updateItemUtils(qDoc, qServerUrl, accessToken);
174
209
  if (result.success) {
175
210
  logSuccess(`Successfully updated item with id ${environment.id} on ${environmentToUpdate} environment`);
176
211
  }
@@ -182,3 +217,8 @@ export default function (command) {
182
217
  }
183
218
  });
184
219
  }
220
+ // Export for testing only
221
+ export const __test__ = {
222
+ uploadFile,
223
+ createGenericFileUploadErrorMsg,
224
+ };
package/dist/index.js CHANGED
@@ -10,40 +10,17 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  import { Command } from 'commander';
12
12
  import packageJson from '../package.json' with { type: 'json' };
13
- import updateItem from './updateItem.js';
14
- import newCustomCode from './newCustomCode.js';
15
- import createCustomCodeItem from './createCustomCodeItem.js';
13
+ import { createCreateCustomCodeItemCommand } from './commands/createCustomCodeItem.js';
14
+ import { createUpdateItemCommand } from './commands/updateItem.js';
15
+ import { createNewCustomCodeCommand } from './commands/newCustomCode.js';
16
16
  const program = new Command();
17
- // Get the version from the package.json file
18
17
  const version = packageJson.version;
19
18
  function main() {
20
19
  return __awaiter(this, void 0, void 0, function* () {
21
20
  program.version(version).description('Q Toolbox cli');
22
- program
23
- .command('new-custom-code')
24
- .argument('<path>', 'the base directory to bootstrap the new custom code project in')
25
- .description('bootstrap a new custom code project')
26
- .action(() => {
27
- const dir = program.args[1];
28
- newCustomCode({ dir });
29
- });
30
- program
31
- .command('update-item')
32
- .description('update q item')
33
- .option('-c, --config [path]', 'set config path which defines the q items to be updated. defaults to ./q.config.json', `${process.cwd()}/q.config.json`)
34
- .option('-e, --environment [env]', 'set environment which should be updated, defaults to update all items of all environments defined in config')
35
- .action((command) => __awaiter(this, void 0, void 0, function* () {
36
- yield updateItem(command);
37
- }));
38
- program
39
- .command('create-custom-code-item')
40
- .description('creates a new q custom code item in the db and adds it to the q config file')
41
- .option('-c, --config [path]', 'set config path to q.config.json. defaults to ./q.config.json', `${process.cwd()}/q.config.json`)
42
- .option('-e, --environment [env]', 'set environment where the new q custom code item should be created in')
43
- .option('-t, --title [title]', 'set title of the new q custom code item')
44
- .action((command) => __awaiter(this, void 0, void 0, function* () {
45
- yield createCustomCodeItem(command);
46
- }));
21
+ createNewCustomCodeCommand(program);
22
+ createUpdateItemCommand(program);
23
+ createCreateCustomCodeItemCommand(program);
47
24
  yield program.parseAsync(process.argv);
48
25
  });
49
26
  }
package/dist/utils.js CHANGED
@@ -37,9 +37,10 @@ export function updateItem(qDoc, qServerUrl, accessToken) {
37
37
  msg: '',
38
38
  success: false,
39
39
  };
40
+ let response;
40
41
  let data;
41
42
  try {
42
- const response = yield fetch(`${qServerUrl}/item`, {
43
+ response = yield fetch(`${qServerUrl}/item`, {
43
44
  method: 'POST',
44
45
  body: JSON.stringify(qDoc),
45
46
  headers: {
@@ -60,6 +61,30 @@ export function updateItem(qDoc, qServerUrl, accessToken) {
60
61
  }
61
62
  return retVal;
62
63
  }
64
+ // Handle different HTTP status codes
65
+ if (!response.ok) {
66
+ switch (response.status) {
67
+ case 400:
68
+ retVal.msg = `Bad Request: ${isAuthError(data) ? data.error : 'Invalid request data'}`;
69
+ break;
70
+ case 401:
71
+ retVal.msg = 'Authentication error: Invalid or expired access token';
72
+ break;
73
+ case 403:
74
+ retVal.msg = 'Authorization error: Insufficient permissions';
75
+ break;
76
+ case 413:
77
+ retVal.msg =
78
+ 'Request too large: The item data is too large. Please reduce the size of your content or contact support to increase the limit.';
79
+ break;
80
+ case 500:
81
+ retVal.msg = 'Server error: Internal server error occurred';
82
+ break;
83
+ default:
84
+ retVal.msg = createUnknownErrorMsg(response, data);
85
+ }
86
+ return retVal;
87
+ }
63
88
  if (isAuthError(data)) {
64
89
  retVal.msg = `Authentication error: ${data.error}`;
65
90
  return retVal;
@@ -73,9 +98,12 @@ export function updateItem(qDoc, qServerUrl, accessToken) {
73
98
  return retVal;
74
99
  });
75
100
  }
101
+ function createUnknownErrorMsg(response, data) {
102
+ return `HTTP ${response.status}: ${isAuthError(data) ? data.error : 'Unknown error occurred'}`;
103
+ }
76
104
  // Use type predicate to check response type
77
105
  function isAuthError(data) {
78
- return 'error' in data;
106
+ return data !== null && typeof data === 'object' && 'error' in data && typeof data.error === 'string';
79
107
  }
80
108
  function readSecret(reference) {
81
109
  try {
@@ -86,3 +114,7 @@ function readSecret(reference) {
86
114
  process.exit(1);
87
115
  }
88
116
  }
117
+ // Export for testing only
118
+ export const __test__ = {
119
+ createUnknownErrorMsg,
120
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nzz/q-cli",
3
- "version": "2.1.2",
3
+ "version": "2.2.1",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -16,6 +16,9 @@
16
16
  "create-custom-code-item": "tsx ./src/index.ts create-custom-code-item -e local -c ./tests/q.config.json -t 'TEST CUSTOM CODE ITEM'",
17
17
  "update-item": "tsx ./src/index.ts update-item -e local -c ./tests/q.config.json",
18
18
  "new-custom-code": "tsx ./src/index.ts new-custom-code -d my-custom-code-project",
19
+ "test": "vitest",
20
+ "test:run": "vitest run",
21
+ "test:coverage": "vitest run --coverage",
19
22
  "eslint": "TIMING=1 eslint --ext .svelte,.ts --config .eslintrc.cjs .",
20
23
  "lint:all": "run-s -c tscheck lint svelte-check",
21
24
  "tscheck": "tsc --noEmit"
@@ -33,6 +36,7 @@
33
36
  "@repo/types-custom-code": "workspace:^",
34
37
  "@repo/types-db": "workspace:^",
35
38
  "@repo/types-files": "workspace:^",
39
+ "@vitest/coverage-v8": "^3.0.8",
36
40
  "delivery-server": "workspace:*",
37
41
  "typescript": "^5.2.2",
38
42
  "vitest": "^0.34.6"