@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.d.cts
CHANGED
|
@@ -268,6 +268,14 @@ declare class RestServer {
|
|
|
268
268
|
* next request.
|
|
269
269
|
*/
|
|
270
270
|
private resolveHostnameCached;
|
|
271
|
+
/**
|
|
272
|
+
* Resolve the environment a request targets: explicit id → tenant hostname
|
|
273
|
+
* → `X-Environment-Id` header → single-project default. Returns undefined
|
|
274
|
+
* for control-plane requests. Shared by every per-environment service
|
|
275
|
+
* resolution (protocol, analytics, …) so they can never disagree about
|
|
276
|
+
* which kernel a request belongs to.
|
|
277
|
+
*/
|
|
278
|
+
private resolveRequestEnvironmentId;
|
|
271
279
|
private resolveProtocol;
|
|
272
280
|
/**
|
|
273
281
|
* Resolve the i18n service for the request's project (or control plane
|
package/dist/index.d.ts
CHANGED
|
@@ -268,6 +268,14 @@ declare class RestServer {
|
|
|
268
268
|
* next request.
|
|
269
269
|
*/
|
|
270
270
|
private resolveHostnameCached;
|
|
271
|
+
/**
|
|
272
|
+
* Resolve the environment a request targets: explicit id → tenant hostname
|
|
273
|
+
* → `X-Environment-Id` header → single-project default. Returns undefined
|
|
274
|
+
* for control-plane requests. Shared by every per-environment service
|
|
275
|
+
* resolution (protocol, analytics, …) so they can never disagree about
|
|
276
|
+
* which kernel a request belongs to.
|
|
277
|
+
*/
|
|
278
|
+
private resolveRequestEnvironmentId;
|
|
271
279
|
private resolveProtocol;
|
|
272
280
|
/**
|
|
273
281
|
* Resolve the i18n service for the request's project (or control plane
|
package/dist/index.js
CHANGED
|
@@ -506,37 +506,49 @@ var RestServer = class {
|
|
|
506
506
|
this.hostnameCache.set(host, { value: result, expiresAt: now + this.hostnameCacheTtlMs });
|
|
507
507
|
return result;
|
|
508
508
|
}
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
509
|
+
/**
|
|
510
|
+
* Resolve the environment a request targets: explicit id → tenant hostname
|
|
511
|
+
* → `X-Environment-Id` header → single-project default. Returns undefined
|
|
512
|
+
* for control-plane requests. Shared by every per-environment service
|
|
513
|
+
* resolution (protocol, analytics, …) so they can never disagree about
|
|
514
|
+
* which kernel a request belongs to.
|
|
515
|
+
*/
|
|
516
|
+
async resolveRequestEnvironmentId(environmentId, req) {
|
|
517
|
+
if (environmentId) return environmentId;
|
|
518
|
+
if (req && this.envRegistry && this.kernelManager) {
|
|
512
519
|
const host = this.extractHostname(req);
|
|
513
520
|
if (host) {
|
|
514
521
|
try {
|
|
515
522
|
const result = await this.resolveHostnameCached(host);
|
|
516
|
-
if (result?.environmentId)
|
|
523
|
+
if (result?.environmentId) return result.environmentId;
|
|
517
524
|
} catch {
|
|
518
525
|
}
|
|
519
526
|
}
|
|
520
|
-
if (
|
|
527
|
+
if (typeof this.envRegistry.resolveById === "function") {
|
|
521
528
|
const headerVal = this.extractProjectIdHeader(req);
|
|
522
529
|
if (headerVal) {
|
|
523
530
|
try {
|
|
524
531
|
const driver = await this.envRegistry.resolveById(headerVal);
|
|
525
|
-
if (driver)
|
|
532
|
+
if (driver) return headerVal;
|
|
526
533
|
} catch {
|
|
527
534
|
}
|
|
528
535
|
}
|
|
529
536
|
}
|
|
530
537
|
}
|
|
531
|
-
if (
|
|
538
|
+
if (this.defaultEnvironmentIdProvider) {
|
|
532
539
|
try {
|
|
533
540
|
const def = this.defaultEnvironmentIdProvider();
|
|
534
|
-
if (def)
|
|
541
|
+
if (def) return def;
|
|
535
542
|
} catch {
|
|
536
543
|
}
|
|
537
544
|
}
|
|
538
|
-
|
|
539
|
-
|
|
545
|
+
return void 0;
|
|
546
|
+
}
|
|
547
|
+
async resolveProtocol(environmentId, req) {
|
|
548
|
+
if (environmentId === "platform") return this.protocol;
|
|
549
|
+
const envId = await this.resolveRequestEnvironmentId(environmentId, req);
|
|
550
|
+
if (!envId || !this.kernelManager) return this.protocol;
|
|
551
|
+
const kernel = await this.kernelManager.getOrCreate(envId);
|
|
540
552
|
return kernel.getServiceAsync("protocol");
|
|
541
553
|
}
|
|
542
554
|
/**
|
|
@@ -2931,7 +2943,16 @@ var RestServer = class {
|
|
|
2931
2943
|
*/
|
|
2932
2944
|
registerAnalyticsEndpoints(basePath) {
|
|
2933
2945
|
const isScoped = basePath.includes("/environments/:environmentId");
|
|
2934
|
-
const resolveService = async (environmentId) => {
|
|
2946
|
+
const resolveService = async (environmentId, req) => {
|
|
2947
|
+
try {
|
|
2948
|
+
const envId = await this.resolveRequestEnvironmentId(environmentId, req);
|
|
2949
|
+
if (envId && envId !== "platform" && this.kernelManager) {
|
|
2950
|
+
const kernel = await this.kernelManager.getOrCreate(envId);
|
|
2951
|
+
const svc = await kernel.getServiceAsync("analytics").catch(() => void 0);
|
|
2952
|
+
if (svc) return svc;
|
|
2953
|
+
}
|
|
2954
|
+
} catch {
|
|
2955
|
+
}
|
|
2935
2956
|
if (!this.analyticsServiceProvider) return void 0;
|
|
2936
2957
|
try {
|
|
2937
2958
|
return await this.analyticsServiceProvider(environmentId);
|
|
@@ -2947,7 +2968,7 @@ var RestServer = class {
|
|
|
2947
2968
|
const environmentId = isScoped ? req.params?.environmentId : void 0;
|
|
2948
2969
|
const context = await this.resolveExecCtx(environmentId, req);
|
|
2949
2970
|
if (this.enforceAuth(req, res, context)) return;
|
|
2950
|
-
const svc = await resolveService(environmentId);
|
|
2971
|
+
const svc = await resolveService(environmentId, req);
|
|
2951
2972
|
if (!svc || typeof svc.queryDataset !== "function") {
|
|
2952
2973
|
return res.status(501).json({
|
|
2953
2974
|
code: "NOT_IMPLEMENTED",
|
|
@@ -2962,10 +2983,11 @@ var RestServer = class {
|
|
|
2962
2983
|
message: "body.selection.measures must be a non-empty array of measure names."
|
|
2963
2984
|
});
|
|
2964
2985
|
}
|
|
2986
|
+
const previewDrafts = body.previewDrafts === true || req.query?.preview === "draft";
|
|
2965
2987
|
let dataset = body.dataset;
|
|
2966
2988
|
if (!dataset && body.datasetName) {
|
|
2967
2989
|
const p = await this.resolveProtocol(environmentId, req);
|
|
2968
|
-
const items = await p.getMetaItems?.({ type: "dataset" }).catch(() => null);
|
|
2990
|
+
const items = await p.getMetaItems?.({ type: "dataset", previewDrafts }).catch(() => null);
|
|
2969
2991
|
const list = Array.isArray(items?.items) ? items.items : Array.isArray(items) ? items : [];
|
|
2970
2992
|
dataset = list.find((d) => d?.name === body.datasetName);
|
|
2971
2993
|
if (!dataset) {
|
|
@@ -2985,7 +3007,12 @@ var RestServer = class {
|
|
|
2985
3007
|
detail: String(verr?.message ?? verr).slice(0, 1e3)
|
|
2986
3008
|
});
|
|
2987
3009
|
}
|
|
2988
|
-
const result = await svc.queryDataset(
|
|
3010
|
+
const result = await svc.queryDataset(
|
|
3011
|
+
dataset,
|
|
3012
|
+
selection,
|
|
3013
|
+
context ?? void 0,
|
|
3014
|
+
previewDrafts ? { previewDrafts: true } : void 0
|
|
3015
|
+
);
|
|
2989
3016
|
res.json(result);
|
|
2990
3017
|
} catch (error) {
|
|
2991
3018
|
const msg = String(error?.message ?? error ?? "");
|
|
@@ -3613,6 +3640,34 @@ var RestServer = class {
|
|
|
3613
3640
|
};
|
|
3614
3641
|
decisionRoute("approve");
|
|
3615
3642
|
decisionRoute("reject");
|
|
3643
|
+
this.routeManager.register({
|
|
3644
|
+
method: "POST",
|
|
3645
|
+
path: `${dataPath}/approvals/requests/:id/recall`,
|
|
3646
|
+
handler: async (req, res) => {
|
|
3647
|
+
try {
|
|
3648
|
+
const environmentId = isScoped ? req.params?.environmentId : void 0;
|
|
3649
|
+
const context = await this.resolveExecCtx(environmentId, req);
|
|
3650
|
+
if (this.enforceAuth(req, res, context)) return;
|
|
3651
|
+
const svc = await resolveService(environmentId);
|
|
3652
|
+
if (!svc || typeof svc.recall !== "function") return respond501(res);
|
|
3653
|
+
const body = req.body ?? {};
|
|
3654
|
+
try {
|
|
3655
|
+
const out = await svc.recall(req.params.id, {
|
|
3656
|
+
actorId: body.actorId ?? body.actor_id ?? context?.userId,
|
|
3657
|
+
comment: body.comment
|
|
3658
|
+
}, context ?? {});
|
|
3659
|
+
res.json(out);
|
|
3660
|
+
} catch (err) {
|
|
3661
|
+
if (handleApprovalError(res, err)) return;
|
|
3662
|
+
throw err;
|
|
3663
|
+
}
|
|
3664
|
+
} catch (error) {
|
|
3665
|
+
logError("[REST] recall approval error:", error);
|
|
3666
|
+
res.status(500).json({ code: "APPROVAL_RECALL_FAILED", error: String(error?.message ?? error).slice(0, 500) });
|
|
3667
|
+
}
|
|
3668
|
+
},
|
|
3669
|
+
metadata: { summary: "Recall (withdraw) an approval request", tags: ["approvals"] }
|
|
3670
|
+
});
|
|
3616
3671
|
this.routeManager.register({
|
|
3617
3672
|
method: "GET",
|
|
3618
3673
|
path: `${dataPath}/approvals/requests/:id/actions`,
|