@aderis-erp/cli 1.1.4 → 1.1.5
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/dist/commands/access-groups.js +42 -8
- package/dist/commands/account-transaction-allocations.js +38 -4
- package/dist/commands/account-transactions.d.ts +2 -0
- package/dist/commands/account-transactions.js +68 -32
- package/dist/commands/accounts.js +46 -12
- package/dist/commands/agent-platform.js +37 -3
- package/dist/commands/agent-runs.js +49 -15
- package/dist/commands/analytics.js +39 -5
- package/dist/commands/api-clients.js +47 -13
- package/dist/commands/api-keys.js +40 -6
- package/dist/commands/async-jobs.js +45 -11
- package/dist/commands/audit-log.js +38 -4
- package/dist/commands/bank-accounts.js +54 -20
- package/dist/commands/bank-integrations.js +56 -22
- package/dist/commands/business-partners.js +77 -43
- package/dist/commands/chat.js +43 -9
- package/dist/commands/credit-card-accounts.js +57 -23
- package/dist/commands/currencies.js +37 -3
- package/dist/commands/custody-accounts.js +41 -7
- package/dist/commands/dashboards.js +52 -18
- package/dist/commands/delivery-notes.js +69 -35
- package/dist/commands/document-checksums.d.ts +1 -0
- package/dist/commands/document-checksums.js +38 -3
- package/dist/commands/dunning-configs.js +41 -7
- package/dist/commands/dunning-reminders.js +44 -10
- package/dist/commands/dunning-runs.js +37 -3
- package/dist/commands/email-transactions.js +39 -5
- package/dist/commands/employees.js +77 -43
- package/dist/commands/entity-configurations.js +39 -5
- package/dist/commands/event-mapping-rules.js +37 -3
- package/dist/commands/event-processor-mappings.js +41 -7
- package/dist/commands/files.js +48 -14
- package/dist/commands/financial-instrument-transactions.d.ts +56 -0
- package/dist/commands/financial-instrument-transactions.js +1369 -0
- package/dist/commands/financial-instrument-types.js +41 -7
- package/dist/commands/financial-instruments.js +41 -7
- package/dist/commands/health.js +41 -7
- package/dist/commands/imports.js +39 -5
- package/dist/commands/inbox.js +42 -8
- package/dist/commands/integrations.js +39 -5
- package/dist/commands/item-categories.js +41 -7
- package/dist/commands/items.js +42 -8
- package/dist/commands/journal-lines.js +37 -3
- package/dist/commands/journals.js +61 -27
- package/dist/commands/kpis.js +44 -10
- package/dist/commands/ledgers.js +83 -49
- package/dist/commands/my-account.js +54 -20
- package/dist/commands/notifications.js +37 -3
- package/dist/commands/office-locations.js +41 -7
- package/dist/commands/payment-methods.js +46 -12
- package/dist/commands/payment-runs.js +54 -20
- package/dist/commands/payment-terms.js +42 -8
- package/dist/commands/payments.js +43 -9
- package/dist/commands/payroll-runs.js +71 -37
- package/dist/commands/permissions.js +37 -3
- package/dist/commands/price-lists.js +48 -14
- package/dist/commands/processor-mappings.js +40 -6
- package/dist/commands/processors.js +46 -12
- package/dist/commands/purchase-order-types.js +41 -7
- package/dist/commands/purchase-orders.js +60 -26
- package/dist/commands/reports.js +43 -8
- package/dist/commands/roles.js +40 -6
- package/dist/commands/salary-types.js +41 -7
- package/dist/commands/sales-invoices.js +85 -51
- package/dist/commands/sales-orders.js +69 -35
- package/dist/commands/saved-views.js +39 -5
- package/dist/commands/scheduled-processor-mappings.js +41 -7
- package/dist/commands/schedules.js +45 -11
- package/dist/commands/settings.js +38 -4
- package/dist/commands/tags.js +42 -8
- package/dist/commands/tax-codes.js +44 -10
- package/dist/commands/tax-matrix.js +41 -7
- package/dist/commands/templates.js +49 -15
- package/dist/commands/tenants.js +44 -10
- package/dist/commands/tools.js +38 -4
- package/dist/commands/users.js +58 -24
- package/dist/commands/vat-declarations.js +61 -27
- package/dist/commands/vendor-invoices.js +81 -47
- package/dist/commands/webhooks.js +43 -9
- package/dist/commands/work-summary.js +37 -3
- package/dist/lib/paths.d.ts +8 -0
- package/dist/lib/paths.js +16 -0
- package/dist/lib/production-guard.js +2 -0
- package/oclif.manifest.json +199 -2
- package/package.json +9 -3
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Command, Args, Flags } from '@oclif/core';
|
|
2
2
|
import { apiRequest, ApiError, isTextualMediaType, normalizeSortBy } from '../lib/api-client.js';
|
|
3
3
|
import { getCurrentContext, getContext, getBaseUrls, hasCredentials, missingCredentialsMessage } from '../lib/config.js';
|
|
4
|
+
import { resolveUserPath } from '../lib/paths.js';
|
|
4
5
|
// 'upsert' loops create/update from the CLI, one request per record. It exists
|
|
5
6
|
// only where the API has no import endpoint of its own; those keep the name
|
|
6
7
|
// 'import' and are listed in SERVER_IMPORTS.
|
|
@@ -11,8 +12,8 @@ const SERVER_IMPORTS = [];
|
|
|
11
12
|
const NESTED_UPSERTS = [];
|
|
12
13
|
const SUBCOMMANDS = [...["definitions", "get"], ...(HAS_UPSERT ? ['upsert'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_UPSERTS.map(n => `upsert-${n}`)];
|
|
13
14
|
const ENDPOINTS = {
|
|
14
|
-
'definitions': { method: 'GET', path: '/v1/settings/definitions/{key}', summary: 'Read localized setting guidance for a dated, authorized target', pathParams: ['key'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
-
'get': { method: 'GET', path: '/v1/settings/{key}', summary: 'Read a dated setting for a registered target', pathParams: ['key'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
+
'definitions': { method: 'GET', path: '/v1/settings/definitions/{key}', summary: 'Read localized setting guidance for a dated, authorized target', pathParams: ['key'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
16
|
+
'get': { method: 'GET', path: '/v1/settings/{key}', summary: 'Read a dated setting for a registered target', pathParams: ['key'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
16
17
|
};
|
|
17
18
|
export default class Settings extends Command {
|
|
18
19
|
static description = 'Manage settings';
|
|
@@ -29,7 +30,7 @@ export default class Settings extends Command {
|
|
|
29
30
|
table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
|
|
30
31
|
csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
|
|
31
32
|
data: Flags.string({ char: 'd', description: 'JSON data for request body' }),
|
|
32
|
-
file: Flags.string({ char: 'f', description: 'Read request body from file' }),
|
|
33
|
+
file: Flags.string({ char: 'f', description: 'Read request body from file', parse: async (input) => resolveUserPath(input) }),
|
|
33
34
|
set: Flags.string({ char: 's', description: 'Set individual field (e.g. --set firstName=John)', multiple: true }),
|
|
34
35
|
page: Flags.string({ description: 'Page number' }),
|
|
35
36
|
limit: Flags.string({ description: 'Items per page' }),
|
|
@@ -160,6 +161,17 @@ export default class Settings extends Command {
|
|
|
160
161
|
}
|
|
161
162
|
}
|
|
162
163
|
}
|
|
164
|
+
// Flags generated from this endpoint's own parameters. Raw-body endpoints
|
|
165
|
+
// have them: their body is not JSON, so --set cannot carry a value there.
|
|
166
|
+
for (const param of endpoint.specParams) {
|
|
167
|
+
const value = flags[param.flag];
|
|
168
|
+
if (value === undefined || value === '') {
|
|
169
|
+
if (param.required)
|
|
170
|
+
this.error(`Missing required flag: --${param.flag}`);
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
queryParams[param.name] = value;
|
|
174
|
+
}
|
|
163
175
|
let data;
|
|
164
176
|
if (endpoint.isMultipart) {
|
|
165
177
|
if (!flags.file)
|
|
@@ -180,6 +192,25 @@ export default class Settings extends Command {
|
|
|
180
192
|
}
|
|
181
193
|
data = form;
|
|
182
194
|
}
|
|
195
|
+
else if (endpoint.bodyMediaType) {
|
|
196
|
+
// The body is not JSON (today only application/x-ndjson): it is sent
|
|
197
|
+
// verbatim, under the content type the endpoint asks for.
|
|
198
|
+
if (flags.file) {
|
|
199
|
+
const fsModule = await import('node:fs');
|
|
200
|
+
try {
|
|
201
|
+
data = fsModule.readFileSync(flags.file, 'utf-8');
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
this.error(`Failed to read file: ${flags.file}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
else if (flags.data) {
|
|
208
|
+
data = flags.data;
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
this.error(`This endpoint takes its body as ${endpoint.bodyMediaType}. Pass the file with -f / --file, or the text with --data.`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
183
214
|
else if (endpoint.hasBody) {
|
|
184
215
|
// For binary-response endpoints, --file is the output path, not a JSON
|
|
185
216
|
// input file — skip the read-as-body branch.
|
|
@@ -275,7 +306,7 @@ export default class Settings extends Command {
|
|
|
275
306
|
process.stderr.write(`Body: [multipart/form-data file upload]\n`);
|
|
276
307
|
}
|
|
277
308
|
else if (data) {
|
|
278
|
-
process.stderr.write(`Body: ${JSON.stringify(data, null, 2)}\n`);
|
|
309
|
+
process.stderr.write(`Body: ${typeof data === 'string' ? data : JSON.stringify(data, null, 2)}\n`);
|
|
279
310
|
}
|
|
280
311
|
process.stderr.write('\n');
|
|
281
312
|
}
|
|
@@ -289,6 +320,7 @@ export default class Settings extends Command {
|
|
|
289
320
|
const binResponse = await apiRequest(endpoint.method, apiPath, {
|
|
290
321
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
291
322
|
data,
|
|
323
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
292
324
|
contextName: flags.context,
|
|
293
325
|
binary: true,
|
|
294
326
|
});
|
|
@@ -314,6 +346,7 @@ export default class Settings extends Command {
|
|
|
314
346
|
const raw = await apiRequest(endpoint.method, apiPath, {
|
|
315
347
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
316
348
|
data,
|
|
349
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
317
350
|
contextName: flags.context,
|
|
318
351
|
binary: true,
|
|
319
352
|
});
|
|
@@ -334,6 +367,7 @@ export default class Settings extends Command {
|
|
|
334
367
|
response = await apiRequest(endpoint.method, apiPath, {
|
|
335
368
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
336
369
|
data,
|
|
370
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
337
371
|
contextName: flags.context,
|
|
338
372
|
acceptAny: true,
|
|
339
373
|
});
|
package/dist/commands/tags.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Command, Args, Flags } from '@oclif/core';
|
|
2
2
|
import { apiRequest, ApiError, isTextualMediaType, normalizeSortBy } from '../lib/api-client.js';
|
|
3
3
|
import { getCurrentContext, getContext, getBaseUrls, hasCredentials, missingCredentialsMessage } from '../lib/config.js';
|
|
4
|
+
import { resolveUserPath } from '../lib/paths.js';
|
|
4
5
|
// 'upsert' loops create/update from the CLI, one request per record. It exists
|
|
5
6
|
// only where the API has no import endpoint of its own; those keep the name
|
|
6
7
|
// 'import' and are listed in SERVER_IMPORTS.
|
|
@@ -11,12 +12,12 @@ const SERVER_IMPORTS = [];
|
|
|
11
12
|
const NESTED_UPSERTS = [];
|
|
12
13
|
const SUBCOMMANDS = [...["list", "create", "get", "update", "delete", "journal-coverage"], ...(HAS_UPSERT ? ['upsert'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_UPSERTS.map(n => `upsert-${n}`)];
|
|
13
14
|
const ENDPOINTS = {
|
|
14
|
-
'list': { method: 'GET', path: '/v1/tags', summary: 'Get all tags with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
-
'create': { method: 'POST', path: '/v1/tags', summary: 'Create a new tag', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['rules'] },
|
|
16
|
-
'get': { method: 'GET', path: '/v1/tags/{id}', summary: 'Get a tag by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
17
|
-
'update': { method: 'PATCH', path: '/v1/tags/{id}', summary: 'Update a tag', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['rules'] },
|
|
18
|
-
'delete': { method: 'DELETE', path: '/v1/tags/{id}', summary: 'Delete a tag', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
19
|
-
'journal-coverage': { method: 'GET', path: '/v1/tags/{id}/journal-coverage', summary: 'How much of the journal-line amount in scope carries one of the tags below this tag', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
+
'list': { method: 'GET', path: '/v1/tags', summary: 'Get all tags with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
16
|
+
'create': { method: 'POST', path: '/v1/tags', summary: 'Create a new tag', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['rules'], bodyMediaType: '', specParams: [] },
|
|
17
|
+
'get': { method: 'GET', path: '/v1/tags/{id}', summary: 'Get a tag by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
18
|
+
'update': { method: 'PATCH', path: '/v1/tags/{id}', summary: 'Update a tag', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['rules'], bodyMediaType: '', specParams: [] },
|
|
19
|
+
'delete': { method: 'DELETE', path: '/v1/tags/{id}', summary: 'Delete a tag', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
20
|
+
'journal-coverage': { method: 'GET', path: '/v1/tags/{id}/journal-coverage', summary: 'How much of the journal-line amount in scope carries one of the tags below this tag', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
20
21
|
};
|
|
21
22
|
export default class Tags extends Command {
|
|
22
23
|
static description = 'Manage tags';
|
|
@@ -38,7 +39,7 @@ export default class Tags extends Command {
|
|
|
38
39
|
table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
|
|
39
40
|
csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
|
|
40
41
|
data: Flags.string({ char: 'd', description: 'JSON data for request body' }),
|
|
41
|
-
file: Flags.string({ char: 'f', description: 'Read request body from file' }),
|
|
42
|
+
file: Flags.string({ char: 'f', description: 'Read request body from file', parse: async (input) => resolveUserPath(input) }),
|
|
42
43
|
set: Flags.string({ char: 's', description: 'Set individual field (e.g. --set firstName=John)', multiple: true }),
|
|
43
44
|
page: Flags.string({ description: 'Page number' }),
|
|
44
45
|
limit: Flags.string({ description: 'Items per page' }),
|
|
@@ -169,6 +170,17 @@ export default class Tags extends Command {
|
|
|
169
170
|
}
|
|
170
171
|
}
|
|
171
172
|
}
|
|
173
|
+
// Flags generated from this endpoint's own parameters. Raw-body endpoints
|
|
174
|
+
// have them: their body is not JSON, so --set cannot carry a value there.
|
|
175
|
+
for (const param of endpoint.specParams) {
|
|
176
|
+
const value = flags[param.flag];
|
|
177
|
+
if (value === undefined || value === '') {
|
|
178
|
+
if (param.required)
|
|
179
|
+
this.error(`Missing required flag: --${param.flag}`);
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
queryParams[param.name] = value;
|
|
183
|
+
}
|
|
172
184
|
let data;
|
|
173
185
|
if (endpoint.isMultipart) {
|
|
174
186
|
if (!flags.file)
|
|
@@ -189,6 +201,25 @@ export default class Tags extends Command {
|
|
|
189
201
|
}
|
|
190
202
|
data = form;
|
|
191
203
|
}
|
|
204
|
+
else if (endpoint.bodyMediaType) {
|
|
205
|
+
// The body is not JSON (today only application/x-ndjson): it is sent
|
|
206
|
+
// verbatim, under the content type the endpoint asks for.
|
|
207
|
+
if (flags.file) {
|
|
208
|
+
const fsModule = await import('node:fs');
|
|
209
|
+
try {
|
|
210
|
+
data = fsModule.readFileSync(flags.file, 'utf-8');
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
this.error(`Failed to read file: ${flags.file}`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
else if (flags.data) {
|
|
217
|
+
data = flags.data;
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
this.error(`This endpoint takes its body as ${endpoint.bodyMediaType}. Pass the file with -f / --file, or the text with --data.`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
192
223
|
else if (endpoint.hasBody) {
|
|
193
224
|
// For binary-response endpoints, --file is the output path, not a JSON
|
|
194
225
|
// input file — skip the read-as-body branch.
|
|
@@ -284,7 +315,7 @@ export default class Tags extends Command {
|
|
|
284
315
|
process.stderr.write(`Body: [multipart/form-data file upload]\n`);
|
|
285
316
|
}
|
|
286
317
|
else if (data) {
|
|
287
|
-
process.stderr.write(`Body: ${JSON.stringify(data, null, 2)}\n`);
|
|
318
|
+
process.stderr.write(`Body: ${typeof data === 'string' ? data : JSON.stringify(data, null, 2)}\n`);
|
|
288
319
|
}
|
|
289
320
|
process.stderr.write('\n');
|
|
290
321
|
}
|
|
@@ -298,6 +329,7 @@ export default class Tags extends Command {
|
|
|
298
329
|
const binResponse = await apiRequest(endpoint.method, apiPath, {
|
|
299
330
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
300
331
|
data,
|
|
332
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
301
333
|
contextName: flags.context,
|
|
302
334
|
binary: true,
|
|
303
335
|
});
|
|
@@ -323,6 +355,7 @@ export default class Tags extends Command {
|
|
|
323
355
|
const raw = await apiRequest(endpoint.method, apiPath, {
|
|
324
356
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
325
357
|
data,
|
|
358
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
326
359
|
contextName: flags.context,
|
|
327
360
|
binary: true,
|
|
328
361
|
});
|
|
@@ -343,6 +376,7 @@ export default class Tags extends Command {
|
|
|
343
376
|
response = await apiRequest(endpoint.method, apiPath, {
|
|
344
377
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
345
378
|
data,
|
|
379
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
346
380
|
contextName: flags.context,
|
|
347
381
|
acceptAny: true,
|
|
348
382
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Command, Args, Flags } from '@oclif/core';
|
|
2
2
|
import { apiRequest, ApiError, isTextualMediaType, normalizeSortBy } from '../lib/api-client.js';
|
|
3
3
|
import { getCurrentContext, getContext, getBaseUrls, hasCredentials, missingCredentialsMessage } from '../lib/config.js';
|
|
4
|
+
import { resolveUserPath } from '../lib/paths.js';
|
|
4
5
|
// 'upsert' loops create/update from the CLI, one request per record. It exists
|
|
5
6
|
// only where the API has no import endpoint of its own; those keep the name
|
|
6
7
|
// 'import' and are listed in SERVER_IMPORTS.
|
|
@@ -11,14 +12,14 @@ const SERVER_IMPORTS = [];
|
|
|
11
12
|
const NESTED_UPSERTS = [];
|
|
12
13
|
const SUBCOMMANDS = [...["list", "create", "tax-codes-standard-rate", "get-by-country-rate", "get-by-code-at-date", "get", "update", "delete"], ...(HAS_UPSERT ? ['upsert'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_UPSERTS.map(n => `upsert-${n}`)];
|
|
13
14
|
const ENDPOINTS = {
|
|
14
|
-
'list': { method: 'GET', path: '/v1/tax/tax-codes', summary: 'Get all tax codes with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
-
'create': { method: 'POST', path: '/v1/tax/tax-codes', summary: 'Create a new tax code', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['directions'] },
|
|
16
|
-
'tax-codes-standard-rate': { method: 'GET', path: '/v1/tax/tax-codes/standard-rate', summary: 'Get the standard-rate tax code for a country at a specific date', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
17
|
-
'get-by-country-rate': { method: 'GET', path: '/v1/tax/tax-codes/by-country-rate', summary: 'Get tax code by country and rate at a specific date', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
18
|
-
'get-by-code-at-date': { method: 'GET', path: '/v1/tax/tax-codes/by-code-at-date', summary: 'Get tax code by code at a specific date', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
19
|
-
'get': { method: 'GET', path: '/v1/tax/tax-codes/{id}', summary: 'Get a tax code by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
20
|
-
'update': { method: 'PATCH', path: '/v1/tax/tax-codes/{id}', summary: 'Update a tax code', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['directions'] },
|
|
21
|
-
'delete': { method: 'DELETE', path: '/v1/tax/tax-codes/{id}', summary: 'Delete a tax code', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
+
'list': { method: 'GET', path: '/v1/tax/tax-codes', summary: 'Get all tax codes with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
16
|
+
'create': { method: 'POST', path: '/v1/tax/tax-codes', summary: 'Create a new tax code', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['directions'], bodyMediaType: '', specParams: [] },
|
|
17
|
+
'tax-codes-standard-rate': { method: 'GET', path: '/v1/tax/tax-codes/standard-rate', summary: 'Get the standard-rate tax code for a country at a specific date', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
18
|
+
'get-by-country-rate': { method: 'GET', path: '/v1/tax/tax-codes/by-country-rate', summary: 'Get tax code by country and rate at a specific date', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
19
|
+
'get-by-code-at-date': { method: 'GET', path: '/v1/tax/tax-codes/by-code-at-date', summary: 'Get tax code by code at a specific date', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
20
|
+
'get': { method: 'GET', path: '/v1/tax/tax-codes/{id}', summary: 'Get a tax code by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
21
|
+
'update': { method: 'PATCH', path: '/v1/tax/tax-codes/{id}', summary: 'Update a tax code', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['directions'], bodyMediaType: '', specParams: [] },
|
|
22
|
+
'delete': { method: 'DELETE', path: '/v1/tax/tax-codes/{id}', summary: 'Delete a tax code', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
22
23
|
};
|
|
23
24
|
export default class TaxCodes extends Command {
|
|
24
25
|
static description = 'Manage tax codes';
|
|
@@ -40,7 +41,7 @@ export default class TaxCodes extends Command {
|
|
|
40
41
|
table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
|
|
41
42
|
csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
|
|
42
43
|
data: Flags.string({ char: 'd', description: 'JSON data for request body' }),
|
|
43
|
-
file: Flags.string({ char: 'f', description: 'Read request body from file' }),
|
|
44
|
+
file: Flags.string({ char: 'f', description: 'Read request body from file', parse: async (input) => resolveUserPath(input) }),
|
|
44
45
|
set: Flags.string({ char: 's', description: 'Set individual field (e.g. --set firstName=John)', multiple: true }),
|
|
45
46
|
page: Flags.string({ description: 'Page number' }),
|
|
46
47
|
limit: Flags.string({ description: 'Items per page' }),
|
|
@@ -171,6 +172,17 @@ export default class TaxCodes extends Command {
|
|
|
171
172
|
}
|
|
172
173
|
}
|
|
173
174
|
}
|
|
175
|
+
// Flags generated from this endpoint's own parameters. Raw-body endpoints
|
|
176
|
+
// have them: their body is not JSON, so --set cannot carry a value there.
|
|
177
|
+
for (const param of endpoint.specParams) {
|
|
178
|
+
const value = flags[param.flag];
|
|
179
|
+
if (value === undefined || value === '') {
|
|
180
|
+
if (param.required)
|
|
181
|
+
this.error(`Missing required flag: --${param.flag}`);
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
queryParams[param.name] = value;
|
|
185
|
+
}
|
|
174
186
|
let data;
|
|
175
187
|
if (endpoint.isMultipart) {
|
|
176
188
|
if (!flags.file)
|
|
@@ -191,6 +203,25 @@ export default class TaxCodes extends Command {
|
|
|
191
203
|
}
|
|
192
204
|
data = form;
|
|
193
205
|
}
|
|
206
|
+
else if (endpoint.bodyMediaType) {
|
|
207
|
+
// The body is not JSON (today only application/x-ndjson): it is sent
|
|
208
|
+
// verbatim, under the content type the endpoint asks for.
|
|
209
|
+
if (flags.file) {
|
|
210
|
+
const fsModule = await import('node:fs');
|
|
211
|
+
try {
|
|
212
|
+
data = fsModule.readFileSync(flags.file, 'utf-8');
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
this.error(`Failed to read file: ${flags.file}`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
else if (flags.data) {
|
|
219
|
+
data = flags.data;
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
this.error(`This endpoint takes its body as ${endpoint.bodyMediaType}. Pass the file with -f / --file, or the text with --data.`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
194
225
|
else if (endpoint.hasBody) {
|
|
195
226
|
// For binary-response endpoints, --file is the output path, not a JSON
|
|
196
227
|
// input file — skip the read-as-body branch.
|
|
@@ -286,7 +317,7 @@ export default class TaxCodes extends Command {
|
|
|
286
317
|
process.stderr.write(`Body: [multipart/form-data file upload]\n`);
|
|
287
318
|
}
|
|
288
319
|
else if (data) {
|
|
289
|
-
process.stderr.write(`Body: ${JSON.stringify(data, null, 2)}\n`);
|
|
320
|
+
process.stderr.write(`Body: ${typeof data === 'string' ? data : JSON.stringify(data, null, 2)}\n`);
|
|
290
321
|
}
|
|
291
322
|
process.stderr.write('\n');
|
|
292
323
|
}
|
|
@@ -300,6 +331,7 @@ export default class TaxCodes extends Command {
|
|
|
300
331
|
const binResponse = await apiRequest(endpoint.method, apiPath, {
|
|
301
332
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
302
333
|
data,
|
|
334
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
303
335
|
contextName: flags.context,
|
|
304
336
|
binary: true,
|
|
305
337
|
});
|
|
@@ -325,6 +357,7 @@ export default class TaxCodes extends Command {
|
|
|
325
357
|
const raw = await apiRequest(endpoint.method, apiPath, {
|
|
326
358
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
327
359
|
data,
|
|
360
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
328
361
|
contextName: flags.context,
|
|
329
362
|
binary: true,
|
|
330
363
|
});
|
|
@@ -345,6 +378,7 @@ export default class TaxCodes extends Command {
|
|
|
345
378
|
response = await apiRequest(endpoint.method, apiPath, {
|
|
346
379
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
347
380
|
data,
|
|
381
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
348
382
|
contextName: flags.context,
|
|
349
383
|
acceptAny: true,
|
|
350
384
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Command, Args, Flags } from '@oclif/core';
|
|
2
2
|
import { apiRequest, ApiError, isTextualMediaType, normalizeSortBy } from '../lib/api-client.js';
|
|
3
3
|
import { getCurrentContext, getContext, getBaseUrls, hasCredentials, missingCredentialsMessage } from '../lib/config.js';
|
|
4
|
+
import { resolveUserPath } from '../lib/paths.js';
|
|
4
5
|
// 'upsert' loops create/update from the CLI, one request per record. It exists
|
|
5
6
|
// only where the API has no import endpoint of its own; those keep the name
|
|
6
7
|
// 'import' and are listed in SERVER_IMPORTS.
|
|
@@ -11,11 +12,11 @@ const SERVER_IMPORTS = [];
|
|
|
11
12
|
const NESTED_UPSERTS = [];
|
|
12
13
|
const SUBCOMMANDS = [...["list", "create", "get", "update", "delete"], ...(HAS_UPSERT ? ['upsert'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_UPSERTS.map(n => `upsert-${n}`)];
|
|
13
14
|
const ENDPOINTS = {
|
|
14
|
-
'list': { method: 'GET', path: '/v1/tax/tax-matrix', summary: 'Get all tax matrix mappings with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
-
'create': { method: 'POST', path: '/v1/tax/tax-matrix', summary: 'Create a new tax matrix mapping', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
16
|
-
'get': { method: 'GET', path: '/v1/tax/tax-matrix/{id}', summary: 'Get a tax matrix mapping by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
17
|
-
'update': { method: 'PATCH', path: '/v1/tax/tax-matrix/{id}', summary: 'Update a tax matrix mapping', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
18
|
-
'delete': { method: 'DELETE', path: '/v1/tax/tax-matrix/{id}', summary: 'Delete a tax matrix mapping', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
+
'list': { method: 'GET', path: '/v1/tax/tax-matrix', summary: 'Get all tax matrix mappings with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
16
|
+
'create': { method: 'POST', path: '/v1/tax/tax-matrix', summary: 'Create a new tax matrix mapping', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
17
|
+
'get': { method: 'GET', path: '/v1/tax/tax-matrix/{id}', summary: 'Get a tax matrix mapping by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
18
|
+
'update': { method: 'PATCH', path: '/v1/tax/tax-matrix/{id}', summary: 'Update a tax matrix mapping', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
19
|
+
'delete': { method: 'DELETE', path: '/v1/tax/tax-matrix/{id}', summary: 'Delete a tax matrix mapping', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
19
20
|
};
|
|
20
21
|
export default class TaxMatrix extends Command {
|
|
21
22
|
static description = 'Manage tax matrix';
|
|
@@ -37,7 +38,7 @@ export default class TaxMatrix extends Command {
|
|
|
37
38
|
table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
|
|
38
39
|
csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
|
|
39
40
|
data: Flags.string({ char: 'd', description: 'JSON data for request body' }),
|
|
40
|
-
file: Flags.string({ char: 'f', description: 'Read request body from file' }),
|
|
41
|
+
file: Flags.string({ char: 'f', description: 'Read request body from file', parse: async (input) => resolveUserPath(input) }),
|
|
41
42
|
set: Flags.string({ char: 's', description: 'Set individual field (e.g. --set firstName=John)', multiple: true }),
|
|
42
43
|
page: Flags.string({ description: 'Page number' }),
|
|
43
44
|
limit: Flags.string({ description: 'Items per page' }),
|
|
@@ -168,6 +169,17 @@ export default class TaxMatrix extends Command {
|
|
|
168
169
|
}
|
|
169
170
|
}
|
|
170
171
|
}
|
|
172
|
+
// Flags generated from this endpoint's own parameters. Raw-body endpoints
|
|
173
|
+
// have them: their body is not JSON, so --set cannot carry a value there.
|
|
174
|
+
for (const param of endpoint.specParams) {
|
|
175
|
+
const value = flags[param.flag];
|
|
176
|
+
if (value === undefined || value === '') {
|
|
177
|
+
if (param.required)
|
|
178
|
+
this.error(`Missing required flag: --${param.flag}`);
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
queryParams[param.name] = value;
|
|
182
|
+
}
|
|
171
183
|
let data;
|
|
172
184
|
if (endpoint.isMultipart) {
|
|
173
185
|
if (!flags.file)
|
|
@@ -188,6 +200,25 @@ export default class TaxMatrix extends Command {
|
|
|
188
200
|
}
|
|
189
201
|
data = form;
|
|
190
202
|
}
|
|
203
|
+
else if (endpoint.bodyMediaType) {
|
|
204
|
+
// The body is not JSON (today only application/x-ndjson): it is sent
|
|
205
|
+
// verbatim, under the content type the endpoint asks for.
|
|
206
|
+
if (flags.file) {
|
|
207
|
+
const fsModule = await import('node:fs');
|
|
208
|
+
try {
|
|
209
|
+
data = fsModule.readFileSync(flags.file, 'utf-8');
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
this.error(`Failed to read file: ${flags.file}`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
else if (flags.data) {
|
|
216
|
+
data = flags.data;
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
this.error(`This endpoint takes its body as ${endpoint.bodyMediaType}. Pass the file with -f / --file, or the text with --data.`);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
191
222
|
else if (endpoint.hasBody) {
|
|
192
223
|
// For binary-response endpoints, --file is the output path, not a JSON
|
|
193
224
|
// input file — skip the read-as-body branch.
|
|
@@ -283,7 +314,7 @@ export default class TaxMatrix extends Command {
|
|
|
283
314
|
process.stderr.write(`Body: [multipart/form-data file upload]\n`);
|
|
284
315
|
}
|
|
285
316
|
else if (data) {
|
|
286
|
-
process.stderr.write(`Body: ${JSON.stringify(data, null, 2)}\n`);
|
|
317
|
+
process.stderr.write(`Body: ${typeof data === 'string' ? data : JSON.stringify(data, null, 2)}\n`);
|
|
287
318
|
}
|
|
288
319
|
process.stderr.write('\n');
|
|
289
320
|
}
|
|
@@ -297,6 +328,7 @@ export default class TaxMatrix extends Command {
|
|
|
297
328
|
const binResponse = await apiRequest(endpoint.method, apiPath, {
|
|
298
329
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
299
330
|
data,
|
|
331
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
300
332
|
contextName: flags.context,
|
|
301
333
|
binary: true,
|
|
302
334
|
});
|
|
@@ -322,6 +354,7 @@ export default class TaxMatrix extends Command {
|
|
|
322
354
|
const raw = await apiRequest(endpoint.method, apiPath, {
|
|
323
355
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
324
356
|
data,
|
|
357
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
325
358
|
contextName: flags.context,
|
|
326
359
|
binary: true,
|
|
327
360
|
});
|
|
@@ -342,6 +375,7 @@ export default class TaxMatrix extends Command {
|
|
|
342
375
|
response = await apiRequest(endpoint.method, apiPath, {
|
|
343
376
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
344
377
|
data,
|
|
378
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
345
379
|
contextName: flags.context,
|
|
346
380
|
acceptAny: true,
|
|
347
381
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Command, Args, Flags } from '@oclif/core';
|
|
2
2
|
import { apiRequest, ApiError, isTextualMediaType, normalizeSortBy } from '../lib/api-client.js';
|
|
3
3
|
import { getCurrentContext, getContext, getBaseUrls, hasCredentials, missingCredentialsMessage } from '../lib/config.js';
|
|
4
|
+
import { resolveUserPath } from '../lib/paths.js';
|
|
4
5
|
// 'upsert' loops create/update from the CLI, one request per record. It exists
|
|
5
6
|
// only where the API has no import endpoint of its own; those keep the name
|
|
6
7
|
// 'import' and are listed in SERVER_IMPORTS.
|
|
@@ -11,19 +12,19 @@ const SERVER_IMPORTS = [];
|
|
|
11
12
|
const NESTED_UPSERTS = [];
|
|
12
13
|
const SUBCOMMANDS = [...["rule-fields", "rule-order", "replace-rule", "delete-rule", "pdf-fields", "list", "create", "get", "update", "delete", "usage", "duplicate", "preview"], ...(HAS_UPSERT ? ['upsert'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_UPSERTS.map(n => `upsert-${n}`)];
|
|
13
14
|
const ENDPOINTS = {
|
|
14
|
-
'rule-fields': { method: 'GET', path: '/v1/templates/rule-fields', summary: 'List the fields a TEXT module’s template rules may test', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
-
'rule-order': { method: 'POST', path: '/v1/templates/rule-order', summary: 'Order the template rules of a TEXT module', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['templateIds'] },
|
|
16
|
-
'replace-rule': { method: 'PUT', path: '/v1/templates/{id}/rule', summary: 'Set the rule of a TEXT template', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
17
|
-
'delete-rule': { method: 'DELETE', path: '/v1/templates/{id}/rule', summary: 'Remove the rule of a template', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
18
|
-
'pdf-fields': { method: 'GET', path: '/v1/templates/pdf-fields', summary: 'List the values a PDF template of a document type can print', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
19
|
-
'list': { method: 'GET', path: '/v1/templates', summary: 'Get all templates with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
20
|
-
'create': { method: 'POST', path: '/v1/templates', summary: 'Create a new template', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['includes'] },
|
|
21
|
-
'get': { method: 'GET', path: '/v1/templates/{id}', summary: 'Get a template by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
22
|
-
'update': { method: 'PATCH', path: '/v1/templates/{id}', summary: 'Update a template', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['includes'] },
|
|
23
|
-
'delete': { method: 'DELETE', path: '/v1/templates/{id}', summary: 'Delete a template', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
24
|
-
'usage': { method: 'GET', path: '/v1/templates/{id}/usage', summary: 'Show which processor uses a template', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
25
|
-
'duplicate': { method: 'POST', path: '/v1/templates/{id}/duplicate', summary: 'Duplicate a template', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
26
|
-
'preview': { method: 'POST', path: '/v1/templates/{id}/preview', summary: 'Preview an EMAIL or TEXT template against a real document', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
+
'rule-fields': { method: 'GET', path: '/v1/templates/rule-fields', summary: 'List the fields a TEXT module’s template rules may test', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
16
|
+
'rule-order': { method: 'POST', path: '/v1/templates/rule-order', summary: 'Order the template rules of a TEXT module', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['templateIds'], bodyMediaType: '', specParams: [] },
|
|
17
|
+
'replace-rule': { method: 'PUT', path: '/v1/templates/{id}/rule', summary: 'Set the rule of a TEXT template', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
18
|
+
'delete-rule': { method: 'DELETE', path: '/v1/templates/{id}/rule', summary: 'Remove the rule of a template', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
19
|
+
'pdf-fields': { method: 'GET', path: '/v1/templates/pdf-fields', summary: 'List the values a PDF template of a document type can print', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
20
|
+
'list': { method: 'GET', path: '/v1/templates', summary: 'Get all templates with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
21
|
+
'create': { method: 'POST', path: '/v1/templates', summary: 'Create a new template', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['includes'], bodyMediaType: '', specParams: [] },
|
|
22
|
+
'get': { method: 'GET', path: '/v1/templates/{id}', summary: 'Get a template by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
23
|
+
'update': { method: 'PATCH', path: '/v1/templates/{id}', summary: 'Update a template', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['includes'], bodyMediaType: '', specParams: [] },
|
|
24
|
+
'delete': { method: 'DELETE', path: '/v1/templates/{id}', summary: 'Delete a template', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
25
|
+
'usage': { method: 'GET', path: '/v1/templates/{id}/usage', summary: 'Show which processor uses a template', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
26
|
+
'duplicate': { method: 'POST', path: '/v1/templates/{id}/duplicate', summary: 'Duplicate a template', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
27
|
+
'preview': { method: 'POST', path: '/v1/templates/{id}/preview', summary: 'Preview an EMAIL or TEXT template against a real document', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
27
28
|
};
|
|
28
29
|
export default class Templates extends Command {
|
|
29
30
|
static description = 'Manage templates';
|
|
@@ -45,7 +46,7 @@ export default class Templates extends Command {
|
|
|
45
46
|
table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
|
|
46
47
|
csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
|
|
47
48
|
data: Flags.string({ char: 'd', description: 'JSON data for request body' }),
|
|
48
|
-
file: Flags.string({ char: 'f', description: 'Read request body from file' }),
|
|
49
|
+
file: Flags.string({ char: 'f', description: 'Read request body from file', parse: async (input) => resolveUserPath(input) }),
|
|
49
50
|
set: Flags.string({ char: 's', description: 'Set individual field (e.g. --set firstName=John)', multiple: true }),
|
|
50
51
|
page: Flags.string({ description: 'Page number' }),
|
|
51
52
|
limit: Flags.string({ description: 'Items per page' }),
|
|
@@ -176,6 +177,17 @@ export default class Templates extends Command {
|
|
|
176
177
|
}
|
|
177
178
|
}
|
|
178
179
|
}
|
|
180
|
+
// Flags generated from this endpoint's own parameters. Raw-body endpoints
|
|
181
|
+
// have them: their body is not JSON, so --set cannot carry a value there.
|
|
182
|
+
for (const param of endpoint.specParams) {
|
|
183
|
+
const value = flags[param.flag];
|
|
184
|
+
if (value === undefined || value === '') {
|
|
185
|
+
if (param.required)
|
|
186
|
+
this.error(`Missing required flag: --${param.flag}`);
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
queryParams[param.name] = value;
|
|
190
|
+
}
|
|
179
191
|
let data;
|
|
180
192
|
if (endpoint.isMultipart) {
|
|
181
193
|
if (!flags.file)
|
|
@@ -196,6 +208,25 @@ export default class Templates extends Command {
|
|
|
196
208
|
}
|
|
197
209
|
data = form;
|
|
198
210
|
}
|
|
211
|
+
else if (endpoint.bodyMediaType) {
|
|
212
|
+
// The body is not JSON (today only application/x-ndjson): it is sent
|
|
213
|
+
// verbatim, under the content type the endpoint asks for.
|
|
214
|
+
if (flags.file) {
|
|
215
|
+
const fsModule = await import('node:fs');
|
|
216
|
+
try {
|
|
217
|
+
data = fsModule.readFileSync(flags.file, 'utf-8');
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
this.error(`Failed to read file: ${flags.file}`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
else if (flags.data) {
|
|
224
|
+
data = flags.data;
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
this.error(`This endpoint takes its body as ${endpoint.bodyMediaType}. Pass the file with -f / --file, or the text with --data.`);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
199
230
|
else if (endpoint.hasBody) {
|
|
200
231
|
// For binary-response endpoints, --file is the output path, not a JSON
|
|
201
232
|
// input file — skip the read-as-body branch.
|
|
@@ -291,7 +322,7 @@ export default class Templates extends Command {
|
|
|
291
322
|
process.stderr.write(`Body: [multipart/form-data file upload]\n`);
|
|
292
323
|
}
|
|
293
324
|
else if (data) {
|
|
294
|
-
process.stderr.write(`Body: ${JSON.stringify(data, null, 2)}\n`);
|
|
325
|
+
process.stderr.write(`Body: ${typeof data === 'string' ? data : JSON.stringify(data, null, 2)}\n`);
|
|
295
326
|
}
|
|
296
327
|
process.stderr.write('\n');
|
|
297
328
|
}
|
|
@@ -305,6 +336,7 @@ export default class Templates extends Command {
|
|
|
305
336
|
const binResponse = await apiRequest(endpoint.method, apiPath, {
|
|
306
337
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
307
338
|
data,
|
|
339
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
308
340
|
contextName: flags.context,
|
|
309
341
|
binary: true,
|
|
310
342
|
});
|
|
@@ -330,6 +362,7 @@ export default class Templates extends Command {
|
|
|
330
362
|
const raw = await apiRequest(endpoint.method, apiPath, {
|
|
331
363
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
332
364
|
data,
|
|
365
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
333
366
|
contextName: flags.context,
|
|
334
367
|
binary: true,
|
|
335
368
|
});
|
|
@@ -350,6 +383,7 @@ export default class Templates extends Command {
|
|
|
350
383
|
response = await apiRequest(endpoint.method, apiPath, {
|
|
351
384
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
352
385
|
data,
|
|
386
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
353
387
|
contextName: flags.context,
|
|
354
388
|
acceptAny: true,
|
|
355
389
|
});
|