@aderis-erp/cli 1.0.17 → 1.1.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/dist/commands/access-groups.js +49 -17
- package/dist/commands/account-transaction-allocations.js +46 -14
- package/dist/commands/account-transactions.js +75 -35
- package/dist/commands/accounts.js +54 -22
- package/dist/commands/agent-platform.js +53 -21
- package/dist/commands/{nango-integrations.d.ts → agent-runs.d.ts} +1 -1
- package/dist/commands/agent-runs.js +1155 -0
- package/dist/commands/analytics.js +47 -15
- package/dist/commands/api-keys.js +49 -16
- package/dist/commands/{text-templates.d.ts → async-jobs.d.ts} +1 -1
- package/dist/commands/async-jobs.js +1158 -0
- package/dist/commands/audit-log.js +46 -14
- package/dist/commands/bank-accounts.js +63 -30
- package/dist/commands/bank-integrations.d.ts +1 -0
- package/dist/commands/bank-integrations.js +66 -31
- package/dist/commands/business-partners.js +77 -44
- package/dist/commands/chat.js +52 -19
- package/dist/commands/credit-card-accounts.d.ts +1 -0
- package/dist/commands/credit-card-accounts.js +67 -29
- package/dist/commands/delivery-notes.js +78 -44
- package/dist/commands/dunning-configs.js +49 -17
- package/dist/commands/dunning-reminders.js +52 -20
- package/dist/commands/dunning-runs.js +45 -13
- package/dist/commands/email-transactions.js +47 -15
- package/dist/commands/employees.js +76 -44
- package/dist/commands/entity-configurations.js +47 -15
- package/dist/commands/event-processor-mappings.js +49 -17
- package/dist/commands/files.js +56 -24
- package/dist/commands/health.js +49 -17
- package/dist/commands/imports.d.ts +39 -0
- package/dist/commands/{nango-integrations.js → imports.js} +61 -25
- package/dist/commands/inbox.js +52 -19
- package/dist/commands/integration-identities.d.ts +40 -0
- package/dist/commands/integration-identities.js +1151 -0
- package/dist/commands/integrations.js +47 -15
- package/dist/commands/item-categories.js +49 -17
- package/dist/commands/items.js +49 -17
- package/dist/commands/journal-lines.js +45 -13
- package/dist/commands/journals.js +68 -36
- package/dist/commands/ledgers.d.ts +3 -1
- package/dist/commands/ledgers.js +95 -34
- package/dist/commands/my-account.d.ts +39 -0
- package/dist/commands/my-account.js +1160 -0
- package/dist/commands/office-locations.js +49 -17
- package/dist/commands/payment-methods.js +54 -22
- package/dist/commands/payment-runs.js +61 -29
- package/dist/commands/payment-terms.js +50 -18
- package/dist/commands/payments.js +51 -19
- package/dist/commands/payroll-runs.d.ts +43 -0
- package/dist/commands/payroll-runs.js +1188 -0
- package/dist/commands/permissions.d.ts +39 -0
- package/dist/commands/{text-templates.js → permissions.js} +54 -30
- package/dist/commands/price-lists.js +54 -22
- package/dist/commands/processor-mappings.js +48 -16
- package/dist/commands/processors.js +55 -24
- package/dist/commands/purchase-order-types.js +49 -17
- package/dist/commands/purchase-orders.js +69 -36
- package/dist/commands/reports.js +49 -17
- package/dist/commands/roles.d.ts +39 -0
- package/dist/commands/roles.js +1152 -0
- package/dist/commands/salary-types.js +49 -17
- package/dist/commands/sales-invoices.js +84 -51
- package/dist/commands/sales-orders.js +78 -44
- package/dist/commands/saved-views.js +47 -15
- package/dist/commands/scheduled-processor-mappings.js +49 -17
- package/dist/commands/schedules.js +53 -21
- package/dist/commands/tag-configurations.js +50 -18
- package/dist/commands/tags.js +49 -17
- package/dist/commands/tax-codes.js +52 -20
- package/dist/commands/tax-matrix.js +49 -17
- package/dist/commands/templates.d.ts +39 -0
- package/dist/commands/templates.js +1157 -0
- package/dist/commands/tenants.js +52 -20
- package/dist/commands/tools.js +46 -14
- package/dist/commands/users.d.ts +42 -0
- package/dist/commands/users.js +1174 -0
- package/dist/commands/vat-declarations.d.ts +2 -0
- package/dist/commands/vat-declarations.js +72 -36
- package/dist/commands/vendor-invoices.js +81 -49
- package/dist/commands/webhooks.js +51 -19
- package/dist/commands/workflows.js +47 -15
- package/dist/lib/api-client.d.ts +6 -0
- package/dist/lib/api-client.js +13 -0
- package/dist/lib/auth/index.js +15 -4
- package/dist/lib/auth/jwks.d.ts +19 -0
- package/dist/lib/auth/jwks.js +112 -0
- package/dist/lib/auth/keycloak-provider.d.ts +23 -7
- package/dist/lib/auth/keycloak-provider.js +53 -24
- package/dist/lib/auth/tokens.d.ts +8 -3
- package/dist/lib/auth/tokens.js +8 -7
- package/dist/lib/auth/types.d.ts +5 -1
- package/oclif.manifest.json +2152 -715
- package/package.json +4 -4
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command, Args, Flags } from '@oclif/core';
|
|
2
|
-
import { apiRequest, ApiError, normalizeSortBy } from '../lib/api-client.js';
|
|
2
|
+
import { apiRequest, ApiError, isTextualMediaType, normalizeSortBy } from '../lib/api-client.js';
|
|
3
3
|
import { getCurrentContext, getContext, getBaseUrls, hasCredentials, missingCredentialsMessage } from '../lib/config.js';
|
|
4
4
|
// Add 'import' action if both create and update exist
|
|
5
5
|
const HAS_IMPORT = false;
|
|
@@ -8,9 +8,9 @@ const HAS_CAMT053 = false;
|
|
|
8
8
|
const NESTED_IMPORTS = [];
|
|
9
9
|
const SUBCOMMANDS = [...["get", "create", "delete"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
|
|
10
10
|
const ENDPOINTS = {
|
|
11
|
-
'get': { method: 'GET', path: '/v1/saved-views', summary: 'List saved views for a page', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
12
|
-
'create': { method: 'POST', path: '/v1/saved-views', summary: 'Create a saved view', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
13
|
-
'delete': { method: 'DELETE', path: '/v1/saved-views/{id}', summary: 'Delete a saved view
|
|
11
|
+
'get': { method: 'GET', path: '/v1/saved-views', summary: 'List saved views for a page', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
12
|
+
'create': { method: 'POST', path: '/v1/saved-views', summary: 'Create a saved view', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
13
|
+
'delete': { method: 'DELETE', path: '/v1/saved-views/{id}', summary: 'Delete a saved view', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
14
14
|
};
|
|
15
15
|
export default class SavedViews extends Command {
|
|
16
16
|
static description = 'Manage saved views';
|
|
@@ -258,10 +258,11 @@ export default class SavedViews extends Command {
|
|
|
258
258
|
process.stderr.write('\n');
|
|
259
259
|
}
|
|
260
260
|
try {
|
|
261
|
-
// Binary response:
|
|
261
|
+
// Binary response: to -f / --file, or to stdout when it is redirected.
|
|
262
262
|
if (endpoint.isBinaryResponse) {
|
|
263
|
-
|
|
264
|
-
|
|
263
|
+
const toStdout = !flags.file;
|
|
264
|
+
if (toStdout && process.stdout.isTTY) {
|
|
265
|
+
this.error(`Endpoint returns a binary file. Specify the output path with -f / --file (e.g. -f report.xlsx), or redirect stdout to a file.`);
|
|
265
266
|
}
|
|
266
267
|
const binResponse = await apiRequest(endpoint.method, apiPath, {
|
|
267
268
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
@@ -269,24 +270,55 @@ export default class SavedViews extends Command {
|
|
|
269
270
|
contextName: flags.context,
|
|
270
271
|
binary: true,
|
|
271
272
|
});
|
|
272
|
-
const fsModule = await import('node:fs');
|
|
273
273
|
const binData = binResponse.data;
|
|
274
274
|
const buffer = Buffer.isBuffer(binData)
|
|
275
275
|
? binData
|
|
276
276
|
: Buffer.from(binData);
|
|
277
|
+
if (toStdout) {
|
|
278
|
+
// Nothing else may reach stdout here — it is the file.
|
|
279
|
+
process.stdout.write(buffer);
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
const fsModule = await import('node:fs');
|
|
277
283
|
fsModule.writeFileSync(flags.file, buffer);
|
|
278
284
|
this.log(`Saved ${buffer.length} bytes to ${flags.file}`);
|
|
279
285
|
return;
|
|
280
286
|
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
+
let response;
|
|
288
|
+
if (endpoint.hasBinaryVariant) {
|
|
289
|
+
// This route answers JSON or a file depending on the request body, so the
|
|
290
|
+
// bytes are read first and the response's own content-type decides. Asking
|
|
291
|
+
// for text here would corrupt an xlsx/pdf before anything could inspect it.
|
|
292
|
+
const raw = await apiRequest(endpoint.method, apiPath, {
|
|
293
|
+
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
294
|
+
data,
|
|
295
|
+
contextName: flags.context,
|
|
296
|
+
binary: true,
|
|
297
|
+
});
|
|
298
|
+
const rawType = String(raw.headers['content-type'] || '').toLowerCase();
|
|
299
|
+
const rawData = raw.data;
|
|
300
|
+
const buffer = Buffer.isBuffer(rawData) ? rawData : Buffer.from(rawData);
|
|
301
|
+
if (!isTextualMediaType(rawType)) {
|
|
302
|
+
if (process.stdout.isTTY) {
|
|
303
|
+
this.error(`The API returned ${rawType || 'binary data'}. Redirect stdout to a file, e.g. ... > report.xlsx`);
|
|
304
|
+
}
|
|
305
|
+
// Nothing else may reach stdout here — it is the file.
|
|
306
|
+
process.stdout.write(buffer);
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
response = { data: buffer.toString('utf8'), headers: raw.headers, status: raw.status };
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
response = await apiRequest(endpoint.method, apiPath, {
|
|
313
|
+
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
314
|
+
data,
|
|
315
|
+
contextName: flags.context,
|
|
316
|
+
acceptAny: true,
|
|
317
|
+
});
|
|
318
|
+
}
|
|
287
319
|
// Check content-type for non-JSON responses (XML, text, etc.)
|
|
288
320
|
const contentType = (response.headers['content-type'] || '').toLowerCase();
|
|
289
|
-
if (contentType
|
|
321
|
+
if (isTextualMediaType(contentType) && !contentType.startsWith('application/json')) {
|
|
290
322
|
// Output raw content for XML/text responses (CSV, plain, HTML, etc.)
|
|
291
323
|
this.log(String(response.data));
|
|
292
324
|
return;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command, Args, Flags } from '@oclif/core';
|
|
2
|
-
import { apiRequest, ApiError, normalizeSortBy } from '../lib/api-client.js';
|
|
2
|
+
import { apiRequest, ApiError, isTextualMediaType, normalizeSortBy } from '../lib/api-client.js';
|
|
3
3
|
import { getCurrentContext, getContext, getBaseUrls, hasCredentials, missingCredentialsMessage } from '../lib/config.js';
|
|
4
4
|
// Add 'import' action if both create and update exist
|
|
5
5
|
const HAS_IMPORT = true;
|
|
@@ -8,11 +8,11 @@ const HAS_CAMT053 = false;
|
|
|
8
8
|
const NESTED_IMPORTS = [];
|
|
9
9
|
const SUBCOMMANDS = [...["list", "create", "get", "update", "delete"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
|
|
10
10
|
const ENDPOINTS = {
|
|
11
|
-
'list': { method: 'GET', path: '/v1/scheduled-processor-mappings', summary: 'List all scheduled processor mappings for the tenant', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
12
|
-
'create': { method: 'POST', path: '/v1/scheduled-processor-mappings', summary: 'Schedule a processor to run on a cron expression', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
13
|
-
'get': { method: 'GET', path: '/v1/scheduled-processor-mappings/{id}', summary: 'Get a scheduled processor mapping by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
14
|
-
'update': { method: 'PATCH', path: '/v1/scheduled-processor-mappings/{id}', summary: 'Update a scheduled processor mapping', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
15
|
-
'delete': { method: 'DELETE', path: '/v1/scheduled-processor-mappings/{id}', summary: 'Delete a scheduled processor mapping and its schedule', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
11
|
+
'list': { method: 'GET', path: '/v1/scheduled-processor-mappings', summary: 'List all scheduled processor mappings for the tenant', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
12
|
+
'create': { method: 'POST', path: '/v1/scheduled-processor-mappings', summary: 'Schedule a processor to run on a cron expression', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
13
|
+
'get': { method: 'GET', path: '/v1/scheduled-processor-mappings/{id}', summary: 'Get a scheduled processor mapping by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
14
|
+
'update': { method: 'PATCH', path: '/v1/scheduled-processor-mappings/{id}', summary: 'Update a scheduled processor mapping', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
+
'delete': { method: 'DELETE', path: '/v1/scheduled-processor-mappings/{id}', summary: 'Delete a scheduled processor mapping and its schedule', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
16
16
|
};
|
|
17
17
|
export default class ScheduledProcessorMappings extends Command {
|
|
18
18
|
static description = 'Manage scheduled processor mappings';
|
|
@@ -264,10 +264,11 @@ export default class ScheduledProcessorMappings extends Command {
|
|
|
264
264
|
process.stderr.write('\n');
|
|
265
265
|
}
|
|
266
266
|
try {
|
|
267
|
-
// Binary response:
|
|
267
|
+
// Binary response: to -f / --file, or to stdout when it is redirected.
|
|
268
268
|
if (endpoint.isBinaryResponse) {
|
|
269
|
-
|
|
270
|
-
|
|
269
|
+
const toStdout = !flags.file;
|
|
270
|
+
if (toStdout && process.stdout.isTTY) {
|
|
271
|
+
this.error(`Endpoint returns a binary file. Specify the output path with -f / --file (e.g. -f report.xlsx), or redirect stdout to a file.`);
|
|
271
272
|
}
|
|
272
273
|
const binResponse = await apiRequest(endpoint.method, apiPath, {
|
|
273
274
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
@@ -275,24 +276,55 @@ export default class ScheduledProcessorMappings extends Command {
|
|
|
275
276
|
contextName: flags.context,
|
|
276
277
|
binary: true,
|
|
277
278
|
});
|
|
278
|
-
const fsModule = await import('node:fs');
|
|
279
279
|
const binData = binResponse.data;
|
|
280
280
|
const buffer = Buffer.isBuffer(binData)
|
|
281
281
|
? binData
|
|
282
282
|
: Buffer.from(binData);
|
|
283
|
+
if (toStdout) {
|
|
284
|
+
// Nothing else may reach stdout here — it is the file.
|
|
285
|
+
process.stdout.write(buffer);
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
const fsModule = await import('node:fs');
|
|
283
289
|
fsModule.writeFileSync(flags.file, buffer);
|
|
284
290
|
this.log(`Saved ${buffer.length} bytes to ${flags.file}`);
|
|
285
291
|
return;
|
|
286
292
|
}
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
+
let response;
|
|
294
|
+
if (endpoint.hasBinaryVariant) {
|
|
295
|
+
// This route answers JSON or a file depending on the request body, so the
|
|
296
|
+
// bytes are read first and the response's own content-type decides. Asking
|
|
297
|
+
// for text here would corrupt an xlsx/pdf before anything could inspect it.
|
|
298
|
+
const raw = await apiRequest(endpoint.method, apiPath, {
|
|
299
|
+
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
300
|
+
data,
|
|
301
|
+
contextName: flags.context,
|
|
302
|
+
binary: true,
|
|
303
|
+
});
|
|
304
|
+
const rawType = String(raw.headers['content-type'] || '').toLowerCase();
|
|
305
|
+
const rawData = raw.data;
|
|
306
|
+
const buffer = Buffer.isBuffer(rawData) ? rawData : Buffer.from(rawData);
|
|
307
|
+
if (!isTextualMediaType(rawType)) {
|
|
308
|
+
if (process.stdout.isTTY) {
|
|
309
|
+
this.error(`The API returned ${rawType || 'binary data'}. Redirect stdout to a file, e.g. ... > report.xlsx`);
|
|
310
|
+
}
|
|
311
|
+
// Nothing else may reach stdout here — it is the file.
|
|
312
|
+
process.stdout.write(buffer);
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
response = { data: buffer.toString('utf8'), headers: raw.headers, status: raw.status };
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
response = await apiRequest(endpoint.method, apiPath, {
|
|
319
|
+
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
320
|
+
data,
|
|
321
|
+
contextName: flags.context,
|
|
322
|
+
acceptAny: true,
|
|
323
|
+
});
|
|
324
|
+
}
|
|
293
325
|
// Check content-type for non-JSON responses (XML, text, etc.)
|
|
294
326
|
const contentType = (response.headers['content-type'] || '').toLowerCase();
|
|
295
|
-
if (contentType
|
|
327
|
+
if (isTextualMediaType(contentType) && !contentType.startsWith('application/json')) {
|
|
296
328
|
// Output raw content for XML/text responses (CSV, plain, HTML, etc.)
|
|
297
329
|
this.log(String(response.data));
|
|
298
330
|
return;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command, Args, Flags } from '@oclif/core';
|
|
2
|
-
import { apiRequest, ApiError, normalizeSortBy } from '../lib/api-client.js';
|
|
2
|
+
import { apiRequest, ApiError, isTextualMediaType, normalizeSortBy } from '../lib/api-client.js';
|
|
3
3
|
import { getCurrentContext, getContext, getBaseUrls, hasCredentials, missingCredentialsMessage } from '../lib/config.js';
|
|
4
4
|
// Add 'import' action if both create and update exist
|
|
5
5
|
const HAS_IMPORT = true;
|
|
@@ -8,15 +8,15 @@ const HAS_CAMT053 = false;
|
|
|
8
8
|
const NESTED_IMPORTS = [];
|
|
9
9
|
const SUBCOMMANDS = [...["list", "create", "get", "update", "delete", "runs", "pause", "resume", "trigger"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
|
|
10
10
|
const ENDPOINTS = {
|
|
11
|
-
'list': { method: 'GET', path: '/v1/schedules', summary: 'List schedules with pagination (admin only)', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
12
|
-
'create': { method: 'POST', path: '/v1/schedules', summary: 'Create a schedule (admin only)', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
13
|
-
'get': { method: 'GET', path: '/v1/schedules/{id}', summary: 'Get a schedule by ID (admin only)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
14
|
-
'update': { method: 'PATCH', path: '/v1/schedules/{id}', summary: 'Update a schedule (admin only)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
15
|
-
'delete': { method: 'DELETE', path: '/v1/schedules/{id}', summary: 'Delete a schedule (admin only)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
16
|
-
'runs': { method: 'GET', path: '/v1/schedules/{id}/runs', summary: 'List a schedule\'s execution history with pagination (admin only)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
17
|
-
'pause': { method: 'POST', path: '/v1/schedules/{id}/pause', summary: 'Pause a schedule so it no longer runs (admin only)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
18
|
-
'resume': { method: 'POST', path: '/v1/schedules/{id}/resume', summary: 'Resume a paused schedule (admin only)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
19
|
-
'trigger': { method: 'POST', path: '/v1/schedules/{id}/trigger', summary: 'Manually run a schedule right now, rate-limited to one accepted trigger per schedule per 60 seconds (admin only)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
11
|
+
'list': { method: 'GET', path: '/v1/schedules', summary: 'List the caller\'s tenant\'s schedules with pagination (admin only)', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
12
|
+
'create': { method: 'POST', path: '/v1/schedules', summary: 'Create a schedule (admin only)', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
13
|
+
'get': { method: 'GET', path: '/v1/schedules/{id}', summary: 'Get a schedule by ID (admin only)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
14
|
+
'update': { method: 'PATCH', path: '/v1/schedules/{id}', summary: 'Update a schedule (admin only)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
+
'delete': { method: 'DELETE', path: '/v1/schedules/{id}', summary: 'Delete a schedule (admin only)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
16
|
+
'runs': { method: 'GET', path: '/v1/schedules/{id}/runs', summary: 'List a schedule\'s execution history with pagination (admin only)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
17
|
+
'pause': { method: 'POST', path: '/v1/schedules/{id}/pause', summary: 'Pause a schedule so it no longer runs (admin only)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
18
|
+
'resume': { method: 'POST', path: '/v1/schedules/{id}/resume', summary: 'Resume a paused schedule (admin only)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
19
|
+
'trigger': { method: 'POST', path: '/v1/schedules/{id}/trigger', summary: 'Manually run a schedule right now, rate-limited to one accepted trigger per schedule per 60 seconds (admin only)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
20
20
|
};
|
|
21
21
|
export default class Schedules extends Command {
|
|
22
22
|
static description = 'Manage schedules';
|
|
@@ -268,10 +268,11 @@ export default class Schedules extends Command {
|
|
|
268
268
|
process.stderr.write('\n');
|
|
269
269
|
}
|
|
270
270
|
try {
|
|
271
|
-
// Binary response:
|
|
271
|
+
// Binary response: to -f / --file, or to stdout when it is redirected.
|
|
272
272
|
if (endpoint.isBinaryResponse) {
|
|
273
|
-
|
|
274
|
-
|
|
273
|
+
const toStdout = !flags.file;
|
|
274
|
+
if (toStdout && process.stdout.isTTY) {
|
|
275
|
+
this.error(`Endpoint returns a binary file. Specify the output path with -f / --file (e.g. -f report.xlsx), or redirect stdout to a file.`);
|
|
275
276
|
}
|
|
276
277
|
const binResponse = await apiRequest(endpoint.method, apiPath, {
|
|
277
278
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
@@ -279,24 +280,55 @@ export default class Schedules extends Command {
|
|
|
279
280
|
contextName: flags.context,
|
|
280
281
|
binary: true,
|
|
281
282
|
});
|
|
282
|
-
const fsModule = await import('node:fs');
|
|
283
283
|
const binData = binResponse.data;
|
|
284
284
|
const buffer = Buffer.isBuffer(binData)
|
|
285
285
|
? binData
|
|
286
286
|
: Buffer.from(binData);
|
|
287
|
+
if (toStdout) {
|
|
288
|
+
// Nothing else may reach stdout here — it is the file.
|
|
289
|
+
process.stdout.write(buffer);
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
const fsModule = await import('node:fs');
|
|
287
293
|
fsModule.writeFileSync(flags.file, buffer);
|
|
288
294
|
this.log(`Saved ${buffer.length} bytes to ${flags.file}`);
|
|
289
295
|
return;
|
|
290
296
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
+
let response;
|
|
298
|
+
if (endpoint.hasBinaryVariant) {
|
|
299
|
+
// This route answers JSON or a file depending on the request body, so the
|
|
300
|
+
// bytes are read first and the response's own content-type decides. Asking
|
|
301
|
+
// for text here would corrupt an xlsx/pdf before anything could inspect it.
|
|
302
|
+
const raw = await apiRequest(endpoint.method, apiPath, {
|
|
303
|
+
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
304
|
+
data,
|
|
305
|
+
contextName: flags.context,
|
|
306
|
+
binary: true,
|
|
307
|
+
});
|
|
308
|
+
const rawType = String(raw.headers['content-type'] || '').toLowerCase();
|
|
309
|
+
const rawData = raw.data;
|
|
310
|
+
const buffer = Buffer.isBuffer(rawData) ? rawData : Buffer.from(rawData);
|
|
311
|
+
if (!isTextualMediaType(rawType)) {
|
|
312
|
+
if (process.stdout.isTTY) {
|
|
313
|
+
this.error(`The API returned ${rawType || 'binary data'}. Redirect stdout to a file, e.g. ... > report.xlsx`);
|
|
314
|
+
}
|
|
315
|
+
// Nothing else may reach stdout here — it is the file.
|
|
316
|
+
process.stdout.write(buffer);
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
response = { data: buffer.toString('utf8'), headers: raw.headers, status: raw.status };
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
response = await apiRequest(endpoint.method, apiPath, {
|
|
323
|
+
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
324
|
+
data,
|
|
325
|
+
contextName: flags.context,
|
|
326
|
+
acceptAny: true,
|
|
327
|
+
});
|
|
328
|
+
}
|
|
297
329
|
// Check content-type for non-JSON responses (XML, text, etc.)
|
|
298
330
|
const contentType = (response.headers['content-type'] || '').toLowerCase();
|
|
299
|
-
if (contentType
|
|
331
|
+
if (isTextualMediaType(contentType) && !contentType.startsWith('application/json')) {
|
|
300
332
|
// Output raw content for XML/text responses (CSV, plain, HTML, etc.)
|
|
301
333
|
this.log(String(response.data));
|
|
302
334
|
return;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command, Args, Flags } from '@oclif/core';
|
|
2
|
-
import { apiRequest, ApiError, normalizeSortBy } from '../lib/api-client.js';
|
|
2
|
+
import { apiRequest, ApiError, isTextualMediaType, normalizeSortBy } from '../lib/api-client.js';
|
|
3
3
|
import { getCurrentContext, getContext, getBaseUrls, hasCredentials, missingCredentialsMessage } from '../lib/config.js';
|
|
4
4
|
// Add 'import' action if both create and update exist
|
|
5
5
|
const HAS_IMPORT = true;
|
|
@@ -8,12 +8,12 @@ const HAS_CAMT053 = false;
|
|
|
8
8
|
const NESTED_IMPORTS = [];
|
|
9
9
|
const SUBCOMMANDS = [...["list", "create", "get", "update", "delete", "journal-coverage"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
|
|
10
10
|
const ENDPOINTS = {
|
|
11
|
-
'list': { method: 'GET', path: '/v1/tag-configurations', summary: 'Get all tag configurations with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
12
|
-
'create': { method: 'POST', path: '/v1/tag-configurations', summary: 'Create a new tag configuration', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['resourceTypes', 'accountTypes', 'tagIds', 'tagCodes'] },
|
|
13
|
-
'get': { method: 'GET', path: '/v1/tag-configurations/{id}', summary: 'Get a tag configuration by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
14
|
-
'update': { method: 'PATCH', path: '/v1/tag-configurations/{id}', summary: 'Update a tag configuration', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['resourceTypes', 'accountTypes', 'tagIds', 'tagCodes'] },
|
|
15
|
-
'delete': { method: 'DELETE', path: '/v1/tag-configurations/{id}', summary: 'Delete a tag configuration', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
16
|
-
'journal-coverage': { method: 'GET', path: '/v1/tag-configurations/{id}/journal-coverage', summary: 'How much of the journal-line amount in scope carries one of this configuration\'s tags', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
11
|
+
'list': { method: 'GET', path: '/v1/tag-configurations', summary: 'Get all tag configurations with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
12
|
+
'create': { method: 'POST', path: '/v1/tag-configurations', summary: 'Create a new tag configuration', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['resourceTypes', 'accountTypes', 'tagIds', 'tagCodes'] },
|
|
13
|
+
'get': { method: 'GET', path: '/v1/tag-configurations/{id}', summary: 'Get a tag configuration by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
14
|
+
'update': { method: 'PATCH', path: '/v1/tag-configurations/{id}', summary: 'Update a tag configuration', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['resourceTypes', 'accountTypes', 'tagIds', 'tagCodes'] },
|
|
15
|
+
'delete': { method: 'DELETE', path: '/v1/tag-configurations/{id}', summary: 'Delete a tag configuration', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
16
|
+
'journal-coverage': { method: 'GET', path: '/v1/tag-configurations/{id}/journal-coverage', summary: 'How much of the journal-line amount in scope carries one of this configuration\'s tags', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
17
17
|
};
|
|
18
18
|
export default class TagConfigurations extends Command {
|
|
19
19
|
static description = 'Manage tag configurations';
|
|
@@ -265,10 +265,11 @@ export default class TagConfigurations extends Command {
|
|
|
265
265
|
process.stderr.write('\n');
|
|
266
266
|
}
|
|
267
267
|
try {
|
|
268
|
-
// Binary response:
|
|
268
|
+
// Binary response: to -f / --file, or to stdout when it is redirected.
|
|
269
269
|
if (endpoint.isBinaryResponse) {
|
|
270
|
-
|
|
271
|
-
|
|
270
|
+
const toStdout = !flags.file;
|
|
271
|
+
if (toStdout && process.stdout.isTTY) {
|
|
272
|
+
this.error(`Endpoint returns a binary file. Specify the output path with -f / --file (e.g. -f report.xlsx), or redirect stdout to a file.`);
|
|
272
273
|
}
|
|
273
274
|
const binResponse = await apiRequest(endpoint.method, apiPath, {
|
|
274
275
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
@@ -276,24 +277,55 @@ export default class TagConfigurations extends Command {
|
|
|
276
277
|
contextName: flags.context,
|
|
277
278
|
binary: true,
|
|
278
279
|
});
|
|
279
|
-
const fsModule = await import('node:fs');
|
|
280
280
|
const binData = binResponse.data;
|
|
281
281
|
const buffer = Buffer.isBuffer(binData)
|
|
282
282
|
? binData
|
|
283
283
|
: Buffer.from(binData);
|
|
284
|
+
if (toStdout) {
|
|
285
|
+
// Nothing else may reach stdout here — it is the file.
|
|
286
|
+
process.stdout.write(buffer);
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
const fsModule = await import('node:fs');
|
|
284
290
|
fsModule.writeFileSync(flags.file, buffer);
|
|
285
291
|
this.log(`Saved ${buffer.length} bytes to ${flags.file}`);
|
|
286
292
|
return;
|
|
287
293
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
+
let response;
|
|
295
|
+
if (endpoint.hasBinaryVariant) {
|
|
296
|
+
// This route answers JSON or a file depending on the request body, so the
|
|
297
|
+
// bytes are read first and the response's own content-type decides. Asking
|
|
298
|
+
// for text here would corrupt an xlsx/pdf before anything could inspect it.
|
|
299
|
+
const raw = await apiRequest(endpoint.method, apiPath, {
|
|
300
|
+
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
301
|
+
data,
|
|
302
|
+
contextName: flags.context,
|
|
303
|
+
binary: true,
|
|
304
|
+
});
|
|
305
|
+
const rawType = String(raw.headers['content-type'] || '').toLowerCase();
|
|
306
|
+
const rawData = raw.data;
|
|
307
|
+
const buffer = Buffer.isBuffer(rawData) ? rawData : Buffer.from(rawData);
|
|
308
|
+
if (!isTextualMediaType(rawType)) {
|
|
309
|
+
if (process.stdout.isTTY) {
|
|
310
|
+
this.error(`The API returned ${rawType || 'binary data'}. Redirect stdout to a file, e.g. ... > report.xlsx`);
|
|
311
|
+
}
|
|
312
|
+
// Nothing else may reach stdout here — it is the file.
|
|
313
|
+
process.stdout.write(buffer);
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
response = { data: buffer.toString('utf8'), headers: raw.headers, status: raw.status };
|
|
317
|
+
}
|
|
318
|
+
else {
|
|
319
|
+
response = await apiRequest(endpoint.method, apiPath, {
|
|
320
|
+
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
321
|
+
data,
|
|
322
|
+
contextName: flags.context,
|
|
323
|
+
acceptAny: true,
|
|
324
|
+
});
|
|
325
|
+
}
|
|
294
326
|
// Check content-type for non-JSON responses (XML, text, etc.)
|
|
295
327
|
const contentType = (response.headers['content-type'] || '').toLowerCase();
|
|
296
|
-
if (contentType
|
|
328
|
+
if (isTextualMediaType(contentType) && !contentType.startsWith('application/json')) {
|
|
297
329
|
// Output raw content for XML/text responses (CSV, plain, HTML, etc.)
|
|
298
330
|
this.log(String(response.data));
|
|
299
331
|
return;
|
package/dist/commands/tags.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command, Args, Flags } from '@oclif/core';
|
|
2
|
-
import { apiRequest, ApiError, normalizeSortBy } from '../lib/api-client.js';
|
|
2
|
+
import { apiRequest, ApiError, isTextualMediaType, normalizeSortBy } from '../lib/api-client.js';
|
|
3
3
|
import { getCurrentContext, getContext, getBaseUrls, hasCredentials, missingCredentialsMessage } from '../lib/config.js';
|
|
4
4
|
// Add 'import' action if both create and update exist
|
|
5
5
|
const HAS_IMPORT = true;
|
|
@@ -8,11 +8,11 @@ const HAS_CAMT053 = false;
|
|
|
8
8
|
const NESTED_IMPORTS = [];
|
|
9
9
|
const SUBCOMMANDS = [...["list", "create", "get", "update", "delete"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
|
|
10
10
|
const ENDPOINTS = {
|
|
11
|
-
'list': { method: 'GET', path: '/v1/tags', summary: 'Get all tags with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
12
|
-
'create': { method: 'POST', path: '/v1/tags', summary: 'Create a new tag', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
13
|
-
'get': { method: 'GET', path: '/v1/tags/{id}', summary: 'Get a tag by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
14
|
-
'update': { method: 'PATCH', path: '/v1/tags/{id}', summary: 'Update a tag', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
15
|
-
'delete': { method: 'DELETE', path: '/v1/tags/{id}', summary: 'Delete a tag', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
11
|
+
'list': { method: 'GET', path: '/v1/tags', summary: 'Get all tags with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
12
|
+
'create': { method: 'POST', path: '/v1/tags', summary: 'Create a new tag', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
13
|
+
'get': { method: 'GET', path: '/v1/tags/{id}', summary: 'Get a tag by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
14
|
+
'update': { method: 'PATCH', path: '/v1/tags/{id}', summary: 'Update a tag', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
+
'delete': { method: 'DELETE', path: '/v1/tags/{id}', summary: 'Delete a tag', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
16
16
|
};
|
|
17
17
|
export default class Tags extends Command {
|
|
18
18
|
static description = 'Manage tags';
|
|
@@ -264,10 +264,11 @@ export default class Tags extends Command {
|
|
|
264
264
|
process.stderr.write('\n');
|
|
265
265
|
}
|
|
266
266
|
try {
|
|
267
|
-
// Binary response:
|
|
267
|
+
// Binary response: to -f / --file, or to stdout when it is redirected.
|
|
268
268
|
if (endpoint.isBinaryResponse) {
|
|
269
|
-
|
|
270
|
-
|
|
269
|
+
const toStdout = !flags.file;
|
|
270
|
+
if (toStdout && process.stdout.isTTY) {
|
|
271
|
+
this.error(`Endpoint returns a binary file. Specify the output path with -f / --file (e.g. -f report.xlsx), or redirect stdout to a file.`);
|
|
271
272
|
}
|
|
272
273
|
const binResponse = await apiRequest(endpoint.method, apiPath, {
|
|
273
274
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
@@ -275,24 +276,55 @@ export default class Tags extends Command {
|
|
|
275
276
|
contextName: flags.context,
|
|
276
277
|
binary: true,
|
|
277
278
|
});
|
|
278
|
-
const fsModule = await import('node:fs');
|
|
279
279
|
const binData = binResponse.data;
|
|
280
280
|
const buffer = Buffer.isBuffer(binData)
|
|
281
281
|
? binData
|
|
282
282
|
: Buffer.from(binData);
|
|
283
|
+
if (toStdout) {
|
|
284
|
+
// Nothing else may reach stdout here — it is the file.
|
|
285
|
+
process.stdout.write(buffer);
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
const fsModule = await import('node:fs');
|
|
283
289
|
fsModule.writeFileSync(flags.file, buffer);
|
|
284
290
|
this.log(`Saved ${buffer.length} bytes to ${flags.file}`);
|
|
285
291
|
return;
|
|
286
292
|
}
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
+
let response;
|
|
294
|
+
if (endpoint.hasBinaryVariant) {
|
|
295
|
+
// This route answers JSON or a file depending on the request body, so the
|
|
296
|
+
// bytes are read first and the response's own content-type decides. Asking
|
|
297
|
+
// for text here would corrupt an xlsx/pdf before anything could inspect it.
|
|
298
|
+
const raw = await apiRequest(endpoint.method, apiPath, {
|
|
299
|
+
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
300
|
+
data,
|
|
301
|
+
contextName: flags.context,
|
|
302
|
+
binary: true,
|
|
303
|
+
});
|
|
304
|
+
const rawType = String(raw.headers['content-type'] || '').toLowerCase();
|
|
305
|
+
const rawData = raw.data;
|
|
306
|
+
const buffer = Buffer.isBuffer(rawData) ? rawData : Buffer.from(rawData);
|
|
307
|
+
if (!isTextualMediaType(rawType)) {
|
|
308
|
+
if (process.stdout.isTTY) {
|
|
309
|
+
this.error(`The API returned ${rawType || 'binary data'}. Redirect stdout to a file, e.g. ... > report.xlsx`);
|
|
310
|
+
}
|
|
311
|
+
// Nothing else may reach stdout here — it is the file.
|
|
312
|
+
process.stdout.write(buffer);
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
response = { data: buffer.toString('utf8'), headers: raw.headers, status: raw.status };
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
response = await apiRequest(endpoint.method, apiPath, {
|
|
319
|
+
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
320
|
+
data,
|
|
321
|
+
contextName: flags.context,
|
|
322
|
+
acceptAny: true,
|
|
323
|
+
});
|
|
324
|
+
}
|
|
293
325
|
// Check content-type for non-JSON responses (XML, text, etc.)
|
|
294
326
|
const contentType = (response.headers['content-type'] || '').toLowerCase();
|
|
295
|
-
if (contentType
|
|
327
|
+
if (isTextualMediaType(contentType) && !contentType.startsWith('application/json')) {
|
|
296
328
|
// Output raw content for XML/text responses (CSV, plain, HTML, etc.)
|
|
297
329
|
this.log(String(response.data));
|
|
298
330
|
return;
|