@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,5 +1,5 @@
|
|
|
1
1
|
import { Command, Args, Flags } from '@oclif/core';
|
|
2
|
-
import { apiRequest, ApiError, normalizeSortBy } from '../lib/api-client.js';
|
|
2
|
+
import { apiRequest, ApiError, isTextualMediaType, normalizeSortBy } from '../lib/api-client.js';
|
|
3
3
|
import { getCurrentContext, getContext, getBaseUrls, hasCredentials, missingCredentialsMessage } from '../lib/config.js';
|
|
4
4
|
// Add 'import' action if both create and update exist
|
|
5
5
|
const HAS_IMPORT = true;
|
|
@@ -8,38 +8,38 @@ const HAS_CAMT053 = false;
|
|
|
8
8
|
const NESTED_IMPORTS = ["salaries", "payments", "notes", "files", "salary-payment-files"];
|
|
9
9
|
const SUBCOMMANDS = [...["list", "create", "get", "update", "delete", "get-salaries", "create-salaries", "get-salaries-salaryId", "update-salaries", "delete-salaries", "get-payments", "create-payments", "get-payments-paymentId", "update-payments", "delete-payments", "list-notes", "create-notes", "get-notes", "update-notes", "delete-notes", "list-files", "create-files", "get-files", "update-files", "delete-files", "files-download", "list-salary-payment-files", "create-salary-payment-files", "get-salary-payment-files", "update-salary-payment-files", "delete-salary-payment-files", "download-salary-payment-files"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
|
|
10
10
|
const ENDPOINTS = {
|
|
11
|
-
'list': { method: 'GET', path: '/v1/employees', summary: 'Get all employees with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
12
|
-
'create': { method: 'POST', path: '/v1/employees', summary: 'Create a new employee', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['beneficiaries', 'children', 'salaries', 'files', 'notes', 'tagIds', 'tagCodes'] },
|
|
13
|
-
'get': { method: 'GET', path: '/v1/employees/{id}', summary: 'Get an employee by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
14
|
-
'update': { method: 'PATCH', path: '/v1/employees/{id}', summary: 'Update an employee', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['beneficiaries', 'children', 'salaries', 'files', 'notes', 'tagIds', 'tagCodes'] },
|
|
15
|
-
'delete': { method: 'DELETE', path: '/v1/employees/{id}', summary: 'Delete an employee', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
16
|
-
'get-salaries': { method: 'GET', path: '/v1/employees/{id}/salaries', summary: 'Get all salaries for an employee', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
17
|
-
'create-salaries': { method: 'POST', path: '/v1/employees/{id}/salaries', summary: 'Add a salary configuration to an employee', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
18
|
-
'get-salaries-salaryId': { method: 'GET', path: '/v1/employees/{id}/salaries/{salaryId}', summary: 'Get a specific salary by ID', pathParams: ['id', 'salaryId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
19
|
-
'update-salaries': { method: 'PATCH', path: '/v1/employees/{id}/salaries/{salaryId}', summary: 'Update a salary configuration', pathParams: ['id', 'salaryId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
20
|
-
'delete-salaries': { method: 'DELETE', path: '/v1/employees/{id}/salaries/{salaryId}', summary: 'Delete a salary configuration', pathParams: ['id', 'salaryId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
21
|
-
'get-payments': { method: 'GET', path: '/v1/employees/{id}/payments', summary: 'Get all payments for an employee', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
22
|
-
'create-payments': { method: 'POST', path: '/v1/employees/{id}/payments', summary: 'Add a payment to an employee', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['salaryComponents', 'files'] },
|
|
23
|
-
'get-payments-paymentId': { method: 'GET', path: '/v1/employees/{id}/payments/{paymentId}', summary: 'Get a specific payment by ID', pathParams: ['id', 'paymentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
24
|
-
'update-payments': { method: 'PATCH', path: '/v1/employees/{id}/payments/{paymentId}', summary: 'Update a payment', pathParams: ['id', 'paymentId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['salaryComponents', 'files'] },
|
|
25
|
-
'delete-payments': { method: 'DELETE', path: '/v1/employees/{id}/payments/{paymentId}', summary: 'Delete a payment', pathParams: ['id', 'paymentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
26
|
-
'list-notes': { method: 'GET', path: '/v1/employees/{employeeId}/notes', summary: 'Get all notes for an employee', pathParams: ['employeeId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
27
|
-
'create-notes': { method: 'POST', path: '/v1/employees/{employeeId}/notes', summary: 'Create a note for an employee', pathParams: ['employeeId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
28
|
-
'get-notes': { method: 'GET', path: '/v1/employees/{employeeId}/notes/{noteId}', summary: 'Get a specific note for an employee', pathParams: ['employeeId', 'noteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
29
|
-
'update-notes': { method: 'PATCH', path: '/v1/employees/{employeeId}/notes/{noteId}', summary: 'Update a note for an employee', pathParams: ['employeeId', 'noteId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
30
|
-
'delete-notes': { method: 'DELETE', path: '/v1/employees/{employeeId}/notes/{noteId}', summary: 'Delete a note from an employee', pathParams: ['employeeId', 'noteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
31
|
-
'list-files': { method: 'GET', path: '/v1/employees/{parentId}/files', summary: 'List file attachments for a employee', pathParams: ['parentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
32
|
-
'create-files': { method: 'POST', path: '/v1/employees/{parentId}/files', summary: 'Upload a file attachment to a employee', pathParams: ['parentId'], hasBody: false, isMultipart: true, isBinaryResponse: false, bodyArrayFields: [] },
|
|
33
|
-
'get-files': { method: 'GET', path: '/v1/employees/{parentId}/files/{fileId}', summary: 'Get a file attachment for a employee', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
34
|
-
'update-files': { method: 'PATCH', path: '/v1/employees/{parentId}/files/{fileId}', summary: 'Update file attachment metadata for a employee', pathParams: ['parentId', 'fileId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
35
|
-
'delete-files': { method: 'DELETE', path: '/v1/employees/{parentId}/files/{fileId}', summary: 'Delete a file attachment from a employee', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
36
|
-
'files-download': { method: 'GET', path: '/v1/employees/{parentId}/files/{fileId}/download', summary: 'Download a file attachment from a employee', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
37
|
-
'list-salary-payment-files': { method: 'GET', path: '/v1/salary-payments/{parentId}/files', summary: 'List file attachments for a salary payment', pathParams: ['parentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
38
|
-
'create-salary-payment-files': { method: 'POST', path: '/v1/salary-payments/{parentId}/files', summary: 'Upload a file attachment to a salary payment', pathParams: ['parentId'], hasBody: false, isMultipart: true, isBinaryResponse: false, bodyArrayFields: [] },
|
|
39
|
-
'get-salary-payment-files': { method: 'GET', path: '/v1/salary-payments/{parentId}/files/{fileId}', summary: 'Get a file attachment for a salary payment', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
40
|
-
'update-salary-payment-files': { method: 'PATCH', path: '/v1/salary-payments/{parentId}/files/{fileId}', summary: 'Update file attachment metadata for a salary payment', pathParams: ['parentId', 'fileId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
41
|
-
'delete-salary-payment-files': { method: 'DELETE', path: '/v1/salary-payments/{parentId}/files/{fileId}', summary: 'Delete a file attachment from a salary payment', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
42
|
-
'download-salary-payment-files': { method: 'GET', path: '/v1/salary-payments/{parentId}/files/{fileId}/download', summary: 'Download a file attachment from a salary payment', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
11
|
+
'list': { method: 'GET', path: '/v1/employees', summary: 'Get all employees with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
12
|
+
'create': { method: 'POST', path: '/v1/employees', summary: 'Create a new employee', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['beneficiaries', 'children', 'salaries', 'files', 'notes', 'tagIds', 'tagCodes'] },
|
|
13
|
+
'get': { method: 'GET', path: '/v1/employees/{id}', summary: 'Get an employee by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
14
|
+
'update': { method: 'PATCH', path: '/v1/employees/{id}', summary: 'Update an employee', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['beneficiaries', 'children', 'salaries', 'files', 'notes', 'tagIds', 'tagCodes'] },
|
|
15
|
+
'delete': { method: 'DELETE', path: '/v1/employees/{id}', summary: 'Delete an employee', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
16
|
+
'get-salaries': { method: 'GET', path: '/v1/employees/{id}/salaries', summary: 'Get all salaries for an employee', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
17
|
+
'create-salaries': { method: 'POST', path: '/v1/employees/{id}/salaries', summary: 'Add a salary configuration to an employee', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
18
|
+
'get-salaries-salaryId': { method: 'GET', path: '/v1/employees/{id}/salaries/{salaryId}', summary: 'Get a specific salary by ID', pathParams: ['id', 'salaryId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
19
|
+
'update-salaries': { method: 'PATCH', path: '/v1/employees/{id}/salaries/{salaryId}', summary: 'Update a salary configuration', pathParams: ['id', 'salaryId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
20
|
+
'delete-salaries': { method: 'DELETE', path: '/v1/employees/{id}/salaries/{salaryId}', summary: 'Delete a salary configuration', pathParams: ['id', 'salaryId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
21
|
+
'get-payments': { method: 'GET', path: '/v1/employees/{id}/payments', summary: 'Get all payments for an employee', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
22
|
+
'create-payments': { method: 'POST', path: '/v1/employees/{id}/payments', summary: 'Add a payment to an employee', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['salaryComponents', 'files'] },
|
|
23
|
+
'get-payments-paymentId': { method: 'GET', path: '/v1/employees/{id}/payments/{paymentId}', summary: 'Get a specific payment by ID', pathParams: ['id', 'paymentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
24
|
+
'update-payments': { method: 'PATCH', path: '/v1/employees/{id}/payments/{paymentId}', summary: 'Update a payment', pathParams: ['id', 'paymentId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['salaryComponents', 'files'] },
|
|
25
|
+
'delete-payments': { method: 'DELETE', path: '/v1/employees/{id}/payments/{paymentId}', summary: 'Delete a payment', pathParams: ['id', 'paymentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
26
|
+
'list-notes': { method: 'GET', path: '/v1/employees/{employeeId}/notes', summary: 'Get all notes for an employee', pathParams: ['employeeId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
27
|
+
'create-notes': { method: 'POST', path: '/v1/employees/{employeeId}/notes', summary: 'Create a note for an employee', pathParams: ['employeeId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
28
|
+
'get-notes': { method: 'GET', path: '/v1/employees/{employeeId}/notes/{noteId}', summary: 'Get a specific note for an employee', pathParams: ['employeeId', 'noteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
29
|
+
'update-notes': { method: 'PATCH', path: '/v1/employees/{employeeId}/notes/{noteId}', summary: 'Update a note for an employee', pathParams: ['employeeId', 'noteId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
30
|
+
'delete-notes': { method: 'DELETE', path: '/v1/employees/{employeeId}/notes/{noteId}', summary: 'Delete a note from an employee', pathParams: ['employeeId', 'noteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
31
|
+
'list-files': { method: 'GET', path: '/v1/employees/{parentId}/files', summary: 'List file attachments for a employee', pathParams: ['parentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
32
|
+
'create-files': { method: 'POST', path: '/v1/employees/{parentId}/files', summary: 'Upload a file attachment to a employee', pathParams: ['parentId'], hasBody: false, isMultipart: true, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
33
|
+
'get-files': { method: 'GET', path: '/v1/employees/{parentId}/files/{fileId}', summary: 'Get a file attachment for a employee', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
34
|
+
'update-files': { method: 'PATCH', path: '/v1/employees/{parentId}/files/{fileId}', summary: 'Update file attachment metadata for a employee', pathParams: ['parentId', 'fileId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
35
|
+
'delete-files': { method: 'DELETE', path: '/v1/employees/{parentId}/files/{fileId}', summary: 'Delete a file attachment from a employee', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
36
|
+
'files-download': { method: 'GET', path: '/v1/employees/{parentId}/files/{fileId}/download', summary: 'Download a file attachment from a employee', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: true, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
37
|
+
'list-salary-payment-files': { method: 'GET', path: '/v1/salary-payments/{parentId}/files', summary: 'List file attachments for a salary payment', pathParams: ['parentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
38
|
+
'create-salary-payment-files': { method: 'POST', path: '/v1/salary-payments/{parentId}/files', summary: 'Upload a file attachment to a salary payment', pathParams: ['parentId'], hasBody: false, isMultipart: true, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
39
|
+
'get-salary-payment-files': { method: 'GET', path: '/v1/salary-payments/{parentId}/files/{fileId}', summary: 'Get a file attachment for a salary payment', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
40
|
+
'update-salary-payment-files': { method: 'PATCH', path: '/v1/salary-payments/{parentId}/files/{fileId}', summary: 'Update file attachment metadata for a salary payment', pathParams: ['parentId', 'fileId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
41
|
+
'delete-salary-payment-files': { method: 'DELETE', path: '/v1/salary-payments/{parentId}/files/{fileId}', summary: 'Delete a file attachment from a salary payment', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
42
|
+
'download-salary-payment-files': { method: 'GET', path: '/v1/salary-payments/{parentId}/files/{fileId}/download', summary: 'Download a file attachment from a salary payment', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: true, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
43
43
|
};
|
|
44
44
|
export default class Employees extends Command {
|
|
45
45
|
static description = 'Manage employees';
|
|
@@ -74,7 +74,7 @@ export default class Employees extends Command {
|
|
|
74
74
|
'sort-by': Flags.string({ description: 'Sort field(s), comma-separated. Direction (:ASC/:DESC) is optional; date-like fields default to DESC, others to ASC.' }),
|
|
75
75
|
filter: Flags.string({ description: 'Filter (e.g. --filter status=ACTIVE). Operators: in:A,B, like:%, gt:, lt:, null:', multiple: true }),
|
|
76
76
|
query: Flags.string({ description: 'Additional query params as JSON' }),
|
|
77
|
-
fields: Flags.string({ description: 'Comma-separated list of fields to
|
|
77
|
+
fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
|
|
78
78
|
sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
|
|
79
79
|
'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
|
|
80
80
|
};
|
|
@@ -146,6 +146,8 @@ export default class Employees extends Command {
|
|
|
146
146
|
queryParams.search = flags.search;
|
|
147
147
|
if (flags['sort-by'])
|
|
148
148
|
queryParams.sortBy = normalizeSortBy(flags['sort-by']);
|
|
149
|
+
if (flags.fields)
|
|
150
|
+
queryParams.fields = flags.fields;
|
|
149
151
|
if (flags.query) {
|
|
150
152
|
try {
|
|
151
153
|
Object.assign(queryParams, JSON.parse(flags.query));
|
|
@@ -293,10 +295,11 @@ export default class Employees extends Command {
|
|
|
293
295
|
process.stderr.write('\n');
|
|
294
296
|
}
|
|
295
297
|
try {
|
|
296
|
-
// Binary response:
|
|
298
|
+
// Binary response: to -f / --file, or to stdout when it is redirected.
|
|
297
299
|
if (endpoint.isBinaryResponse) {
|
|
298
|
-
|
|
299
|
-
|
|
300
|
+
const toStdout = !flags.file;
|
|
301
|
+
if (toStdout && process.stdout.isTTY) {
|
|
302
|
+
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.`);
|
|
300
303
|
}
|
|
301
304
|
const binResponse = await apiRequest(endpoint.method, apiPath, {
|
|
302
305
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
@@ -304,24 +307,55 @@ export default class Employees extends Command {
|
|
|
304
307
|
contextName: flags.context,
|
|
305
308
|
binary: true,
|
|
306
309
|
});
|
|
307
|
-
const fsModule = await import('node:fs');
|
|
308
310
|
const binData = binResponse.data;
|
|
309
311
|
const buffer = Buffer.isBuffer(binData)
|
|
310
312
|
? binData
|
|
311
313
|
: Buffer.from(binData);
|
|
314
|
+
if (toStdout) {
|
|
315
|
+
// Nothing else may reach stdout here — it is the file.
|
|
316
|
+
process.stdout.write(buffer);
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
const fsModule = await import('node:fs');
|
|
312
320
|
fsModule.writeFileSync(flags.file, buffer);
|
|
313
321
|
this.log(`Saved ${buffer.length} bytes to ${flags.file}`);
|
|
314
322
|
return;
|
|
315
323
|
}
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
324
|
+
let response;
|
|
325
|
+
if (endpoint.hasBinaryVariant) {
|
|
326
|
+
// This route answers JSON or a file depending on the request body, so the
|
|
327
|
+
// bytes are read first and the response's own content-type decides. Asking
|
|
328
|
+
// for text here would corrupt an xlsx/pdf before anything could inspect it.
|
|
329
|
+
const raw = await apiRequest(endpoint.method, apiPath, {
|
|
330
|
+
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
331
|
+
data,
|
|
332
|
+
contextName: flags.context,
|
|
333
|
+
binary: true,
|
|
334
|
+
});
|
|
335
|
+
const rawType = String(raw.headers['content-type'] || '').toLowerCase();
|
|
336
|
+
const rawData = raw.data;
|
|
337
|
+
const buffer = Buffer.isBuffer(rawData) ? rawData : Buffer.from(rawData);
|
|
338
|
+
if (!isTextualMediaType(rawType)) {
|
|
339
|
+
if (process.stdout.isTTY) {
|
|
340
|
+
this.error(`The API returned ${rawType || 'binary data'}. Redirect stdout to a file, e.g. ... > report.xlsx`);
|
|
341
|
+
}
|
|
342
|
+
// Nothing else may reach stdout here — it is the file.
|
|
343
|
+
process.stdout.write(buffer);
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
response = { data: buffer.toString('utf8'), headers: raw.headers, status: raw.status };
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
response = await apiRequest(endpoint.method, apiPath, {
|
|
350
|
+
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
351
|
+
data,
|
|
352
|
+
contextName: flags.context,
|
|
353
|
+
acceptAny: true,
|
|
354
|
+
});
|
|
355
|
+
}
|
|
322
356
|
// Check content-type for non-JSON responses (XML, text, etc.)
|
|
323
357
|
const contentType = (response.headers['content-type'] || '').toLowerCase();
|
|
324
|
-
if (contentType
|
|
358
|
+
if (isTextualMediaType(contentType) && !contentType.startsWith('application/json')) {
|
|
325
359
|
// Output raw content for XML/text responses (CSV, plain, HTML, etc.)
|
|
326
360
|
this.log(String(response.data));
|
|
327
361
|
return;
|
|
@@ -348,11 +382,6 @@ export default class Employees extends Command {
|
|
|
348
382
|
output = obj[arrayKeys[0]];
|
|
349
383
|
}
|
|
350
384
|
}
|
|
351
|
-
// Filter fields if --fields flag is provided
|
|
352
|
-
if (flags.fields) {
|
|
353
|
-
const fieldList = flags.fields.split(',').map(f => f.trim());
|
|
354
|
-
output = this.filterFields(output, fieldList);
|
|
355
|
-
}
|
|
356
385
|
// Sanitize output if requested
|
|
357
386
|
if (flags.sanitize) {
|
|
358
387
|
output = this.sanitizeOutput(output);
|
|
@@ -607,6 +636,8 @@ export default class Employees extends Command {
|
|
|
607
636
|
baseParams.sortBy = normalizeSortBy(flags['sort-by']);
|
|
608
637
|
if (flags.include)
|
|
609
638
|
baseParams.include = flags.include;
|
|
639
|
+
if (flags.fields)
|
|
640
|
+
baseParams.fields = flags.fields;
|
|
610
641
|
if (flags.query) {
|
|
611
642
|
try {
|
|
612
643
|
Object.assign(baseParams, JSON.parse(flags.query));
|
|
@@ -661,12 +692,7 @@ export default class Employees extends Command {
|
|
|
661
692
|
}
|
|
662
693
|
}
|
|
663
694
|
process.stderr.write(' done\n');
|
|
664
|
-
// Filter fields if specified
|
|
665
695
|
let output = allItems;
|
|
666
|
-
if (flags.fields) {
|
|
667
|
-
const fieldList = flags.fields.split(',').map(f => f.trim());
|
|
668
|
-
output = this.filterFields(allItems, fieldList);
|
|
669
|
-
}
|
|
670
696
|
// Sanitize output if requested
|
|
671
697
|
if (flags.sanitize) {
|
|
672
698
|
output = this.sanitizeOutput(output);
|
|
@@ -995,56 +1021,6 @@ export default class Employees extends Command {
|
|
|
995
1021
|
return `\$${op}`;
|
|
996
1022
|
return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
|
|
997
1023
|
}
|
|
998
|
-
filterFields(data, fields) {
|
|
999
|
-
const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
|
|
1000
|
-
const pick = (obj, keys) => {
|
|
1001
|
-
// Group keys by their first segment so sibling sub-paths
|
|
1002
|
-
// (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
|
|
1003
|
-
// and don't clobber each other on merge.
|
|
1004
|
-
const directKeys = new Set();
|
|
1005
|
-
const nestedKeys = new Map();
|
|
1006
|
-
for (const key of keys) {
|
|
1007
|
-
if (key.split('.').some(isUnsafeKey))
|
|
1008
|
-
continue;
|
|
1009
|
-
if (key.includes('.')) {
|
|
1010
|
-
const [first, ...rest] = key.split('.');
|
|
1011
|
-
if (isUnsafeKey(first))
|
|
1012
|
-
continue;
|
|
1013
|
-
const list = nestedKeys.get(first);
|
|
1014
|
-
if (list) {
|
|
1015
|
-
list.push(rest.join('.'));
|
|
1016
|
-
}
|
|
1017
|
-
else {
|
|
1018
|
-
nestedKeys.set(first, [rest.join('.')]);
|
|
1019
|
-
}
|
|
1020
|
-
}
|
|
1021
|
-
else {
|
|
1022
|
-
directKeys.add(key);
|
|
1023
|
-
}
|
|
1024
|
-
}
|
|
1025
|
-
const result = {};
|
|
1026
|
-
for (const key of directKeys) {
|
|
1027
|
-
if (key in obj)
|
|
1028
|
-
result[key] = obj[key];
|
|
1029
|
-
}
|
|
1030
|
-
for (const [first, restKeys] of nestedKeys) {
|
|
1031
|
-
const val = obj[first];
|
|
1032
|
-
if (!val || typeof val !== 'object')
|
|
1033
|
-
continue;
|
|
1034
|
-
if (Array.isArray(val)) {
|
|
1035
|
-
result[first] = val.map(item => pick(item, restKeys));
|
|
1036
|
-
}
|
|
1037
|
-
else {
|
|
1038
|
-
result[first] = pick(val, restKeys);
|
|
1039
|
-
}
|
|
1040
|
-
}
|
|
1041
|
-
return result;
|
|
1042
|
-
};
|
|
1043
|
-
if (Array.isArray(data)) {
|
|
1044
|
-
return data.map(item => pick(item, fields));
|
|
1045
|
-
}
|
|
1046
|
-
return pick(data, fields);
|
|
1047
|
-
}
|
|
1048
1024
|
sanitizeOutput(data) {
|
|
1049
1025
|
const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
|
|
1050
1026
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command, Args, Flags } from '@oclif/core';
|
|
2
|
-
import { apiRequest, ApiError, normalizeSortBy } from '../lib/api-client.js';
|
|
2
|
+
import { apiRequest, ApiError, isTextualMediaType, normalizeSortBy } from '../lib/api-client.js';
|
|
3
3
|
import { getCurrentContext, getContext, getBaseUrls, hasCredentials, missingCredentialsMessage } from '../lib/config.js';
|
|
4
4
|
// Add 'import' action if both create and update exist
|
|
5
5
|
const HAS_IMPORT = false;
|
|
@@ -8,9 +8,9 @@ const HAS_CAMT053 = false;
|
|
|
8
8
|
const NESTED_IMPORTS = [];
|
|
9
9
|
const SUBCOMMANDS = [...["list-entities", "get-entities", "update-entities"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
|
|
10
10
|
const ENDPOINTS = {
|
|
11
|
-
'list-entities': { method: 'GET', path: '/v1/config/entities', summary: 'List all entity-type configs', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
12
|
-
'get-entities': { method: 'GET', path: '/v1/config/entities/{entityType}', summary: 'Get one entity-type config', pathParams: ['entityType'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
13
|
-
'update-entities': { method: 'PATCH', path: '/v1/config/entities/{entityType}', summary: 'Adjust one entity-type config (admin only)', pathParams: ['entityType'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
11
|
+
'list-entities': { method: 'GET', path: '/v1/config/entities', summary: 'List all entity-type configs', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
12
|
+
'get-entities': { method: 'GET', path: '/v1/config/entities/{entityType}', summary: 'Get one entity-type config', pathParams: ['entityType'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
13
|
+
'update-entities': { method: 'PATCH', path: '/v1/config/entities/{entityType}', summary: 'Adjust one entity-type config (admin only)', pathParams: ['entityType'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
14
14
|
};
|
|
15
15
|
export default class EntityConfigurations extends Command {
|
|
16
16
|
static description = 'Manage entity configurations';
|
|
@@ -34,7 +34,7 @@ export default class EntityConfigurations extends Command {
|
|
|
34
34
|
'sort-by': Flags.string({ description: 'Sort field(s), comma-separated. Direction (:ASC/:DESC) is optional; date-like fields default to DESC, others to ASC.' }),
|
|
35
35
|
filter: Flags.string({ description: 'Filter (e.g. --filter status=ACTIVE). Operators: in:A,B, like:%, gt:, lt:, null:', multiple: true }),
|
|
36
36
|
query: Flags.string({ description: 'Additional query params as JSON' }),
|
|
37
|
-
fields: Flags.string({ description: 'Comma-separated list of fields to
|
|
37
|
+
fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
|
|
38
38
|
sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
|
|
39
39
|
'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
|
|
40
40
|
};
|
|
@@ -106,6 +106,8 @@ export default class EntityConfigurations extends Command {
|
|
|
106
106
|
queryParams.search = flags.search;
|
|
107
107
|
if (flags['sort-by'])
|
|
108
108
|
queryParams.sortBy = normalizeSortBy(flags['sort-by']);
|
|
109
|
+
if (flags.fields)
|
|
110
|
+
queryParams.fields = flags.fields;
|
|
109
111
|
if (flags.query) {
|
|
110
112
|
try {
|
|
111
113
|
Object.assign(queryParams, JSON.parse(flags.query));
|
|
@@ -253,10 +255,11 @@ export default class EntityConfigurations extends Command {
|
|
|
253
255
|
process.stderr.write('\n');
|
|
254
256
|
}
|
|
255
257
|
try {
|
|
256
|
-
// Binary response:
|
|
258
|
+
// Binary response: to -f / --file, or to stdout when it is redirected.
|
|
257
259
|
if (endpoint.isBinaryResponse) {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
+
const toStdout = !flags.file;
|
|
261
|
+
if (toStdout && process.stdout.isTTY) {
|
|
262
|
+
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.`);
|
|
260
263
|
}
|
|
261
264
|
const binResponse = await apiRequest(endpoint.method, apiPath, {
|
|
262
265
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
@@ -264,24 +267,55 @@ export default class EntityConfigurations extends Command {
|
|
|
264
267
|
contextName: flags.context,
|
|
265
268
|
binary: true,
|
|
266
269
|
});
|
|
267
|
-
const fsModule = await import('node:fs');
|
|
268
270
|
const binData = binResponse.data;
|
|
269
271
|
const buffer = Buffer.isBuffer(binData)
|
|
270
272
|
? binData
|
|
271
273
|
: Buffer.from(binData);
|
|
274
|
+
if (toStdout) {
|
|
275
|
+
// Nothing else may reach stdout here — it is the file.
|
|
276
|
+
process.stdout.write(buffer);
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
const fsModule = await import('node:fs');
|
|
272
280
|
fsModule.writeFileSync(flags.file, buffer);
|
|
273
281
|
this.log(`Saved ${buffer.length} bytes to ${flags.file}`);
|
|
274
282
|
return;
|
|
275
283
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
284
|
+
let response;
|
|
285
|
+
if (endpoint.hasBinaryVariant) {
|
|
286
|
+
// This route answers JSON or a file depending on the request body, so the
|
|
287
|
+
// bytes are read first and the response's own content-type decides. Asking
|
|
288
|
+
// for text here would corrupt an xlsx/pdf before anything could inspect it.
|
|
289
|
+
const raw = await apiRequest(endpoint.method, apiPath, {
|
|
290
|
+
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
291
|
+
data,
|
|
292
|
+
contextName: flags.context,
|
|
293
|
+
binary: true,
|
|
294
|
+
});
|
|
295
|
+
const rawType = String(raw.headers['content-type'] || '').toLowerCase();
|
|
296
|
+
const rawData = raw.data;
|
|
297
|
+
const buffer = Buffer.isBuffer(rawData) ? rawData : Buffer.from(rawData);
|
|
298
|
+
if (!isTextualMediaType(rawType)) {
|
|
299
|
+
if (process.stdout.isTTY) {
|
|
300
|
+
this.error(`The API returned ${rawType || 'binary data'}. Redirect stdout to a file, e.g. ... > report.xlsx`);
|
|
301
|
+
}
|
|
302
|
+
// Nothing else may reach stdout here — it is the file.
|
|
303
|
+
process.stdout.write(buffer);
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
response = { data: buffer.toString('utf8'), headers: raw.headers, status: raw.status };
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
response = await apiRequest(endpoint.method, apiPath, {
|
|
310
|
+
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
311
|
+
data,
|
|
312
|
+
contextName: flags.context,
|
|
313
|
+
acceptAny: true,
|
|
314
|
+
});
|
|
315
|
+
}
|
|
282
316
|
// Check content-type for non-JSON responses (XML, text, etc.)
|
|
283
317
|
const contentType = (response.headers['content-type'] || '').toLowerCase();
|
|
284
|
-
if (contentType
|
|
318
|
+
if (isTextualMediaType(contentType) && !contentType.startsWith('application/json')) {
|
|
285
319
|
// Output raw content for XML/text responses (CSV, plain, HTML, etc.)
|
|
286
320
|
this.log(String(response.data));
|
|
287
321
|
return;
|
|
@@ -308,11 +342,6 @@ export default class EntityConfigurations extends Command {
|
|
|
308
342
|
output = obj[arrayKeys[0]];
|
|
309
343
|
}
|
|
310
344
|
}
|
|
311
|
-
// Filter fields if --fields flag is provided
|
|
312
|
-
if (flags.fields) {
|
|
313
|
-
const fieldList = flags.fields.split(',').map(f => f.trim());
|
|
314
|
-
output = this.filterFields(output, fieldList);
|
|
315
|
-
}
|
|
316
345
|
// Sanitize output if requested
|
|
317
346
|
if (flags.sanitize) {
|
|
318
347
|
output = this.sanitizeOutput(output);
|
|
@@ -567,6 +596,8 @@ export default class EntityConfigurations extends Command {
|
|
|
567
596
|
baseParams.sortBy = normalizeSortBy(flags['sort-by']);
|
|
568
597
|
if (flags.include)
|
|
569
598
|
baseParams.include = flags.include;
|
|
599
|
+
if (flags.fields)
|
|
600
|
+
baseParams.fields = flags.fields;
|
|
570
601
|
if (flags.query) {
|
|
571
602
|
try {
|
|
572
603
|
Object.assign(baseParams, JSON.parse(flags.query));
|
|
@@ -621,12 +652,7 @@ export default class EntityConfigurations extends Command {
|
|
|
621
652
|
}
|
|
622
653
|
}
|
|
623
654
|
process.stderr.write(' done\n');
|
|
624
|
-
// Filter fields if specified
|
|
625
655
|
let output = allItems;
|
|
626
|
-
if (flags.fields) {
|
|
627
|
-
const fieldList = flags.fields.split(',').map(f => f.trim());
|
|
628
|
-
output = this.filterFields(allItems, fieldList);
|
|
629
|
-
}
|
|
630
656
|
// Sanitize output if requested
|
|
631
657
|
if (flags.sanitize) {
|
|
632
658
|
output = this.sanitizeOutput(output);
|
|
@@ -955,56 +981,6 @@ export default class EntityConfigurations extends Command {
|
|
|
955
981
|
return `\$${op}`;
|
|
956
982
|
return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
|
|
957
983
|
}
|
|
958
|
-
filterFields(data, fields) {
|
|
959
|
-
const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
|
|
960
|
-
const pick = (obj, keys) => {
|
|
961
|
-
// Group keys by their first segment so sibling sub-paths
|
|
962
|
-
// (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
|
|
963
|
-
// and don't clobber each other on merge.
|
|
964
|
-
const directKeys = new Set();
|
|
965
|
-
const nestedKeys = new Map();
|
|
966
|
-
for (const key of keys) {
|
|
967
|
-
if (key.split('.').some(isUnsafeKey))
|
|
968
|
-
continue;
|
|
969
|
-
if (key.includes('.')) {
|
|
970
|
-
const [first, ...rest] = key.split('.');
|
|
971
|
-
if (isUnsafeKey(first))
|
|
972
|
-
continue;
|
|
973
|
-
const list = nestedKeys.get(first);
|
|
974
|
-
if (list) {
|
|
975
|
-
list.push(rest.join('.'));
|
|
976
|
-
}
|
|
977
|
-
else {
|
|
978
|
-
nestedKeys.set(first, [rest.join('.')]);
|
|
979
|
-
}
|
|
980
|
-
}
|
|
981
|
-
else {
|
|
982
|
-
directKeys.add(key);
|
|
983
|
-
}
|
|
984
|
-
}
|
|
985
|
-
const result = {};
|
|
986
|
-
for (const key of directKeys) {
|
|
987
|
-
if (key in obj)
|
|
988
|
-
result[key] = obj[key];
|
|
989
|
-
}
|
|
990
|
-
for (const [first, restKeys] of nestedKeys) {
|
|
991
|
-
const val = obj[first];
|
|
992
|
-
if (!val || typeof val !== 'object')
|
|
993
|
-
continue;
|
|
994
|
-
if (Array.isArray(val)) {
|
|
995
|
-
result[first] = val.map(item => pick(item, restKeys));
|
|
996
|
-
}
|
|
997
|
-
else {
|
|
998
|
-
result[first] = pick(val, restKeys);
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
1001
|
-
return result;
|
|
1002
|
-
};
|
|
1003
|
-
if (Array.isArray(data)) {
|
|
1004
|
-
return data.map(item => pick(item, fields));
|
|
1005
|
-
}
|
|
1006
|
-
return pick(data, fields);
|
|
1007
|
-
}
|
|
1008
984
|
sanitizeOutput(data) {
|
|
1009
985
|
const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
|
|
1010
986
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|