Skip to content

Commit 129b8c8

Browse files
author
Jurij Skornik
committed
[feat] Add Essentials /api/dkg/create endpoint and remove leaked sqlite file
Expose sync publish over HTTP with the same create logic as dkg-create MCP (including blob id support). Remove accidentally committed apps/agent/original_dkg_node file and add API coverage tests.
1 parent 1f0f1c6 commit 129b8c8

3 files changed

Lines changed: 125 additions & 14 deletions

File tree

apps/agent/original_dkg_node

-56 KB
Binary file not shown.

packages/plugin-dkg-essentials/src/plugins/dkg-tools.ts

Lines changed: 96 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,25 @@ export default defineDkgPlugin((ctx, mcp, api) => {
3535
}
3636
}
3737

38+
async function resolveJsonLdInput(jsonld: string): Promise<string> {
39+
if (!jsonld) {
40+
console.error("No JSON-LD content provided after file read.");
41+
throw new Error("No JSON-LD content provided.");
42+
}
43+
44+
if (jsonld.startsWith("{") || jsonld.startsWith("[")) {
45+
return jsonld;
46+
}
47+
48+
const blob = await ctx.blob.get(jsonld);
49+
if (!blob) {
50+
console.error(`File with id "${jsonld}" not found`);
51+
throw new Error(`File with id "${jsonld}" not found`);
52+
}
53+
54+
return consumers.text(blob.data);
55+
}
56+
3857
function validateSparqlInput(query: string): SparqlValidationResult {
3958
const validation = validateSparqlQuery(query);
4059
if (!validation.valid) {
@@ -191,21 +210,8 @@ export default defineDkgPlugin((ctx, mcp, api) => {
191210
},
192211
},
193212
async (input) => {
194-
if (!input.jsonld) {
195-
console.error("No JSON-LD content provided after file read.");
196-
throw new Error("No JSON-LD content provided.");
197-
}
198213
const privacy = input.privacy || "private";
199-
const content =
200-
input.jsonld.startsWith("{") || input.jsonld.startsWith("[")
201-
? input.jsonld
202-
: await ctx.blob.get(input.jsonld).then((r) => {
203-
if (!r) {
204-
console.error(`File with id "${input.jsonld}" not found`);
205-
throw new Error(`File with id "${input.jsonld}" not found`);
206-
}
207-
return consumers.text(r.data);
208-
});
214+
const content = await resolveJsonLdInput(input.jsonld);
209215

210216
const { ual, error } = await publishJsonLdAsset(content, privacy);
211217
if (error) {
@@ -306,6 +312,82 @@ export default defineDkgPlugin((ctx, mcp, api) => {
306312
},
307313
);
308314

315+
api.post(
316+
"/api/dkg/create",
317+
openAPIRoute(
318+
{
319+
tag: "DKG Publishing",
320+
summary: "Create and Publish DKG Asset",
321+
description:
322+
"Synchronously create and publish a Knowledge Asset on DKG from JSON-LD content or uploaded blob id.",
323+
body: z.object({
324+
jsonld: z
325+
.string()
326+
.describe("JSON-LD content or ID of an uploaded file"),
327+
privacy: z.enum(["private", "public"]).optional().default("private"),
328+
}),
329+
response: {
330+
schema: z.object({
331+
success: z.boolean(),
332+
data: z
333+
.object({
334+
ual: z.string(),
335+
explorerLink: z.string(),
336+
message: z.string(),
337+
})
338+
.optional(),
339+
error: z.string().optional(),
340+
}),
341+
},
342+
finalizeRouteConfig: (config) => ({
343+
...config,
344+
security: [],
345+
}),
346+
},
347+
async (req, res) => {
348+
try {
349+
const privacy = req.body.privacy || "private";
350+
const content = await resolveJsonLdInput(req.body.jsonld);
351+
352+
const { ual, error } = await publishJsonLdAsset(content, privacy);
353+
if (error) {
354+
console.error("Error creating asset:", error);
355+
return res.status(500).json({
356+
success: false,
357+
error: "Failed to create asset: " + error,
358+
});
359+
}
360+
361+
if (!ual) {
362+
return res.status(500).json({
363+
success: false,
364+
error: "Failed to create asset: missing UAL in response",
365+
});
366+
}
367+
368+
const explorerLink = getExplorerUrl(ual);
369+
const message = `Knowledge Asset collection successfully created.\n\nUAL: ${ual}\nDKG Explorer link: ${explorerLink}`;
370+
371+
return res.json({
372+
success: true,
373+
data: {
374+
ual,
375+
explorerLink,
376+
message,
377+
},
378+
});
379+
} catch (error) {
380+
const errorMessage =
381+
error instanceof Error ? error.message : String(error);
382+
return res.status(500).json({
383+
success: false,
384+
error: errorMessage,
385+
});
386+
}
387+
},
388+
),
389+
);
390+
309391
api.post(
310392
"/api/dkg/query",
311393
openAPIRoute(

packages/plugin-dkg-essentials/tests/dkg-tools.spec.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,35 @@ describe("@dkg/plugin-dkg-essentials checks", () => {
451451
});
452452

453453
describe("HTTP API Routes", () => {
454+
it("should expose POST /api/dkg/create from Essentials", async () => {
455+
const response = await request(app)
456+
.post("/api/dkg/create")
457+
.send({
458+
jsonld: JSON.stringify({
459+
"@context": "https://schema.org",
460+
"@type": "Organization",
461+
name: "HTTP Create Test Organization",
462+
}),
463+
privacy: "private",
464+
})
465+
.expect(200);
466+
467+
expect(response.body.success).to.equal(true);
468+
expect(response.body.data.ual).to.equal("did:dkg:otp:20430/0x123456/12345");
469+
expect(response.body.data.explorerLink).to.include(
470+
"https://dkg-testnet.origintrail.io/explore?ual=",
471+
);
472+
});
473+
474+
it("should return 400 for invalid POST /api/dkg/create input", async () => {
475+
await request(app)
476+
.post("/api/dkg/create")
477+
.send({
478+
privacy: "private",
479+
})
480+
.expect(400);
481+
});
482+
454483
it("should expose POST /api/dkg/query from Essentials", async () => {
455484
const response = await request(app)
456485
.post("/api/dkg/query")

0 commit comments

Comments
 (0)