@objectstack/rest 9.0.0 → 9.0.1
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 +69 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +69 -14
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -546,37 +546,49 @@ var RestServer = class {
|
|
|
546
546
|
this.hostnameCache.set(host, { value: result, expiresAt: now + this.hostnameCacheTtlMs });
|
|
547
547
|
return result;
|
|
548
548
|
}
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
549
|
+
/**
|
|
550
|
+
* Resolve the environment a request targets: explicit id → tenant hostname
|
|
551
|
+
* → `X-Environment-Id` header → single-project default. Returns undefined
|
|
552
|
+
* for control-plane requests. Shared by every per-environment service
|
|
553
|
+
* resolution (protocol, analytics, …) so they can never disagree about
|
|
554
|
+
* which kernel a request belongs to.
|
|
555
|
+
*/
|
|
556
|
+
async resolveRequestEnvironmentId(environmentId, req) {
|
|
557
|
+
if (environmentId) return environmentId;
|
|
558
|
+
if (req && this.envRegistry && this.kernelManager) {
|
|
552
559
|
const host = this.extractHostname(req);
|
|
553
560
|
if (host) {
|
|
554
561
|
try {
|
|
555
562
|
const result = await this.resolveHostnameCached(host);
|
|
556
|
-
if (result?.environmentId)
|
|
563
|
+
if (result?.environmentId) return result.environmentId;
|
|
557
564
|
} catch {
|
|
558
565
|
}
|
|
559
566
|
}
|
|
560
|
-
if (
|
|
567
|
+
if (typeof this.envRegistry.resolveById === "function") {
|
|
561
568
|
const headerVal = this.extractProjectIdHeader(req);
|
|
562
569
|
if (headerVal) {
|
|
563
570
|
try {
|
|
564
571
|
const driver = await this.envRegistry.resolveById(headerVal);
|
|
565
|
-
if (driver)
|
|
572
|
+
if (driver) return headerVal;
|
|
566
573
|
} catch {
|
|
567
574
|
}
|
|
568
575
|
}
|
|
569
576
|
}
|
|
570
577
|
}
|
|
571
|
-
if (
|
|
578
|
+
if (this.defaultEnvironmentIdProvider) {
|
|
572
579
|
try {
|
|
573
580
|
const def = this.defaultEnvironmentIdProvider();
|
|
574
|
-
if (def)
|
|
581
|
+
if (def) return def;
|
|
575
582
|
} catch {
|
|
576
583
|
}
|
|
577
584
|
}
|
|
578
|
-
|
|
579
|
-
|
|
585
|
+
return void 0;
|
|
586
|
+
}
|
|
587
|
+
async resolveProtocol(environmentId, req) {
|
|
588
|
+
if (environmentId === "platform") return this.protocol;
|
|
589
|
+
const envId = await this.resolveRequestEnvironmentId(environmentId, req);
|
|
590
|
+
if (!envId || !this.kernelManager) return this.protocol;
|
|
591
|
+
const kernel = await this.kernelManager.getOrCreate(envId);
|
|
580
592
|
return kernel.getServiceAsync("protocol");
|
|
581
593
|
}
|
|
582
594
|
/**
|
|
@@ -2971,7 +2983,16 @@ var RestServer = class {
|
|
|
2971
2983
|
*/
|
|
2972
2984
|
registerAnalyticsEndpoints(basePath) {
|
|
2973
2985
|
const isScoped = basePath.includes("/environments/:environmentId");
|
|
2974
|
-
const resolveService = async (environmentId) => {
|
|
2986
|
+
const resolveService = async (environmentId, req) => {
|
|
2987
|
+
try {
|
|
2988
|
+
const envId = await this.resolveRequestEnvironmentId(environmentId, req);
|
|
2989
|
+
if (envId && envId !== "platform" && this.kernelManager) {
|
|
2990
|
+
const kernel = await this.kernelManager.getOrCreate(envId);
|
|
2991
|
+
const svc = await kernel.getServiceAsync("analytics").catch(() => void 0);
|
|
2992
|
+
if (svc) return svc;
|
|
2993
|
+
}
|
|
2994
|
+
} catch {
|
|
2995
|
+
}
|
|
2975
2996
|
if (!this.analyticsServiceProvider) return void 0;
|
|
2976
2997
|
try {
|
|
2977
2998
|
return await this.analyticsServiceProvider(environmentId);
|
|
@@ -2987,7 +3008,7 @@ var RestServer = class {
|
|
|
2987
3008
|
const environmentId = isScoped ? req.params?.environmentId : void 0;
|
|
2988
3009
|
const context = await this.resolveExecCtx(environmentId, req);
|
|
2989
3010
|
if (this.enforceAuth(req, res, context)) return;
|
|
2990
|
-
const svc = await resolveService(environmentId);
|
|
3011
|
+
const svc = await resolveService(environmentId, req);
|
|
2991
3012
|
if (!svc || typeof svc.queryDataset !== "function") {
|
|
2992
3013
|
return res.status(501).json({
|
|
2993
3014
|
code: "NOT_IMPLEMENTED",
|
|
@@ -3002,10 +3023,11 @@ var RestServer = class {
|
|
|
3002
3023
|
message: "body.selection.measures must be a non-empty array of measure names."
|
|
3003
3024
|
});
|
|
3004
3025
|
}
|
|
3026
|
+
const previewDrafts = body.previewDrafts === true || req.query?.preview === "draft";
|
|
3005
3027
|
let dataset = body.dataset;
|
|
3006
3028
|
if (!dataset && body.datasetName) {
|
|
3007
3029
|
const p = await this.resolveProtocol(environmentId, req);
|
|
3008
|
-
const items = await p.getMetaItems?.({ type: "dataset" }).catch(() => null);
|
|
3030
|
+
const items = await p.getMetaItems?.({ type: "dataset", previewDrafts }).catch(() => null);
|
|
3009
3031
|
const list = Array.isArray(items?.items) ? items.items : Array.isArray(items) ? items : [];
|
|
3010
3032
|
dataset = list.find((d) => d?.name === body.datasetName);
|
|
3011
3033
|
if (!dataset) {
|
|
@@ -3025,7 +3047,12 @@ var RestServer = class {
|
|
|
3025
3047
|
detail: String(verr?.message ?? verr).slice(0, 1e3)
|
|
3026
3048
|
});
|
|
3027
3049
|
}
|
|
3028
|
-
const result = await svc.queryDataset(
|
|
3050
|
+
const result = await svc.queryDataset(
|
|
3051
|
+
dataset,
|
|
3052
|
+
selection,
|
|
3053
|
+
context ?? void 0,
|
|
3054
|
+
previewDrafts ? { previewDrafts: true } : void 0
|
|
3055
|
+
);
|
|
3029
3056
|
res.json(result);
|
|
3030
3057
|
} catch (error) {
|
|
3031
3058
|
const msg = String(error?.message ?? error ?? "");
|
|
@@ -3653,6 +3680,34 @@ var RestServer = class {
|
|
|
3653
3680
|
};
|
|
3654
3681
|
decisionRoute("approve");
|
|
3655
3682
|
decisionRoute("reject");
|
|
3683
|
+
this.routeManager.register({
|
|
3684
|
+
method: "POST",
|
|
3685
|
+
path: `${dataPath}/approvals/requests/:id/recall`,
|
|
3686
|
+
handler: async (req, res) => {
|
|
3687
|
+
try {
|
|
3688
|
+
const environmentId = isScoped ? req.params?.environmentId : void 0;
|
|
3689
|
+
const context = await this.resolveExecCtx(environmentId, req);
|
|
3690
|
+
if (this.enforceAuth(req, res, context)) return;
|
|
3691
|
+
const svc = await resolveService(environmentId);
|
|
3692
|
+
if (!svc || typeof svc.recall !== "function") return respond501(res);
|
|
3693
|
+
const body = req.body ?? {};
|
|
3694
|
+
try {
|
|
3695
|
+
const out = await svc.recall(req.params.id, {
|
|
3696
|
+
actorId: body.actorId ?? body.actor_id ?? context?.userId,
|
|
3697
|
+
comment: body.comment
|
|
3698
|
+
}, context ?? {});
|
|
3699
|
+
res.json(out);
|
|
3700
|
+
} catch (err) {
|
|
3701
|
+
if (handleApprovalError(res, err)) return;
|
|
3702
|
+
throw err;
|
|
3703
|
+
}
|
|
3704
|
+
} catch (error) {
|
|
3705
|
+
logError("[REST] recall approval error:", error);
|
|
3706
|
+
res.status(500).json({ code: "APPROVAL_RECALL_FAILED", error: String(error?.message ?? error).slice(0, 500) });
|
|
3707
|
+
}
|
|
3708
|
+
},
|
|
3709
|
+
metadata: { summary: "Recall (withdraw) an approval request", tags: ["approvals"] }
|
|
3710
|
+
});
|
|
3656
3711
|
this.routeManager.register({
|
|
3657
3712
|
method: "GET",
|
|
3658
3713
|
path: `${dataPath}/approvals/requests/:id/actions`,
|