@aderis-erp/cli 1.0.15 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -8
- package/dist/commands/access-groups.d.ts +0 -1
- package/dist/commands/access-groups.js +54 -78
- package/dist/commands/account-transaction-allocations.d.ts +0 -1
- package/dist/commands/account-transaction-allocations.js +51 -75
- package/dist/commands/account-transactions.d.ts +0 -1
- package/dist/commands/account-transactions.js +80 -94
- package/dist/commands/accounts.d.ts +0 -1
- package/dist/commands/accounts.js +59 -83
- package/dist/commands/agent-platform.d.ts +0 -1
- package/dist/commands/agent-platform.js +58 -82
- package/dist/commands/agent-runs.d.ts +39 -0
- package/dist/commands/agent-runs.js +1155 -0
- package/dist/commands/analytics.d.ts +0 -1
- package/dist/commands/analytics.js +52 -76
- package/dist/commands/{text-templates.d.ts → api-keys.d.ts} +1 -2
- package/dist/commands/api-keys.js +1150 -0
- package/dist/commands/async-jobs.d.ts +39 -0
- package/dist/commands/async-jobs.js +1158 -0
- package/dist/commands/audit-log.d.ts +0 -1
- package/dist/commands/audit-log.js +51 -75
- package/dist/commands/auth/login.d.ts +9 -0
- package/dist/commands/auth/login.js +73 -20
- package/dist/commands/auth/logout.d.ts +4 -0
- package/dist/commands/auth/logout.js +4 -0
- package/dist/commands/auth/status.js +26 -17
- package/dist/commands/auth/tenant.js +3 -6
- package/dist/commands/auth.d.ts +0 -1
- package/dist/commands/auth.js +5 -34
- package/dist/commands/bank-accounts.d.ts +0 -1
- package/dist/commands/bank-accounts.js +68 -91
- package/dist/commands/bank-integrations.d.ts +1 -1
- package/dist/commands/bank-integrations.js +71 -92
- package/dist/commands/business-partners.d.ts +1 -1
- package/dist/commands/business-partners.js +84 -100
- package/dist/commands/chat.d.ts +0 -1
- package/dist/commands/chat.js +57 -80
- package/dist/commands/config/add.d.ts +3 -0
- package/dist/commands/config/add.js +34 -16
- package/dist/commands/config/current.js +3 -0
- package/dist/commands/config/list.js +3 -0
- package/dist/commands/credit-card-accounts.d.ts +1 -1
- package/dist/commands/credit-card-accounts.js +72 -90
- package/dist/commands/delivery-notes.d.ts +0 -1
- package/dist/commands/delivery-notes.js +83 -105
- package/dist/commands/dunning-configs.d.ts +0 -1
- package/dist/commands/dunning-configs.js +54 -78
- package/dist/commands/dunning-reminders.d.ts +0 -1
- package/dist/commands/dunning-reminders.js +57 -81
- package/dist/commands/dunning-runs.d.ts +0 -1
- package/dist/commands/dunning-runs.js +50 -74
- package/dist/commands/email-transactions.d.ts +0 -1
- package/dist/commands/email-transactions.js +52 -76
- package/dist/commands/employees.d.ts +0 -1
- package/dist/commands/employees.js +81 -105
- package/dist/commands/entity-configurations.d.ts +0 -1
- package/dist/commands/entity-configurations.js +52 -76
- package/dist/commands/event-processor-mappings.d.ts +0 -1
- package/dist/commands/event-processor-mappings.js +54 -78
- package/dist/commands/files.d.ts +0 -1
- package/dist/commands/files.js +61 -85
- package/dist/commands/health.d.ts +0 -1
- package/dist/commands/health.js +54 -78
- package/dist/commands/imports.d.ts +39 -0
- package/dist/commands/imports.js +1151 -0
- package/dist/commands/inbox.d.ts +0 -1
- package/dist/commands/inbox.js +57 -80
- package/dist/commands/integration-identities.d.ts +40 -0
- package/dist/commands/integration-identities.js +1151 -0
- package/dist/commands/integrations.d.ts +0 -1
- package/dist/commands/integrations.js +52 -76
- package/dist/commands/item-categories.d.ts +0 -1
- package/dist/commands/item-categories.js +54 -78
- package/dist/commands/items.d.ts +0 -1
- package/dist/commands/items.js +54 -78
- package/dist/commands/journal-lines.d.ts +0 -1
- package/dist/commands/journal-lines.js +50 -74
- package/dist/commands/journals.d.ts +1 -1
- package/dist/commands/journals.js +75 -96
- package/dist/commands/ledgers.d.ts +3 -2
- package/dist/commands/ledgers.js +100 -95
- package/dist/commands/my-account.d.ts +39 -0
- package/dist/commands/my-account.js +1160 -0
- package/dist/commands/office-locations.d.ts +0 -1
- package/dist/commands/office-locations.js +54 -78
- package/dist/commands/payment-methods.d.ts +0 -1
- package/dist/commands/payment-methods.js +59 -83
- package/dist/commands/payment-runs.d.ts +0 -1
- package/dist/commands/payment-runs.js +66 -90
- package/dist/commands/payment-terms.d.ts +0 -1
- package/dist/commands/payment-terms.js +55 -79
- package/dist/commands/payments.d.ts +0 -1
- package/dist/commands/payments.js +56 -80
- package/dist/commands/payroll-runs.d.ts +43 -0
- package/dist/commands/payroll-runs.js +1188 -0
- package/dist/commands/permissions.d.ts +39 -0
- package/dist/commands/permissions.js +1146 -0
- package/dist/commands/price-lists.d.ts +0 -1
- package/dist/commands/price-lists.js +59 -83
- package/dist/commands/processor-mappings.d.ts +0 -1
- package/dist/commands/processor-mappings.js +53 -77
- package/dist/commands/processors.d.ts +0 -1
- package/dist/commands/processors.js +60 -85
- package/dist/commands/purchase-order-types.d.ts +0 -1
- package/dist/commands/purchase-order-types.js +54 -78
- package/dist/commands/purchase-orders.d.ts +0 -1
- package/dist/commands/purchase-orders.js +74 -97
- package/dist/commands/reports.d.ts +0 -1
- package/dist/commands/reports.js +54 -78
- package/dist/commands/roles.d.ts +39 -0
- package/dist/commands/{text-templates.js → roles.js} +64 -90
- package/dist/commands/salary-types.d.ts +0 -1
- package/dist/commands/salary-types.js +54 -78
- package/dist/commands/sales-invoices.d.ts +0 -1
- package/dist/commands/sales-invoices.js +90 -110
- package/dist/commands/sales-orders.d.ts +0 -1
- package/dist/commands/sales-orders.js +83 -105
- package/dist/commands/saved-views.d.ts +0 -1
- package/dist/commands/saved-views.js +52 -76
- package/dist/commands/scheduled-processor-mappings.d.ts +0 -1
- package/dist/commands/scheduled-processor-mappings.js +54 -78
- package/dist/commands/schedules.d.ts +0 -1
- package/dist/commands/schedules.js +58 -82
- package/dist/commands/switch.js +4 -4
- package/dist/commands/tag-configurations.d.ts +0 -1
- package/dist/commands/tag-configurations.js +56 -79
- package/dist/commands/tags.d.ts +0 -1
- package/dist/commands/tags.js +54 -78
- package/dist/commands/tax-codes.d.ts +0 -1
- package/dist/commands/tax-codes.js +57 -81
- package/dist/commands/tax-matrix.d.ts +0 -1
- package/dist/commands/tax-matrix.js +54 -78
- package/dist/commands/templates.d.ts +39 -0
- package/dist/commands/templates.js +1157 -0
- package/dist/commands/tenants.d.ts +0 -1
- package/dist/commands/tenants.js +58 -81
- package/dist/commands/tools.d.ts +0 -1
- package/dist/commands/tools.js +51 -75
- package/dist/commands/users.d.ts +42 -0
- package/dist/commands/users.js +1174 -0
- package/dist/commands/vat-declarations.d.ts +3 -1
- package/dist/commands/vat-declarations.js +78 -93
- package/dist/commands/vendor-invoices.d.ts +0 -1
- package/dist/commands/vendor-invoices.js +87 -110
- package/dist/commands/webhooks.d.ts +0 -1
- package/dist/commands/webhooks.js +56 -80
- package/dist/commands/workflows.d.ts +0 -1
- package/dist/commands/workflows.js +52 -76
- package/dist/lib/api-client.d.ts +6 -0
- package/dist/lib/api-client.js +16 -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 +228 -0
- package/dist/lib/auth/jwks.d.ts +19 -0
- package/dist/lib/auth/jwks.js +112 -0
- package/dist/lib/auth/keycloak-provider.d.ts +91 -0
- package/dist/lib/auth/keycloak-provider.js +244 -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 +59 -0
- package/dist/lib/auth/tokens.js +105 -0
- package/dist/lib/auth/types.d.ts +61 -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/config.d.ts +13 -3
- package/dist/lib/config.js +24 -19
- package/oclif.manifest.json +2447 -641
- package/package.json +5 -4
- package/dist/lib/auth.d.ts +0 -43
- package/dist/lib/auth.js +0 -610
|
@@ -1,30 +1,31 @@
|
|
|
1
1
|
import { Command, Args, Flags } from '@oclif/core';
|
|
2
|
-
import { apiRequest, ApiError, normalizeSortBy } from '../lib/api-client.js';
|
|
2
|
+
import { apiRequest, ApiError, isTextualMediaType, normalizeSortBy } from '../lib/api-client.js';
|
|
3
3
|
import { getCurrentContext, getContext, getBaseUrls, hasCredentials, missingCredentialsMessage } from '../lib/config.js';
|
|
4
4
|
// Add 'import' action if both create and update exist
|
|
5
5
|
const HAS_IMPORT = true;
|
|
6
6
|
const HAS_EXPORT = true;
|
|
7
7
|
const HAS_CAMT053 = false;
|
|
8
8
|
const NESTED_IMPORTS = ["ledger-mappings", "notes"];
|
|
9
|
-
const SUBCOMMANDS = [...["list", "create", "get", "update", "delete", "get-balances", "get-balances-latest", "list-ledger-mappings", "create-ledger-mappings", "get-ledger-mappings", "update-ledger-mappings", "delete-ledger-mappings", "list-notes", "create-notes", "get-notes", "update-notes", "delete-notes"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
|
|
9
|
+
const SUBCOMMANDS = [...["list", "create", "get", "update", "delete", "get-balances", "get-balances-latest", "list-ledger-mappings", "create-ledger-mappings", "get-ledger-mappings", "update-ledger-mappings", "delete-ledger-mappings", "list-notes", "create-notes", "get-notes", "update-notes", "delete-notes", "list-daily-balances"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
|
|
10
10
|
const ENDPOINTS = {
|
|
11
|
-
'list': { method: 'GET', path: '/v1/bank-accounts', summary: 'Get all bank accounts with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
12
|
-
'create': { method: 'POST', path: '/v1/bank-accounts', summary: 'Create a new bank account (with optional notes)', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['paymentRunDays', 'notes'] },
|
|
13
|
-
'get': { method: 'GET', path: '/v1/bank-accounts/{id}', summary: 'Get a bank account by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
14
|
-
'update': { method: 'PATCH', path: '/v1/bank-accounts/{id}', summary: 'Update a bank account', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['paymentRunDays', 'notes'] },
|
|
15
|
-
'delete': { method: 'DELETE', path: '/v1/bank-accounts/{id}', summary: 'Delete a bank account', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
16
|
-
'get-balances': { method: 'GET', path: '/v1/bank-accounts/{id}/balances', summary: 'Get balance history for a bank account', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
17
|
-
'get-balances-latest': { method: 'GET', path: '/v1/bank-accounts/{id}/balances/latest', summary: 'Get latest balances (OPBD, CLBD, CLAV) for a bank account', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
18
|
-
'list-ledger-mappings': { method: 'GET', path: '/v1/bank-accounts/{bankAccountId}/ledger-mappings', summary: 'Get all ledger mappings for a bank account', pathParams: ['bankAccountId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
19
|
-
'create-ledger-mappings': { method: 'POST', path: '/v1/bank-accounts/{bankAccountId}/ledger-mappings', summary: 'Create a ledger mapping for a bank account', pathParams: ['bankAccountId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
20
|
-
'get-ledger-mappings': { method: 'GET', path: '/v1/bank-accounts/{bankAccountId}/ledger-mappings/{mappingId}', summary: 'Get a ledger mapping by ID', pathParams: ['bankAccountId', 'mappingId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
21
|
-
'update-ledger-mappings': { method: 'PATCH', path: '/v1/bank-accounts/{bankAccountId}/ledger-mappings/{mappingId}', summary: 'Update a ledger mapping', pathParams: ['bankAccountId', 'mappingId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
22
|
-
'delete-ledger-mappings': { method: 'DELETE', path: '/v1/bank-accounts/{bankAccountId}/ledger-mappings/{mappingId}', summary: 'Delete a ledger mapping', pathParams: ['bankAccountId', 'mappingId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
23
|
-
'list-notes': { method: 'GET', path: '/v1/bank-accounts/{bankAccountId}/notes', summary: 'Get all notes for a bank account', pathParams: ['bankAccountId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
24
|
-
'create-notes': { method: 'POST', path: '/v1/bank-accounts/{bankAccountId}/notes', summary: 'Create a note for a bank account', pathParams: ['bankAccountId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
25
|
-
'get-notes': { method: 'GET', path: '/v1/bank-accounts/{bankAccountId}/notes/{noteId}', summary: 'Get a specific note for a bank account', pathParams: ['bankAccountId', 'noteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
26
|
-
'update-notes': { method: 'PATCH', path: '/v1/bank-accounts/{bankAccountId}/notes/{noteId}', summary: 'Update a note for a bank account', pathParams: ['bankAccountId', 'noteId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
27
|
-
'delete-notes': { method: 'DELETE', path: '/v1/bank-accounts/{bankAccountId}/notes/{noteId}', summary: 'Delete a note from a bank account', pathParams: ['bankAccountId', 'noteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
11
|
+
'list': { method: 'GET', path: '/v1/bank-accounts', summary: 'Get all bank accounts with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
12
|
+
'create': { method: 'POST', path: '/v1/bank-accounts', summary: 'Create a new bank account (with optional notes)', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['paymentRunDays', 'notes'] },
|
|
13
|
+
'get': { method: 'GET', path: '/v1/bank-accounts/{id}', summary: 'Get a bank account by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
14
|
+
'update': { method: 'PATCH', path: '/v1/bank-accounts/{id}', summary: 'Update a bank account', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['paymentRunDays', 'notes'] },
|
|
15
|
+
'delete': { method: 'DELETE', path: '/v1/bank-accounts/{id}', summary: 'Delete a bank account', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
16
|
+
'get-balances': { method: 'GET', path: '/v1/bank-accounts/{id}/balances', summary: 'Get balance history for a bank account', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
17
|
+
'get-balances-latest': { method: 'GET', path: '/v1/bank-accounts/{id}/balances/latest', summary: 'Get latest balances (OPBD, CLBD, CLAV) for a bank account', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
18
|
+
'list-ledger-mappings': { method: 'GET', path: '/v1/bank-accounts/{bankAccountId}/ledger-mappings', summary: 'Get all ledger mappings for a bank account', pathParams: ['bankAccountId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
19
|
+
'create-ledger-mappings': { method: 'POST', path: '/v1/bank-accounts/{bankAccountId}/ledger-mappings', summary: 'Create a ledger mapping for a bank account', pathParams: ['bankAccountId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
20
|
+
'get-ledger-mappings': { method: 'GET', path: '/v1/bank-accounts/{bankAccountId}/ledger-mappings/{mappingId}', summary: 'Get a ledger mapping by ID', pathParams: ['bankAccountId', 'mappingId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
21
|
+
'update-ledger-mappings': { method: 'PATCH', path: '/v1/bank-accounts/{bankAccountId}/ledger-mappings/{mappingId}', summary: 'Update a ledger mapping', pathParams: ['bankAccountId', 'mappingId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
22
|
+
'delete-ledger-mappings': { method: 'DELETE', path: '/v1/bank-accounts/{bankAccountId}/ledger-mappings/{mappingId}', summary: 'Delete a ledger mapping', pathParams: ['bankAccountId', 'mappingId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
23
|
+
'list-notes': { method: 'GET', path: '/v1/bank-accounts/{bankAccountId}/notes', summary: 'Get all notes for a bank account', pathParams: ['bankAccountId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
24
|
+
'create-notes': { method: 'POST', path: '/v1/bank-accounts/{bankAccountId}/notes', summary: 'Create a note for a bank account', pathParams: ['bankAccountId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
25
|
+
'get-notes': { method: 'GET', path: '/v1/bank-accounts/{bankAccountId}/notes/{noteId}', summary: 'Get a specific note for a bank account', pathParams: ['bankAccountId', 'noteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
26
|
+
'update-notes': { method: 'PATCH', path: '/v1/bank-accounts/{bankAccountId}/notes/{noteId}', summary: 'Update a note for a bank account', pathParams: ['bankAccountId', 'noteId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
27
|
+
'delete-notes': { method: 'DELETE', path: '/v1/bank-accounts/{bankAccountId}/notes/{noteId}', summary: 'Delete a note from a bank account', pathParams: ['bankAccountId', 'noteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
28
|
+
'list-daily-balances': { method: 'GET', path: '/v1/bank-accounts/{bankAccountId}/daily-balances', summary: 'Get a bank account\'s daily closing balances for a date range', pathParams: ['bankAccountId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
28
29
|
};
|
|
29
30
|
export default class BankAccounts extends Command {
|
|
30
31
|
static description = 'Manage bank accounts';
|
|
@@ -57,7 +58,7 @@ export default class BankAccounts extends Command {
|
|
|
57
58
|
'sort-by': Flags.string({ description: 'Sort field(s), comma-separated. Direction (:ASC/:DESC) is optional; date-like fields default to DESC, others to ASC.' }),
|
|
58
59
|
filter: Flags.string({ description: 'Filter (e.g. --filter status=ACTIVE). Operators: in:A,B, like:%, gt:, lt:, null:', multiple: true }),
|
|
59
60
|
query: Flags.string({ description: 'Additional query params as JSON' }),
|
|
60
|
-
fields: Flags.string({ description: 'Comma-separated list of fields to
|
|
61
|
+
fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
|
|
61
62
|
sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
|
|
62
63
|
'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
|
|
63
64
|
};
|
|
@@ -129,6 +130,8 @@ export default class BankAccounts extends Command {
|
|
|
129
130
|
queryParams.search = flags.search;
|
|
130
131
|
if (flags['sort-by'])
|
|
131
132
|
queryParams.sortBy = normalizeSortBy(flags['sort-by']);
|
|
133
|
+
if (flags.fields)
|
|
134
|
+
queryParams.fields = flags.fields;
|
|
132
135
|
if (flags.query) {
|
|
133
136
|
try {
|
|
134
137
|
Object.assign(queryParams, JSON.parse(flags.query));
|
|
@@ -276,10 +279,11 @@ export default class BankAccounts extends Command {
|
|
|
276
279
|
process.stderr.write('\n');
|
|
277
280
|
}
|
|
278
281
|
try {
|
|
279
|
-
// Binary response:
|
|
282
|
+
// Binary response: to -f / --file, or to stdout when it is redirected.
|
|
280
283
|
if (endpoint.isBinaryResponse) {
|
|
281
|
-
|
|
282
|
-
|
|
284
|
+
const toStdout = !flags.file;
|
|
285
|
+
if (toStdout && process.stdout.isTTY) {
|
|
286
|
+
this.error(`Endpoint returns a binary file. Specify the output path with -f / --file (e.g. -f report.xlsx), or redirect stdout to a file.`);
|
|
283
287
|
}
|
|
284
288
|
const binResponse = await apiRequest(endpoint.method, apiPath, {
|
|
285
289
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
@@ -287,24 +291,55 @@ export default class BankAccounts extends Command {
|
|
|
287
291
|
contextName: flags.context,
|
|
288
292
|
binary: true,
|
|
289
293
|
});
|
|
290
|
-
const fsModule = await import('node:fs');
|
|
291
294
|
const binData = binResponse.data;
|
|
292
295
|
const buffer = Buffer.isBuffer(binData)
|
|
293
296
|
? binData
|
|
294
297
|
: Buffer.from(binData);
|
|
298
|
+
if (toStdout) {
|
|
299
|
+
// Nothing else may reach stdout here — it is the file.
|
|
300
|
+
process.stdout.write(buffer);
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
const fsModule = await import('node:fs');
|
|
295
304
|
fsModule.writeFileSync(flags.file, buffer);
|
|
296
305
|
this.log(`Saved ${buffer.length} bytes to ${flags.file}`);
|
|
297
306
|
return;
|
|
298
307
|
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
308
|
+
let response;
|
|
309
|
+
if (endpoint.hasBinaryVariant) {
|
|
310
|
+
// This route answers JSON or a file depending on the request body, so the
|
|
311
|
+
// bytes are read first and the response's own content-type decides. Asking
|
|
312
|
+
// for text here would corrupt an xlsx/pdf before anything could inspect it.
|
|
313
|
+
const raw = await apiRequest(endpoint.method, apiPath, {
|
|
314
|
+
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
315
|
+
data,
|
|
316
|
+
contextName: flags.context,
|
|
317
|
+
binary: true,
|
|
318
|
+
});
|
|
319
|
+
const rawType = String(raw.headers['content-type'] || '').toLowerCase();
|
|
320
|
+
const rawData = raw.data;
|
|
321
|
+
const buffer = Buffer.isBuffer(rawData) ? rawData : Buffer.from(rawData);
|
|
322
|
+
if (!isTextualMediaType(rawType)) {
|
|
323
|
+
if (process.stdout.isTTY) {
|
|
324
|
+
this.error(`The API returned ${rawType || 'binary data'}. Redirect stdout to a file, e.g. ... > report.xlsx`);
|
|
325
|
+
}
|
|
326
|
+
// Nothing else may reach stdout here — it is the file.
|
|
327
|
+
process.stdout.write(buffer);
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
response = { data: buffer.toString('utf8'), headers: raw.headers, status: raw.status };
|
|
331
|
+
}
|
|
332
|
+
else {
|
|
333
|
+
response = await apiRequest(endpoint.method, apiPath, {
|
|
334
|
+
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
335
|
+
data,
|
|
336
|
+
contextName: flags.context,
|
|
337
|
+
acceptAny: true,
|
|
338
|
+
});
|
|
339
|
+
}
|
|
305
340
|
// Check content-type for non-JSON responses (XML, text, etc.)
|
|
306
341
|
const contentType = (response.headers['content-type'] || '').toLowerCase();
|
|
307
|
-
if (contentType
|
|
342
|
+
if (isTextualMediaType(contentType) && !contentType.startsWith('application/json')) {
|
|
308
343
|
// Output raw content for XML/text responses (CSV, plain, HTML, etc.)
|
|
309
344
|
this.log(String(response.data));
|
|
310
345
|
return;
|
|
@@ -331,11 +366,6 @@ export default class BankAccounts extends Command {
|
|
|
331
366
|
output = obj[arrayKeys[0]];
|
|
332
367
|
}
|
|
333
368
|
}
|
|
334
|
-
// Filter fields if --fields flag is provided
|
|
335
|
-
if (flags.fields) {
|
|
336
|
-
const fieldList = flags.fields.split(',').map(f => f.trim());
|
|
337
|
-
output = this.filterFields(output, fieldList);
|
|
338
|
-
}
|
|
339
369
|
// Sanitize output if requested
|
|
340
370
|
if (flags.sanitize) {
|
|
341
371
|
output = this.sanitizeOutput(output);
|
|
@@ -590,6 +620,8 @@ export default class BankAccounts extends Command {
|
|
|
590
620
|
baseParams.sortBy = normalizeSortBy(flags['sort-by']);
|
|
591
621
|
if (flags.include)
|
|
592
622
|
baseParams.include = flags.include;
|
|
623
|
+
if (flags.fields)
|
|
624
|
+
baseParams.fields = flags.fields;
|
|
593
625
|
if (flags.query) {
|
|
594
626
|
try {
|
|
595
627
|
Object.assign(baseParams, JSON.parse(flags.query));
|
|
@@ -644,12 +676,7 @@ export default class BankAccounts extends Command {
|
|
|
644
676
|
}
|
|
645
677
|
}
|
|
646
678
|
process.stderr.write(' done\n');
|
|
647
|
-
// Filter fields if specified
|
|
648
679
|
let output = allItems;
|
|
649
|
-
if (flags.fields) {
|
|
650
|
-
const fieldList = flags.fields.split(',').map(f => f.trim());
|
|
651
|
-
output = this.filterFields(allItems, fieldList);
|
|
652
|
-
}
|
|
653
680
|
// Sanitize output if requested
|
|
654
681
|
if (flags.sanitize) {
|
|
655
682
|
output = this.sanitizeOutput(output);
|
|
@@ -978,56 +1005,6 @@ export default class BankAccounts extends Command {
|
|
|
978
1005
|
return `\$${op}`;
|
|
979
1006
|
return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
|
|
980
1007
|
}
|
|
981
|
-
filterFields(data, fields) {
|
|
982
|
-
const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
|
|
983
|
-
const pick = (obj, keys) => {
|
|
984
|
-
// Group keys by their first segment so sibling sub-paths
|
|
985
|
-
// (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
|
|
986
|
-
// and don't clobber each other on merge.
|
|
987
|
-
const directKeys = new Set();
|
|
988
|
-
const nestedKeys = new Map();
|
|
989
|
-
for (const key of keys) {
|
|
990
|
-
if (key.split('.').some(isUnsafeKey))
|
|
991
|
-
continue;
|
|
992
|
-
if (key.includes('.')) {
|
|
993
|
-
const [first, ...rest] = key.split('.');
|
|
994
|
-
if (isUnsafeKey(first))
|
|
995
|
-
continue;
|
|
996
|
-
const list = nestedKeys.get(first);
|
|
997
|
-
if (list) {
|
|
998
|
-
list.push(rest.join('.'));
|
|
999
|
-
}
|
|
1000
|
-
else {
|
|
1001
|
-
nestedKeys.set(first, [rest.join('.')]);
|
|
1002
|
-
}
|
|
1003
|
-
}
|
|
1004
|
-
else {
|
|
1005
|
-
directKeys.add(key);
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
const result = {};
|
|
1009
|
-
for (const key of directKeys) {
|
|
1010
|
-
if (key in obj)
|
|
1011
|
-
result[key] = obj[key];
|
|
1012
|
-
}
|
|
1013
|
-
for (const [first, restKeys] of nestedKeys) {
|
|
1014
|
-
const val = obj[first];
|
|
1015
|
-
if (!val || typeof val !== 'object')
|
|
1016
|
-
continue;
|
|
1017
|
-
if (Array.isArray(val)) {
|
|
1018
|
-
result[first] = val.map(item => pick(item, restKeys));
|
|
1019
|
-
}
|
|
1020
|
-
else {
|
|
1021
|
-
result[first] = pick(val, restKeys);
|
|
1022
|
-
}
|
|
1023
|
-
}
|
|
1024
|
-
return result;
|
|
1025
|
-
};
|
|
1026
|
-
if (Array.isArray(data)) {
|
|
1027
|
-
return data.map(item => pick(item, fields));
|
|
1028
|
-
}
|
|
1029
|
-
return pick(data, fields);
|
|
1030
|
-
}
|
|
1031
1008
|
sanitizeOutput(data) {
|
|
1032
1009
|
const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
|
|
1033
1010
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
@@ -15,6 +15,7 @@ export default class BankIntegrations extends Command {
|
|
|
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
17
|
'import-run-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
18
|
+
'file-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
18
19
|
'statement-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>;
|
|
20
21
|
limit: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -33,7 +34,6 @@ export default class BankIntegrations 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,31 +1,33 @@
|
|
|
1
1
|
import { Command, Args, Flags } from '@oclif/core';
|
|
2
|
-
import { apiRequest, ApiError, normalizeSortBy } from '../lib/api-client.js';
|
|
2
|
+
import { apiRequest, ApiError, isTextualMediaType, normalizeSortBy } from '../lib/api-client.js';
|
|
3
3
|
import { getCurrentContext, getContext, getBaseUrls, hasCredentials, missingCredentialsMessage } from '../lib/config.js';
|
|
4
4
|
// Add 'import' action if both create and update exist
|
|
5
5
|
const HAS_IMPORT = true;
|
|
6
6
|
const HAS_EXPORT = true;
|
|
7
7
|
const HAS_CAMT053 = false;
|
|
8
8
|
const NESTED_IMPORTS = [];
|
|
9
|
-
const SUBCOMMANDS = [...["initiate", "providers-supported", "providers-consent-status", "delete-providers-consent", "list", "create", "get", "update", "delete", "log", "list-account-statement-runs", "trigger-account-statement-runs", "get-account-statement-runs", "account-statement-runs-files", "trigger-account-statement-runs-reimport", "list-statements", "statements", "download-statements-xml"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
|
|
9
|
+
const SUBCOMMANDS = [...["initiate", "providers-supported", "providers-consent-status", "delete-providers-consent", "list", "create", "get", "update", "delete", "log", "list-account-statement-runs", "trigger-account-statement-runs", "get-account-statement-runs", "account-statement-runs-files", "trigger-account-statement-runs-reimport", "account-statement-runs-files-download", "list-all", "list-statements", "statements", "download-statements-xml"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
|
|
10
10
|
const ENDPOINTS = {
|
|
11
|
-
'initiate': { method: 'POST', path: '/v1/bank-integrations/initiate', summary: 'Initiate the OAuth/auth flow for a bank integration', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
12
|
-
'providers-supported': { method: 'GET', path: '/v1/bank-integrations/providers/supported', summary: 'List supported bLink providers', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
13
|
-
'providers-consent-status': { method: 'GET', path: '/v1/bank-integrations/providers/consent-status', summary: 'Get bLink consent status', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
14
|
-
'delete-providers-consent': { method: 'DELETE', path: '/v1/bank-integrations/providers/{providerId}/consent', summary: 'Revoke bLink consent for a provider', pathParams: ['providerId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
15
|
-
'list': { method: 'GET', path: '/v1/bank-integrations', summary: 'List all BankIntegrations for the current tenant with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
16
|
-
'create': { method: 'POST', path: '/v1/bank-integrations', summary: 'Create a BankIntegration record', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['methods'] },
|
|
17
|
-
'get': { method: 'GET', path: '/v1/bank-integrations/{id}', summary: 'Get a BankIntegration by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
18
|
-
'update': { method: 'PATCH', path: '/v1/bank-integrations/{id}', summary: 'Update the display name of a BankIntegration', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
19
|
-
'delete': { method: 'DELETE', path: '/v1/bank-integrations/{id}', summary: 'Delete a BankIntegration', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
20
|
-
'log': { method: 'GET', path: '/v1/bank-integrations/{id}/log', summary: 'Get activity log for a bank integration', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
21
|
-
'list-account-statement-runs': { method: 'GET', path: '/v1/bank-integrations/{bankIntegrationId}/account-statement-runs', summary: 'List bank statement import runs', pathParams: ['bankIntegrationId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
22
|
-
'trigger-account-statement-runs': { method: 'POST', path: '/v1/bank-integrations/{bankIntegrationId}/account-statement-runs', summary: 'Trigger a bank statement import', pathParams: ['bankIntegrationId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
23
|
-
'get-account-statement-runs': { method: 'GET', path: '/v1/bank-integrations/{bankIntegrationId}/account-statement-runs/{importRunId}', summary: 'Get import run details', pathParams: ['bankIntegrationId', 'importRunId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
24
|
-
'account-statement-runs-files': { method: 'GET', path: '/v1/bank-integrations/{bankIntegrationId}/account-statement-runs/{importRunId}/files', summary: 'Get files for an import run', pathParams: ['bankIntegrationId', 'importRunId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
25
|
-
'trigger-account-statement-runs-reimport': { method: 'POST', path: '/v1/bank-integrations/{bankIntegrationId}/account-statement-runs/{importRunId}/reimport', summary: 'Trigger reimport of an existing account statement import run', pathParams: ['bankIntegrationId', 'importRunId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['fileIds'] },
|
|
26
|
-
'
|
|
27
|
-
'
|
|
28
|
-
'
|
|
11
|
+
'initiate': { method: 'POST', path: '/v1/bank-integrations/initiate', summary: 'Initiate the OAuth/auth flow for a bank integration', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
12
|
+
'providers-supported': { method: 'GET', path: '/v1/bank-integrations/providers/supported', summary: 'List supported bLink providers', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
13
|
+
'providers-consent-status': { method: 'GET', path: '/v1/bank-integrations/providers/consent-status', summary: 'Get bLink consent status', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
14
|
+
'delete-providers-consent': { method: 'DELETE', path: '/v1/bank-integrations/providers/{providerId}/consent', summary: 'Revoke bLink consent for a provider', pathParams: ['providerId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
+
'list': { method: 'GET', path: '/v1/bank-integrations', summary: 'List all BankIntegrations for the current tenant with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
16
|
+
'create': { method: 'POST', path: '/v1/bank-integrations', summary: 'Create a BankIntegration record', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['methods'] },
|
|
17
|
+
'get': { method: 'GET', path: '/v1/bank-integrations/{id}', summary: 'Get a BankIntegration by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
18
|
+
'update': { method: 'PATCH', path: '/v1/bank-integrations/{id}', summary: 'Update the display name of a BankIntegration', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
19
|
+
'delete': { method: 'DELETE', path: '/v1/bank-integrations/{id}', summary: 'Delete a BankIntegration', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
20
|
+
'log': { method: 'GET', path: '/v1/bank-integrations/{id}/log', summary: 'Get activity log for a bank integration', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
21
|
+
'list-account-statement-runs': { method: 'GET', path: '/v1/bank-integrations/{bankIntegrationId}/account-statement-runs', summary: 'List bank statement import runs', pathParams: ['bankIntegrationId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
22
|
+
'trigger-account-statement-runs': { method: 'POST', path: '/v1/bank-integrations/{bankIntegrationId}/account-statement-runs', summary: 'Trigger a bank statement import', pathParams: ['bankIntegrationId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
23
|
+
'get-account-statement-runs': { method: 'GET', path: '/v1/bank-integrations/{bankIntegrationId}/account-statement-runs/{importRunId}', summary: 'Get import run details', pathParams: ['bankIntegrationId', 'importRunId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
24
|
+
'account-statement-runs-files': { method: 'GET', path: '/v1/bank-integrations/{bankIntegrationId}/account-statement-runs/{importRunId}/files', summary: 'Get files for an import run', pathParams: ['bankIntegrationId', 'importRunId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
25
|
+
'trigger-account-statement-runs-reimport': { method: 'POST', path: '/v1/bank-integrations/{bankIntegrationId}/account-statement-runs/{importRunId}/reimport', summary: 'Trigger reimport of an existing account statement import run', pathParams: ['bankIntegrationId', 'importRunId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['fileIds'] },
|
|
26
|
+
'account-statement-runs-files-download': { method: 'GET', path: '/v1/bank-integrations/{bankIntegrationId}/account-statement-runs/{importRunId}/files/{fileId}/download', summary: 'Download the stored CAMT XML for a statement file', pathParams: ['bankIntegrationId', 'importRunId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: true, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
27
|
+
'list-all': { method: 'GET', path: '/v1/account-statement-files', summary: 'List statement import files across this tenant’s bank integrations', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
28
|
+
'list-statements': { method: 'GET', path: '/v1/bank-integrations/{bankIntegrationId}/statements', summary: 'List CAMT.053 statements for a bank integration', pathParams: ['bankIntegrationId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
29
|
+
'statements': { method: 'GET', path: '/v1/bank-integrations/{bankIntegrationId}/statements/{statementId}', summary: 'Get CAMT.053 statement detail', pathParams: ['bankIntegrationId', 'statementId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
30
|
+
'download-statements-xml': { method: 'GET', path: '/v1/bank-integrations/{bankIntegrationId}/statements/{statementId}/xml', summary: 'Get raw CAMT.053 XML for a statement', pathParams: ['bankIntegrationId', 'statementId'], hasBody: false, isMultipart: false, isBinaryResponse: true, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
29
31
|
};
|
|
30
32
|
export default class BankIntegrations extends Command {
|
|
31
33
|
static description = 'Manage bank integrations';
|
|
@@ -50,6 +52,7 @@ export default class BankIntegrations extends Command {
|
|
|
50
52
|
file: Flags.string({ char: 'f', description: 'Read request body from file' }),
|
|
51
53
|
set: Flags.string({ char: 's', description: 'Set individual field (e.g. --set firstName=John)', multiple: true }),
|
|
52
54
|
'import-run-id': Flags.string({ description: 'Path param: importRunId' }),
|
|
55
|
+
'file-id': Flags.string({ description: 'Path param: fileId' }),
|
|
53
56
|
'statement-id': Flags.string({ description: 'Path param: statementId' }),
|
|
54
57
|
page: Flags.string({ description: 'Page number' }),
|
|
55
58
|
limit: Flags.string({ description: 'Items per page' }),
|
|
@@ -58,7 +61,7 @@ export default class BankIntegrations extends Command {
|
|
|
58
61
|
'sort-by': Flags.string({ description: 'Sort field(s), comma-separated. Direction (:ASC/:DESC) is optional; date-like fields default to DESC, others to ASC.' }),
|
|
59
62
|
filter: Flags.string({ description: 'Filter (e.g. --filter status=ACTIVE). Operators: in:A,B, like:%, gt:, lt:, null:', multiple: true }),
|
|
60
63
|
query: Flags.string({ description: 'Additional query params as JSON' }),
|
|
61
|
-
fields: Flags.string({ description: 'Comma-separated list of fields to
|
|
64
|
+
fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
|
|
62
65
|
sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
|
|
63
66
|
'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
|
|
64
67
|
};
|
|
@@ -130,6 +133,8 @@ export default class BankIntegrations extends Command {
|
|
|
130
133
|
queryParams.search = flags.search;
|
|
131
134
|
if (flags['sort-by'])
|
|
132
135
|
queryParams.sortBy = normalizeSortBy(flags['sort-by']);
|
|
136
|
+
if (flags.fields)
|
|
137
|
+
queryParams.fields = flags.fields;
|
|
133
138
|
if (flags.query) {
|
|
134
139
|
try {
|
|
135
140
|
Object.assign(queryParams, JSON.parse(flags.query));
|
|
@@ -277,10 +282,11 @@ export default class BankIntegrations extends Command {
|
|
|
277
282
|
process.stderr.write('\n');
|
|
278
283
|
}
|
|
279
284
|
try {
|
|
280
|
-
// Binary response:
|
|
285
|
+
// Binary response: to -f / --file, or to stdout when it is redirected.
|
|
281
286
|
if (endpoint.isBinaryResponse) {
|
|
282
|
-
|
|
283
|
-
|
|
287
|
+
const toStdout = !flags.file;
|
|
288
|
+
if (toStdout && process.stdout.isTTY) {
|
|
289
|
+
this.error(`Endpoint returns a binary file. Specify the output path with -f / --file (e.g. -f report.xlsx), or redirect stdout to a file.`);
|
|
284
290
|
}
|
|
285
291
|
const binResponse = await apiRequest(endpoint.method, apiPath, {
|
|
286
292
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
@@ -288,24 +294,55 @@ export default class BankIntegrations extends Command {
|
|
|
288
294
|
contextName: flags.context,
|
|
289
295
|
binary: true,
|
|
290
296
|
});
|
|
291
|
-
const fsModule = await import('node:fs');
|
|
292
297
|
const binData = binResponse.data;
|
|
293
298
|
const buffer = Buffer.isBuffer(binData)
|
|
294
299
|
? binData
|
|
295
300
|
: Buffer.from(binData);
|
|
301
|
+
if (toStdout) {
|
|
302
|
+
// Nothing else may reach stdout here — it is the file.
|
|
303
|
+
process.stdout.write(buffer);
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
const fsModule = await import('node:fs');
|
|
296
307
|
fsModule.writeFileSync(flags.file, buffer);
|
|
297
308
|
this.log(`Saved ${buffer.length} bytes to ${flags.file}`);
|
|
298
309
|
return;
|
|
299
310
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
311
|
+
let response;
|
|
312
|
+
if (endpoint.hasBinaryVariant) {
|
|
313
|
+
// This route answers JSON or a file depending on the request body, so the
|
|
314
|
+
// bytes are read first and the response's own content-type decides. Asking
|
|
315
|
+
// for text here would corrupt an xlsx/pdf before anything could inspect it.
|
|
316
|
+
const raw = await apiRequest(endpoint.method, apiPath, {
|
|
317
|
+
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
318
|
+
data,
|
|
319
|
+
contextName: flags.context,
|
|
320
|
+
binary: true,
|
|
321
|
+
});
|
|
322
|
+
const rawType = String(raw.headers['content-type'] || '').toLowerCase();
|
|
323
|
+
const rawData = raw.data;
|
|
324
|
+
const buffer = Buffer.isBuffer(rawData) ? rawData : Buffer.from(rawData);
|
|
325
|
+
if (!isTextualMediaType(rawType)) {
|
|
326
|
+
if (process.stdout.isTTY) {
|
|
327
|
+
this.error(`The API returned ${rawType || 'binary data'}. Redirect stdout to a file, e.g. ... > report.xlsx`);
|
|
328
|
+
}
|
|
329
|
+
// Nothing else may reach stdout here — it is the file.
|
|
330
|
+
process.stdout.write(buffer);
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
response = { data: buffer.toString('utf8'), headers: raw.headers, status: raw.status };
|
|
334
|
+
}
|
|
335
|
+
else {
|
|
336
|
+
response = await apiRequest(endpoint.method, apiPath, {
|
|
337
|
+
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
338
|
+
data,
|
|
339
|
+
contextName: flags.context,
|
|
340
|
+
acceptAny: true,
|
|
341
|
+
});
|
|
342
|
+
}
|
|
306
343
|
// Check content-type for non-JSON responses (XML, text, etc.)
|
|
307
344
|
const contentType = (response.headers['content-type'] || '').toLowerCase();
|
|
308
|
-
if (contentType
|
|
345
|
+
if (isTextualMediaType(contentType) && !contentType.startsWith('application/json')) {
|
|
309
346
|
// Output raw content for XML/text responses (CSV, plain, HTML, etc.)
|
|
310
347
|
this.log(String(response.data));
|
|
311
348
|
return;
|
|
@@ -332,11 +369,6 @@ export default class BankIntegrations extends Command {
|
|
|
332
369
|
output = obj[arrayKeys[0]];
|
|
333
370
|
}
|
|
334
371
|
}
|
|
335
|
-
// Filter fields if --fields flag is provided
|
|
336
|
-
if (flags.fields) {
|
|
337
|
-
const fieldList = flags.fields.split(',').map(f => f.trim());
|
|
338
|
-
output = this.filterFields(output, fieldList);
|
|
339
|
-
}
|
|
340
372
|
// Sanitize output if requested
|
|
341
373
|
if (flags.sanitize) {
|
|
342
374
|
output = this.sanitizeOutput(output);
|
|
@@ -591,6 +623,8 @@ export default class BankIntegrations extends Command {
|
|
|
591
623
|
baseParams.sortBy = normalizeSortBy(flags['sort-by']);
|
|
592
624
|
if (flags.include)
|
|
593
625
|
baseParams.include = flags.include;
|
|
626
|
+
if (flags.fields)
|
|
627
|
+
baseParams.fields = flags.fields;
|
|
594
628
|
if (flags.query) {
|
|
595
629
|
try {
|
|
596
630
|
Object.assign(baseParams, JSON.parse(flags.query));
|
|
@@ -645,12 +679,7 @@ export default class BankIntegrations extends Command {
|
|
|
645
679
|
}
|
|
646
680
|
}
|
|
647
681
|
process.stderr.write(' done\n');
|
|
648
|
-
// Filter fields if specified
|
|
649
682
|
let output = allItems;
|
|
650
|
-
if (flags.fields) {
|
|
651
|
-
const fieldList = flags.fields.split(',').map(f => f.trim());
|
|
652
|
-
output = this.filterFields(allItems, fieldList);
|
|
653
|
-
}
|
|
654
683
|
// Sanitize output if requested
|
|
655
684
|
if (flags.sanitize) {
|
|
656
685
|
output = this.sanitizeOutput(output);
|
|
@@ -979,56 +1008,6 @@ export default class BankIntegrations extends Command {
|
|
|
979
1008
|
return `\$${op}`;
|
|
980
1009
|
return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
|
|
981
1010
|
}
|
|
982
|
-
filterFields(data, fields) {
|
|
983
|
-
const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
|
|
984
|
-
const pick = (obj, keys) => {
|
|
985
|
-
// Group keys by their first segment so sibling sub-paths
|
|
986
|
-
// (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
|
|
987
|
-
// and don't clobber each other on merge.
|
|
988
|
-
const directKeys = new Set();
|
|
989
|
-
const nestedKeys = new Map();
|
|
990
|
-
for (const key of keys) {
|
|
991
|
-
if (key.split('.').some(isUnsafeKey))
|
|
992
|
-
continue;
|
|
993
|
-
if (key.includes('.')) {
|
|
994
|
-
const [first, ...rest] = key.split('.');
|
|
995
|
-
if (isUnsafeKey(first))
|
|
996
|
-
continue;
|
|
997
|
-
const list = nestedKeys.get(first);
|
|
998
|
-
if (list) {
|
|
999
|
-
list.push(rest.join('.'));
|
|
1000
|
-
}
|
|
1001
|
-
else {
|
|
1002
|
-
nestedKeys.set(first, [rest.join('.')]);
|
|
1003
|
-
}
|
|
1004
|
-
}
|
|
1005
|
-
else {
|
|
1006
|
-
directKeys.add(key);
|
|
1007
|
-
}
|
|
1008
|
-
}
|
|
1009
|
-
const result = {};
|
|
1010
|
-
for (const key of directKeys) {
|
|
1011
|
-
if (key in obj)
|
|
1012
|
-
result[key] = obj[key];
|
|
1013
|
-
}
|
|
1014
|
-
for (const [first, restKeys] of nestedKeys) {
|
|
1015
|
-
const val = obj[first];
|
|
1016
|
-
if (!val || typeof val !== 'object')
|
|
1017
|
-
continue;
|
|
1018
|
-
if (Array.isArray(val)) {
|
|
1019
|
-
result[first] = val.map(item => pick(item, restKeys));
|
|
1020
|
-
}
|
|
1021
|
-
else {
|
|
1022
|
-
result[first] = pick(val, restKeys);
|
|
1023
|
-
}
|
|
1024
|
-
}
|
|
1025
|
-
return result;
|
|
1026
|
-
};
|
|
1027
|
-
if (Array.isArray(data)) {
|
|
1028
|
-
return data.map(item => pick(item, fields));
|
|
1029
|
-
}
|
|
1030
|
-
return pick(data, fields);
|
|
1031
|
-
}
|
|
1032
1011
|
sanitizeOutput(data) {
|
|
1033
1012
|
const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
|
|
1034
1013
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
@@ -15,6 +15,7 @@ export default class BusinessPartners extends Command {
|
|
|
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
17
|
'note-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
18
|
+
'file-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
18
19
|
'business-partner-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>;
|
|
20
21
|
limit: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -33,7 +34,6 @@ export default class BusinessPartners 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;
|