@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
package/dist/commands/items.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 = true;
|
|
6
6
|
const HAS_EXPORT = true;
|
|
@@ -9,9 +9,9 @@ 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
11
|
'list': { method: 'GET', path: '/v1/items', summary: 'Get all items with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
12
|
-
'create': { method: 'POST', path: '/v1/items', summary: 'Create a new item', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['tagIds'] },
|
|
12
|
+
'create': { method: 'POST', path: '/v1/items', summary: 'Create a new item', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['tagIds', 'tagCodes'] },
|
|
13
13
|
'get': { method: 'GET', path: '/v1/items/{id}', summary: 'Get an item by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
14
|
-
'update': { method: 'PATCH', path: '/v1/items/{id}', summary: 'Update an item', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['tagIds'] },
|
|
14
|
+
'update': { method: 'PATCH', path: '/v1/items/{id}', summary: 'Update an item', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['tagIds', 'tagCodes'] },
|
|
15
15
|
'delete': { method: 'DELETE', path: '/v1/items/{id}', summary: 'Delete an item', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
16
16
|
};
|
|
17
17
|
export default class Items extends Command {
|
|
@@ -29,7 +29,7 @@ export default class Items extends Command {
|
|
|
29
29
|
id: Args.string({ description: 'Resource ID (for get, update, delete operations)', required: false }),
|
|
30
30
|
};
|
|
31
31
|
static flags = {
|
|
32
|
-
context: Flags.string({ char: 'c', description: 'Context to use' }),
|
|
32
|
+
context: Flags.string({ char: 'c', aliases: ['config'], env: 'ADERIS_CONTEXT', description: 'Context to use for this command, overriding the current context (alias: --config)' }),
|
|
33
33
|
output: Flags.string({ char: 'o', description: 'Output format (default: table for import, json for other commands)', options: ['json', 'table', 'csv'] }),
|
|
34
34
|
table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
|
|
35
35
|
csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
|
|
@@ -43,7 +43,7 @@ export default class Items extends Command {
|
|
|
43
43
|
'sort-by': Flags.string({ description: 'Sort field(s), comma-separated. Direction (:ASC/:DESC) is optional; date-like fields default to DESC, others to ASC.' }),
|
|
44
44
|
filter: Flags.string({ description: 'Filter (e.g. --filter status=ACTIVE). Operators: in:A,B, like:%, gt:, lt:, null:', multiple: true }),
|
|
45
45
|
query: Flags.string({ description: 'Additional query params as JSON' }),
|
|
46
|
-
fields: Flags.string({ description: 'Comma-separated list of fields to
|
|
46
|
+
fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
|
|
47
47
|
sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
|
|
48
48
|
'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
|
|
49
49
|
};
|
|
@@ -101,9 +101,9 @@ export default class Items extends Command {
|
|
|
101
101
|
}
|
|
102
102
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
103
103
|
if (!context)
|
|
104
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
105
|
-
if (!context
|
|
106
|
-
this.error(
|
|
104
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
105
|
+
if (!hasCredentials(context))
|
|
106
|
+
this.error(missingCredentialsMessage(context));
|
|
107
107
|
const queryParams = {};
|
|
108
108
|
if (flags.page)
|
|
109
109
|
queryParams.page = flags.page;
|
|
@@ -115,6 +115,8 @@ export default class Items extends Command {
|
|
|
115
115
|
queryParams.search = flags.search;
|
|
116
116
|
if (flags['sort-by'])
|
|
117
117
|
queryParams.sortBy = normalizeSortBy(flags['sort-by']);
|
|
118
|
+
if (flags.fields)
|
|
119
|
+
queryParams.fields = flags.fields;
|
|
118
120
|
if (flags.query) {
|
|
119
121
|
try {
|
|
120
122
|
Object.assign(queryParams, JSON.parse(flags.query));
|
|
@@ -317,11 +319,6 @@ export default class Items extends Command {
|
|
|
317
319
|
output = obj[arrayKeys[0]];
|
|
318
320
|
}
|
|
319
321
|
}
|
|
320
|
-
// Filter fields if --fields flag is provided
|
|
321
|
-
if (flags.fields) {
|
|
322
|
-
const fieldList = flags.fields.split(',').map(f => f.trim());
|
|
323
|
-
output = this.filterFields(output, fieldList);
|
|
324
|
-
}
|
|
325
322
|
// Sanitize output if requested
|
|
326
323
|
if (flags.sanitize) {
|
|
327
324
|
output = this.sanitizeOutput(output);
|
|
@@ -346,9 +343,9 @@ export default class Items extends Command {
|
|
|
346
343
|
async runImport(flags) {
|
|
347
344
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
348
345
|
if (!context)
|
|
349
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
350
|
-
if (!context
|
|
351
|
-
this.error(
|
|
346
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
347
|
+
if (!hasCredentials(context))
|
|
348
|
+
this.error(missingCredentialsMessage(context));
|
|
352
349
|
// Get data from file, --data, or --set flags
|
|
353
350
|
let items = [];
|
|
354
351
|
if (flags.file) {
|
|
@@ -558,9 +555,9 @@ export default class Items extends Command {
|
|
|
558
555
|
async runExport(flags) {
|
|
559
556
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
560
557
|
if (!context)
|
|
561
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
562
|
-
if (!context
|
|
563
|
-
this.error(
|
|
558
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
559
|
+
if (!hasCredentials(context))
|
|
560
|
+
this.error(missingCredentialsMessage(context));
|
|
564
561
|
const listEndpoint = ENDPOINTS['list'];
|
|
565
562
|
if (!listEndpoint)
|
|
566
563
|
this.error('Export requires a list endpoint');
|
|
@@ -576,6 +573,8 @@ export default class Items extends Command {
|
|
|
576
573
|
baseParams.sortBy = normalizeSortBy(flags['sort-by']);
|
|
577
574
|
if (flags.include)
|
|
578
575
|
baseParams.include = flags.include;
|
|
576
|
+
if (flags.fields)
|
|
577
|
+
baseParams.fields = flags.fields;
|
|
579
578
|
if (flags.query) {
|
|
580
579
|
try {
|
|
581
580
|
Object.assign(baseParams, JSON.parse(flags.query));
|
|
@@ -630,12 +629,7 @@ export default class Items extends Command {
|
|
|
630
629
|
}
|
|
631
630
|
}
|
|
632
631
|
process.stderr.write(' done\n');
|
|
633
|
-
// Filter fields if specified
|
|
634
632
|
let output = allItems;
|
|
635
|
-
if (flags.fields) {
|
|
636
|
-
const fieldList = flags.fields.split(',').map(f => f.trim());
|
|
637
|
-
output = this.filterFields(allItems, fieldList);
|
|
638
|
-
}
|
|
639
633
|
// Sanitize output if requested
|
|
640
634
|
if (flags.sanitize) {
|
|
641
635
|
output = this.sanitizeOutput(output);
|
|
@@ -652,9 +646,9 @@ export default class Items extends Command {
|
|
|
652
646
|
async runCamt053Import(flags) {
|
|
653
647
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
654
648
|
if (!context)
|
|
655
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
656
|
-
if (!context
|
|
657
|
-
this.error(
|
|
649
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
650
|
+
if (!hasCredentials(context))
|
|
651
|
+
this.error(missingCredentialsMessage(context));
|
|
658
652
|
if (!flags.file) {
|
|
659
653
|
this.error('CAMT.053 import requires --file (-f) flag with path to XML file');
|
|
660
654
|
}
|
|
@@ -742,9 +736,9 @@ export default class Items extends Command {
|
|
|
742
736
|
}
|
|
743
737
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
744
738
|
if (!context)
|
|
745
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
746
|
-
if (!context
|
|
747
|
-
this.error(
|
|
739
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
740
|
+
if (!hasCredentials(context))
|
|
741
|
+
this.error(missingCredentialsMessage(context));
|
|
748
742
|
// Get data from file, --data, or --set flags
|
|
749
743
|
let items = [];
|
|
750
744
|
if (flags.file) {
|
|
@@ -964,56 +958,6 @@ export default class Items extends Command {
|
|
|
964
958
|
return `\$${op}`;
|
|
965
959
|
return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
|
|
966
960
|
}
|
|
967
|
-
filterFields(data, fields) {
|
|
968
|
-
const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
|
|
969
|
-
const pick = (obj, keys) => {
|
|
970
|
-
// Group keys by their first segment so sibling sub-paths
|
|
971
|
-
// (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
|
|
972
|
-
// and don't clobber each other on merge.
|
|
973
|
-
const directKeys = new Set();
|
|
974
|
-
const nestedKeys = new Map();
|
|
975
|
-
for (const key of keys) {
|
|
976
|
-
if (key.split('.').some(isUnsafeKey))
|
|
977
|
-
continue;
|
|
978
|
-
if (key.includes('.')) {
|
|
979
|
-
const [first, ...rest] = key.split('.');
|
|
980
|
-
if (isUnsafeKey(first))
|
|
981
|
-
continue;
|
|
982
|
-
const list = nestedKeys.get(first);
|
|
983
|
-
if (list) {
|
|
984
|
-
list.push(rest.join('.'));
|
|
985
|
-
}
|
|
986
|
-
else {
|
|
987
|
-
nestedKeys.set(first, [rest.join('.')]);
|
|
988
|
-
}
|
|
989
|
-
}
|
|
990
|
-
else {
|
|
991
|
-
directKeys.add(key);
|
|
992
|
-
}
|
|
993
|
-
}
|
|
994
|
-
const result = {};
|
|
995
|
-
for (const key of directKeys) {
|
|
996
|
-
if (key in obj)
|
|
997
|
-
result[key] = obj[key];
|
|
998
|
-
}
|
|
999
|
-
for (const [first, restKeys] of nestedKeys) {
|
|
1000
|
-
const val = obj[first];
|
|
1001
|
-
if (!val || typeof val !== 'object')
|
|
1002
|
-
continue;
|
|
1003
|
-
if (Array.isArray(val)) {
|
|
1004
|
-
result[first] = val.map(item => pick(item, restKeys));
|
|
1005
|
-
}
|
|
1006
|
-
else {
|
|
1007
|
-
result[first] = pick(val, restKeys);
|
|
1008
|
-
}
|
|
1009
|
-
}
|
|
1010
|
-
return result;
|
|
1011
|
-
};
|
|
1012
|
-
if (Array.isArray(data)) {
|
|
1013
|
-
return data.map(item => pick(item, fields));
|
|
1014
|
-
}
|
|
1015
|
-
return pick(data, fields);
|
|
1016
|
-
}
|
|
1017
961
|
sanitizeOutput(data) {
|
|
1018
962
|
const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
|
|
1019
963
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
@@ -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 = true;
|
|
@@ -21,7 +21,7 @@ export default class JournalLines extends Command {
|
|
|
21
21
|
id: Args.string({ description: 'Resource ID (for get, update, delete operations)', required: false }),
|
|
22
22
|
};
|
|
23
23
|
static flags = {
|
|
24
|
-
context: Flags.string({ char: 'c', description: 'Context to use' }),
|
|
24
|
+
context: Flags.string({ char: 'c', aliases: ['config'], env: 'ADERIS_CONTEXT', description: 'Context to use for this command, overriding the current context (alias: --config)' }),
|
|
25
25
|
output: Flags.string({ char: 'o', description: 'Output format (default: table for import, json for other commands)', options: ['json', 'table', 'csv'] }),
|
|
26
26
|
table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
|
|
27
27
|
csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
|
|
@@ -35,7 +35,7 @@ export default class JournalLines extends Command {
|
|
|
35
35
|
'sort-by': Flags.string({ description: 'Sort field(s), comma-separated. Direction (:ASC/:DESC) is optional; date-like fields default to DESC, others to ASC.' }),
|
|
36
36
|
filter: Flags.string({ description: 'Filter (e.g. --filter status=ACTIVE). Operators: in:A,B, like:%, gt:, lt:, null:', multiple: true }),
|
|
37
37
|
query: Flags.string({ description: 'Additional query params as JSON' }),
|
|
38
|
-
fields: Flags.string({ description: 'Comma-separated list of fields to
|
|
38
|
+
fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
|
|
39
39
|
sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
|
|
40
40
|
'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
|
|
41
41
|
};
|
|
@@ -93,9 +93,9 @@ export default class JournalLines extends Command {
|
|
|
93
93
|
}
|
|
94
94
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
95
95
|
if (!context)
|
|
96
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
97
|
-
if (!context
|
|
98
|
-
this.error(
|
|
96
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
97
|
+
if (!hasCredentials(context))
|
|
98
|
+
this.error(missingCredentialsMessage(context));
|
|
99
99
|
const queryParams = {};
|
|
100
100
|
if (flags.page)
|
|
101
101
|
queryParams.page = flags.page;
|
|
@@ -107,6 +107,8 @@ export default class JournalLines extends Command {
|
|
|
107
107
|
queryParams.search = flags.search;
|
|
108
108
|
if (flags['sort-by'])
|
|
109
109
|
queryParams.sortBy = normalizeSortBy(flags['sort-by']);
|
|
110
|
+
if (flags.fields)
|
|
111
|
+
queryParams.fields = flags.fields;
|
|
110
112
|
if (flags.query) {
|
|
111
113
|
try {
|
|
112
114
|
Object.assign(queryParams, JSON.parse(flags.query));
|
|
@@ -309,11 +311,6 @@ export default class JournalLines extends Command {
|
|
|
309
311
|
output = obj[arrayKeys[0]];
|
|
310
312
|
}
|
|
311
313
|
}
|
|
312
|
-
// Filter fields if --fields flag is provided
|
|
313
|
-
if (flags.fields) {
|
|
314
|
-
const fieldList = flags.fields.split(',').map(f => f.trim());
|
|
315
|
-
output = this.filterFields(output, fieldList);
|
|
316
|
-
}
|
|
317
314
|
// Sanitize output if requested
|
|
318
315
|
if (flags.sanitize) {
|
|
319
316
|
output = this.sanitizeOutput(output);
|
|
@@ -338,9 +335,9 @@ export default class JournalLines extends Command {
|
|
|
338
335
|
async runImport(flags) {
|
|
339
336
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
340
337
|
if (!context)
|
|
341
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
342
|
-
if (!context
|
|
343
|
-
this.error(
|
|
338
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
339
|
+
if (!hasCredentials(context))
|
|
340
|
+
this.error(missingCredentialsMessage(context));
|
|
344
341
|
// Get data from file, --data, or --set flags
|
|
345
342
|
let items = [];
|
|
346
343
|
if (flags.file) {
|
|
@@ -550,9 +547,9 @@ export default class JournalLines extends Command {
|
|
|
550
547
|
async runExport(flags) {
|
|
551
548
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
552
549
|
if (!context)
|
|
553
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
554
|
-
if (!context
|
|
555
|
-
this.error(
|
|
550
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
551
|
+
if (!hasCredentials(context))
|
|
552
|
+
this.error(missingCredentialsMessage(context));
|
|
556
553
|
const listEndpoint = ENDPOINTS['list'];
|
|
557
554
|
if (!listEndpoint)
|
|
558
555
|
this.error('Export requires a list endpoint');
|
|
@@ -568,6 +565,8 @@ export default class JournalLines extends Command {
|
|
|
568
565
|
baseParams.sortBy = normalizeSortBy(flags['sort-by']);
|
|
569
566
|
if (flags.include)
|
|
570
567
|
baseParams.include = flags.include;
|
|
568
|
+
if (flags.fields)
|
|
569
|
+
baseParams.fields = flags.fields;
|
|
571
570
|
if (flags.query) {
|
|
572
571
|
try {
|
|
573
572
|
Object.assign(baseParams, JSON.parse(flags.query));
|
|
@@ -622,12 +621,7 @@ export default class JournalLines extends Command {
|
|
|
622
621
|
}
|
|
623
622
|
}
|
|
624
623
|
process.stderr.write(' done\n');
|
|
625
|
-
// Filter fields if specified
|
|
626
624
|
let output = allItems;
|
|
627
|
-
if (flags.fields) {
|
|
628
|
-
const fieldList = flags.fields.split(',').map(f => f.trim());
|
|
629
|
-
output = this.filterFields(allItems, fieldList);
|
|
630
|
-
}
|
|
631
625
|
// Sanitize output if requested
|
|
632
626
|
if (flags.sanitize) {
|
|
633
627
|
output = this.sanitizeOutput(output);
|
|
@@ -644,9 +638,9 @@ export default class JournalLines extends Command {
|
|
|
644
638
|
async runCamt053Import(flags) {
|
|
645
639
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
646
640
|
if (!context)
|
|
647
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
648
|
-
if (!context
|
|
649
|
-
this.error(
|
|
641
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
642
|
+
if (!hasCredentials(context))
|
|
643
|
+
this.error(missingCredentialsMessage(context));
|
|
650
644
|
if (!flags.file) {
|
|
651
645
|
this.error('CAMT.053 import requires --file (-f) flag with path to XML file');
|
|
652
646
|
}
|
|
@@ -734,9 +728,9 @@ export default class JournalLines extends Command {
|
|
|
734
728
|
}
|
|
735
729
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
736
730
|
if (!context)
|
|
737
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
738
|
-
if (!context
|
|
739
|
-
this.error(
|
|
731
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
732
|
+
if (!hasCredentials(context))
|
|
733
|
+
this.error(missingCredentialsMessage(context));
|
|
740
734
|
// Get data from file, --data, or --set flags
|
|
741
735
|
let items = [];
|
|
742
736
|
if (flags.file) {
|
|
@@ -956,56 +950,6 @@ export default class JournalLines extends Command {
|
|
|
956
950
|
return `\$${op}`;
|
|
957
951
|
return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
|
|
958
952
|
}
|
|
959
|
-
filterFields(data, fields) {
|
|
960
|
-
const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
|
|
961
|
-
const pick = (obj, keys) => {
|
|
962
|
-
// Group keys by their first segment so sibling sub-paths
|
|
963
|
-
// (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
|
|
964
|
-
// and don't clobber each other on merge.
|
|
965
|
-
const directKeys = new Set();
|
|
966
|
-
const nestedKeys = new Map();
|
|
967
|
-
for (const key of keys) {
|
|
968
|
-
if (key.split('.').some(isUnsafeKey))
|
|
969
|
-
continue;
|
|
970
|
-
if (key.includes('.')) {
|
|
971
|
-
const [first, ...rest] = key.split('.');
|
|
972
|
-
if (isUnsafeKey(first))
|
|
973
|
-
continue;
|
|
974
|
-
const list = nestedKeys.get(first);
|
|
975
|
-
if (list) {
|
|
976
|
-
list.push(rest.join('.'));
|
|
977
|
-
}
|
|
978
|
-
else {
|
|
979
|
-
nestedKeys.set(first, [rest.join('.')]);
|
|
980
|
-
}
|
|
981
|
-
}
|
|
982
|
-
else {
|
|
983
|
-
directKeys.add(key);
|
|
984
|
-
}
|
|
985
|
-
}
|
|
986
|
-
const result = {};
|
|
987
|
-
for (const key of directKeys) {
|
|
988
|
-
if (key in obj)
|
|
989
|
-
result[key] = obj[key];
|
|
990
|
-
}
|
|
991
|
-
for (const [first, restKeys] of nestedKeys) {
|
|
992
|
-
const val = obj[first];
|
|
993
|
-
if (!val || typeof val !== 'object')
|
|
994
|
-
continue;
|
|
995
|
-
if (Array.isArray(val)) {
|
|
996
|
-
result[first] = val.map(item => pick(item, restKeys));
|
|
997
|
-
}
|
|
998
|
-
else {
|
|
999
|
-
result[first] = pick(val, restKeys);
|
|
1000
|
-
}
|
|
1001
|
-
}
|
|
1002
|
-
return result;
|
|
1003
|
-
};
|
|
1004
|
-
if (Array.isArray(data)) {
|
|
1005
|
-
return data.map(item => pick(item, fields));
|
|
1006
|
-
}
|
|
1007
|
-
return pick(data, fields);
|
|
1008
|
-
}
|
|
1009
953
|
sanitizeOutput(data) {
|
|
1010
954
|
const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
|
|
1011
955
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
@@ -14,6 +14,7 @@ export default class Journals extends Command {
|
|
|
14
14
|
data: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
15
|
file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
16
|
set: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
17
|
+
'line-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
17
18
|
'note-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
18
19
|
'file-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
19
20
|
page: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -33,7 +34,6 @@ export default class Journals extends Command {
|
|
|
33
34
|
private runCamt053Import;
|
|
34
35
|
private runNestedImport;
|
|
35
36
|
private parseFilterValue;
|
|
36
|
-
private filterFields;
|
|
37
37
|
private sanitizeOutput;
|
|
38
38
|
private getColumns;
|
|
39
39
|
private getValue;
|
|
@@ -1,21 +1,24 @@
|
|
|
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;
|
|
7
7
|
const HAS_CAMT053 = false;
|
|
8
8
|
const NESTED_IMPORTS = ["notes", "files"];
|
|
9
|
-
const SUBCOMMANDS = [...["list", "create", "get", "update", "delete", "post", "approve", "cancel", "get-deferrals", "create-deferrals", "list-notes", "create-notes", "get-notes", "update-notes", "delete-notes", "list-files", "create-files", "get-files", "update-files", "delete-files", "files-download"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
|
|
9
|
+
const SUBCOMMANDS = [...["list", "create", "stats", "get", "update", "delete", "post", "approve", "cancel", "reverse", "update-lines", "get-deferrals", "create-deferrals", "list-notes", "create-notes", "get-notes", "update-notes", "delete-notes", "list-files", "create-files", "get-files", "update-files", "delete-files", "files-download"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
|
|
10
10
|
const ENDPOINTS = {
|
|
11
11
|
'list': { method: 'GET', path: '/v1/journals', summary: 'List all journals', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
12
|
-
'create': { method: 'POST', path: '/v1/journals', summary: 'Create a new journal (with embedded lines)', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['
|
|
12
|
+
'create': { method: 'POST', path: '/v1/journals', summary: 'Create a new journal (with embedded lines)', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['lines', 'notes'] },
|
|
13
|
+
'stats': { method: 'GET', path: '/v1/journals/stats', summary: 'Journal counts and debit totals per date range', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
13
14
|
'get': { method: 'GET', path: '/v1/journals/{id}', summary: 'Get a journal by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
14
|
-
'update': { method: 'PATCH', path: '/v1/journals/{id}', summary: 'Update a journal (with embedded lines upsert)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['
|
|
15
|
+
'update': { method: 'PATCH', path: '/v1/journals/{id}', summary: 'Update a journal (with embedded lines upsert)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['lines'] },
|
|
15
16
|
'delete': { method: 'DELETE', path: '/v1/journals/{id}', summary: 'Delete a journal', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
16
17
|
'post': { method: 'POST', path: '/v1/journals/{id}/post', summary: 'Post journal (finalize)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
17
18
|
'approve': { method: 'POST', path: '/v1/journals/{id}/approve', summary: 'Approve a journal (transitions DRAFT to APPROVED)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
18
19
|
'cancel': { method: 'POST', path: '/v1/journals/{id}/cancel', summary: 'Cancel a journal', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
20
|
+
'reverse': { method: 'POST', path: '/v1/journals/{id}/reverse', summary: 'Reverse a posted journal', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
21
|
+
'update-lines': { method: 'PATCH', path: '/v1/journals/{id}/lines/{lineId}', summary: 'Update a single journal line in place', pathParams: ['id', 'lineId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['tagIds', 'tagCodes'] },
|
|
19
22
|
'get-deferrals': { method: 'GET', path: '/v1/journals/{id}/deferrals', summary: 'List the deferral schedules of a journal', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
20
23
|
'create-deferrals': { method: 'POST', path: '/v1/journals/{id}/deferrals', summary: 'Create deferral schedules for one line of a journal (manual trigger)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['periods'] },
|
|
21
24
|
'list-notes': { method: 'GET', path: '/v1/journals/{journalId}/notes', summary: 'Get all notes for a journal', pathParams: ['journalId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
@@ -45,13 +48,14 @@ export default class Journals extends Command {
|
|
|
45
48
|
id: Args.string({ description: 'Resource ID (for get, update, delete operations)', required: false }),
|
|
46
49
|
};
|
|
47
50
|
static flags = {
|
|
48
|
-
context: Flags.string({ char: 'c', description: 'Context to use' }),
|
|
51
|
+
context: Flags.string({ char: 'c', aliases: ['config'], env: 'ADERIS_CONTEXT', description: 'Context to use for this command, overriding the current context (alias: --config)' }),
|
|
49
52
|
output: Flags.string({ char: 'o', description: 'Output format (default: table for import, json for other commands)', options: ['json', 'table', 'csv'] }),
|
|
50
53
|
table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
|
|
51
54
|
csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
|
|
52
55
|
data: Flags.string({ char: 'd', description: 'JSON data for request body' }),
|
|
53
56
|
file: Flags.string({ char: 'f', description: 'Read request body from file' }),
|
|
54
57
|
set: Flags.string({ char: 's', description: 'Set individual field (e.g. --set firstName=John)', multiple: true }),
|
|
58
|
+
'line-id': Flags.string({ description: 'Path param: lineId' }),
|
|
55
59
|
'note-id': Flags.string({ description: 'Path param: noteId' }),
|
|
56
60
|
'file-id': Flags.string({ description: 'Path param: fileId' }),
|
|
57
61
|
page: Flags.string({ description: 'Page number' }),
|
|
@@ -61,7 +65,7 @@ export default class Journals extends Command {
|
|
|
61
65
|
'sort-by': Flags.string({ description: 'Sort field(s), comma-separated. Direction (:ASC/:DESC) is optional; date-like fields default to DESC, others to ASC.' }),
|
|
62
66
|
filter: Flags.string({ description: 'Filter (e.g. --filter status=ACTIVE). Operators: in:A,B, like:%, gt:, lt:, null:', multiple: true }),
|
|
63
67
|
query: Flags.string({ description: 'Additional query params as JSON' }),
|
|
64
|
-
fields: Flags.string({ description: 'Comma-separated list of fields to
|
|
68
|
+
fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
|
|
65
69
|
sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
|
|
66
70
|
'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
|
|
67
71
|
};
|
|
@@ -119,9 +123,9 @@ export default class Journals extends Command {
|
|
|
119
123
|
}
|
|
120
124
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
121
125
|
if (!context)
|
|
122
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
123
|
-
if (!context
|
|
124
|
-
this.error(
|
|
126
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
127
|
+
if (!hasCredentials(context))
|
|
128
|
+
this.error(missingCredentialsMessage(context));
|
|
125
129
|
const queryParams = {};
|
|
126
130
|
if (flags.page)
|
|
127
131
|
queryParams.page = flags.page;
|
|
@@ -133,6 +137,8 @@ export default class Journals extends Command {
|
|
|
133
137
|
queryParams.search = flags.search;
|
|
134
138
|
if (flags['sort-by'])
|
|
135
139
|
queryParams.sortBy = normalizeSortBy(flags['sort-by']);
|
|
140
|
+
if (flags.fields)
|
|
141
|
+
queryParams.fields = flags.fields;
|
|
136
142
|
if (flags.query) {
|
|
137
143
|
try {
|
|
138
144
|
Object.assign(queryParams, JSON.parse(flags.query));
|
|
@@ -335,11 +341,6 @@ export default class Journals extends Command {
|
|
|
335
341
|
output = obj[arrayKeys[0]];
|
|
336
342
|
}
|
|
337
343
|
}
|
|
338
|
-
// Filter fields if --fields flag is provided
|
|
339
|
-
if (flags.fields) {
|
|
340
|
-
const fieldList = flags.fields.split(',').map(f => f.trim());
|
|
341
|
-
output = this.filterFields(output, fieldList);
|
|
342
|
-
}
|
|
343
344
|
// Sanitize output if requested
|
|
344
345
|
if (flags.sanitize) {
|
|
345
346
|
output = this.sanitizeOutput(output);
|
|
@@ -364,9 +365,9 @@ export default class Journals extends Command {
|
|
|
364
365
|
async runImport(flags) {
|
|
365
366
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
366
367
|
if (!context)
|
|
367
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
368
|
-
if (!context
|
|
369
|
-
this.error(
|
|
368
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
369
|
+
if (!hasCredentials(context))
|
|
370
|
+
this.error(missingCredentialsMessage(context));
|
|
370
371
|
// Get data from file, --data, or --set flags
|
|
371
372
|
let items = [];
|
|
372
373
|
if (flags.file) {
|
|
@@ -576,9 +577,9 @@ export default class Journals extends Command {
|
|
|
576
577
|
async runExport(flags) {
|
|
577
578
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
578
579
|
if (!context)
|
|
579
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
580
|
-
if (!context
|
|
581
|
-
this.error(
|
|
580
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
581
|
+
if (!hasCredentials(context))
|
|
582
|
+
this.error(missingCredentialsMessage(context));
|
|
582
583
|
const listEndpoint = ENDPOINTS['list'];
|
|
583
584
|
if (!listEndpoint)
|
|
584
585
|
this.error('Export requires a list endpoint');
|
|
@@ -594,6 +595,8 @@ export default class Journals extends Command {
|
|
|
594
595
|
baseParams.sortBy = normalizeSortBy(flags['sort-by']);
|
|
595
596
|
if (flags.include)
|
|
596
597
|
baseParams.include = flags.include;
|
|
598
|
+
if (flags.fields)
|
|
599
|
+
baseParams.fields = flags.fields;
|
|
597
600
|
if (flags.query) {
|
|
598
601
|
try {
|
|
599
602
|
Object.assign(baseParams, JSON.parse(flags.query));
|
|
@@ -648,12 +651,7 @@ export default class Journals extends Command {
|
|
|
648
651
|
}
|
|
649
652
|
}
|
|
650
653
|
process.stderr.write(' done\n');
|
|
651
|
-
// Filter fields if specified
|
|
652
654
|
let output = allItems;
|
|
653
|
-
if (flags.fields) {
|
|
654
|
-
const fieldList = flags.fields.split(',').map(f => f.trim());
|
|
655
|
-
output = this.filterFields(allItems, fieldList);
|
|
656
|
-
}
|
|
657
655
|
// Sanitize output if requested
|
|
658
656
|
if (flags.sanitize) {
|
|
659
657
|
output = this.sanitizeOutput(output);
|
|
@@ -670,9 +668,9 @@ export default class Journals extends Command {
|
|
|
670
668
|
async runCamt053Import(flags) {
|
|
671
669
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
672
670
|
if (!context)
|
|
673
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
674
|
-
if (!context
|
|
675
|
-
this.error(
|
|
671
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
672
|
+
if (!hasCredentials(context))
|
|
673
|
+
this.error(missingCredentialsMessage(context));
|
|
676
674
|
if (!flags.file) {
|
|
677
675
|
this.error('CAMT.053 import requires --file (-f) flag with path to XML file');
|
|
678
676
|
}
|
|
@@ -760,9 +758,9 @@ export default class Journals extends Command {
|
|
|
760
758
|
}
|
|
761
759
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
762
760
|
if (!context)
|
|
763
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
764
|
-
if (!context
|
|
765
|
-
this.error(
|
|
761
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
762
|
+
if (!hasCredentials(context))
|
|
763
|
+
this.error(missingCredentialsMessage(context));
|
|
766
764
|
// Get data from file, --data, or --set flags
|
|
767
765
|
let items = [];
|
|
768
766
|
if (flags.file) {
|
|
@@ -982,56 +980,6 @@ export default class Journals extends Command {
|
|
|
982
980
|
return `\$${op}`;
|
|
983
981
|
return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
|
|
984
982
|
}
|
|
985
|
-
filterFields(data, fields) {
|
|
986
|
-
const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
|
|
987
|
-
const pick = (obj, keys) => {
|
|
988
|
-
// Group keys by their first segment so sibling sub-paths
|
|
989
|
-
// (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
|
|
990
|
-
// and don't clobber each other on merge.
|
|
991
|
-
const directKeys = new Set();
|
|
992
|
-
const nestedKeys = new Map();
|
|
993
|
-
for (const key of keys) {
|
|
994
|
-
if (key.split('.').some(isUnsafeKey))
|
|
995
|
-
continue;
|
|
996
|
-
if (key.includes('.')) {
|
|
997
|
-
const [first, ...rest] = key.split('.');
|
|
998
|
-
if (isUnsafeKey(first))
|
|
999
|
-
continue;
|
|
1000
|
-
const list = nestedKeys.get(first);
|
|
1001
|
-
if (list) {
|
|
1002
|
-
list.push(rest.join('.'));
|
|
1003
|
-
}
|
|
1004
|
-
else {
|
|
1005
|
-
nestedKeys.set(first, [rest.join('.')]);
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
else {
|
|
1009
|
-
directKeys.add(key);
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
const result = {};
|
|
1013
|
-
for (const key of directKeys) {
|
|
1014
|
-
if (key in obj)
|
|
1015
|
-
result[key] = obj[key];
|
|
1016
|
-
}
|
|
1017
|
-
for (const [first, restKeys] of nestedKeys) {
|
|
1018
|
-
const val = obj[first];
|
|
1019
|
-
if (!val || typeof val !== 'object')
|
|
1020
|
-
continue;
|
|
1021
|
-
if (Array.isArray(val)) {
|
|
1022
|
-
result[first] = val.map(item => pick(item, restKeys));
|
|
1023
|
-
}
|
|
1024
|
-
else {
|
|
1025
|
-
result[first] = pick(val, restKeys);
|
|
1026
|
-
}
|
|
1027
|
-
}
|
|
1028
|
-
return result;
|
|
1029
|
-
};
|
|
1030
|
-
if (Array.isArray(data)) {
|
|
1031
|
-
return data.map(item => pick(item, fields));
|
|
1032
|
-
}
|
|
1033
|
-
return pick(data, fields);
|
|
1034
|
-
}
|
|
1035
983
|
sanitizeOutput(data) {
|
|
1036
984
|
const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
|
|
1037
985
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|