@objectstack/rest 12.5.0 → 12.6.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/dist/index.cjs +79 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +75 -20
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
package/dist/index.cjs
CHANGED
|
@@ -38,7 +38,7 @@ __export(index_exports, {
|
|
|
38
38
|
module.exports = __toCommonJS(index_exports);
|
|
39
39
|
|
|
40
40
|
// src/rest-server.ts
|
|
41
|
-
var
|
|
41
|
+
var import_core2 = require("@objectstack/core");
|
|
42
42
|
|
|
43
43
|
// src/route-manager.ts
|
|
44
44
|
var RouteManager = class {
|
|
@@ -558,6 +558,17 @@ async function coerceRow(rawRow, metaMap, ctx) {
|
|
|
558
558
|
}
|
|
559
559
|
|
|
560
560
|
// src/import-runner.ts
|
|
561
|
+
var import_core = require("@objectstack/core");
|
|
562
|
+
function extractRecordId(rec) {
|
|
563
|
+
const id = rec?.id ?? rec?.record?.id;
|
|
564
|
+
return id != null ? String(id) : void 0;
|
|
565
|
+
}
|
|
566
|
+
function toFailedResult(rowNo, err) {
|
|
567
|
+
const code = err?.code ?? "IMPORT_ROW_FAILED";
|
|
568
|
+
const message = typeof err?.message === "string" ? err.message.slice(0, 300) : "Row failed";
|
|
569
|
+
return { row: rowNo, ok: false, action: "failed", error: message, code };
|
|
570
|
+
}
|
|
571
|
+
var MAX_CREATE_BATCH_SIZE = 200;
|
|
561
572
|
function runImport(opts) {
|
|
562
573
|
const {
|
|
563
574
|
p,
|
|
@@ -645,7 +656,7 @@ function runImport(opts) {
|
|
|
645
656
|
return recs[0];
|
|
646
657
|
};
|
|
647
658
|
const writeCtx = { ...context ?? {}, skipAutomations: !runAutomations };
|
|
648
|
-
const results =
|
|
659
|
+
const results = new Array(rows.length);
|
|
649
660
|
let okCount = 0, errCount = 0, created = 0, updated = 0, skipped = 0;
|
|
650
661
|
let cancelled = false;
|
|
651
662
|
const snapshot = (processed) => ({
|
|
@@ -656,6 +667,47 @@ function runImport(opts) {
|
|
|
656
667
|
skipped,
|
|
657
668
|
errors: errCount
|
|
658
669
|
});
|
|
670
|
+
const canBulkCreate = typeof p.createManyData === "function";
|
|
671
|
+
const pendingCreates = [];
|
|
672
|
+
const flushPendingCreates = async () => {
|
|
673
|
+
if (pendingCreates.length === 0) return;
|
|
674
|
+
const batch = pendingCreates.splice(0, pendingCreates.length);
|
|
675
|
+
const writeResults = await (0, import_core.bulkWrite)(
|
|
676
|
+
batch.map((b) => b.data),
|
|
677
|
+
{
|
|
678
|
+
// Flush cadence follows progressEvery, but the write batch itself is
|
|
679
|
+
// capped independently — a caller-supplied progressEvery far above
|
|
680
|
+
// the issue's suggested 100-500 rows/batch must not translate into
|
|
681
|
+
// one oversized multi-row INSERT statement.
|
|
682
|
+
batchSize: Math.min(progressEvery, MAX_CREATE_BATCH_SIZE),
|
|
683
|
+
writeBatch: (chunk) => p.createManyData({
|
|
684
|
+
object: objectName,
|
|
685
|
+
records: chunk,
|
|
686
|
+
context: writeCtx,
|
|
687
|
+
...environmentId ? { environmentId } : {}
|
|
688
|
+
}).then((r) => r.records),
|
|
689
|
+
writeOne: (row) => p.createData({
|
|
690
|
+
object: objectName,
|
|
691
|
+
data: row,
|
|
692
|
+
context: writeCtx,
|
|
693
|
+
...environmentId ? { environmentId } : {}
|
|
694
|
+
})
|
|
695
|
+
}
|
|
696
|
+
);
|
|
697
|
+
for (const res of writeResults) {
|
|
698
|
+
const { index, rowNo } = batch[res.index];
|
|
699
|
+
if (res.ok) {
|
|
700
|
+
const id = extractRecordId(res.record);
|
|
701
|
+
okCount++;
|
|
702
|
+
created++;
|
|
703
|
+
if (collectUndo && id != null) undoLog.created.push(id);
|
|
704
|
+
results[index] = { row: rowNo, ok: true, action: "created", id };
|
|
705
|
+
} else {
|
|
706
|
+
errCount++;
|
|
707
|
+
results[index] = toFailedResult(rowNo, res.error);
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
};
|
|
659
711
|
return (async () => {
|
|
660
712
|
for (let i = 0; i < rows.length; i++) {
|
|
661
713
|
const rowNo = i + 1;
|
|
@@ -669,7 +721,7 @@ function runImport(opts) {
|
|
|
669
721
|
if (errors.length > 0) {
|
|
670
722
|
const first2 = errors[0];
|
|
671
723
|
errCount++;
|
|
672
|
-
results
|
|
724
|
+
results[i] = { row: rowNo, ok: false, action: "failed", field: first2.field, code: first2.code, error: first2.message };
|
|
673
725
|
} else {
|
|
674
726
|
let existing = "none";
|
|
675
727
|
let handled = false;
|
|
@@ -677,11 +729,11 @@ function runImport(opts) {
|
|
|
677
729
|
existing = await findExisting(data);
|
|
678
730
|
if (existing === "ambiguous") {
|
|
679
731
|
errCount++;
|
|
680
|
-
results
|
|
732
|
+
results[i] = { row: rowNo, ok: false, action: "failed", code: "AMBIGUOUS_MATCH", error: `matchFields matched more than one ${objectName} record` };
|
|
681
733
|
handled = true;
|
|
682
734
|
} else if (existing === "blank" && (skipBlankMatchKey || writeMode === "update")) {
|
|
683
735
|
skipped++;
|
|
684
|
-
results
|
|
736
|
+
results[i] = { row: rowNo, ok: true, action: "skipped", code: "BLANK_MATCH_KEY" };
|
|
685
737
|
handled = true;
|
|
686
738
|
}
|
|
687
739
|
}
|
|
@@ -690,45 +742,46 @@ function runImport(opts) {
|
|
|
690
742
|
const willCreate = !willUpdate && (writeMode === "insert" || writeMode === "upsert");
|
|
691
743
|
if (!willUpdate && !willCreate) {
|
|
692
744
|
skipped++;
|
|
693
|
-
results
|
|
745
|
+
results[i] = { row: rowNo, ok: true, action: "skipped", code: "NO_MATCH" };
|
|
694
746
|
} else if (dryRun) {
|
|
695
747
|
okCount++;
|
|
696
748
|
if (willUpdate) {
|
|
697
749
|
updated++;
|
|
698
|
-
results
|
|
750
|
+
results[i] = { row: rowNo, ok: true, action: "updated", id: String(existing.id ?? "") || void 0 };
|
|
699
751
|
} else {
|
|
700
752
|
created++;
|
|
701
|
-
results
|
|
753
|
+
results[i] = { row: rowNo, ok: true, action: "created" };
|
|
702
754
|
}
|
|
703
755
|
} else if (willUpdate) {
|
|
704
756
|
const target = existing;
|
|
705
|
-
const res2 = await p.updateData({ object: objectName, id: target.id, data, context: writeCtx, ...environmentId ? { environmentId } : {} });
|
|
706
|
-
const id = res2
|
|
757
|
+
const res2 = await (0, import_core.withTransientRetry)(() => p.updateData({ object: objectName, id: target.id, data, context: writeCtx, ...environmentId ? { environmentId } : {} }));
|
|
758
|
+
const id = extractRecordId(res2) ?? String(target.id);
|
|
707
759
|
okCount++;
|
|
708
760
|
updated++;
|
|
709
761
|
if (collectUndo && target.id != null) {
|
|
710
762
|
undoLog.updated.push({ id: String(target.id), before: captureBefore(target, data) });
|
|
711
763
|
}
|
|
712
|
-
results
|
|
764
|
+
results[i] = { row: rowNo, ok: true, action: "updated", id };
|
|
765
|
+
} else if (canBulkCreate) {
|
|
766
|
+
pendingCreates.push({ index: i, rowNo, data });
|
|
713
767
|
} else {
|
|
714
768
|
const res2 = await p.createData({ object: objectName, data, context: writeCtx, ...environmentId ? { environmentId } : {} });
|
|
715
|
-
const id = res2
|
|
769
|
+
const id = extractRecordId(res2);
|
|
716
770
|
okCount++;
|
|
717
771
|
created++;
|
|
718
|
-
if (collectUndo && id != null) undoLog.created.push(
|
|
719
|
-
results
|
|
772
|
+
if (collectUndo && id != null) undoLog.created.push(id);
|
|
773
|
+
results[i] = { row: rowNo, ok: true, action: "created", id };
|
|
720
774
|
}
|
|
721
775
|
}
|
|
722
776
|
}
|
|
723
777
|
} catch (err) {
|
|
724
778
|
errCount++;
|
|
725
|
-
|
|
726
|
-
const message = typeof err?.message === "string" ? err.message.slice(0, 300) : "Row failed";
|
|
727
|
-
results.push({ row: rowNo, ok: false, action: "failed", error: message, code });
|
|
779
|
+
results[i] = toFailedResult(rowNo, err);
|
|
728
780
|
}
|
|
729
781
|
const processed = i + 1;
|
|
730
|
-
if (
|
|
731
|
-
await
|
|
782
|
+
if (processed % progressEvery === 0 || processed === rows.length) {
|
|
783
|
+
await flushPendingCreates();
|
|
784
|
+
if (onProgress) await onProgress(snapshot(processed));
|
|
732
785
|
}
|
|
733
786
|
if (shouldCancel && processed < rows.length && processed % progressEvery === 0) {
|
|
734
787
|
if (await shouldCancel()) {
|
|
@@ -737,10 +790,12 @@ function runImport(opts) {
|
|
|
737
790
|
}
|
|
738
791
|
}
|
|
739
792
|
}
|
|
793
|
+
await flushPendingCreates();
|
|
794
|
+
const compacted = results.filter((r) => r !== void 0);
|
|
740
795
|
return {
|
|
741
|
-
...snapshot(
|
|
796
|
+
...snapshot(compacted.length),
|
|
742
797
|
ok: okCount,
|
|
743
|
-
results,
|
|
798
|
+
results: compacted,
|
|
744
799
|
cancelled,
|
|
745
800
|
...collectUndo ? { undoLog } : {}
|
|
746
801
|
};
|
|
@@ -1561,7 +1616,7 @@ var RestServer = class {
|
|
|
1561
1616
|
*/
|
|
1562
1617
|
enforceAuth(req, res, context) {
|
|
1563
1618
|
const gate = context?.authGate;
|
|
1564
|
-
if (gate && req?.method !== "OPTIONS" && !(0,
|
|
1619
|
+
if (gate && req?.method !== "OPTIONS" && !(0, import_core2.isAuthGateAllowlisted)(req?.path)) {
|
|
1565
1620
|
res.status(403).json({ error: { code: gate.code, message: gate.message } });
|
|
1566
1621
|
return true;
|
|
1567
1622
|
}
|
|
@@ -1717,10 +1772,10 @@ var RestServer = class {
|
|
|
1717
1772
|
return void 0;
|
|
1718
1773
|
}
|
|
1719
1774
|
};
|
|
1720
|
-
const authz = await (0,
|
|
1775
|
+
const authz = await (0, import_core2.resolveAuthzContext)({ ql, headers, getSession });
|
|
1721
1776
|
if (!authz.userId) return void 0;
|
|
1722
1777
|
const settings = this.settingsServiceProvider ? await this.settingsServiceProvider(environmentId).catch(() => void 0) : void 0;
|
|
1723
|
-
const localization = await (0,
|
|
1778
|
+
const localization = await (0, import_core2.resolveLocalizationContext)({
|
|
1724
1779
|
ql,
|
|
1725
1780
|
settings,
|
|
1726
1781
|
tenantId: authz.tenantId,
|