@objectstack/rest 7.3.0 → 7.4.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 +105 -151
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -9
- package/dist/index.d.ts +7 -9
- package/dist/index.js +105 -151
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.d.cts
CHANGED
|
@@ -521,19 +521,17 @@ declare class RestServer {
|
|
|
521
521
|
*/
|
|
522
522
|
private registerReportsEndpoints;
|
|
523
523
|
/**
|
|
524
|
-
* Register approval
|
|
524
|
+
* Register approval endpoints (ADR-0019: approval as a flow node).
|
|
525
|
+
*
|
|
526
|
+
* Approval is no longer a standalone process engine — a flow's Approval
|
|
527
|
+
* node opens a request and suspends the run; a decision resumes it. There
|
|
528
|
+
* are no process-authoring or submit routes anymore.
|
|
525
529
|
*
|
|
526
530
|
* Routes (all under {basePath}/approvals):
|
|
527
|
-
* GET /processes — list approval processes
|
|
528
|
-
* POST /processes — upsert (defineProcess)
|
|
529
|
-
* GET /processes/:id — get by id or name
|
|
530
|
-
* DELETE /processes/:id — delete process
|
|
531
|
-
* POST /requests — submit
|
|
532
531
|
* GET /requests — list (filters: status, object, recordId, approverId, submitterId)
|
|
533
532
|
* GET /requests/:id — get request
|
|
534
|
-
* POST /requests/:id/approve — approve
|
|
535
|
-
* POST /requests/:id/reject — reject
|
|
536
|
-
* POST /requests/:id/recall — recall (submitter only)
|
|
533
|
+
* POST /requests/:id/approve — record an approve decision (resumes the flow)
|
|
534
|
+
* POST /requests/:id/reject — record a reject decision (resumes the flow)
|
|
537
535
|
* GET /requests/:id/actions — audit trail
|
|
538
536
|
*
|
|
539
537
|
* Returns 501 when `approvalsServiceProvider` is unset so deployments
|
package/dist/index.d.ts
CHANGED
|
@@ -521,19 +521,17 @@ declare class RestServer {
|
|
|
521
521
|
*/
|
|
522
522
|
private registerReportsEndpoints;
|
|
523
523
|
/**
|
|
524
|
-
* Register approval
|
|
524
|
+
* Register approval endpoints (ADR-0019: approval as a flow node).
|
|
525
|
+
*
|
|
526
|
+
* Approval is no longer a standalone process engine — a flow's Approval
|
|
527
|
+
* node opens a request and suspends the run; a decision resumes it. There
|
|
528
|
+
* are no process-authoring or submit routes anymore.
|
|
525
529
|
*
|
|
526
530
|
* Routes (all under {basePath}/approvals):
|
|
527
|
-
* GET /processes — list approval processes
|
|
528
|
-
* POST /processes — upsert (defineProcess)
|
|
529
|
-
* GET /processes/:id — get by id or name
|
|
530
|
-
* DELETE /processes/:id — delete process
|
|
531
|
-
* POST /requests — submit
|
|
532
531
|
* GET /requests — list (filters: status, object, recordId, approverId, submitterId)
|
|
533
532
|
* GET /requests/:id — get request
|
|
534
|
-
* POST /requests/:id/approve — approve
|
|
535
|
-
* POST /requests/:id/reject — reject
|
|
536
|
-
* POST /requests/:id/recall — recall (submitter only)
|
|
533
|
+
* POST /requests/:id/approve — record an approve decision (resumes the flow)
|
|
534
|
+
* POST /requests/:id/reject — record a reject decision (resumes the flow)
|
|
537
535
|
* GET /requests/:id/actions — audit trail
|
|
538
536
|
*
|
|
539
537
|
* Returns 501 when `approvalsServiceProvider` is unset so deployments
|
package/dist/index.js
CHANGED
|
@@ -210,6 +210,7 @@ var RouteGroupBuilder = class {
|
|
|
210
210
|
|
|
211
211
|
// src/rest-server.ts
|
|
212
212
|
var logError = (...args) => globalThis.console?.error(...args);
|
|
213
|
+
var TRANSLATABLE_META_TYPES = /* @__PURE__ */ new Set(["view", "action", "object", "app", "dashboard"]);
|
|
213
214
|
function mapDataError(error, object) {
|
|
214
215
|
if (error?.code === "CONCURRENT_UPDATE" || error?.name === "ConcurrentUpdateError") {
|
|
215
216
|
return {
|
|
@@ -819,10 +820,10 @@ var RestServer = class {
|
|
|
819
820
|
* locale yields a match. Falls through unchanged for unsupported types
|
|
820
821
|
* or missing translations.
|
|
821
822
|
*/
|
|
822
|
-
async translateMetaItem(req, type, environmentId, item) {
|
|
823
|
+
async translateMetaItem(req, type, environmentId, item, i18nService) {
|
|
823
824
|
if (!item || typeof item !== "object") return item;
|
|
824
|
-
if (type
|
|
825
|
-
const i18n = await this.resolveI18nService(environmentId, req);
|
|
825
|
+
if (!TRANSLATABLE_META_TYPES.has(type)) return item;
|
|
826
|
+
const i18n = i18nService !== void 0 ? i18nService : await this.resolveI18nService(environmentId, req);
|
|
826
827
|
const bundle = this.buildTranslationBundle(i18n);
|
|
827
828
|
if (!bundle) return item;
|
|
828
829
|
const locale = this.extractLocale(req, i18n);
|
|
@@ -835,7 +836,7 @@ var RestServer = class {
|
|
|
835
836
|
*/
|
|
836
837
|
async translateMetaItems(req, type, environmentId, items) {
|
|
837
838
|
if (!Array.isArray(items)) return items;
|
|
838
|
-
if (type
|
|
839
|
+
if (!TRANSLATABLE_META_TYPES.has(type)) return items;
|
|
839
840
|
const i18n = await this.resolveI18nService(environmentId, req);
|
|
840
841
|
const bundle = this.buildTranslationBundle(i18n);
|
|
841
842
|
if (!bundle) return items;
|
|
@@ -1338,6 +1339,15 @@ var RestServer = class {
|
|
|
1338
1339
|
}
|
|
1339
1340
|
}
|
|
1340
1341
|
}
|
|
1342
|
+
if (req.params.type === "view" && req.query?.object) {
|
|
1343
|
+
const obj = String(req.query.object);
|
|
1344
|
+
const raw = visible;
|
|
1345
|
+
const list = Array.isArray(raw) ? raw : raw && typeof raw === "object" && Array.isArray(raw.items) ? raw.items : null;
|
|
1346
|
+
if (list) {
|
|
1347
|
+
const filtered = list.filter((v) => v && typeof v === "object" && v.viewKind && v.object === obj).sort((a, b) => (a.order ?? 0) - (b.order ?? 0) || String(a.name).localeCompare(String(b.name)));
|
|
1348
|
+
visible = Array.isArray(raw) ? filtered : { ...raw, items: filtered };
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1341
1351
|
const translated = await this.translateMetaItems(req, req.params.type, environmentId, visible);
|
|
1342
1352
|
res.header("Vary", "Accept-Language");
|
|
1343
1353
|
res.json(translated);
|
|
@@ -1404,10 +1414,13 @@ var RestServer = class {
|
|
|
1404
1414
|
ifNoneMatch: req.headers["if-none-match"],
|
|
1405
1415
|
ifModifiedSince: req.headers["if-modified-since"]
|
|
1406
1416
|
};
|
|
1417
|
+
const cacheI18n = await this.resolveI18nService(environmentId, req);
|
|
1418
|
+
const cacheLocale = this.extractLocale(req, cacheI18n);
|
|
1407
1419
|
const result = await p.getMetaItemCached({
|
|
1408
1420
|
type: req.params.type,
|
|
1409
1421
|
name: req.params.name,
|
|
1410
1422
|
cacheRequest,
|
|
1423
|
+
...cacheLocale ? { locale: cacheLocale } : {},
|
|
1411
1424
|
...environmentId ? { environmentId } : {}
|
|
1412
1425
|
});
|
|
1413
1426
|
if (result.notModified) {
|
|
@@ -1427,7 +1440,7 @@ var RestServer = class {
|
|
|
1427
1440
|
res.header("Cache-Control", directives + maxAge);
|
|
1428
1441
|
}
|
|
1429
1442
|
res.header("Vary", "Accept-Language");
|
|
1430
|
-
res.json(await this.translateMetaItem(req, req.params.type, environmentId, result.data));
|
|
1443
|
+
res.json(await this.translateMetaItem(req, req.params.type, environmentId, result.data, cacheI18n));
|
|
1431
1444
|
} else {
|
|
1432
1445
|
const packageId = req.query?.package || void 0;
|
|
1433
1446
|
const stateParam = typeof req.query?.state === "string" ? req.query.state.toLowerCase() : void 0;
|
|
@@ -3263,19 +3276,17 @@ var RestServer = class {
|
|
|
3263
3276
|
});
|
|
3264
3277
|
}
|
|
3265
3278
|
/**
|
|
3266
|
-
* Register approval
|
|
3279
|
+
* Register approval endpoints (ADR-0019: approval as a flow node).
|
|
3280
|
+
*
|
|
3281
|
+
* Approval is no longer a standalone process engine — a flow's Approval
|
|
3282
|
+
* node opens a request and suspends the run; a decision resumes it. There
|
|
3283
|
+
* are no process-authoring or submit routes anymore.
|
|
3267
3284
|
*
|
|
3268
3285
|
* Routes (all under {basePath}/approvals):
|
|
3269
|
-
* GET /processes — list approval processes
|
|
3270
|
-
* POST /processes — upsert (defineProcess)
|
|
3271
|
-
* GET /processes/:id — get by id or name
|
|
3272
|
-
* DELETE /processes/:id — delete process
|
|
3273
|
-
* POST /requests — submit
|
|
3274
3286
|
* GET /requests — list (filters: status, object, recordId, approverId, submitterId)
|
|
3275
3287
|
* GET /requests/:id — get request
|
|
3276
|
-
* POST /requests/:id/approve — approve
|
|
3277
|
-
* POST /requests/:id/reject — reject
|
|
3278
|
-
* POST /requests/:id/recall — recall (submitter only)
|
|
3288
|
+
* POST /requests/:id/approve — record an approve decision (resumes the flow)
|
|
3289
|
+
* POST /requests/:id/reject — record a reject decision (resumes the flow)
|
|
3279
3290
|
* GET /requests/:id/actions — audit trail
|
|
3280
3291
|
*
|
|
3281
3292
|
* Returns 501 when `approvalsServiceProvider` is unset so deployments
|
|
@@ -3303,8 +3314,6 @@ var RestServer = class {
|
|
|
3303
3314
|
[/^DUPLICATE_REQUEST/, 409, "DUPLICATE_REQUEST"],
|
|
3304
3315
|
[/^INVALID_STATE/, 409, "INVALID_STATE"],
|
|
3305
3316
|
[/^FORBIDDEN/, 403, "FORBIDDEN"],
|
|
3306
|
-
[/^NO_ACTIVE_PROCESS/, 404, "NO_ACTIVE_PROCESS"],
|
|
3307
|
-
[/^PROCESS_NOT_FOUND/, 404, "PROCESS_NOT_FOUND"],
|
|
3308
3317
|
[/^REQUEST_NOT_FOUND/, 404, "REQUEST_NOT_FOUND"]
|
|
3309
3318
|
];
|
|
3310
3319
|
for (const [re, status, code] of mapping) {
|
|
@@ -3315,127 +3324,6 @@ var RestServer = class {
|
|
|
3315
3324
|
}
|
|
3316
3325
|
return false;
|
|
3317
3326
|
};
|
|
3318
|
-
this.routeManager.register({
|
|
3319
|
-
method: "GET",
|
|
3320
|
-
path: `${dataPath}/approvals/processes`,
|
|
3321
|
-
handler: async (req, res) => {
|
|
3322
|
-
try {
|
|
3323
|
-
const environmentId = isScoped ? req.params?.environmentId : void 0;
|
|
3324
|
-
const context = await this.resolveExecCtx(environmentId, req);
|
|
3325
|
-
if (this.enforceAuth(req, res, context)) return;
|
|
3326
|
-
const svc = await resolveService(environmentId);
|
|
3327
|
-
if (!svc) return respond501(res);
|
|
3328
|
-
const q = req.query ?? {};
|
|
3329
|
-
const rows = await svc.listProcesses({
|
|
3330
|
-
object: q.object,
|
|
3331
|
-
activeOnly: q.activeOnly === "true" || q.activeOnly === true
|
|
3332
|
-
}, context ?? {});
|
|
3333
|
-
res.json({ data: rows });
|
|
3334
|
-
} catch (error) {
|
|
3335
|
-
logError("[REST] List approval processes error:", error);
|
|
3336
|
-
res.status(500).json({ code: "APPROVAL_PROCESS_LIST_FAILED", error: String(error?.message ?? error).slice(0, 500) });
|
|
3337
|
-
}
|
|
3338
|
-
},
|
|
3339
|
-
metadata: { summary: "List approval processes", tags: ["approvals"] }
|
|
3340
|
-
});
|
|
3341
|
-
this.routeManager.register({
|
|
3342
|
-
method: "POST",
|
|
3343
|
-
path: `${dataPath}/approvals/processes`,
|
|
3344
|
-
handler: async (req, res) => {
|
|
3345
|
-
try {
|
|
3346
|
-
const environmentId = isScoped ? req.params?.environmentId : void 0;
|
|
3347
|
-
const context = await this.resolveExecCtx(environmentId, req);
|
|
3348
|
-
if (this.enforceAuth(req, res, context)) return;
|
|
3349
|
-
const svc = await resolveService(environmentId);
|
|
3350
|
-
if (!svc) return respond501(res);
|
|
3351
|
-
try {
|
|
3352
|
-
const row = await svc.defineProcess(req.body ?? {}, context ?? {});
|
|
3353
|
-
res.status(201).json(row);
|
|
3354
|
-
} catch (err) {
|
|
3355
|
-
if (handleApprovalError(res, err)) return;
|
|
3356
|
-
throw err;
|
|
3357
|
-
}
|
|
3358
|
-
} catch (error) {
|
|
3359
|
-
logError("[REST] Define approval process error:", error);
|
|
3360
|
-
res.status(500).json({ code: "APPROVAL_PROCESS_DEFINE_FAILED", error: String(error?.message ?? error).slice(0, 500) });
|
|
3361
|
-
}
|
|
3362
|
-
},
|
|
3363
|
-
metadata: { summary: "Define (upsert) an approval process", tags: ["approvals"] }
|
|
3364
|
-
});
|
|
3365
|
-
this.routeManager.register({
|
|
3366
|
-
method: "GET",
|
|
3367
|
-
path: `${dataPath}/approvals/processes/:id`,
|
|
3368
|
-
handler: async (req, res) => {
|
|
3369
|
-
try {
|
|
3370
|
-
const environmentId = isScoped ? req.params?.environmentId : void 0;
|
|
3371
|
-
const context = await this.resolveExecCtx(environmentId, req);
|
|
3372
|
-
if (this.enforceAuth(req, res, context)) return;
|
|
3373
|
-
const svc = await resolveService(environmentId);
|
|
3374
|
-
if (!svc) return respond501(res);
|
|
3375
|
-
const row = await svc.getProcess(req.params.id, context ?? {});
|
|
3376
|
-
if (!row) {
|
|
3377
|
-
res.status(404).json({ code: "PROCESS_NOT_FOUND", error: `Approval process '${req.params.id}' not found` });
|
|
3378
|
-
return;
|
|
3379
|
-
}
|
|
3380
|
-
res.json(row);
|
|
3381
|
-
} catch (error) {
|
|
3382
|
-
logError("[REST] Get approval process error:", error);
|
|
3383
|
-
res.status(500).json({ code: "APPROVAL_PROCESS_GET_FAILED", error: String(error?.message ?? error).slice(0, 500) });
|
|
3384
|
-
}
|
|
3385
|
-
},
|
|
3386
|
-
metadata: { summary: "Get an approval process by id or name", tags: ["approvals"] }
|
|
3387
|
-
});
|
|
3388
|
-
this.routeManager.register({
|
|
3389
|
-
method: "DELETE",
|
|
3390
|
-
path: `${dataPath}/approvals/processes/:id`,
|
|
3391
|
-
handler: async (req, res) => {
|
|
3392
|
-
try {
|
|
3393
|
-
const environmentId = isScoped ? req.params?.environmentId : void 0;
|
|
3394
|
-
const context = await this.resolveExecCtx(environmentId, req);
|
|
3395
|
-
if (this.enforceAuth(req, res, context)) return;
|
|
3396
|
-
const svc = await resolveService(environmentId);
|
|
3397
|
-
if (!svc) return respond501(res);
|
|
3398
|
-
await svc.deleteProcess(req.params.id, context ?? {});
|
|
3399
|
-
res.status(204).end();
|
|
3400
|
-
} catch (error) {
|
|
3401
|
-
logError("[REST] Delete approval process error:", error);
|
|
3402
|
-
res.status(500).json({ code: "APPROVAL_PROCESS_DELETE_FAILED", error: String(error?.message ?? error).slice(0, 500) });
|
|
3403
|
-
}
|
|
3404
|
-
},
|
|
3405
|
-
metadata: { summary: "Delete an approval process", tags: ["approvals"] }
|
|
3406
|
-
});
|
|
3407
|
-
this.routeManager.register({
|
|
3408
|
-
method: "POST",
|
|
3409
|
-
path: `${dataPath}/approvals/requests`,
|
|
3410
|
-
handler: async (req, res) => {
|
|
3411
|
-
try {
|
|
3412
|
-
const environmentId = isScoped ? req.params?.environmentId : void 0;
|
|
3413
|
-
const context = await this.resolveExecCtx(environmentId, req);
|
|
3414
|
-
if (this.enforceAuth(req, res, context)) return;
|
|
3415
|
-
const svc = await resolveService(environmentId);
|
|
3416
|
-
if (!svc) return respond501(res);
|
|
3417
|
-
const body = req.body ?? {};
|
|
3418
|
-
try {
|
|
3419
|
-
const row = await svc.submit({
|
|
3420
|
-
object: body.object,
|
|
3421
|
-
recordId: body.recordId ?? body.record_id,
|
|
3422
|
-
processName: body.processName ?? body.process_name,
|
|
3423
|
-
submitterId: body.submitterId ?? body.submitter_id ?? context?.userId,
|
|
3424
|
-
comment: body.comment,
|
|
3425
|
-
payload: body.payload
|
|
3426
|
-
}, context ?? {});
|
|
3427
|
-
res.status(201).json(row);
|
|
3428
|
-
} catch (err) {
|
|
3429
|
-
if (handleApprovalError(res, err)) return;
|
|
3430
|
-
throw err;
|
|
3431
|
-
}
|
|
3432
|
-
} catch (error) {
|
|
3433
|
-
logError("[REST] Submit approval error:", error);
|
|
3434
|
-
res.status(500).json({ code: "APPROVAL_SUBMIT_FAILED", error: String(error?.message ?? error).slice(0, 500) });
|
|
3435
|
-
}
|
|
3436
|
-
},
|
|
3437
|
-
metadata: { summary: "Submit a record for approval", tags: ["approvals"] }
|
|
3438
|
-
});
|
|
3439
3327
|
this.routeManager.register({
|
|
3440
3328
|
method: "GET",
|
|
3441
3329
|
path: `${dataPath}/approvals/requests`,
|
|
@@ -3488,10 +3376,10 @@ var RestServer = class {
|
|
|
3488
3376
|
},
|
|
3489
3377
|
metadata: { summary: "Get an approval request by id", tags: ["approvals"] }
|
|
3490
3378
|
});
|
|
3491
|
-
const decisionRoute = (
|
|
3379
|
+
const decisionRoute = (decision) => {
|
|
3492
3380
|
this.routeManager.register({
|
|
3493
3381
|
method: "POST",
|
|
3494
|
-
path: `${dataPath}/approvals/requests/:id/${
|
|
3382
|
+
path: `${dataPath}/approvals/requests/:id/${decision}`,
|
|
3495
3383
|
handler: async (req, res) => {
|
|
3496
3384
|
try {
|
|
3497
3385
|
const environmentId = isScoped ? req.params?.environmentId : void 0;
|
|
@@ -3501,7 +3389,8 @@ var RestServer = class {
|
|
|
3501
3389
|
if (!svc) return respond501(res);
|
|
3502
3390
|
const body = req.body ?? {};
|
|
3503
3391
|
try {
|
|
3504
|
-
const out = await svc
|
|
3392
|
+
const out = await svc.decide(req.params.id, {
|
|
3393
|
+
decision,
|
|
3505
3394
|
actorId: body.actorId ?? body.actor_id ?? context?.userId,
|
|
3506
3395
|
comment: body.comment
|
|
3507
3396
|
}, context ?? {});
|
|
@@ -3511,16 +3400,15 @@ var RestServer = class {
|
|
|
3511
3400
|
throw err;
|
|
3512
3401
|
}
|
|
3513
3402
|
} catch (error) {
|
|
3514
|
-
logError(`[REST] ${
|
|
3515
|
-
res.status(500).json({ code: `APPROVAL_${
|
|
3403
|
+
logError(`[REST] ${decision} approval error:`, error);
|
|
3404
|
+
res.status(500).json({ code: `APPROVAL_${decision.toUpperCase()}_FAILED`, error: String(error?.message ?? error).slice(0, 500) });
|
|
3516
3405
|
}
|
|
3517
3406
|
},
|
|
3518
|
-
metadata: { summary: `${
|
|
3407
|
+
metadata: { summary: `${decision[0].toUpperCase()}${decision.slice(1)} an approval request`, tags: ["approvals"] }
|
|
3519
3408
|
});
|
|
3520
3409
|
};
|
|
3521
|
-
decisionRoute("approve"
|
|
3522
|
-
decisionRoute("reject"
|
|
3523
|
-
decisionRoute("recall", "recall");
|
|
3410
|
+
decisionRoute("approve");
|
|
3411
|
+
decisionRoute("reject");
|
|
3524
3412
|
this.routeManager.register({
|
|
3525
3413
|
method: "GET",
|
|
3526
3414
|
path: `${dataPath}/approvals/requests/:id/actions`,
|
|
@@ -3793,6 +3681,66 @@ function registerPackageRoutes(server, packageService, basePath = "/api/v1", opt
|
|
|
3793
3681
|
});
|
|
3794
3682
|
}
|
|
3795
3683
|
|
|
3684
|
+
// src/external-datasource-routes.ts
|
|
3685
|
+
function registerExternalDatasourceRoutes(server, ctx, basePath = "/api/v1") {
|
|
3686
|
+
const ext = `${basePath}/datasources/:name/external`;
|
|
3687
|
+
const externalService = () => {
|
|
3688
|
+
try {
|
|
3689
|
+
return ctx.getService("external-datasource");
|
|
3690
|
+
} catch {
|
|
3691
|
+
return void 0;
|
|
3692
|
+
}
|
|
3693
|
+
};
|
|
3694
|
+
const unavailable = (res) => res.status(503).json({ error: "external_service_unavailable" });
|
|
3695
|
+
server.get(`${ext}/tables`, async (req, res) => {
|
|
3696
|
+
const svc = externalService();
|
|
3697
|
+
if (!svc?.listRemoteTables) return unavailable(res);
|
|
3698
|
+
const schema = typeof req.query?.schema === "string" ? req.query.schema : void 0;
|
|
3699
|
+
const tables = await svc.listRemoteTables(req.params.name, { schema });
|
|
3700
|
+
res.json({ tables });
|
|
3701
|
+
});
|
|
3702
|
+
server.post(`${ext}/tables/:remote/draft`, async (req, res) => {
|
|
3703
|
+
const svc = externalService();
|
|
3704
|
+
if (!svc?.generateObjectDraft) return unavailable(res);
|
|
3705
|
+
const draft = await svc.generateObjectDraft(
|
|
3706
|
+
req.params.name,
|
|
3707
|
+
req.params.remote,
|
|
3708
|
+
req.body ?? {}
|
|
3709
|
+
);
|
|
3710
|
+
res.json({ draft });
|
|
3711
|
+
});
|
|
3712
|
+
server.post(`${ext}/tables/:remote/import`, async (req, res) => {
|
|
3713
|
+
const svc = externalService();
|
|
3714
|
+
if (!svc?.importObject) return unavailable(res);
|
|
3715
|
+
try {
|
|
3716
|
+
const result = await svc.importObject(
|
|
3717
|
+
req.params.name,
|
|
3718
|
+
req.params.remote,
|
|
3719
|
+
req.body ?? {}
|
|
3720
|
+
);
|
|
3721
|
+
res.status(201).json({ object: result });
|
|
3722
|
+
} catch (err) {
|
|
3723
|
+
res.status(400).json({
|
|
3724
|
+
error: "external_import_error",
|
|
3725
|
+
message: err instanceof Error ? err.message : String(err)
|
|
3726
|
+
});
|
|
3727
|
+
}
|
|
3728
|
+
});
|
|
3729
|
+
server.post(`${ext}/refresh-catalog`, async (req, res) => {
|
|
3730
|
+
const svc = externalService();
|
|
3731
|
+
if (!svc?.refreshCatalog) return unavailable(res);
|
|
3732
|
+
const catalog = await svc.refreshCatalog(req.params.name);
|
|
3733
|
+
res.json({ catalog });
|
|
3734
|
+
});
|
|
3735
|
+
server.post(`${ext}/validate`, async (req, res) => {
|
|
3736
|
+
const svc = externalService();
|
|
3737
|
+
if (!svc?.validateAll) return unavailable(res);
|
|
3738
|
+
const report = await svc.validateAll();
|
|
3739
|
+
const results = (report.results ?? []).filter((r) => r.datasource === req.params.name);
|
|
3740
|
+
res.json({ ok: results.every((r) => r.ok), results });
|
|
3741
|
+
});
|
|
3742
|
+
}
|
|
3743
|
+
|
|
3796
3744
|
// src/rest-api-plugin.ts
|
|
3797
3745
|
function createRestApiPlugin(config = {}) {
|
|
3798
3746
|
return {
|
|
@@ -3905,14 +3853,14 @@ function createRestApiPlugin(config = {}) {
|
|
|
3905
3853
|
ctx.logger.error("Failed to register REST API routes", { error: err.message });
|
|
3906
3854
|
throw err;
|
|
3907
3855
|
}
|
|
3856
|
+
const basePath = config.api?.api?.basePath || "/api";
|
|
3857
|
+
const version = config.api?.api?.version || "v1";
|
|
3858
|
+
const versionedBase = `${basePath}/${version}`;
|
|
3859
|
+
const enableProjectScoping = config.api?.api?.enableProjectScoping ?? false;
|
|
3860
|
+
const projectResolution = config.api?.api?.projectResolution ?? "auto";
|
|
3908
3861
|
try {
|
|
3909
3862
|
const packageService = ctx.getService("package");
|
|
3910
3863
|
if (packageService) {
|
|
3911
|
-
const basePath = config.api?.api?.basePath || "/api";
|
|
3912
|
-
const version = config.api?.api?.version || "v1";
|
|
3913
|
-
const versionedBase = `${basePath}/${version}`;
|
|
3914
|
-
const enableProjectScoping = config.api?.api?.enableProjectScoping ?? false;
|
|
3915
|
-
const projectResolution = config.api?.api?.projectResolution ?? "auto";
|
|
3916
3864
|
if (enableProjectScoping && projectResolution === "required") {
|
|
3917
3865
|
registerPackageRoutes(server, packageService, `${versionedBase}/environments/:environmentId`, {
|
|
3918
3866
|
protocol
|
|
@@ -3930,6 +3878,12 @@ function createRestApiPlugin(config = {}) {
|
|
|
3930
3878
|
} catch (e) {
|
|
3931
3879
|
ctx.logger.debug("Package service not available, package routes skipped");
|
|
3932
3880
|
}
|
|
3881
|
+
try {
|
|
3882
|
+
registerExternalDatasourceRoutes(server, ctx, versionedBase);
|
|
3883
|
+
ctx.logger.info("Datasource federation routes registered");
|
|
3884
|
+
} catch (e) {
|
|
3885
|
+
ctx.logger.warn("Datasource federation routes registration failed", { error: e?.message });
|
|
3886
|
+
}
|
|
3933
3887
|
}
|
|
3934
3888
|
};
|
|
3935
3889
|
}
|