@aderis-erp/cli 1.0.10 → 1.0.12
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 +1 -0
- package/dist/commands/access-groups.js +178 -86
- package/dist/commands/account-transaction-allocations.js +178 -86
- package/dist/commands/account-transactions.js +181 -87
- package/dist/commands/accounts.js +178 -86
- package/dist/commands/agent-runs.d.ts +1 -0
- package/dist/commands/agent-runs.js +183 -87
- package/dist/commands/audit-log.js +178 -86
- package/dist/commands/auth/login.js +4 -3
- package/dist/commands/bank-accounts.js +178 -86
- package/dist/commands/bank-integrations.js +178 -86
- package/dist/commands/business-partners.js +178 -86
- package/dist/commands/chat.d.ts +40 -0
- package/dist/commands/chat.js +1173 -0
- package/dist/commands/config/add.js +4 -3
- package/dist/commands/credit-card-accounts.js +178 -86
- package/dist/commands/documents.js +178 -86
- package/dist/commands/dunning-configs.js +180 -88
- package/dist/commands/dunning-reminders.js +178 -86
- package/dist/commands/dunning-runs.js +178 -86
- package/dist/commands/email-transactions.d.ts +40 -0
- package/dist/commands/email-transactions.js +1173 -0
- package/dist/commands/employees.js +180 -88
- package/dist/commands/entity-configurations.d.ts +40 -0
- package/dist/commands/entity-configurations.js +1169 -0
- package/dist/commands/event-processor-mappings.js +178 -86
- package/dist/commands/files-internal.js +178 -86
- package/dist/commands/health.js +178 -86
- package/dist/commands/inbox.js +181 -89
- package/dist/commands/integrations.js +178 -86
- package/dist/commands/item-categories.js +178 -86
- package/dist/commands/items.js +178 -86
- package/dist/commands/journal-lines.js +178 -86
- package/dist/commands/journals.js +178 -86
- package/dist/commands/ledgers.js +178 -86
- package/dist/commands/office-locations.js +178 -86
- package/dist/commands/payment-methods.js +178 -86
- package/dist/commands/payment-runs.js +178 -86
- package/dist/commands/payment-terms.js +178 -86
- package/dist/commands/price-lists.js +178 -86
- package/dist/commands/processor-mappings.js +178 -86
- package/dist/commands/processors.js +181 -87
- package/dist/commands/purchase-order-types.d.ts +40 -0
- package/dist/commands/purchase-order-types.js +1178 -0
- package/dist/commands/purchase-orders.d.ts +42 -0
- package/dist/commands/purchase-orders.js +1192 -0
- package/dist/commands/reports.js +178 -86
- package/dist/commands/salary-types.js +178 -86
- package/dist/commands/sales-invoices.js +187 -93
- package/dist/commands/sales-orders.js +186 -93
- package/dist/commands/saved-views.js +178 -86
- package/dist/commands/tags.js +178 -86
- package/dist/commands/tax-codes.js +180 -88
- package/dist/commands/tax-matrix.js +178 -86
- package/dist/commands/tenants.js +180 -88
- package/dist/commands/text-templates.js +178 -86
- package/dist/commands/tools.js +178 -86
- package/dist/commands/vat-declarations.d.ts +1 -0
- package/dist/commands/vat-declarations.js +188 -89
- package/dist/commands/vendor-invoices.js +181 -87
- package/dist/commands/webhooks.js +178 -86
- package/dist/commands/workflows.js +178 -86
- package/dist/lib/auth.js +2 -2
- package/dist/lib/config.d.ts +1 -1
- package/dist/lib/config.js +5 -0
- package/oclif.manifest.json +1174 -328
- package/package.json +2 -1
|
@@ -6,7 +6,7 @@ const HAS_IMPORT = true;
|
|
|
6
6
|
const HAS_EXPORT = true;
|
|
7
7
|
const HAS_CAMT053 = false;
|
|
8
8
|
const NESTED_IMPORTS = ["line-items", "notes", "files", "payments"];
|
|
9
|
-
const SUBCOMMANDS = [...["list", "create", "get", "update", "delete", "submit", "approve", "decline", "book", "cancel", "confirm-cancellation", "change-vendor", "validate", "create-line-items", "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", "list-payments", "create-payments", "get-payments", "update-payments", "delete-payments", "payments-approve", "payments-cancel"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
|
|
9
|
+
const SUBCOMMANDS = [...["list", "create", "get", "update", "delete", "submit", "approve", "decline", "book", "rebuild-journal", "cancel", "confirm-cancellation", "change-vendor", "recalculate-payment-status", "validate", "create-line-items", "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", "list-payments", "create-payments", "get-payments", "update-payments", "delete-payments", "payments-approve", "payments-cancel"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
|
|
10
10
|
const ENDPOINTS = {
|
|
11
11
|
'list': { method: 'GET', path: '/v1/vendor-invoices', summary: 'Get all vendor invoices with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
12
12
|
'create': { method: 'POST', path: '/v1/vendor-invoices', summary: 'Create a new vendor invoice', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['totalAmountByTaxRate', 'files', 'notes', 'lineItems', 'tagIds', 'payments'] },
|
|
@@ -17,9 +17,11 @@ const ENDPOINTS = {
|
|
|
17
17
|
'approve': { method: 'POST', path: '/v1/vendor-invoices/{id}/approve', summary: 'Approve a vendor invoice (transitions DRAFT/PENDING_APPROVAL to APPROVED)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['totalAmountByTaxRate', 'files', 'notes', 'lineItems', 'tagIds', 'removeFileIds'] },
|
|
18
18
|
'decline': { method: 'POST', path: '/v1/vendor-invoices/{id}/decline', summary: 'Decline a vendor invoice (transitions PENDING_APPROVAL to DECLINED)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
19
19
|
'book': { method: 'POST', path: '/v1/vendor-invoices/{id}/book', summary: 'Manually book a vendor invoice (dispatch to ledger-booking-agent)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
20
|
+
'rebuild-journal': { method: 'POST', path: '/v1/vendor-invoices/{id}/rebuild-journal', summary: 'Rebuild a vendor invoice booking journal (dispatch to ledger-booking-agent)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
20
21
|
'cancel': { method: 'POST', path: '/v1/vendor-invoices/{id}/cancel', summary: 'Cancel a vendor invoice', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
21
22
|
'confirm-cancellation': { method: 'POST', path: '/v1/vendor-invoices/{id}/confirm-cancellation', summary: 'Confirm bank cancellation for a vendor invoice (PENDING_CANCELLATION to CANCELLED)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
22
23
|
'change-vendor': { method: 'POST', path: '/v1/vendor-invoices/{id}/change-vendor', summary: 'Change the vendor on a vendor invoice', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
24
|
+
'recalculate-payment-status': { method: 'POST', path: '/v1/vendor-invoices/{id}/recalculate-payment-status', summary: 'Recalculate a vendor invoice\'s open amount and payment status', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
23
25
|
'validate': { method: 'POST', path: '/v1/vendor-invoices/{id}/validate', summary: 'Validate a vendor invoice update without persisting changes', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['totalAmountByTaxRate', 'files', 'notes', 'lineItems', 'tagIds', 'removeFileIds'] },
|
|
24
26
|
'create-line-items': { method: 'POST', path: '/v1/vendor-invoices/{invoiceId}/line-items', summary: 'Add a line item to a vendor invoice', pathParams: ['invoiceId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
25
27
|
'update-line-items': { method: 'PATCH', path: '/v1/vendor-invoices/{invoiceId}/line-items/{lineItemId}', summary: 'Update a line item on a vendor invoice', pathParams: ['invoiceId', 'lineItemId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
@@ -464,76 +466,126 @@ export default class VendorInvoices extends Command {
|
|
|
464
466
|
return '';
|
|
465
467
|
};
|
|
466
468
|
const results = [];
|
|
467
|
-
|
|
469
|
+
// Stream output row-by-row so progress is visible during long imports.
|
|
470
|
+
// Pre-compute the identifier column width from the input items so the
|
|
471
|
+
// table stays aligned without buffering all rows.
|
|
472
|
+
// When stdout is piped (IDE terminals, tee, CI), Node switches to async-buffered
|
|
473
|
+
// mode and rows accumulate in the pipe buffer until it fills. Force blocking
|
|
474
|
+
// mode so each \n-terminated write flushes immediately.
|
|
475
|
+
const stdoutHandle = process.stdout._handle;
|
|
476
|
+
if (stdoutHandle?.setBlocking)
|
|
477
|
+
stdoutHandle.setBlocking(true);
|
|
478
|
+
const outputFormat = flags.output || 'table';
|
|
479
|
+
const useCsv = outputFormat === 'csv' || Boolean(flags.csv);
|
|
480
|
+
const useJson = outputFormat === 'json';
|
|
481
|
+
const statusWidth = 8;
|
|
482
|
+
const identifierWidth = Math.max(10, ...items.map(i => getItemIdentifier(i).length));
|
|
483
|
+
if (useCsv) {
|
|
484
|
+
this.log('status,identifier,id,error');
|
|
485
|
+
}
|
|
486
|
+
else if (!useJson) {
|
|
487
|
+
this.log(`${'status'.padEnd(statusWidth)} ${'identifier'.padEnd(identifierWidth)} error`);
|
|
488
|
+
this.log(`${'-'.repeat(statusWidth)} ${'-'.repeat(identifierWidth)} ${'-'.repeat(50)}`);
|
|
489
|
+
}
|
|
490
|
+
const emitRow = (r) => {
|
|
491
|
+
if (useJson)
|
|
492
|
+
return;
|
|
493
|
+
if (useCsv) {
|
|
494
|
+
const err = (r.error || '').replace(/"/g, '""');
|
|
495
|
+
this.log(`${r.status},${r.identifier || ''},${r.id || ''},"${err}"`);
|
|
496
|
+
}
|
|
497
|
+
else {
|
|
498
|
+
this.log(`${r.status.padEnd(statusWidth)} ${(r.identifier || '').padEnd(identifierWidth)} ${r.error || ''}`);
|
|
499
|
+
}
|
|
500
|
+
};
|
|
501
|
+
// Server-managed fields the API rejects in create/update bodies. Stripped
|
|
502
|
+
// before every request so re-importing a previously-exported payload Just Works.
|
|
503
|
+
const STRIP_FIELDS = new Set(['id', '_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy']);
|
|
504
|
+
const stripServerFields = (item) => {
|
|
505
|
+
const out = {};
|
|
506
|
+
for (const [k, v] of Object.entries(item)) {
|
|
507
|
+
if (!STRIP_FIELDS.has(k))
|
|
508
|
+
out[k] = v;
|
|
509
|
+
}
|
|
510
|
+
return out;
|
|
511
|
+
};
|
|
512
|
+
const processItem = async (item) => {
|
|
468
513
|
const identifier = getItemIdentifier(item);
|
|
514
|
+
const body = stripServerFields(item);
|
|
515
|
+
// If the payload carries an id (e.g. re-importing an export), PATCH directly.
|
|
516
|
+
// Fall through to POST only on 404 (so a missing record still gets created).
|
|
517
|
+
const itemId = typeof item.id === 'string' ? item.id : undefined;
|
|
518
|
+
if (itemId) {
|
|
519
|
+
try {
|
|
520
|
+
const updatePath = updateEndpoint.path.replace('{id}', itemId);
|
|
521
|
+
await apiRequest('PATCH', updatePath, {
|
|
522
|
+
data: body,
|
|
523
|
+
contextName: flags.context,
|
|
524
|
+
});
|
|
525
|
+
return { status: 'updated', id: itemId, identifier };
|
|
526
|
+
}
|
|
527
|
+
catch (error) {
|
|
528
|
+
if (!(error instanceof ApiError && error.status === 404)) {
|
|
529
|
+
const msg = error instanceof ApiError ? `(${error.status}) ${error.message}` : String(error);
|
|
530
|
+
return { status: 'error', id: itemId, identifier, error: msg };
|
|
531
|
+
}
|
|
532
|
+
// 404 → record gone, try create below
|
|
533
|
+
}
|
|
534
|
+
}
|
|
469
535
|
try {
|
|
470
|
-
// Try to create
|
|
471
536
|
const response = await apiRequest('POST', createEndpoint.path, {
|
|
472
|
-
data:
|
|
537
|
+
data: body,
|
|
473
538
|
contextName: flags.context,
|
|
474
539
|
});
|
|
475
540
|
const id = response.data?.id;
|
|
476
|
-
|
|
541
|
+
return { status: 'created', id: id ? String(id) : undefined, identifier };
|
|
477
542
|
}
|
|
478
543
|
catch (error) {
|
|
479
544
|
if (error instanceof ApiError && error.status === 409) {
|
|
480
|
-
// Conflict - try to update existing
|
|
481
545
|
const errorData = error.data;
|
|
482
546
|
const existingId = errorData?.existingId;
|
|
483
547
|
if (existingId) {
|
|
484
548
|
try {
|
|
485
549
|
const updatePath = updateEndpoint.path.replace('{id}', existingId);
|
|
486
550
|
await apiRequest('PATCH', updatePath, {
|
|
487
|
-
data:
|
|
551
|
+
data: body,
|
|
488
552
|
contextName: flags.context,
|
|
489
553
|
});
|
|
490
|
-
|
|
554
|
+
return { status: 'updated', id: existingId, identifier };
|
|
491
555
|
}
|
|
492
556
|
catch (updateError) {
|
|
493
557
|
const msg = updateError instanceof ApiError ? updateError.message : String(updateError);
|
|
494
|
-
|
|
558
|
+
return { status: 'error', id: existingId, identifier, error: `Update failed: ${msg}` };
|
|
495
559
|
}
|
|
496
560
|
}
|
|
497
|
-
|
|
498
|
-
results.push({ status: 'error', identifier, error: 'Conflict but no existingId returned' });
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
else {
|
|
502
|
-
const msg = error instanceof ApiError ? `(${error.status}) ${error.message}` : String(error);
|
|
503
|
-
results.push({ status: 'error', identifier, error: msg });
|
|
561
|
+
return { status: 'error', identifier, error: 'Conflict but no existingId returned' };
|
|
504
562
|
}
|
|
563
|
+
const msg = error instanceof ApiError ? `(${error.status}) ${error.message}` : String(error);
|
|
564
|
+
return { status: 'error', identifier, error: msg };
|
|
505
565
|
}
|
|
506
|
-
}
|
|
566
|
+
};
|
|
567
|
+
// Worker pool: keep N requests in flight at all times. Rows are emitted as
|
|
568
|
+
// each request resolves (so output order may differ from input order). The
|
|
569
|
+
// summary at the end remains accurate regardless.
|
|
570
|
+
const concurrency = 4;
|
|
571
|
+
const queue = [...items];
|
|
572
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, queue.length) }, async () => {
|
|
573
|
+
for (;;) {
|
|
574
|
+
const item = queue.shift();
|
|
575
|
+
if (!item)
|
|
576
|
+
return;
|
|
577
|
+
const r = await processItem(item);
|
|
578
|
+
results.push(r);
|
|
579
|
+
emitRow(r);
|
|
580
|
+
}
|
|
581
|
+
}));
|
|
507
582
|
// Summary counts
|
|
508
583
|
const created = results.filter(r => r.status === 'created').length;
|
|
509
584
|
const updated = results.filter(r => r.status === 'updated').length;
|
|
510
585
|
const errors = results.filter(r => r.status === 'error');
|
|
511
|
-
|
|
512
|
-
const outputFormat = flags.output || 'table';
|
|
513
|
-
if (outputFormat === 'json') {
|
|
586
|
+
if (useJson) {
|
|
514
587
|
this.log(JSON.stringify(results, null, 2));
|
|
515
588
|
}
|
|
516
|
-
else if (outputFormat === 'csv' || flags.csv) {
|
|
517
|
-
// CSV output
|
|
518
|
-
this.log('status,identifier,id,error');
|
|
519
|
-
for (const r of results) {
|
|
520
|
-
const err = (r.error || '').replace(/"/g, '""');
|
|
521
|
-
this.log(`${r.status},${r.identifier || ''},${r.id || ''},"${err}"`);
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
else {
|
|
525
|
-
// Table output (default)
|
|
526
|
-
if (results.length > 0) {
|
|
527
|
-
const statusWidth = 8;
|
|
528
|
-
const identifierWidth = Math.max(10, ...results.map(r => (r.identifier || '').length));
|
|
529
|
-
this.log(`${'status'.padEnd(statusWidth)} ${'identifier'.padEnd(identifierWidth)} error`);
|
|
530
|
-
this.log(`${'-'.repeat(statusWidth)} ${'-'.repeat(identifierWidth)} ${'-'.repeat(50)}`);
|
|
531
|
-
for (const r of results) {
|
|
532
|
-
const status = r.status === 'created' ? 'created' : r.status === 'updated' ? 'updated' : 'error';
|
|
533
|
-
this.log(`${status.padEnd(statusWidth)} ${(r.identifier || '').padEnd(identifierWidth)} ${r.error || ''}`);
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
589
|
// Always show summary at the end
|
|
538
590
|
this.log('');
|
|
539
591
|
this.log(`Summary: ${created} created, ${updated} updated, ${errors.length} failed`);
|
|
@@ -813,80 +865,122 @@ export default class VendorInvoices extends Command {
|
|
|
813
865
|
return '';
|
|
814
866
|
};
|
|
815
867
|
const results = [];
|
|
816
|
-
|
|
868
|
+
const stdoutHandle = process.stdout._handle;
|
|
869
|
+
if (stdoutHandle?.setBlocking)
|
|
870
|
+
stdoutHandle.setBlocking(true);
|
|
871
|
+
const outputFormat = flags.output || 'table';
|
|
872
|
+
const useCsv = outputFormat === 'csv' || Boolean(flags.csv);
|
|
873
|
+
const useJson = outputFormat === 'json';
|
|
874
|
+
const statusWidth = 8;
|
|
875
|
+
const identifierWidth = Math.max(10, ...items.map(i => getItemIdentifier(i).length));
|
|
876
|
+
if (useCsv) {
|
|
877
|
+
this.log('status,identifier,id,error');
|
|
878
|
+
}
|
|
879
|
+
else if (!useJson) {
|
|
880
|
+
this.log(`${'status'.padEnd(statusWidth)} ${'identifier'.padEnd(identifierWidth)} error`);
|
|
881
|
+
this.log(`${'-'.repeat(statusWidth)} ${'-'.repeat(identifierWidth)} ${'-'.repeat(50)}`);
|
|
882
|
+
}
|
|
883
|
+
const emitRow = (r) => {
|
|
884
|
+
if (useJson)
|
|
885
|
+
return;
|
|
886
|
+
if (useCsv) {
|
|
887
|
+
const err = (r.error || '').replace(/"/g, '""');
|
|
888
|
+
this.log(`${r.status},${r.identifier || ''},${r.id || ''},"${err}"`);
|
|
889
|
+
}
|
|
890
|
+
else {
|
|
891
|
+
this.log(`${r.status.padEnd(statusWidth)} ${(r.identifier || '').padEnd(identifierWidth)} ${r.error || ''}`);
|
|
892
|
+
}
|
|
893
|
+
};
|
|
894
|
+
const STRIP_FIELDS = new Set(['id', '_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy']);
|
|
895
|
+
const stripServerFields = (item) => {
|
|
896
|
+
const out = {};
|
|
897
|
+
for (const [k, v] of Object.entries(item)) {
|
|
898
|
+
if (!STRIP_FIELDS.has(k))
|
|
899
|
+
out[k] = v;
|
|
900
|
+
}
|
|
901
|
+
return out;
|
|
902
|
+
};
|
|
903
|
+
const buildUpdatePath = (existingId) => {
|
|
904
|
+
const firstUpdateParam = updateEndpoint.pathParams[0];
|
|
905
|
+
const nestedIdParam = updateEndpoint.pathParams.find((p, i) => i > 0);
|
|
906
|
+
let updatePath = updateEndpoint.path.replace(`{${firstUpdateParam}}`, parentId);
|
|
907
|
+
if (nestedIdParam) {
|
|
908
|
+
updatePath = updatePath.replace(`{${nestedIdParam}}`, existingId);
|
|
909
|
+
}
|
|
910
|
+
return updatePath;
|
|
911
|
+
};
|
|
912
|
+
const processItem = async (item) => {
|
|
817
913
|
const identifier = getItemIdentifier(item);
|
|
914
|
+
const body = stripServerFields(item);
|
|
915
|
+
const itemId = typeof item.id === 'string' ? item.id : undefined;
|
|
916
|
+
if (itemId) {
|
|
917
|
+
try {
|
|
918
|
+
await apiRequest('PATCH', buildUpdatePath(itemId), {
|
|
919
|
+
data: body,
|
|
920
|
+
contextName: flags.context,
|
|
921
|
+
});
|
|
922
|
+
return { status: 'updated', id: itemId, identifier };
|
|
923
|
+
}
|
|
924
|
+
catch (error) {
|
|
925
|
+
if (!(error instanceof ApiError && error.status === 404)) {
|
|
926
|
+
const msg = error instanceof ApiError ? `(${error.status}) ${error.message}` : String(error);
|
|
927
|
+
return { status: 'error', id: itemId, identifier, error: msg };
|
|
928
|
+
}
|
|
929
|
+
// 404 → fall through and try create
|
|
930
|
+
}
|
|
931
|
+
}
|
|
818
932
|
try {
|
|
819
|
-
// Try to create
|
|
820
933
|
const response = await apiRequest('POST', basePath, {
|
|
821
|
-
data:
|
|
934
|
+
data: body,
|
|
822
935
|
contextName: flags.context,
|
|
823
936
|
});
|
|
824
937
|
const id = response.data?.id;
|
|
825
|
-
|
|
938
|
+
return { status: 'created', id: id ? String(id) : undefined, identifier };
|
|
826
939
|
}
|
|
827
940
|
catch (error) {
|
|
828
941
|
if (error instanceof ApiError && error.status === 409) {
|
|
829
|
-
// Conflict - try to update existing
|
|
830
942
|
const errorData = error.data;
|
|
831
943
|
const existingId = errorData?.existingId || item.id;
|
|
832
944
|
if (existingId) {
|
|
833
945
|
try {
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
const nestedIdParam = updateEndpoint.pathParams.find((p, i) => i > 0);
|
|
837
|
-
let updatePath = updateEndpoint.path.replace(`{${firstUpdateParam}}`, parentId);
|
|
838
|
-
if (nestedIdParam) {
|
|
839
|
-
updatePath = updatePath.replace(`{${nestedIdParam}}`, existingId);
|
|
840
|
-
}
|
|
841
|
-
await apiRequest('PATCH', updatePath, {
|
|
842
|
-
data: item,
|
|
946
|
+
await apiRequest('PATCH', buildUpdatePath(existingId), {
|
|
947
|
+
data: body,
|
|
843
948
|
contextName: flags.context,
|
|
844
949
|
});
|
|
845
|
-
|
|
950
|
+
return { status: 'updated', id: existingId, identifier };
|
|
846
951
|
}
|
|
847
952
|
catch (updateError) {
|
|
848
953
|
const msg = updateError instanceof ApiError ? updateError.message : String(updateError);
|
|
849
|
-
|
|
954
|
+
return { status: 'error', id: existingId, identifier, error: `Update failed: ${msg}` };
|
|
850
955
|
}
|
|
851
956
|
}
|
|
852
|
-
|
|
853
|
-
results.push({ status: 'error', identifier, error: 'Conflict but no existingId returned' });
|
|
854
|
-
}
|
|
855
|
-
}
|
|
856
|
-
else {
|
|
857
|
-
const msg = error instanceof ApiError ? `(${error.status}) ${error.message}` : String(error);
|
|
858
|
-
results.push({ status: 'error', identifier, error: msg });
|
|
957
|
+
return { status: 'error', identifier, error: 'Conflict but no existingId returned' };
|
|
859
958
|
}
|
|
959
|
+
const msg = error instanceof ApiError ? `(${error.status}) ${error.message}` : String(error);
|
|
960
|
+
return { status: 'error', identifier, error: msg };
|
|
860
961
|
}
|
|
861
|
-
}
|
|
962
|
+
};
|
|
963
|
+
// Worker pool: 4 requests in flight at a time. Rows are emitted as each
|
|
964
|
+
// request resolves so output order may differ from input order.
|
|
965
|
+
const concurrency = 4;
|
|
966
|
+
const queue = [...items];
|
|
967
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, queue.length) }, async () => {
|
|
968
|
+
for (;;) {
|
|
969
|
+
const item = queue.shift();
|
|
970
|
+
if (!item)
|
|
971
|
+
return;
|
|
972
|
+
const r = await processItem(item);
|
|
973
|
+
results.push(r);
|
|
974
|
+
emitRow(r);
|
|
975
|
+
}
|
|
976
|
+
}));
|
|
862
977
|
// Summary counts
|
|
863
978
|
const created = results.filter(r => r.status === 'created').length;
|
|
864
979
|
const updated = results.filter(r => r.status === 'updated').length;
|
|
865
980
|
const errors = results.filter(r => r.status === 'error');
|
|
866
|
-
|
|
867
|
-
const outputFormat = flags.output || 'table';
|
|
868
|
-
if (outputFormat === 'json') {
|
|
981
|
+
if (useJson) {
|
|
869
982
|
this.log(JSON.stringify(results, null, 2));
|
|
870
983
|
}
|
|
871
|
-
else if (outputFormat === 'csv' || flags.csv) {
|
|
872
|
-
this.log('status,identifier,id,error');
|
|
873
|
-
for (const r of results) {
|
|
874
|
-
const err = (r.error || '').replace(/"/g, '""');
|
|
875
|
-
this.log(`${r.status},${r.identifier || ''},${r.id || ''},"${err}"`);
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
|
-
else {
|
|
879
|
-
if (results.length > 0) {
|
|
880
|
-
const statusWidth = 8;
|
|
881
|
-
const identifierWidth = Math.max(10, ...results.map(r => (r.identifier || '').length));
|
|
882
|
-
this.log(`${'status'.padEnd(statusWidth)} ${'identifier'.padEnd(identifierWidth)} error`);
|
|
883
|
-
this.log(`${'-'.repeat(statusWidth)} ${'-'.repeat(identifierWidth)} ${'-'.repeat(50)}`);
|
|
884
|
-
for (const r of results) {
|
|
885
|
-
const status = r.status === 'created' ? 'created' : r.status === 'updated' ? 'updated' : 'error';
|
|
886
|
-
this.log(`${status.padEnd(statusWidth)} ${(r.identifier || '').padEnd(identifierWidth)} ${r.error || ''}`);
|
|
887
|
-
}
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
984
|
this.log('');
|
|
891
985
|
this.log(`Summary: ${created} created, ${updated} updated, ${errors.length} failed`);
|
|
892
986
|
}
|