Skip to content

Commit a939863

Browse files
committed
feat: enhance metadata handling in Hono, NestJS, and NextJS adapters to support PUT and POST requests with body parsing
1 parent 449589d commit a939863

4 files changed

Lines changed: 54 additions & 6 deletions

File tree

packages/adapters/hono/src/index.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,20 @@ export function createHonoApp(options: ObjectStackHonoOptions) {
7676
app.all(`${prefix}/metadata*`, async (c) => {
7777
try {
7878
const path = c.req.path.substring(c.req.path.indexOf('/metadata') + 9);
79-
const result = await dispatcher.handleMetadata(path, { request: c.req.raw });
79+
const method = c.req.method;
80+
let body = undefined;
81+
82+
if (method === 'PUT' || method === 'POST') {
83+
// Attempt to parse JSON body
84+
try {
85+
body = await c.req.json();
86+
} catch (e) {
87+
// Ignore parse errors, body remains undefined or empty
88+
body = {};
89+
}
90+
}
91+
92+
const result = await dispatcher.handleMetadata(path, { request: c.req.raw }, method, body);
8093
return normalizeResponse(c, result);
8194
} catch (err: any) {
8295
return c.json({ success: false, error: { message: err.message, code: err.statusCode || 500 } }, err.statusCode || 500);

packages/adapters/nestjs/src/index.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,14 +114,18 @@ export class ObjectStackController {
114114

115115
// Metadata
116116
@All('metadata*')
117-
async metadata(@Req() req: any, @Res() res: any) {
117+
async metadata(@Req() req: any, @Res() res: any, @Body() body?: any) {
118118
try {
119119
// /api/metadata/objects -> objects
120120
let path = req.params[0] || '';
121121
if (req.url.includes('/metadata')) {
122122
path = req.url.split('/metadata')[1].split('?')[0];
123123
}
124-
const result = await this.service.dispatcher.handleMetadata(path, { request: req });
124+
125+
// Use injected body or fallback to req.body
126+
const payload = body || req.body;
127+
128+
const result = await this.service.dispatcher.handleMetadata(path, { request: req }, req.method, payload);
125129
return this.normalizeResponse(result, res);
126130
} catch (err) {
127131
return this.handleError(err, res);

packages/adapters/nextjs/src/index.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,13 @@ export function createRouteHandler(options: NextAdapterOptions) {
7575
// --- 3. Metadata ---
7676
if (segments[0] === 'metadata') {
7777
const subPath = segments.slice(1).join('/');
78-
const result = await dispatcher.handleMetadata(subPath, { request: rawRequest });
78+
79+
let body: any = undefined;
80+
if (method === 'PUT' || method === 'POST') {
81+
body = await req.json().catch(() => ({}));
82+
}
83+
84+
const result = await dispatcher.handleMetadata(subPath, { request: rawRequest }, method, body);
7985
return toResponse(result);
8086
}
8187

packages/runtime/src/http-dispatcher.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ export class HttpDispatcher {
131131
* Standard: /metadata/:type/:name
132132
* Fallback for backward compat: /metadata (all objects), /metadata/:objectName (get object)
133133
*/
134-
async handleMetadata(path: string, context: HttpProtocolContext): Promise<HttpDispatcherResult> {
134+
async handleMetadata(path: string, context: HttpProtocolContext, method?: string, body?: any): Promise<HttpDispatcherResult> {
135135
const broker = this.ensureBroker();
136136
const parts = path.replace(/^\/+/, '').split('/').filter(Boolean);
137137

@@ -142,9 +142,34 @@ export class HttpDispatcher {
142142
return { handled: true, response: this.success({ types: ['objects', 'apps', 'plugins'] }) };
143143
}
144144

145-
// GET /metadata/:type/:name
145+
// /metadata/:type/:name
146146
if (parts.length === 2) {
147147
const [type, name] = parts;
148+
149+
// PUT /metadata/:type/:name (Save)
150+
if (method === 'PUT' && body) {
151+
// Try to get the protocol service directly
152+
const protocol = this.kernel?.context?.getService ? this.kernel.context.getService('protocol') : null;
153+
154+
if (protocol && typeof protocol.saveMetaItem === 'function') {
155+
try {
156+
const result = await protocol.saveMetaItem({ type, name, item: body });
157+
return { handled: true, response: this.success(result) };
158+
} catch (e: any) {
159+
return { handled: true, response: this.error(e.message, 400) };
160+
}
161+
}
162+
163+
// Fallback to broker if protocol not available (legacy)
164+
try {
165+
const data = await broker.call('metadata.saveItem', { type, name, item: body }, { request: context.request });
166+
return { handled: true, response: this.success(data) };
167+
} catch (e: any) {
168+
// If broker doesn't support it either
169+
return { handled: true, response: this.error(e.message || 'Save not supported', 501) };
170+
}
171+
}
172+
148173
try {
149174
// Try specific calls based on type
150175
if (type === 'objects') {

0 commit comments

Comments
 (0)