@aderis-erp/cli 1.0.14 → 1.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -8
- package/dist/commands/access-groups.d.ts +0 -1
- package/dist/commands/access-groups.js +22 -78
- package/dist/commands/account-transaction-allocations.d.ts +0 -1
- package/dist/commands/account-transaction-allocations.js +22 -78
- package/dist/commands/account-transactions.d.ts +0 -1
- package/dist/commands/account-transactions.js +28 -82
- package/dist/commands/accounts.d.ts +0 -1
- package/dist/commands/accounts.js +24 -80
- package/dist/commands/agent-platform.d.ts +0 -1
- package/dist/commands/agent-platform.js +24 -79
- package/dist/commands/analytics.d.ts +39 -0
- package/dist/commands/analytics.js +1113 -0
- package/dist/commands/{documents.d.ts → api-keys.d.ts} +1 -2
- package/dist/commands/api-keys.js +1117 -0
- package/dist/commands/audit-log.d.ts +0 -1
- package/dist/commands/audit-log.js +22 -78
- package/dist/commands/auth/login.d.ts +9 -0
- package/dist/commands/auth/login.js +83 -21
- package/dist/commands/auth/logout.d.ts +6 -0
- package/dist/commands/auth/logout.js +23 -6
- package/dist/commands/auth/status.js +46 -23
- package/dist/commands/auth/tenant.js +15 -9
- package/dist/commands/auth.d.ts +0 -1
- package/dist/commands/auth.js +37 -51
- package/dist/commands/bank-accounts.d.ts +0 -1
- package/dist/commands/bank-accounts.js +22 -78
- package/dist/commands/bank-integrations.d.ts +0 -1
- package/dist/commands/bank-integrations.js +22 -78
- package/dist/commands/business-partners.d.ts +1 -1
- package/dist/commands/business-partners.js +38 -84
- package/dist/commands/chat.d.ts +0 -1
- package/dist/commands/chat.js +22 -78
- package/dist/commands/config/add.d.ts +12 -0
- package/dist/commands/config/add.js +97 -17
- package/dist/commands/config/current.js +12 -4
- package/dist/commands/config/list.js +12 -4
- package/dist/commands/config/set-api-key.d.ts +10 -0
- package/dist/commands/config/set-api-key.js +49 -0
- package/dist/commands/config/use.js +3 -4
- package/dist/commands/credit-card-accounts.d.ts +0 -1
- package/dist/commands/credit-card-accounts.js +22 -78
- package/dist/commands/delivery-notes.d.ts +0 -1
- package/dist/commands/delivery-notes.js +29 -84
- package/dist/commands/dunning-configs.d.ts +0 -1
- package/dist/commands/dunning-configs.js +22 -78
- package/dist/commands/dunning-reminders.d.ts +0 -1
- package/dist/commands/dunning-reminders.js +22 -78
- package/dist/commands/dunning-runs.d.ts +0 -1
- package/dist/commands/dunning-runs.js +22 -78
- package/dist/commands/email-transactions.d.ts +0 -1
- package/dist/commands/email-transactions.js +22 -78
- package/dist/commands/employees.d.ts +0 -1
- package/dist/commands/employees.js +24 -80
- package/dist/commands/entity-configurations.d.ts +0 -1
- package/dist/commands/entity-configurations.js +22 -78
- package/dist/commands/event-processor-mappings.d.ts +0 -1
- package/dist/commands/event-processor-mappings.js +22 -78
- package/dist/commands/{files-internal.d.ts → files.d.ts} +1 -2
- package/dist/commands/{files-internal.js → files.js} +40 -90
- package/dist/commands/health.d.ts +0 -1
- package/dist/commands/health.js +22 -78
- package/dist/commands/inbox.d.ts +0 -1
- package/dist/commands/inbox.js +22 -78
- package/dist/commands/integrations.d.ts +0 -1
- package/dist/commands/integrations.js +22 -78
- package/dist/commands/item-categories.d.ts +0 -1
- package/dist/commands/item-categories.js +22 -78
- package/dist/commands/items.d.ts +0 -1
- package/dist/commands/items.js +24 -80
- package/dist/commands/journal-lines.d.ts +0 -1
- package/dist/commands/journal-lines.js +22 -78
- package/dist/commands/journals.d.ts +1 -1
- package/dist/commands/journals.js +29 -81
- package/dist/commands/ledgers.d.ts +0 -1
- package/dist/commands/ledgers.js +26 -79
- package/dist/commands/nango-integrations.d.ts +39 -0
- package/dist/commands/nango-integrations.js +1115 -0
- package/dist/commands/office-locations.d.ts +0 -1
- package/dist/commands/office-locations.js +22 -78
- package/dist/commands/payment-methods.d.ts +0 -1
- package/dist/commands/payment-methods.js +22 -78
- package/dist/commands/payment-runs.d.ts +0 -1
- package/dist/commands/payment-runs.js +24 -79
- package/dist/commands/payment-terms.d.ts +0 -1
- package/dist/commands/payment-terms.js +22 -78
- package/dist/commands/payments.d.ts +0 -1
- package/dist/commands/payments.js +22 -78
- package/dist/commands/price-lists.d.ts +0 -1
- package/dist/commands/price-lists.js +22 -78
- package/dist/commands/processor-mappings.d.ts +0 -1
- package/dist/commands/processor-mappings.js +22 -78
- package/dist/commands/processors.d.ts +0 -1
- package/dist/commands/processors.js +22 -78
- package/dist/commands/purchase-order-types.d.ts +0 -1
- package/dist/commands/purchase-order-types.js +22 -78
- package/dist/commands/purchase-orders.d.ts +1 -1
- package/dist/commands/purchase-orders.js +35 -84
- package/dist/commands/reports.d.ts +0 -1
- package/dist/commands/reports.js +23 -79
- package/dist/commands/salary-types.d.ts +0 -1
- package/dist/commands/salary-types.js +22 -78
- package/dist/commands/sales-invoices.d.ts +0 -1
- package/dist/commands/sales-invoices.js +38 -89
- package/dist/commands/sales-orders.d.ts +0 -1
- package/dist/commands/sales-orders.js +26 -82
- package/dist/commands/saved-views.d.ts +0 -1
- package/dist/commands/saved-views.js +22 -78
- package/dist/commands/scheduled-processor-mappings.d.ts +0 -1
- package/dist/commands/scheduled-processor-mappings.js +22 -78
- package/dist/commands/schedules.d.ts +0 -1
- package/dist/commands/schedules.js +22 -78
- package/dist/commands/switch.js +14 -7
- package/dist/commands/tag-configurations.d.ts +39 -0
- package/dist/commands/{documents.js → tag-configurations.js} +40 -96
- package/dist/commands/tags.d.ts +0 -1
- package/dist/commands/tags.js +22 -78
- package/dist/commands/tax-codes.d.ts +0 -1
- package/dist/commands/tax-codes.js +22 -78
- package/dist/commands/tax-matrix.d.ts +0 -1
- package/dist/commands/tax-matrix.js +22 -78
- package/dist/commands/tenants.d.ts +0 -1
- package/dist/commands/tenants.js +24 -79
- package/dist/commands/text-templates.d.ts +0 -1
- package/dist/commands/text-templates.js +22 -78
- package/dist/commands/tools.d.ts +0 -1
- package/dist/commands/tools.js +22 -78
- package/dist/commands/vat-declarations.d.ts +1 -1
- package/dist/commands/vat-declarations.js +28 -79
- package/dist/commands/vendor-invoices.d.ts +0 -1
- package/dist/commands/vendor-invoices.js +31 -86
- package/dist/commands/webhooks.d.ts +0 -1
- package/dist/commands/webhooks.js +22 -78
- package/dist/commands/workflows.d.ts +0 -1
- package/dist/commands/workflows.js +22 -78
- package/dist/lib/api-client.js +8 -6
- package/dist/lib/auth/constants.d.ts +11 -0
- package/dist/lib/auth/constants.js +11 -0
- package/dist/lib/auth/errors.d.ts +17 -0
- package/dist/lib/auth/errors.js +22 -0
- package/dist/lib/auth/flow.d.ts +27 -0
- package/dist/lib/auth/flow.js +163 -0
- package/dist/lib/auth/guards.d.ts +5 -0
- package/dist/lib/auth/guards.js +14 -0
- package/dist/lib/auth/http.d.ts +16 -0
- package/dist/lib/auth/http.js +49 -0
- package/dist/lib/auth/index.d.ts +49 -0
- package/dist/lib/auth/index.js +217 -0
- package/dist/lib/auth/keycloak-provider.d.ts +75 -0
- package/dist/lib/auth/keycloak-provider.js +215 -0
- package/dist/lib/auth/pages.d.ts +25 -0
- package/dist/lib/auth/pages.js +123 -0
- package/dist/lib/auth/pkce.d.ts +11 -0
- package/dist/lib/auth/pkce.js +17 -0
- package/dist/lib/auth/tokens.d.ts +54 -0
- package/dist/lib/auth/tokens.js +104 -0
- package/dist/lib/auth/types.d.ts +57 -0
- package/dist/lib/auth/types.js +1 -0
- package/dist/lib/auth/validated-url.d.ts +6 -0
- package/dist/lib/auth/validated-url.js +53 -0
- package/dist/lib/base-api-command.js +6 -4
- package/dist/lib/config.d.ts +44 -4
- package/dist/lib/config.js +55 -19
- package/oclif.manifest.json +1181 -330
- package/package.json +3 -2
- package/dist/lib/auth.d.ts +0 -35
- package/dist/lib/auth.js +0 -566
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Command, Args, Flags } from '@oclif/core';
|
|
2
2
|
import { apiRequest, ApiError, normalizeSortBy } from '../lib/api-client.js';
|
|
3
|
-
import { getCurrentContext, getContext, getBaseUrls } from '../lib/config.js';
|
|
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;
|
|
6
6
|
const HAS_EXPORT = true;
|
|
@@ -28,7 +28,7 @@ export default class EventProcessorMappings extends Command {
|
|
|
28
28
|
id: Args.string({ description: 'Resource ID (for get, update, delete operations)', required: false }),
|
|
29
29
|
};
|
|
30
30
|
static flags = {
|
|
31
|
-
context: Flags.string({ char: 'c', description: 'Context to use' }),
|
|
31
|
+
context: Flags.string({ char: 'c', aliases: ['config'], env: 'ADERIS_CONTEXT', description: 'Context to use for this command, overriding the current context (alias: --config)' }),
|
|
32
32
|
output: Flags.string({ char: 'o', description: 'Output format (default: table for import, json for other commands)', options: ['json', 'table', 'csv'] }),
|
|
33
33
|
table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
|
|
34
34
|
csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
|
|
@@ -42,7 +42,7 @@ export default class EventProcessorMappings extends Command {
|
|
|
42
42
|
'sort-by': Flags.string({ description: 'Sort field(s), comma-separated. Direction (:ASC/:DESC) is optional; date-like fields default to DESC, others to ASC.' }),
|
|
43
43
|
filter: Flags.string({ description: 'Filter (e.g. --filter status=ACTIVE). Operators: in:A,B, like:%, gt:, lt:, null:', multiple: true }),
|
|
44
44
|
query: Flags.string({ description: 'Additional query params as JSON' }),
|
|
45
|
-
fields: Flags.string({ description: 'Comma-separated list of fields to
|
|
45
|
+
fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
|
|
46
46
|
sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
|
|
47
47
|
'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
|
|
48
48
|
};
|
|
@@ -100,9 +100,9 @@ export default class EventProcessorMappings extends Command {
|
|
|
100
100
|
}
|
|
101
101
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
102
102
|
if (!context)
|
|
103
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
104
|
-
if (!context
|
|
105
|
-
this.error(
|
|
103
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
104
|
+
if (!hasCredentials(context))
|
|
105
|
+
this.error(missingCredentialsMessage(context));
|
|
106
106
|
const queryParams = {};
|
|
107
107
|
if (flags.page)
|
|
108
108
|
queryParams.page = flags.page;
|
|
@@ -114,6 +114,8 @@ export default class EventProcessorMappings extends Command {
|
|
|
114
114
|
queryParams.search = flags.search;
|
|
115
115
|
if (flags['sort-by'])
|
|
116
116
|
queryParams.sortBy = normalizeSortBy(flags['sort-by']);
|
|
117
|
+
if (flags.fields)
|
|
118
|
+
queryParams.fields = flags.fields;
|
|
117
119
|
if (flags.query) {
|
|
118
120
|
try {
|
|
119
121
|
Object.assign(queryParams, JSON.parse(flags.query));
|
|
@@ -316,11 +318,6 @@ export default class EventProcessorMappings extends Command {
|
|
|
316
318
|
output = obj[arrayKeys[0]];
|
|
317
319
|
}
|
|
318
320
|
}
|
|
319
|
-
// Filter fields if --fields flag is provided
|
|
320
|
-
if (flags.fields) {
|
|
321
|
-
const fieldList = flags.fields.split(',').map(f => f.trim());
|
|
322
|
-
output = this.filterFields(output, fieldList);
|
|
323
|
-
}
|
|
324
321
|
// Sanitize output if requested
|
|
325
322
|
if (flags.sanitize) {
|
|
326
323
|
output = this.sanitizeOutput(output);
|
|
@@ -345,9 +342,9 @@ export default class EventProcessorMappings extends Command {
|
|
|
345
342
|
async runImport(flags) {
|
|
346
343
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
347
344
|
if (!context)
|
|
348
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
349
|
-
if (!context
|
|
350
|
-
this.error(
|
|
345
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
346
|
+
if (!hasCredentials(context))
|
|
347
|
+
this.error(missingCredentialsMessage(context));
|
|
351
348
|
// Get data from file, --data, or --set flags
|
|
352
349
|
let items = [];
|
|
353
350
|
if (flags.file) {
|
|
@@ -557,9 +554,9 @@ export default class EventProcessorMappings extends Command {
|
|
|
557
554
|
async runExport(flags) {
|
|
558
555
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
559
556
|
if (!context)
|
|
560
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
561
|
-
if (!context
|
|
562
|
-
this.error(
|
|
557
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
558
|
+
if (!hasCredentials(context))
|
|
559
|
+
this.error(missingCredentialsMessage(context));
|
|
563
560
|
const listEndpoint = ENDPOINTS['list'];
|
|
564
561
|
if (!listEndpoint)
|
|
565
562
|
this.error('Export requires a list endpoint');
|
|
@@ -575,6 +572,8 @@ export default class EventProcessorMappings extends Command {
|
|
|
575
572
|
baseParams.sortBy = normalizeSortBy(flags['sort-by']);
|
|
576
573
|
if (flags.include)
|
|
577
574
|
baseParams.include = flags.include;
|
|
575
|
+
if (flags.fields)
|
|
576
|
+
baseParams.fields = flags.fields;
|
|
578
577
|
if (flags.query) {
|
|
579
578
|
try {
|
|
580
579
|
Object.assign(baseParams, JSON.parse(flags.query));
|
|
@@ -629,12 +628,7 @@ export default class EventProcessorMappings extends Command {
|
|
|
629
628
|
}
|
|
630
629
|
}
|
|
631
630
|
process.stderr.write(' done\n');
|
|
632
|
-
// Filter fields if specified
|
|
633
631
|
let output = allItems;
|
|
634
|
-
if (flags.fields) {
|
|
635
|
-
const fieldList = flags.fields.split(',').map(f => f.trim());
|
|
636
|
-
output = this.filterFields(allItems, fieldList);
|
|
637
|
-
}
|
|
638
632
|
// Sanitize output if requested
|
|
639
633
|
if (flags.sanitize) {
|
|
640
634
|
output = this.sanitizeOutput(output);
|
|
@@ -651,9 +645,9 @@ export default class EventProcessorMappings extends Command {
|
|
|
651
645
|
async runCamt053Import(flags) {
|
|
652
646
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
653
647
|
if (!context)
|
|
654
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
655
|
-
if (!context
|
|
656
|
-
this.error(
|
|
648
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
649
|
+
if (!hasCredentials(context))
|
|
650
|
+
this.error(missingCredentialsMessage(context));
|
|
657
651
|
if (!flags.file) {
|
|
658
652
|
this.error('CAMT.053 import requires --file (-f) flag with path to XML file');
|
|
659
653
|
}
|
|
@@ -741,9 +735,9 @@ export default class EventProcessorMappings extends Command {
|
|
|
741
735
|
}
|
|
742
736
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
743
737
|
if (!context)
|
|
744
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
745
|
-
if (!context
|
|
746
|
-
this.error(
|
|
738
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
739
|
+
if (!hasCredentials(context))
|
|
740
|
+
this.error(missingCredentialsMessage(context));
|
|
747
741
|
// Get data from file, --data, or --set flags
|
|
748
742
|
let items = [];
|
|
749
743
|
if (flags.file) {
|
|
@@ -963,56 +957,6 @@ export default class EventProcessorMappings extends Command {
|
|
|
963
957
|
return `\$${op}`;
|
|
964
958
|
return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
|
|
965
959
|
}
|
|
966
|
-
filterFields(data, fields) {
|
|
967
|
-
const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
|
|
968
|
-
const pick = (obj, keys) => {
|
|
969
|
-
// Group keys by their first segment so sibling sub-paths
|
|
970
|
-
// (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
|
|
971
|
-
// and don't clobber each other on merge.
|
|
972
|
-
const directKeys = new Set();
|
|
973
|
-
const nestedKeys = new Map();
|
|
974
|
-
for (const key of keys) {
|
|
975
|
-
if (key.split('.').some(isUnsafeKey))
|
|
976
|
-
continue;
|
|
977
|
-
if (key.includes('.')) {
|
|
978
|
-
const [first, ...rest] = key.split('.');
|
|
979
|
-
if (isUnsafeKey(first))
|
|
980
|
-
continue;
|
|
981
|
-
const list = nestedKeys.get(first);
|
|
982
|
-
if (list) {
|
|
983
|
-
list.push(rest.join('.'));
|
|
984
|
-
}
|
|
985
|
-
else {
|
|
986
|
-
nestedKeys.set(first, [rest.join('.')]);
|
|
987
|
-
}
|
|
988
|
-
}
|
|
989
|
-
else {
|
|
990
|
-
directKeys.add(key);
|
|
991
|
-
}
|
|
992
|
-
}
|
|
993
|
-
const result = {};
|
|
994
|
-
for (const key of directKeys) {
|
|
995
|
-
if (key in obj)
|
|
996
|
-
result[key] = obj[key];
|
|
997
|
-
}
|
|
998
|
-
for (const [first, restKeys] of nestedKeys) {
|
|
999
|
-
const val = obj[first];
|
|
1000
|
-
if (!val || typeof val !== 'object')
|
|
1001
|
-
continue;
|
|
1002
|
-
if (Array.isArray(val)) {
|
|
1003
|
-
result[first] = val.map(item => pick(item, restKeys));
|
|
1004
|
-
}
|
|
1005
|
-
else {
|
|
1006
|
-
result[first] = pick(val, restKeys);
|
|
1007
|
-
}
|
|
1008
|
-
}
|
|
1009
|
-
return result;
|
|
1010
|
-
};
|
|
1011
|
-
if (Array.isArray(data)) {
|
|
1012
|
-
return data.map(item => pick(item, fields));
|
|
1013
|
-
}
|
|
1014
|
-
return pick(data, fields);
|
|
1015
|
-
}
|
|
1016
960
|
sanitizeOutput(data) {
|
|
1017
961
|
const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
|
|
1018
962
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
|
-
export default class
|
|
2
|
+
export default class Files extends Command {
|
|
3
3
|
static description: string;
|
|
4
4
|
static examples: string[];
|
|
5
5
|
static args: {
|
|
@@ -31,7 +31,6 @@ export default class FilesInternal extends Command {
|
|
|
31
31
|
private runCamt053Import;
|
|
32
32
|
private runNestedImport;
|
|
33
33
|
private parseFilterValue;
|
|
34
|
-
private filterFields;
|
|
35
34
|
private sanitizeOutput;
|
|
36
35
|
private getColumns;
|
|
37
36
|
private getValue;
|
|
@@ -1,36 +1,42 @@
|
|
|
1
1
|
import { Command, Args, Flags } from '@oclif/core';
|
|
2
2
|
import { apiRequest, ApiError, normalizeSortBy } from '../lib/api-client.js';
|
|
3
|
-
import { getCurrentContext, getContext, getBaseUrls } from '../lib/config.js';
|
|
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;
|
|
6
|
-
const HAS_EXPORT =
|
|
6
|
+
const HAS_EXPORT = true;
|
|
7
7
|
const HAS_CAMT053 = false;
|
|
8
8
|
const NESTED_IMPORTS = [];
|
|
9
|
-
const SUBCOMMANDS = [...["create", "download-all", "download", "references", "update", "
|
|
9
|
+
const SUBCOMMANDS = [...["list", "create", "download-all", "download", "get", "references", "update", "discard", "move", "retention", "hold", "reclassify"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
|
|
10
10
|
const ENDPOINTS = {
|
|
11
|
+
'list': { method: 'GET', path: '/v1/files', summary: 'List files (paginated, permission-scoped)', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
11
12
|
'create': { method: 'POST', path: '/v1/files', summary: 'Upload a file (streaming, memory-efficient)', pathParams: [], hasBody: false, isMultipart: true, isBinaryResponse: false, bodyArrayFields: [] },
|
|
12
13
|
'download-all': { method: 'GET', path: '/v1/files/download/*', summary: 'Download file directly by storage key', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
13
14
|
'download': { method: 'GET', path: '/v1/files/{id}/download', summary: 'Get a signed download URL for a file', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
15
|
+
'get': { method: 'GET', path: '/v1/files/{id}', summary: 'Get a file by id (permission-scoped)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
14
16
|
'references': { method: 'POST', path: '/v1/files/references', summary: 'Register a file reference to an existing storage object', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
15
|
-
'update': { method: 'PATCH', path: '/v1/files/{fileId}', summary: 'Update file metadata (type, parent, sortOrder)', pathParams: ['fileId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
17
|
+
'update': { method: 'PATCH', path: '/v1/files/{fileId}', summary: 'Update file metadata (type, parent, sortOrder)', pathParams: ['fileId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['tags', 'references'] },
|
|
18
|
+
'discard': { method: 'POST', path: '/v1/files/{fileId}/discard', summary: 'Discard a file (sets status DISCARDED; stays readable by id)', pathParams: ['fileId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
16
19
|
'move': { method: 'POST', path: '/v1/files/{fileId}/move', summary: 'Move file between storage zones', pathParams: ['fileId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
17
|
-
'renditions': { method: 'POST', path: '/v1/files/{fileId}/renditions', summary: 'Register a business-significant rendition', pathParams: ['fileId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
18
20
|
'retention': { method: 'POST', path: '/v1/files/{fileId}/retention', summary: 'Set or override retention period on a file', pathParams: ['fileId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
19
21
|
'hold': { method: 'POST', path: '/v1/files/{fileId}/hold', summary: 'Set or release legal hold on a file', pathParams: ['fileId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
22
|
+
'reclassify': { method: 'POST', path: '/v1/files/{fileId}/reclassify', summary: 'Reclassify a file and reprocess it through the inbox pipeline', pathParams: ['fileId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
20
23
|
};
|
|
21
|
-
export default class
|
|
22
|
-
static description = 'Manage files
|
|
24
|
+
export default class Files extends Command {
|
|
25
|
+
static description = 'Manage files';
|
|
23
26
|
static examples = [
|
|
24
|
-
'<%= config.bin %> files
|
|
25
|
-
'<%= config.bin %> files
|
|
26
|
-
'<%= config.bin %> files
|
|
27
|
+
'<%= config.bin %> files list',
|
|
28
|
+
'<%= config.bin %> files get <id>',
|
|
29
|
+
'<%= config.bin %> files create --data \'{...}\'',
|
|
30
|
+
'<%= config.bin %> files update <id> --set field=value',
|
|
31
|
+
'<%= config.bin %> files import --file data.json',
|
|
32
|
+
'<%= config.bin %> files export --csv > files.csv',
|
|
27
33
|
];
|
|
28
34
|
static args = {
|
|
29
35
|
action: Args.string({ description: 'Action: ' + SUBCOMMANDS.join(', '), required: true }),
|
|
30
36
|
id: Args.string({ description: 'Resource ID (for get, update, delete operations)', required: false }),
|
|
31
37
|
};
|
|
32
38
|
static flags = {
|
|
33
|
-
context: Flags.string({ char: 'c', description: 'Context to use' }),
|
|
39
|
+
context: Flags.string({ char: 'c', aliases: ['config'], env: 'ADERIS_CONTEXT', description: 'Context to use for this command, overriding the current context (alias: --config)' }),
|
|
34
40
|
output: Flags.string({ char: 'o', description: 'Output format (default: table for import, json for other commands)', options: ['json', 'table', 'csv'] }),
|
|
35
41
|
table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
|
|
36
42
|
csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
|
|
@@ -44,12 +50,12 @@ export default class FilesInternal extends Command {
|
|
|
44
50
|
'sort-by': Flags.string({ description: 'Sort field(s), comma-separated. Direction (:ASC/:DESC) is optional; date-like fields default to DESC, others to ASC.' }),
|
|
45
51
|
filter: Flags.string({ description: 'Filter (e.g. --filter status=ACTIVE). Operators: in:A,B, like:%, gt:, lt:, null:', multiple: true }),
|
|
46
52
|
query: Flags.string({ description: 'Additional query params as JSON' }),
|
|
47
|
-
fields: Flags.string({ description: 'Comma-separated list of fields to
|
|
53
|
+
fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
|
|
48
54
|
sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
|
|
49
55
|
'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
|
|
50
56
|
};
|
|
51
57
|
async run() {
|
|
52
|
-
const { args, flags } = await this.parse(
|
|
58
|
+
const { args, flags } = await this.parse(Files);
|
|
53
59
|
// Handle import action specially
|
|
54
60
|
if (args.action === 'import') {
|
|
55
61
|
if (!HAS_IMPORT)
|
|
@@ -90,7 +96,7 @@ export default class FilesInternal extends Command {
|
|
|
90
96
|
// First path param always comes from positional argument
|
|
91
97
|
value = args.id;
|
|
92
98
|
if (!value)
|
|
93
|
-
this.error('Missing required argument: id. Usage: aderis files
|
|
99
|
+
this.error('Missing required argument: id. Usage: aderis files <action> <id>');
|
|
94
100
|
}
|
|
95
101
|
else {
|
|
96
102
|
const flagName = param.replace(/\./g, '-').replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, '');
|
|
@@ -102,9 +108,9 @@ export default class FilesInternal extends Command {
|
|
|
102
108
|
}
|
|
103
109
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
104
110
|
if (!context)
|
|
105
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
106
|
-
if (!context
|
|
107
|
-
this.error(
|
|
111
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
112
|
+
if (!hasCredentials(context))
|
|
113
|
+
this.error(missingCredentialsMessage(context));
|
|
108
114
|
const queryParams = {};
|
|
109
115
|
if (flags.page)
|
|
110
116
|
queryParams.page = flags.page;
|
|
@@ -116,6 +122,8 @@ export default class FilesInternal extends Command {
|
|
|
116
122
|
queryParams.search = flags.search;
|
|
117
123
|
if (flags['sort-by'])
|
|
118
124
|
queryParams.sortBy = normalizeSortBy(flags['sort-by']);
|
|
125
|
+
if (flags.fields)
|
|
126
|
+
queryParams.fields = flags.fields;
|
|
119
127
|
if (flags.query) {
|
|
120
128
|
try {
|
|
121
129
|
Object.assign(queryParams, JSON.parse(flags.query));
|
|
@@ -318,11 +326,6 @@ export default class FilesInternal extends Command {
|
|
|
318
326
|
output = obj[arrayKeys[0]];
|
|
319
327
|
}
|
|
320
328
|
}
|
|
321
|
-
// Filter fields if --fields flag is provided
|
|
322
|
-
if (flags.fields) {
|
|
323
|
-
const fieldList = flags.fields.split(',').map(f => f.trim());
|
|
324
|
-
output = this.filterFields(output, fieldList);
|
|
325
|
-
}
|
|
326
329
|
// Sanitize output if requested
|
|
327
330
|
if (flags.sanitize) {
|
|
328
331
|
output = this.sanitizeOutput(output);
|
|
@@ -347,9 +350,9 @@ export default class FilesInternal extends Command {
|
|
|
347
350
|
async runImport(flags) {
|
|
348
351
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
349
352
|
if (!context)
|
|
350
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
351
|
-
if (!context
|
|
352
|
-
this.error(
|
|
353
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
354
|
+
if (!hasCredentials(context))
|
|
355
|
+
this.error(missingCredentialsMessage(context));
|
|
353
356
|
// Get data from file, --data, or --set flags
|
|
354
357
|
let items = [];
|
|
355
358
|
if (flags.file) {
|
|
@@ -559,9 +562,9 @@ export default class FilesInternal extends Command {
|
|
|
559
562
|
async runExport(flags) {
|
|
560
563
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
561
564
|
if (!context)
|
|
562
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
563
|
-
if (!context
|
|
564
|
-
this.error(
|
|
565
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
566
|
+
if (!hasCredentials(context))
|
|
567
|
+
this.error(missingCredentialsMessage(context));
|
|
565
568
|
const listEndpoint = ENDPOINTS['list'];
|
|
566
569
|
if (!listEndpoint)
|
|
567
570
|
this.error('Export requires a list endpoint');
|
|
@@ -577,6 +580,8 @@ export default class FilesInternal extends Command {
|
|
|
577
580
|
baseParams.sortBy = normalizeSortBy(flags['sort-by']);
|
|
578
581
|
if (flags.include)
|
|
579
582
|
baseParams.include = flags.include;
|
|
583
|
+
if (flags.fields)
|
|
584
|
+
baseParams.fields = flags.fields;
|
|
580
585
|
if (flags.query) {
|
|
581
586
|
try {
|
|
582
587
|
Object.assign(baseParams, JSON.parse(flags.query));
|
|
@@ -631,12 +636,7 @@ export default class FilesInternal extends Command {
|
|
|
631
636
|
}
|
|
632
637
|
}
|
|
633
638
|
process.stderr.write(' done\n');
|
|
634
|
-
// Filter fields if specified
|
|
635
639
|
let output = allItems;
|
|
636
|
-
if (flags.fields) {
|
|
637
|
-
const fieldList = flags.fields.split(',').map(f => f.trim());
|
|
638
|
-
output = this.filterFields(allItems, fieldList);
|
|
639
|
-
}
|
|
640
640
|
// Sanitize output if requested
|
|
641
641
|
if (flags.sanitize) {
|
|
642
642
|
output = this.sanitizeOutput(output);
|
|
@@ -653,9 +653,9 @@ export default class FilesInternal extends Command {
|
|
|
653
653
|
async runCamt053Import(flags) {
|
|
654
654
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
655
655
|
if (!context)
|
|
656
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
657
|
-
if (!context
|
|
658
|
-
this.error(
|
|
656
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
657
|
+
if (!hasCredentials(context))
|
|
658
|
+
this.error(missingCredentialsMessage(context));
|
|
659
659
|
if (!flags.file) {
|
|
660
660
|
this.error('CAMT.053 import requires --file (-f) flag with path to XML file');
|
|
661
661
|
}
|
|
@@ -739,13 +739,13 @@ export default class FilesInternal extends Command {
|
|
|
739
739
|
}
|
|
740
740
|
async runNestedImport(suffix, parentId, flags) {
|
|
741
741
|
if (!parentId) {
|
|
742
|
-
this.error(`Missing required argument: id. Usage: aderis files
|
|
742
|
+
this.error(`Missing required argument: id. Usage: aderis files import-${suffix} <parent-id> --file data.json`);
|
|
743
743
|
}
|
|
744
744
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
745
745
|
if (!context)
|
|
746
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
747
|
-
if (!context
|
|
748
|
-
this.error(
|
|
746
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
747
|
+
if (!hasCredentials(context))
|
|
748
|
+
this.error(missingCredentialsMessage(context));
|
|
749
749
|
// Get data from file, --data, or --set flags
|
|
750
750
|
let items = [];
|
|
751
751
|
if (flags.file) {
|
|
@@ -965,56 +965,6 @@ export default class FilesInternal extends Command {
|
|
|
965
965
|
return `\$${op}`;
|
|
966
966
|
return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
|
|
967
967
|
}
|
|
968
|
-
filterFields(data, fields) {
|
|
969
|
-
const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
|
|
970
|
-
const pick = (obj, keys) => {
|
|
971
|
-
// Group keys by their first segment so sibling sub-paths
|
|
972
|
-
// (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
|
|
973
|
-
// and don't clobber each other on merge.
|
|
974
|
-
const directKeys = new Set();
|
|
975
|
-
const nestedKeys = new Map();
|
|
976
|
-
for (const key of keys) {
|
|
977
|
-
if (key.split('.').some(isUnsafeKey))
|
|
978
|
-
continue;
|
|
979
|
-
if (key.includes('.')) {
|
|
980
|
-
const [first, ...rest] = key.split('.');
|
|
981
|
-
if (isUnsafeKey(first))
|
|
982
|
-
continue;
|
|
983
|
-
const list = nestedKeys.get(first);
|
|
984
|
-
if (list) {
|
|
985
|
-
list.push(rest.join('.'));
|
|
986
|
-
}
|
|
987
|
-
else {
|
|
988
|
-
nestedKeys.set(first, [rest.join('.')]);
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
|
-
else {
|
|
992
|
-
directKeys.add(key);
|
|
993
|
-
}
|
|
994
|
-
}
|
|
995
|
-
const result = {};
|
|
996
|
-
for (const key of directKeys) {
|
|
997
|
-
if (key in obj)
|
|
998
|
-
result[key] = obj[key];
|
|
999
|
-
}
|
|
1000
|
-
for (const [first, restKeys] of nestedKeys) {
|
|
1001
|
-
const val = obj[first];
|
|
1002
|
-
if (!val || typeof val !== 'object')
|
|
1003
|
-
continue;
|
|
1004
|
-
if (Array.isArray(val)) {
|
|
1005
|
-
result[first] = val.map(item => pick(item, restKeys));
|
|
1006
|
-
}
|
|
1007
|
-
else {
|
|
1008
|
-
result[first] = pick(val, restKeys);
|
|
1009
|
-
}
|
|
1010
|
-
}
|
|
1011
|
-
return result;
|
|
1012
|
-
};
|
|
1013
|
-
if (Array.isArray(data)) {
|
|
1014
|
-
return data.map(item => pick(item, fields));
|
|
1015
|
-
}
|
|
1016
|
-
return pick(data, fields);
|
|
1017
|
-
}
|
|
1018
968
|
sanitizeOutput(data) {
|
|
1019
969
|
const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
|
|
1020
970
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
package/dist/commands/health.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Command, Args, Flags } from '@oclif/core';
|
|
2
2
|
import { apiRequest, ApiError, normalizeSortBy } from '../lib/api-client.js';
|
|
3
|
-
import { getCurrentContext, getContext, getBaseUrls } from '../lib/config.js';
|
|
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;
|
|
6
6
|
const HAS_EXPORT = false;
|
|
@@ -24,7 +24,7 @@ export default class Health extends Command {
|
|
|
24
24
|
id: Args.string({ description: 'Resource ID (for get, update, delete operations)', required: false }),
|
|
25
25
|
};
|
|
26
26
|
static flags = {
|
|
27
|
-
context: Flags.string({ char: 'c', description: 'Context to use' }),
|
|
27
|
+
context: Flags.string({ char: 'c', aliases: ['config'], env: 'ADERIS_CONTEXT', description: 'Context to use for this command, overriding the current context (alias: --config)' }),
|
|
28
28
|
output: Flags.string({ char: 'o', description: 'Output format (default: table for import, json for other commands)', options: ['json', 'table', 'csv'] }),
|
|
29
29
|
table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
|
|
30
30
|
csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
|
|
@@ -38,7 +38,7 @@ export default class Health extends Command {
|
|
|
38
38
|
'sort-by': Flags.string({ description: 'Sort field(s), comma-separated. Direction (:ASC/:DESC) is optional; date-like fields default to DESC, others to ASC.' }),
|
|
39
39
|
filter: Flags.string({ description: 'Filter (e.g. --filter status=ACTIVE). Operators: in:A,B, like:%, gt:, lt:, null:', multiple: true }),
|
|
40
40
|
query: Flags.string({ description: 'Additional query params as JSON' }),
|
|
41
|
-
fields: Flags.string({ description: 'Comma-separated list of fields to
|
|
41
|
+
fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
|
|
42
42
|
sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
|
|
43
43
|
'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
|
|
44
44
|
};
|
|
@@ -96,9 +96,9 @@ export default class Health extends Command {
|
|
|
96
96
|
}
|
|
97
97
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
98
98
|
if (!context)
|
|
99
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
100
|
-
if (!context
|
|
101
|
-
this.error(
|
|
99
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
100
|
+
if (!hasCredentials(context))
|
|
101
|
+
this.error(missingCredentialsMessage(context));
|
|
102
102
|
const queryParams = {};
|
|
103
103
|
if (flags.page)
|
|
104
104
|
queryParams.page = flags.page;
|
|
@@ -110,6 +110,8 @@ export default class Health extends Command {
|
|
|
110
110
|
queryParams.search = flags.search;
|
|
111
111
|
if (flags['sort-by'])
|
|
112
112
|
queryParams.sortBy = normalizeSortBy(flags['sort-by']);
|
|
113
|
+
if (flags.fields)
|
|
114
|
+
queryParams.fields = flags.fields;
|
|
113
115
|
if (flags.query) {
|
|
114
116
|
try {
|
|
115
117
|
Object.assign(queryParams, JSON.parse(flags.query));
|
|
@@ -312,11 +314,6 @@ export default class Health extends Command {
|
|
|
312
314
|
output = obj[arrayKeys[0]];
|
|
313
315
|
}
|
|
314
316
|
}
|
|
315
|
-
// Filter fields if --fields flag is provided
|
|
316
|
-
if (flags.fields) {
|
|
317
|
-
const fieldList = flags.fields.split(',').map(f => f.trim());
|
|
318
|
-
output = this.filterFields(output, fieldList);
|
|
319
|
-
}
|
|
320
317
|
// Sanitize output if requested
|
|
321
318
|
if (flags.sanitize) {
|
|
322
319
|
output = this.sanitizeOutput(output);
|
|
@@ -341,9 +338,9 @@ export default class Health extends Command {
|
|
|
341
338
|
async runImport(flags) {
|
|
342
339
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
343
340
|
if (!context)
|
|
344
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
345
|
-
if (!context
|
|
346
|
-
this.error(
|
|
341
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
342
|
+
if (!hasCredentials(context))
|
|
343
|
+
this.error(missingCredentialsMessage(context));
|
|
347
344
|
// Get data from file, --data, or --set flags
|
|
348
345
|
let items = [];
|
|
349
346
|
if (flags.file) {
|
|
@@ -553,9 +550,9 @@ export default class Health extends Command {
|
|
|
553
550
|
async runExport(flags) {
|
|
554
551
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
555
552
|
if (!context)
|
|
556
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
557
|
-
if (!context
|
|
558
|
-
this.error(
|
|
553
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
554
|
+
if (!hasCredentials(context))
|
|
555
|
+
this.error(missingCredentialsMessage(context));
|
|
559
556
|
const listEndpoint = ENDPOINTS['list'];
|
|
560
557
|
if (!listEndpoint)
|
|
561
558
|
this.error('Export requires a list endpoint');
|
|
@@ -571,6 +568,8 @@ export default class Health extends Command {
|
|
|
571
568
|
baseParams.sortBy = normalizeSortBy(flags['sort-by']);
|
|
572
569
|
if (flags.include)
|
|
573
570
|
baseParams.include = flags.include;
|
|
571
|
+
if (flags.fields)
|
|
572
|
+
baseParams.fields = flags.fields;
|
|
574
573
|
if (flags.query) {
|
|
575
574
|
try {
|
|
576
575
|
Object.assign(baseParams, JSON.parse(flags.query));
|
|
@@ -625,12 +624,7 @@ export default class Health extends Command {
|
|
|
625
624
|
}
|
|
626
625
|
}
|
|
627
626
|
process.stderr.write(' done\n');
|
|
628
|
-
// Filter fields if specified
|
|
629
627
|
let output = allItems;
|
|
630
|
-
if (flags.fields) {
|
|
631
|
-
const fieldList = flags.fields.split(',').map(f => f.trim());
|
|
632
|
-
output = this.filterFields(allItems, fieldList);
|
|
633
|
-
}
|
|
634
628
|
// Sanitize output if requested
|
|
635
629
|
if (flags.sanitize) {
|
|
636
630
|
output = this.sanitizeOutput(output);
|
|
@@ -647,9 +641,9 @@ export default class Health extends Command {
|
|
|
647
641
|
async runCamt053Import(flags) {
|
|
648
642
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
649
643
|
if (!context)
|
|
650
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
651
|
-
if (!context
|
|
652
|
-
this.error(
|
|
644
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
645
|
+
if (!hasCredentials(context))
|
|
646
|
+
this.error(missingCredentialsMessage(context));
|
|
653
647
|
if (!flags.file) {
|
|
654
648
|
this.error('CAMT.053 import requires --file (-f) flag with path to XML file');
|
|
655
649
|
}
|
|
@@ -737,9 +731,9 @@ export default class Health extends Command {
|
|
|
737
731
|
}
|
|
738
732
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
739
733
|
if (!context)
|
|
740
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
741
|
-
if (!context
|
|
742
|
-
this.error(
|
|
734
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
735
|
+
if (!hasCredentials(context))
|
|
736
|
+
this.error(missingCredentialsMessage(context));
|
|
743
737
|
// Get data from file, --data, or --set flags
|
|
744
738
|
let items = [];
|
|
745
739
|
if (flags.file) {
|
|
@@ -959,56 +953,6 @@ export default class Health extends Command {
|
|
|
959
953
|
return `\$${op}`;
|
|
960
954
|
return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
|
|
961
955
|
}
|
|
962
|
-
filterFields(data, fields) {
|
|
963
|
-
const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
|
|
964
|
-
const pick = (obj, keys) => {
|
|
965
|
-
// Group keys by their first segment so sibling sub-paths
|
|
966
|
-
// (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
|
|
967
|
-
// and don't clobber each other on merge.
|
|
968
|
-
const directKeys = new Set();
|
|
969
|
-
const nestedKeys = new Map();
|
|
970
|
-
for (const key of keys) {
|
|
971
|
-
if (key.split('.').some(isUnsafeKey))
|
|
972
|
-
continue;
|
|
973
|
-
if (key.includes('.')) {
|
|
974
|
-
const [first, ...rest] = key.split('.');
|
|
975
|
-
if (isUnsafeKey(first))
|
|
976
|
-
continue;
|
|
977
|
-
const list = nestedKeys.get(first);
|
|
978
|
-
if (list) {
|
|
979
|
-
list.push(rest.join('.'));
|
|
980
|
-
}
|
|
981
|
-
else {
|
|
982
|
-
nestedKeys.set(first, [rest.join('.')]);
|
|
983
|
-
}
|
|
984
|
-
}
|
|
985
|
-
else {
|
|
986
|
-
directKeys.add(key);
|
|
987
|
-
}
|
|
988
|
-
}
|
|
989
|
-
const result = {};
|
|
990
|
-
for (const key of directKeys) {
|
|
991
|
-
if (key in obj)
|
|
992
|
-
result[key] = obj[key];
|
|
993
|
-
}
|
|
994
|
-
for (const [first, restKeys] of nestedKeys) {
|
|
995
|
-
const val = obj[first];
|
|
996
|
-
if (!val || typeof val !== 'object')
|
|
997
|
-
continue;
|
|
998
|
-
if (Array.isArray(val)) {
|
|
999
|
-
result[first] = val.map(item => pick(item, restKeys));
|
|
1000
|
-
}
|
|
1001
|
-
else {
|
|
1002
|
-
result[first] = pick(val, restKeys);
|
|
1003
|
-
}
|
|
1004
|
-
}
|
|
1005
|
-
return result;
|
|
1006
|
-
};
|
|
1007
|
-
if (Array.isArray(data)) {
|
|
1008
|
-
return data.map(item => pick(item, fields));
|
|
1009
|
-
}
|
|
1010
|
-
return pick(data, fields);
|
|
1011
|
-
}
|
|
1012
956
|
sanitizeOutput(data) {
|
|
1013
957
|
const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
|
|
1014
958
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|