@aderis-erp/cli 1.1.4 → 1.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/access-groups.js +42 -8
- package/dist/commands/account-transaction-allocations.js +38 -4
- package/dist/commands/account-transactions.d.ts +2 -0
- package/dist/commands/account-transactions.js +68 -32
- package/dist/commands/accounts.js +46 -12
- package/dist/commands/agent-platform.js +37 -3
- package/dist/commands/agent-runs.js +49 -15
- package/dist/commands/analytics.js +39 -5
- package/dist/commands/api-clients.js +47 -13
- package/dist/commands/api-keys.js +40 -6
- package/dist/commands/async-jobs.js +45 -11
- package/dist/commands/audit-log.js +38 -4
- package/dist/commands/bank-accounts.js +54 -20
- package/dist/commands/bank-integrations.js +56 -22
- package/dist/commands/business-partners.js +77 -43
- package/dist/commands/chat.js +43 -9
- package/dist/commands/credit-card-accounts.js +57 -23
- package/dist/commands/currencies.js +37 -3
- package/dist/commands/custody-accounts.js +41 -7
- package/dist/commands/dashboards.js +52 -18
- package/dist/commands/delivery-notes.js +69 -35
- package/dist/commands/document-checksums.d.ts +1 -0
- package/dist/commands/document-checksums.js +38 -3
- package/dist/commands/dunning-configs.js +41 -7
- package/dist/commands/dunning-reminders.js +44 -10
- package/dist/commands/dunning-runs.js +37 -3
- package/dist/commands/email-transactions.js +39 -5
- package/dist/commands/employees.js +77 -43
- package/dist/commands/entity-configurations.js +39 -5
- package/dist/commands/event-mapping-rules.js +37 -3
- package/dist/commands/event-processor-mappings.js +41 -7
- package/dist/commands/files.js +48 -14
- package/dist/commands/financial-instrument-transactions.d.ts +56 -0
- package/dist/commands/financial-instrument-transactions.js +1369 -0
- package/dist/commands/financial-instrument-types.js +41 -7
- package/dist/commands/financial-instruments.js +41 -7
- package/dist/commands/health.js +41 -7
- package/dist/commands/imports.js +39 -5
- package/dist/commands/inbox.js +42 -8
- package/dist/commands/integrations.js +39 -5
- package/dist/commands/item-categories.js +41 -7
- package/dist/commands/items.js +42 -8
- package/dist/commands/journal-lines.js +37 -3
- package/dist/commands/journals.js +61 -27
- package/dist/commands/kpis.js +44 -10
- package/dist/commands/ledgers.js +83 -49
- package/dist/commands/my-account.js +54 -20
- package/dist/commands/notifications.js +37 -3
- package/dist/commands/office-locations.js +41 -7
- package/dist/commands/payment-methods.js +46 -12
- package/dist/commands/payment-runs.js +54 -20
- package/dist/commands/payment-terms.js +42 -8
- package/dist/commands/payments.js +43 -9
- package/dist/commands/payroll-runs.js +71 -37
- package/dist/commands/permissions.js +37 -3
- package/dist/commands/price-lists.js +48 -14
- package/dist/commands/processor-mappings.js +40 -6
- package/dist/commands/processors.js +46 -12
- package/dist/commands/purchase-order-types.js +41 -7
- package/dist/commands/purchase-orders.js +60 -26
- package/dist/commands/reports.js +43 -8
- package/dist/commands/roles.js +40 -6
- package/dist/commands/salary-types.js +41 -7
- package/dist/commands/sales-invoices.js +85 -51
- package/dist/commands/sales-orders.js +69 -35
- package/dist/commands/saved-views.js +39 -5
- package/dist/commands/scheduled-processor-mappings.js +41 -7
- package/dist/commands/schedules.js +45 -11
- package/dist/commands/settings.js +38 -4
- package/dist/commands/tags.js +42 -8
- package/dist/commands/tax-codes.js +44 -10
- package/dist/commands/tax-matrix.js +41 -7
- package/dist/commands/templates.js +49 -15
- package/dist/commands/tenants.js +44 -10
- package/dist/commands/tools.js +38 -4
- package/dist/commands/users.js +58 -24
- package/dist/commands/vat-declarations.js +61 -27
- package/dist/commands/vendor-invoices.js +81 -47
- package/dist/commands/webhooks.js +43 -9
- package/dist/commands/work-summary.js +37 -3
- package/dist/lib/paths.d.ts +8 -0
- package/dist/lib/paths.js +16 -0
- package/dist/lib/production-guard.js +2 -0
- package/oclif.manifest.json +199 -2
- package/package.json +9 -3
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Command, Args, Flags } from '@oclif/core';
|
|
2
2
|
import { apiRequest, ApiError, isTextualMediaType, normalizeSortBy } from '../lib/api-client.js';
|
|
3
3
|
import { getCurrentContext, getContext, getBaseUrls, hasCredentials, missingCredentialsMessage } from '../lib/config.js';
|
|
4
|
+
import { resolveUserPath } from '../lib/paths.js';
|
|
4
5
|
// 'upsert' loops create/update from the CLI, one request per record. It exists
|
|
5
6
|
// only where the API has no import endpoint of its own; those keep the name
|
|
6
7
|
// 'import' and are listed in SERVER_IMPORTS.
|
|
@@ -11,39 +12,39 @@ const SERVER_IMPORTS = [];
|
|
|
11
12
|
const NESTED_UPSERTS = ["line-items", "notes", "files"];
|
|
12
13
|
const SUBCOMMANDS = [...["list", "create", "create-invoices", "get", "update", "delete", "get-pdf", "create-pdf", "submit", "approve", "accept", "terminate", "cancel", "replace", "copy", "merge", "create-invoice", "send", "create-line-items", "update-line-items-reorder", "update-line-items", "delete-line-items", "list-notes", "create-notes", "get-notes", "update-notes", "delete-notes", "list-files", "create-files", "get-files", "update-files", "delete-files", "files-download"], ...(HAS_UPSERT ? ['upsert'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_UPSERTS.map(n => `upsert-${n}`)];
|
|
13
14
|
const ENDPOINTS = {
|
|
14
|
-
'list': { method: 'GET', path: '/v1/sales-orders', summary: 'Get all sales orders with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
-
'create': { method: 'POST', path: '/v1/sales-orders', summary: 'Create a new sales order', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['files', 'notes', 'tagIds', 'tagCodes', 'discounts', 'lineItems'] },
|
|
16
|
-
'create-invoices': { method: 'POST', path: '/v1/sales-orders/create-invoices', summary: 'Create ONE draft invoice from several sales orders (same customer)', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['salesOrderIds'] },
|
|
17
|
-
'get': { method: 'GET', path: '/v1/sales-orders/{id}', summary: 'Get a sales order by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
18
|
-
'update': { method: 'PATCH', path: '/v1/sales-orders/{id}', summary: 'Update a sales order', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['files', 'notes', 'tagIds', 'tagCodes', 'discounts', 'lineItems', 'removeFileIds'] },
|
|
19
|
-
'delete': { method: 'DELETE', path: '/v1/sales-orders/{id}', summary: 'Delete a sales order', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
20
|
-
'get-pdf': { method: 'GET', path: '/v1/sales-orders/{id}/pdf', summary: 'Render a sales order PDF as the mapped event would, or preview a stored template', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
21
|
-
'create-pdf': { method: 'POST', path: '/v1/sales-orders/{id}/pdf', summary: 'Render a sales order PDF from unsaved HTML posted in the body', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
22
|
-
'submit': { method: 'POST', path: '/v1/sales-orders/{id}/submit', summary: 'Submit order for approval (DRAFT or CANCELLED → PENDING_APPROVAL)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
23
|
-
'approve': { method: 'POST', path: '/v1/sales-orders/{id}/approve', summary: 'Approve order (DRAFT, PENDING_APPROVAL or CANCELLED → APPROVED). Generates order number if missing.', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
24
|
-
'accept': { method: 'POST', path: '/v1/sales-orders/{id}/accept', summary: 'Accept order (APPROVED → ACCEPTED). Adjusts billing start date.', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
25
|
-
'terminate': { method: 'POST', path: '/v1/sales-orders/{id}/terminate', summary: 'Terminate order (ACCEPTED → TERMINATED). Requires billingEndDate.', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
26
|
-
'cancel': { method: 'POST', path: '/v1/sales-orders/{id}/cancel', summary: 'Cancel order (pre-active or REPLACED → CANCELLED)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
27
|
-
'replace': { method: 'POST', path: '/v1/sales-orders/{id}/replace', summary: 'Replace order with corrected version (APPROVED → creates new DRAFT)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
28
|
-
'copy': { method: 'POST', path: '/v1/sales-orders/{id}/copy', summary: 'Create a copy of a sales order as a new draft', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
29
|
-
'merge': { method: 'POST', path: '/v1/sales-orders/{id}/merge', summary: 'Merge draft orders into this master', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['sourceIds'] },
|
|
30
|
-
'create-invoice': { method: 'POST', path: '/v1/sales-orders/{id}/create-invoice', summary: 'Create invoice from sales order (ACCEPTED → creates DRAFT invoice)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
31
|
-
'send': { method: 'POST', path: '/v1/sales-orders/{id}/send', summary: 'Send a sales order to the customer by email', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['emailCc', 'emailBcc', 'files'] },
|
|
32
|
-
'create-line-items': { method: 'POST', path: '/v1/sales-orders/{orderId}/line-items', summary: 'Add a line item to a sales order', pathParams: ['orderId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['discounts', 'tagIds', 'tagCodes'] },
|
|
33
|
-
'update-line-items-reorder': { method: 'PATCH', path: '/v1/sales-orders/{orderId}/line-items/reorder', summary: 'Bulk reorder line items (drag-and-drop)', pathParams: ['orderId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['items'] },
|
|
34
|
-
'update-line-items': { method: 'PATCH', path: '/v1/sales-orders/{orderId}/line-items/{lineItemId}', summary: 'Update a line item on a sales order', pathParams: ['orderId', 'lineItemId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['discounts', 'tagIds', 'tagCodes'] },
|
|
35
|
-
'delete-line-items': { method: 'DELETE', path: '/v1/sales-orders/{orderId}/line-items/{lineItemId}', summary: 'Remove a line item from a sales order', pathParams: ['orderId', 'lineItemId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
36
|
-
'list-notes': { method: 'GET', path: '/v1/sales-orders/{salesOrderId}/notes', summary: 'Get all notes for a sales order', pathParams: ['salesOrderId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
37
|
-
'create-notes': { method: 'POST', path: '/v1/sales-orders/{salesOrderId}/notes', summary: 'Create a note for a sales order', pathParams: ['salesOrderId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
38
|
-
'get-notes': { method: 'GET', path: '/v1/sales-orders/{salesOrderId}/notes/{noteId}', summary: 'Get a specific note for a sales order', pathParams: ['salesOrderId', 'noteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
39
|
-
'update-notes': { method: 'PATCH', path: '/v1/sales-orders/{salesOrderId}/notes/{noteId}', summary: 'Update a note for a sales order', pathParams: ['salesOrderId', 'noteId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
40
|
-
'delete-notes': { method: 'DELETE', path: '/v1/sales-orders/{salesOrderId}/notes/{noteId}', summary: 'Delete a note from a sales order', pathParams: ['salesOrderId', 'noteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
41
|
-
'list-files': { method: 'GET', path: '/v1/sales-orders/{parentId}/files', summary: 'List file attachments for a sales order', pathParams: ['parentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
42
|
-
'create-files': { method: 'POST', path: '/v1/sales-orders/{parentId}/files', summary: 'Upload a file attachment to a sales order', pathParams: ['parentId'], hasBody: false, isMultipart: true, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
43
|
-
'get-files': { method: 'GET', path: '/v1/sales-orders/{parentId}/files/{fileId}', summary: 'Get a file attachment for a sales order', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
44
|
-
'update-files': { method: 'PATCH', path: '/v1/sales-orders/{parentId}/files/{fileId}', summary: 'Update file attachment metadata for a sales order', pathParams: ['parentId', 'fileId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
45
|
-
'delete-files': { method: 'DELETE', path: '/v1/sales-orders/{parentId}/files/{fileId}', summary: 'Delete a file attachment from a sales order', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
46
|
-
'files-download': { method: 'GET', path: '/v1/sales-orders/{parentId}/files/{fileId}/download', summary: 'Download a file attachment from a sales order', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: true, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
+
'list': { method: 'GET', path: '/v1/sales-orders', summary: 'Get all sales orders with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
16
|
+
'create': { method: 'POST', path: '/v1/sales-orders', summary: 'Create a new sales order', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['files', 'notes', 'tagIds', 'tagCodes', 'discounts', 'lineItems'], bodyMediaType: '', specParams: [] },
|
|
17
|
+
'create-invoices': { method: 'POST', path: '/v1/sales-orders/create-invoices', summary: 'Create ONE draft invoice from several sales orders (same customer)', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['salesOrderIds'], bodyMediaType: '', specParams: [] },
|
|
18
|
+
'get': { method: 'GET', path: '/v1/sales-orders/{id}', summary: 'Get a sales order by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
19
|
+
'update': { method: 'PATCH', path: '/v1/sales-orders/{id}', summary: 'Update a sales order', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['files', 'notes', 'tagIds', 'tagCodes', 'discounts', 'lineItems', 'removeFileIds'], bodyMediaType: '', specParams: [] },
|
|
20
|
+
'delete': { method: 'DELETE', path: '/v1/sales-orders/{id}', summary: 'Delete a sales order', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
21
|
+
'get-pdf': { method: 'GET', path: '/v1/sales-orders/{id}/pdf', summary: 'Render a sales order PDF as the mapped event would, or preview a stored template', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
22
|
+
'create-pdf': { method: 'POST', path: '/v1/sales-orders/{id}/pdf', summary: 'Render a sales order PDF from unsaved HTML posted in the body', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
23
|
+
'submit': { method: 'POST', path: '/v1/sales-orders/{id}/submit', summary: 'Submit order for approval (DRAFT or CANCELLED → PENDING_APPROVAL)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
24
|
+
'approve': { method: 'POST', path: '/v1/sales-orders/{id}/approve', summary: 'Approve order (DRAFT, PENDING_APPROVAL or CANCELLED → APPROVED). Generates order number if missing.', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
25
|
+
'accept': { method: 'POST', path: '/v1/sales-orders/{id}/accept', summary: 'Accept order (APPROVED → ACCEPTED). Adjusts billing start date.', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
26
|
+
'terminate': { method: 'POST', path: '/v1/sales-orders/{id}/terminate', summary: 'Terminate order (ACCEPTED → TERMINATED). Requires billingEndDate.', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
27
|
+
'cancel': { method: 'POST', path: '/v1/sales-orders/{id}/cancel', summary: 'Cancel order (pre-active or REPLACED → CANCELLED)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
28
|
+
'replace': { method: 'POST', path: '/v1/sales-orders/{id}/replace', summary: 'Replace order with corrected version (APPROVED → creates new DRAFT)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
29
|
+
'copy': { method: 'POST', path: '/v1/sales-orders/{id}/copy', summary: 'Create a copy of a sales order as a new draft', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
30
|
+
'merge': { method: 'POST', path: '/v1/sales-orders/{id}/merge', summary: 'Merge draft orders into this master', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['sourceIds'], bodyMediaType: '', specParams: [] },
|
|
31
|
+
'create-invoice': { method: 'POST', path: '/v1/sales-orders/{id}/create-invoice', summary: 'Create invoice from sales order (ACCEPTED → creates DRAFT invoice)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
32
|
+
'send': { method: 'POST', path: '/v1/sales-orders/{id}/send', summary: 'Send a sales order to the customer by email', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['emailCc', 'emailBcc', 'files'], bodyMediaType: '', specParams: [] },
|
|
33
|
+
'create-line-items': { method: 'POST', path: '/v1/sales-orders/{orderId}/line-items', summary: 'Add a line item to a sales order', pathParams: ['orderId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['discounts', 'tagIds', 'tagCodes'], bodyMediaType: '', specParams: [] },
|
|
34
|
+
'update-line-items-reorder': { method: 'PATCH', path: '/v1/sales-orders/{orderId}/line-items/reorder', summary: 'Bulk reorder line items (drag-and-drop)', pathParams: ['orderId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['items'], bodyMediaType: '', specParams: [] },
|
|
35
|
+
'update-line-items': { method: 'PATCH', path: '/v1/sales-orders/{orderId}/line-items/{lineItemId}', summary: 'Update a line item on a sales order', pathParams: ['orderId', 'lineItemId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['discounts', 'tagIds', 'tagCodes'], bodyMediaType: '', specParams: [] },
|
|
36
|
+
'delete-line-items': { method: 'DELETE', path: '/v1/sales-orders/{orderId}/line-items/{lineItemId}', summary: 'Remove a line item from a sales order', pathParams: ['orderId', 'lineItemId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
37
|
+
'list-notes': { method: 'GET', path: '/v1/sales-orders/{salesOrderId}/notes', summary: 'Get all notes for a sales order', pathParams: ['salesOrderId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
38
|
+
'create-notes': { method: 'POST', path: '/v1/sales-orders/{salesOrderId}/notes', summary: 'Create a note for a sales order', pathParams: ['salesOrderId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
39
|
+
'get-notes': { method: 'GET', path: '/v1/sales-orders/{salesOrderId}/notes/{noteId}', summary: 'Get a specific note for a sales order', pathParams: ['salesOrderId', 'noteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
40
|
+
'update-notes': { method: 'PATCH', path: '/v1/sales-orders/{salesOrderId}/notes/{noteId}', summary: 'Update a note for a sales order', pathParams: ['salesOrderId', 'noteId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
41
|
+
'delete-notes': { method: 'DELETE', path: '/v1/sales-orders/{salesOrderId}/notes/{noteId}', summary: 'Delete a note from a sales order', pathParams: ['salesOrderId', 'noteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
42
|
+
'list-files': { method: 'GET', path: '/v1/sales-orders/{parentId}/files', summary: 'List file attachments for a sales order', pathParams: ['parentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
43
|
+
'create-files': { method: 'POST', path: '/v1/sales-orders/{parentId}/files', summary: 'Upload a file attachment to a sales order', pathParams: ['parentId'], hasBody: false, isMultipart: true, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
44
|
+
'get-files': { method: 'GET', path: '/v1/sales-orders/{parentId}/files/{fileId}', summary: 'Get a file attachment for a sales order', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
45
|
+
'update-files': { method: 'PATCH', path: '/v1/sales-orders/{parentId}/files/{fileId}', summary: 'Update file attachment metadata for a sales order', pathParams: ['parentId', 'fileId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
46
|
+
'delete-files': { method: 'DELETE', path: '/v1/sales-orders/{parentId}/files/{fileId}', summary: 'Delete a file attachment from a sales order', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
47
|
+
'files-download': { method: 'GET', path: '/v1/sales-orders/{parentId}/files/{fileId}/download', summary: 'Download a file attachment from a sales order', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: true, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
47
48
|
};
|
|
48
49
|
export default class SalesOrders extends Command {
|
|
49
50
|
static description = 'Manage sales orders';
|
|
@@ -65,7 +66,7 @@ export default class SalesOrders extends Command {
|
|
|
65
66
|
table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
|
|
66
67
|
csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
|
|
67
68
|
data: Flags.string({ char: 'd', description: 'JSON data for request body' }),
|
|
68
|
-
file: Flags.string({ char: 'f', description: 'Read request body from file' }),
|
|
69
|
+
file: Flags.string({ char: 'f', description: 'Read request body from file', parse: async (input) => resolveUserPath(input) }),
|
|
69
70
|
set: Flags.string({ char: 's', description: 'Set individual field (e.g. --set firstName=John)', multiple: true }),
|
|
70
71
|
'line-item-id': Flags.string({ description: 'Path param: lineItemId' }),
|
|
71
72
|
'note-id': Flags.string({ description: 'Path param: noteId' }),
|
|
@@ -199,6 +200,17 @@ export default class SalesOrders extends Command {
|
|
|
199
200
|
}
|
|
200
201
|
}
|
|
201
202
|
}
|
|
203
|
+
// Flags generated from this endpoint's own parameters. Raw-body endpoints
|
|
204
|
+
// have them: their body is not JSON, so --set cannot carry a value there.
|
|
205
|
+
for (const param of endpoint.specParams) {
|
|
206
|
+
const value = flags[param.flag];
|
|
207
|
+
if (value === undefined || value === '') {
|
|
208
|
+
if (param.required)
|
|
209
|
+
this.error(`Missing required flag: --${param.flag}`);
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
queryParams[param.name] = value;
|
|
213
|
+
}
|
|
202
214
|
let data;
|
|
203
215
|
if (endpoint.isMultipart) {
|
|
204
216
|
if (!flags.file)
|
|
@@ -219,6 +231,25 @@ export default class SalesOrders extends Command {
|
|
|
219
231
|
}
|
|
220
232
|
data = form;
|
|
221
233
|
}
|
|
234
|
+
else if (endpoint.bodyMediaType) {
|
|
235
|
+
// The body is not JSON (today only application/x-ndjson): it is sent
|
|
236
|
+
// verbatim, under the content type the endpoint asks for.
|
|
237
|
+
if (flags.file) {
|
|
238
|
+
const fsModule = await import('node:fs');
|
|
239
|
+
try {
|
|
240
|
+
data = fsModule.readFileSync(flags.file, 'utf-8');
|
|
241
|
+
}
|
|
242
|
+
catch {
|
|
243
|
+
this.error(`Failed to read file: ${flags.file}`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
else if (flags.data) {
|
|
247
|
+
data = flags.data;
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
this.error(`This endpoint takes its body as ${endpoint.bodyMediaType}. Pass the file with -f / --file, or the text with --data.`);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
222
253
|
else if (endpoint.hasBody) {
|
|
223
254
|
// For binary-response endpoints, --file is the output path, not a JSON
|
|
224
255
|
// input file — skip the read-as-body branch.
|
|
@@ -314,7 +345,7 @@ export default class SalesOrders extends Command {
|
|
|
314
345
|
process.stderr.write(`Body: [multipart/form-data file upload]\n`);
|
|
315
346
|
}
|
|
316
347
|
else if (data) {
|
|
317
|
-
process.stderr.write(`Body: ${JSON.stringify(data, null, 2)}\n`);
|
|
348
|
+
process.stderr.write(`Body: ${typeof data === 'string' ? data : JSON.stringify(data, null, 2)}\n`);
|
|
318
349
|
}
|
|
319
350
|
process.stderr.write('\n');
|
|
320
351
|
}
|
|
@@ -328,6 +359,7 @@ export default class SalesOrders extends Command {
|
|
|
328
359
|
const binResponse = await apiRequest(endpoint.method, apiPath, {
|
|
329
360
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
330
361
|
data,
|
|
362
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
331
363
|
contextName: flags.context,
|
|
332
364
|
binary: true,
|
|
333
365
|
});
|
|
@@ -353,6 +385,7 @@ export default class SalesOrders extends Command {
|
|
|
353
385
|
const raw = await apiRequest(endpoint.method, apiPath, {
|
|
354
386
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
355
387
|
data,
|
|
388
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
356
389
|
contextName: flags.context,
|
|
357
390
|
binary: true,
|
|
358
391
|
});
|
|
@@ -373,6 +406,7 @@ export default class SalesOrders extends Command {
|
|
|
373
406
|
response = await apiRequest(endpoint.method, apiPath, {
|
|
374
407
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
375
408
|
data,
|
|
409
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
376
410
|
contextName: flags.context,
|
|
377
411
|
acceptAny: true,
|
|
378
412
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Command, Args, Flags } from '@oclif/core';
|
|
2
2
|
import { apiRequest, ApiError, isTextualMediaType, normalizeSortBy } from '../lib/api-client.js';
|
|
3
3
|
import { getCurrentContext, getContext, getBaseUrls, hasCredentials, missingCredentialsMessage } from '../lib/config.js';
|
|
4
|
+
import { resolveUserPath } from '../lib/paths.js';
|
|
4
5
|
// 'upsert' loops create/update from the CLI, one request per record. It exists
|
|
5
6
|
// only where the API has no import endpoint of its own; those keep the name
|
|
6
7
|
// 'import' and are listed in SERVER_IMPORTS.
|
|
@@ -11,9 +12,9 @@ const SERVER_IMPORTS = [];
|
|
|
11
12
|
const NESTED_UPSERTS = [];
|
|
12
13
|
const SUBCOMMANDS = [...["get", "create", "delete"], ...(HAS_UPSERT ? ['upsert'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_UPSERTS.map(n => `upsert-${n}`)];
|
|
13
14
|
const ENDPOINTS = {
|
|
14
|
-
'get': { method: 'GET', path: '/v1/saved-views', summary: 'List saved views for a page', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
-
'create': { method: 'POST', path: '/v1/saved-views', summary: 'Create a saved view', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
16
|
-
'delete': { method: 'DELETE', path: '/v1/saved-views/{id}', summary: 'Delete a saved view', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
+
'get': { method: 'GET', path: '/v1/saved-views', summary: 'List saved views for a page', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
16
|
+
'create': { method: 'POST', path: '/v1/saved-views', summary: 'Create a saved view', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
17
|
+
'delete': { method: 'DELETE', path: '/v1/saved-views/{id}', summary: 'Delete a saved view', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
17
18
|
};
|
|
18
19
|
export default class SavedViews extends Command {
|
|
19
20
|
static description = 'Manage saved views';
|
|
@@ -31,7 +32,7 @@ export default class SavedViews extends Command {
|
|
|
31
32
|
table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
|
|
32
33
|
csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
|
|
33
34
|
data: Flags.string({ char: 'd', description: 'JSON data for request body' }),
|
|
34
|
-
file: Flags.string({ char: 'f', description: 'Read request body from file' }),
|
|
35
|
+
file: Flags.string({ char: 'f', description: 'Read request body from file', parse: async (input) => resolveUserPath(input) }),
|
|
35
36
|
set: Flags.string({ char: 's', description: 'Set individual field (e.g. --set firstName=John)', multiple: true }),
|
|
36
37
|
page: Flags.string({ description: 'Page number' }),
|
|
37
38
|
limit: Flags.string({ description: 'Items per page' }),
|
|
@@ -162,6 +163,17 @@ export default class SavedViews extends Command {
|
|
|
162
163
|
}
|
|
163
164
|
}
|
|
164
165
|
}
|
|
166
|
+
// Flags generated from this endpoint's own parameters. Raw-body endpoints
|
|
167
|
+
// have them: their body is not JSON, so --set cannot carry a value there.
|
|
168
|
+
for (const param of endpoint.specParams) {
|
|
169
|
+
const value = flags[param.flag];
|
|
170
|
+
if (value === undefined || value === '') {
|
|
171
|
+
if (param.required)
|
|
172
|
+
this.error(`Missing required flag: --${param.flag}`);
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
queryParams[param.name] = value;
|
|
176
|
+
}
|
|
165
177
|
let data;
|
|
166
178
|
if (endpoint.isMultipart) {
|
|
167
179
|
if (!flags.file)
|
|
@@ -182,6 +194,25 @@ export default class SavedViews extends Command {
|
|
|
182
194
|
}
|
|
183
195
|
data = form;
|
|
184
196
|
}
|
|
197
|
+
else if (endpoint.bodyMediaType) {
|
|
198
|
+
// The body is not JSON (today only application/x-ndjson): it is sent
|
|
199
|
+
// verbatim, under the content type the endpoint asks for.
|
|
200
|
+
if (flags.file) {
|
|
201
|
+
const fsModule = await import('node:fs');
|
|
202
|
+
try {
|
|
203
|
+
data = fsModule.readFileSync(flags.file, 'utf-8');
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
this.error(`Failed to read file: ${flags.file}`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
else if (flags.data) {
|
|
210
|
+
data = flags.data;
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
this.error(`This endpoint takes its body as ${endpoint.bodyMediaType}. Pass the file with -f / --file, or the text with --data.`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
185
216
|
else if (endpoint.hasBody) {
|
|
186
217
|
// For binary-response endpoints, --file is the output path, not a JSON
|
|
187
218
|
// input file — skip the read-as-body branch.
|
|
@@ -277,7 +308,7 @@ export default class SavedViews extends Command {
|
|
|
277
308
|
process.stderr.write(`Body: [multipart/form-data file upload]\n`);
|
|
278
309
|
}
|
|
279
310
|
else if (data) {
|
|
280
|
-
process.stderr.write(`Body: ${JSON.stringify(data, null, 2)}\n`);
|
|
311
|
+
process.stderr.write(`Body: ${typeof data === 'string' ? data : JSON.stringify(data, null, 2)}\n`);
|
|
281
312
|
}
|
|
282
313
|
process.stderr.write('\n');
|
|
283
314
|
}
|
|
@@ -291,6 +322,7 @@ export default class SavedViews extends Command {
|
|
|
291
322
|
const binResponse = await apiRequest(endpoint.method, apiPath, {
|
|
292
323
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
293
324
|
data,
|
|
325
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
294
326
|
contextName: flags.context,
|
|
295
327
|
binary: true,
|
|
296
328
|
});
|
|
@@ -316,6 +348,7 @@ export default class SavedViews extends Command {
|
|
|
316
348
|
const raw = await apiRequest(endpoint.method, apiPath, {
|
|
317
349
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
318
350
|
data,
|
|
351
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
319
352
|
contextName: flags.context,
|
|
320
353
|
binary: true,
|
|
321
354
|
});
|
|
@@ -336,6 +369,7 @@ export default class SavedViews extends Command {
|
|
|
336
369
|
response = await apiRequest(endpoint.method, apiPath, {
|
|
337
370
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
338
371
|
data,
|
|
372
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
339
373
|
contextName: flags.context,
|
|
340
374
|
acceptAny: true,
|
|
341
375
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Command, Args, Flags } from '@oclif/core';
|
|
2
2
|
import { apiRequest, ApiError, isTextualMediaType, normalizeSortBy } from '../lib/api-client.js';
|
|
3
3
|
import { getCurrentContext, getContext, getBaseUrls, hasCredentials, missingCredentialsMessage } from '../lib/config.js';
|
|
4
|
+
import { resolveUserPath } from '../lib/paths.js';
|
|
4
5
|
// 'upsert' loops create/update from the CLI, one request per record. It exists
|
|
5
6
|
// only where the API has no import endpoint of its own; those keep the name
|
|
6
7
|
// 'import' and are listed in SERVER_IMPORTS.
|
|
@@ -11,11 +12,11 @@ const SERVER_IMPORTS = [];
|
|
|
11
12
|
const NESTED_UPSERTS = [];
|
|
12
13
|
const SUBCOMMANDS = [...["list", "create", "get", "update", "delete"], ...(HAS_UPSERT ? ['upsert'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_UPSERTS.map(n => `upsert-${n}`)];
|
|
13
14
|
const ENDPOINTS = {
|
|
14
|
-
'list': { method: 'GET', path: '/v1/scheduled-processor-mappings', summary: 'List all scheduled processor mappings for the tenant', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
-
'create': { method: 'POST', path: '/v1/scheduled-processor-mappings', summary: 'Schedule a processor to run on a cron expression', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
16
|
-
'get': { method: 'GET', path: '/v1/scheduled-processor-mappings/{id}', summary: 'Get a scheduled processor mapping by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
17
|
-
'update': { method: 'PATCH', path: '/v1/scheduled-processor-mappings/{id}', summary: 'Update a scheduled processor mapping', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
18
|
-
'delete': { method: 'DELETE', path: '/v1/scheduled-processor-mappings/{id}', summary: 'Delete a scheduled processor mapping and its schedule', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
+
'list': { method: 'GET', path: '/v1/scheduled-processor-mappings', summary: 'List all scheduled processor mappings for the tenant', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
16
|
+
'create': { method: 'POST', path: '/v1/scheduled-processor-mappings', summary: 'Schedule a processor to run on a cron expression', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
17
|
+
'get': { method: 'GET', path: '/v1/scheduled-processor-mappings/{id}', summary: 'Get a scheduled processor mapping by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
18
|
+
'update': { method: 'PATCH', path: '/v1/scheduled-processor-mappings/{id}', summary: 'Update a scheduled processor mapping', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
19
|
+
'delete': { method: 'DELETE', path: '/v1/scheduled-processor-mappings/{id}', summary: 'Delete a scheduled processor mapping and its schedule', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
19
20
|
};
|
|
20
21
|
export default class ScheduledProcessorMappings extends Command {
|
|
21
22
|
static description = 'Manage scheduled processor mappings';
|
|
@@ -37,7 +38,7 @@ export default class ScheduledProcessorMappings extends Command {
|
|
|
37
38
|
table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
|
|
38
39
|
csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
|
|
39
40
|
data: Flags.string({ char: 'd', description: 'JSON data for request body' }),
|
|
40
|
-
file: Flags.string({ char: 'f', description: 'Read request body from file' }),
|
|
41
|
+
file: Flags.string({ char: 'f', description: 'Read request body from file', parse: async (input) => resolveUserPath(input) }),
|
|
41
42
|
set: Flags.string({ char: 's', description: 'Set individual field (e.g. --set firstName=John)', multiple: true }),
|
|
42
43
|
page: Flags.string({ description: 'Page number' }),
|
|
43
44
|
limit: Flags.string({ description: 'Items per page' }),
|
|
@@ -168,6 +169,17 @@ export default class ScheduledProcessorMappings extends Command {
|
|
|
168
169
|
}
|
|
169
170
|
}
|
|
170
171
|
}
|
|
172
|
+
// Flags generated from this endpoint's own parameters. Raw-body endpoints
|
|
173
|
+
// have them: their body is not JSON, so --set cannot carry a value there.
|
|
174
|
+
for (const param of endpoint.specParams) {
|
|
175
|
+
const value = flags[param.flag];
|
|
176
|
+
if (value === undefined || value === '') {
|
|
177
|
+
if (param.required)
|
|
178
|
+
this.error(`Missing required flag: --${param.flag}`);
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
queryParams[param.name] = value;
|
|
182
|
+
}
|
|
171
183
|
let data;
|
|
172
184
|
if (endpoint.isMultipart) {
|
|
173
185
|
if (!flags.file)
|
|
@@ -188,6 +200,25 @@ export default class ScheduledProcessorMappings extends Command {
|
|
|
188
200
|
}
|
|
189
201
|
data = form;
|
|
190
202
|
}
|
|
203
|
+
else if (endpoint.bodyMediaType) {
|
|
204
|
+
// The body is not JSON (today only application/x-ndjson): it is sent
|
|
205
|
+
// verbatim, under the content type the endpoint asks for.
|
|
206
|
+
if (flags.file) {
|
|
207
|
+
const fsModule = await import('node:fs');
|
|
208
|
+
try {
|
|
209
|
+
data = fsModule.readFileSync(flags.file, 'utf-8');
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
this.error(`Failed to read file: ${flags.file}`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
else if (flags.data) {
|
|
216
|
+
data = flags.data;
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
this.error(`This endpoint takes its body as ${endpoint.bodyMediaType}. Pass the file with -f / --file, or the text with --data.`);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
191
222
|
else if (endpoint.hasBody) {
|
|
192
223
|
// For binary-response endpoints, --file is the output path, not a JSON
|
|
193
224
|
// input file — skip the read-as-body branch.
|
|
@@ -283,7 +314,7 @@ export default class ScheduledProcessorMappings extends Command {
|
|
|
283
314
|
process.stderr.write(`Body: [multipart/form-data file upload]\n`);
|
|
284
315
|
}
|
|
285
316
|
else if (data) {
|
|
286
|
-
process.stderr.write(`Body: ${JSON.stringify(data, null, 2)}\n`);
|
|
317
|
+
process.stderr.write(`Body: ${typeof data === 'string' ? data : JSON.stringify(data, null, 2)}\n`);
|
|
287
318
|
}
|
|
288
319
|
process.stderr.write('\n');
|
|
289
320
|
}
|
|
@@ -297,6 +328,7 @@ export default class ScheduledProcessorMappings extends Command {
|
|
|
297
328
|
const binResponse = await apiRequest(endpoint.method, apiPath, {
|
|
298
329
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
299
330
|
data,
|
|
331
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
300
332
|
contextName: flags.context,
|
|
301
333
|
binary: true,
|
|
302
334
|
});
|
|
@@ -322,6 +354,7 @@ export default class ScheduledProcessorMappings extends Command {
|
|
|
322
354
|
const raw = await apiRequest(endpoint.method, apiPath, {
|
|
323
355
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
324
356
|
data,
|
|
357
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
325
358
|
contextName: flags.context,
|
|
326
359
|
binary: true,
|
|
327
360
|
});
|
|
@@ -342,6 +375,7 @@ export default class ScheduledProcessorMappings extends Command {
|
|
|
342
375
|
response = await apiRequest(endpoint.method, apiPath, {
|
|
343
376
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
344
377
|
data,
|
|
378
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
345
379
|
contextName: flags.context,
|
|
346
380
|
acceptAny: true,
|
|
347
381
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Command, Args, Flags } from '@oclif/core';
|
|
2
2
|
import { apiRequest, ApiError, isTextualMediaType, normalizeSortBy } from '../lib/api-client.js';
|
|
3
3
|
import { getCurrentContext, getContext, getBaseUrls, hasCredentials, missingCredentialsMessage } from '../lib/config.js';
|
|
4
|
+
import { resolveUserPath } from '../lib/paths.js';
|
|
4
5
|
// 'upsert' loops create/update from the CLI, one request per record. It exists
|
|
5
6
|
// only where the API has no import endpoint of its own; those keep the name
|
|
6
7
|
// 'import' and are listed in SERVER_IMPORTS.
|
|
@@ -11,15 +12,15 @@ const SERVER_IMPORTS = [];
|
|
|
11
12
|
const NESTED_UPSERTS = [];
|
|
12
13
|
const SUBCOMMANDS = [...["list", "create", "get", "update", "delete", "runs", "pause", "resume", "trigger"], ...(HAS_UPSERT ? ['upsert'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_UPSERTS.map(n => `upsert-${n}`)];
|
|
13
14
|
const ENDPOINTS = {
|
|
14
|
-
'list': { method: 'GET', path: '/v1/schedules', summary: 'List the caller\'s tenant\'s schedules with pagination (admin only)', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
-
'create': { method: 'POST', path: '/v1/schedules', summary: 'Create a schedule (admin only)', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
16
|
-
'get': { method: 'GET', path: '/v1/schedules/{id}', summary: 'Get a schedule by ID (admin only)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
17
|
-
'update': { method: 'PATCH', path: '/v1/schedules/{id}', summary: 'Update a schedule (admin only)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
18
|
-
'delete': { method: 'DELETE', path: '/v1/schedules/{id}', summary: 'Delete a schedule (admin only)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
19
|
-
'runs': { method: 'GET', path: '/v1/schedules/{id}/runs', summary: 'List a schedule\'s execution history with pagination (admin only)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
20
|
-
'pause': { method: 'POST', path: '/v1/schedules/{id}/pause', summary: 'Pause a schedule so it no longer runs (admin only)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
21
|
-
'resume': { method: 'POST', path: '/v1/schedules/{id}/resume', summary: 'Resume a paused schedule (admin only)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
22
|
-
'trigger': { method: 'POST', path: '/v1/schedules/{id}/trigger', summary: 'Manually run a schedule right now, rate-limited to one accepted trigger per schedule per 60 seconds (admin only)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
+
'list': { method: 'GET', path: '/v1/schedules', summary: 'List the caller\'s tenant\'s schedules with pagination (admin only)', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
16
|
+
'create': { method: 'POST', path: '/v1/schedules', summary: 'Create a schedule (admin only)', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
17
|
+
'get': { method: 'GET', path: '/v1/schedules/{id}', summary: 'Get a schedule by ID (admin only)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
18
|
+
'update': { method: 'PATCH', path: '/v1/schedules/{id}', summary: 'Update a schedule (admin only)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
19
|
+
'delete': { method: 'DELETE', path: '/v1/schedules/{id}', summary: 'Delete a schedule (admin only)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
20
|
+
'runs': { method: 'GET', path: '/v1/schedules/{id}/runs', summary: 'List a schedule\'s execution history with pagination (admin only)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
21
|
+
'pause': { method: 'POST', path: '/v1/schedules/{id}/pause', summary: 'Pause a schedule so it no longer runs (admin only)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
22
|
+
'resume': { method: 'POST', path: '/v1/schedules/{id}/resume', summary: 'Resume a paused schedule (admin only)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
23
|
+
'trigger': { method: 'POST', path: '/v1/schedules/{id}/trigger', summary: 'Manually run a schedule right now, rate-limited to one accepted trigger per schedule per 60 seconds (admin only)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
23
24
|
};
|
|
24
25
|
export default class Schedules extends Command {
|
|
25
26
|
static description = 'Manage schedules';
|
|
@@ -41,7 +42,7 @@ export default class Schedules extends Command {
|
|
|
41
42
|
table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
|
|
42
43
|
csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
|
|
43
44
|
data: Flags.string({ char: 'd', description: 'JSON data for request body' }),
|
|
44
|
-
file: Flags.string({ char: 'f', description: 'Read request body from file' }),
|
|
45
|
+
file: Flags.string({ char: 'f', description: 'Read request body from file', parse: async (input) => resolveUserPath(input) }),
|
|
45
46
|
set: Flags.string({ char: 's', description: 'Set individual field (e.g. --set firstName=John)', multiple: true }),
|
|
46
47
|
page: Flags.string({ description: 'Page number' }),
|
|
47
48
|
limit: Flags.string({ description: 'Items per page' }),
|
|
@@ -172,6 +173,17 @@ export default class Schedules extends Command {
|
|
|
172
173
|
}
|
|
173
174
|
}
|
|
174
175
|
}
|
|
176
|
+
// Flags generated from this endpoint's own parameters. Raw-body endpoints
|
|
177
|
+
// have them: their body is not JSON, so --set cannot carry a value there.
|
|
178
|
+
for (const param of endpoint.specParams) {
|
|
179
|
+
const value = flags[param.flag];
|
|
180
|
+
if (value === undefined || value === '') {
|
|
181
|
+
if (param.required)
|
|
182
|
+
this.error(`Missing required flag: --${param.flag}`);
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
queryParams[param.name] = value;
|
|
186
|
+
}
|
|
175
187
|
let data;
|
|
176
188
|
if (endpoint.isMultipart) {
|
|
177
189
|
if (!flags.file)
|
|
@@ -192,6 +204,25 @@ export default class Schedules extends Command {
|
|
|
192
204
|
}
|
|
193
205
|
data = form;
|
|
194
206
|
}
|
|
207
|
+
else if (endpoint.bodyMediaType) {
|
|
208
|
+
// The body is not JSON (today only application/x-ndjson): it is sent
|
|
209
|
+
// verbatim, under the content type the endpoint asks for.
|
|
210
|
+
if (flags.file) {
|
|
211
|
+
const fsModule = await import('node:fs');
|
|
212
|
+
try {
|
|
213
|
+
data = fsModule.readFileSync(flags.file, 'utf-8');
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
this.error(`Failed to read file: ${flags.file}`);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
else if (flags.data) {
|
|
220
|
+
data = flags.data;
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
this.error(`This endpoint takes its body as ${endpoint.bodyMediaType}. Pass the file with -f / --file, or the text with --data.`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
195
226
|
else if (endpoint.hasBody) {
|
|
196
227
|
// For binary-response endpoints, --file is the output path, not a JSON
|
|
197
228
|
// input file — skip the read-as-body branch.
|
|
@@ -287,7 +318,7 @@ export default class Schedules extends Command {
|
|
|
287
318
|
process.stderr.write(`Body: [multipart/form-data file upload]\n`);
|
|
288
319
|
}
|
|
289
320
|
else if (data) {
|
|
290
|
-
process.stderr.write(`Body: ${JSON.stringify(data, null, 2)}\n`);
|
|
321
|
+
process.stderr.write(`Body: ${typeof data === 'string' ? data : JSON.stringify(data, null, 2)}\n`);
|
|
291
322
|
}
|
|
292
323
|
process.stderr.write('\n');
|
|
293
324
|
}
|
|
@@ -301,6 +332,7 @@ export default class Schedules extends Command {
|
|
|
301
332
|
const binResponse = await apiRequest(endpoint.method, apiPath, {
|
|
302
333
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
303
334
|
data,
|
|
335
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
304
336
|
contextName: flags.context,
|
|
305
337
|
binary: true,
|
|
306
338
|
});
|
|
@@ -326,6 +358,7 @@ export default class Schedules extends Command {
|
|
|
326
358
|
const raw = await apiRequest(endpoint.method, apiPath, {
|
|
327
359
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
328
360
|
data,
|
|
361
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
329
362
|
contextName: flags.context,
|
|
330
363
|
binary: true,
|
|
331
364
|
});
|
|
@@ -346,6 +379,7 @@ export default class Schedules extends Command {
|
|
|
346
379
|
response = await apiRequest(endpoint.method, apiPath, {
|
|
347
380
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
348
381
|
data,
|
|
382
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
349
383
|
contextName: flags.context,
|
|
350
384
|
acceptAny: true,
|
|
351
385
|
});
|