Skip to content

Commit dfe08e6

Browse files
authored
Merge pull request #1155 from objectstack-ai/copilot/fix-ci-errors-7957bead-2272-48a0-a2b8-adff3e5c8ee6
2 parents a432ff6 + 2b8d4a0 commit dfe08e6

2 files changed

Lines changed: 67 additions & 22 deletions

File tree

apps/studio/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
### Patch Changes
66

7+
- Fix simulateBrowser mock handlers to properly support query parameters (top, skip, sort, select, filter) in data endpoints, use protocol service for metadata endpoints (types, items), and return correct response formats matching the ObjectStack protocol spec
78
- Updated dependencies [ee39bff]
89
- @objectstack/service-ai@4.0.3
910
- @objectstack/plugin-auth@4.0.3

apps/studio/src/mocks/simulateBrowser.ts

Lines changed: 66 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,51 @@ import { http, HttpResponse } from 'msw';
66
import { ObjectStackClient } from '@objectstack/client';
77
import studioConfig from '../../objectstack.config';
88

9+
/**
10+
* Parse query parameters from a request URL into an ObjectQL query options object.
11+
* Supports: top, skip, sort, select, filter (JSON), and individual key-value filters.
12+
*/
13+
function parseQueryOptions(url: URL): Record<string, any> {
14+
const query: Record<string, any> = {};
15+
const where: Record<string, any> = {};
16+
17+
url.searchParams.forEach((val, key) => {
18+
switch (key) {
19+
case 'top':
20+
case '$top':
21+
query.limit = parseInt(val, 10);
22+
break;
23+
case 'skip':
24+
case '$skip':
25+
query.offset = parseInt(val, 10);
26+
break;
27+
case 'sort':
28+
case '$sort': {
29+
// JSON array or comma-separated string
30+
try { query.orderBy = JSON.parse(val); } catch {
31+
query.orderBy = val.split(',').map((s: string) => s.trim());
32+
}
33+
break;
34+
}
35+
case 'select':
36+
case '$select':
37+
query.fields = val.split(',').map((s: string) => s.trim());
38+
break;
39+
case 'filter':
40+
case '$filter':
41+
try { Object.assign(where, JSON.parse(val)); } catch { /* ignore malformed */ }
42+
break;
43+
default:
44+
// Individual key-value filter params (e.g. id=abc)
45+
where[key] = val;
46+
break;
47+
}
48+
});
49+
50+
if (Object.keys(where).length > 0) query.where = where;
51+
return query;
52+
}
53+
954
/**
1055
* Creates a Realistic Browser Simulation
1156
*
@@ -28,6 +73,7 @@ export async function simulateBrowser() {
2873
// 2. Define Network Handlers (The "Virtual Router")
2974
// These map HTTP requests -> Kernel ObjectQL service actions
3075
const ql = (kernel as any).context?.getService('objectql');
76+
const protocol = (kernel as any).context?.getService('protocol');
3177
const handlers = [
3278
// Discovery
3379
http.get('http://localhost:3000/.well-known/objectstack', () => {
@@ -47,17 +93,14 @@ export async function simulateBrowser() {
4793
});
4894
}),
4995

50-
// Query / Find
96+
// Query / Find — parse query params for filtering, pagination, sorting, field selection
5197
http.get('http://localhost:3000/api/v1/data/:object', async ({ params, request }) => {
5298
const url = new URL(request.url);
53-
const filters = {};
54-
url.searchParams.forEach((val, key) => {
55-
(filters as any)[key] = val;
56-
});
57-
console.log(`[VirtualNetwork] GET /data/${params.object}`, filters);
99+
const queryOpts = parseQueryOptions(url);
100+
console.log(`[VirtualNetwork] GET /data/${params.object}`, queryOpts);
58101

59102
try {
60-
let all = await ql.find(params.object);
103+
let all = await ql.find(params.object, queryOpts);
61104
if (!Array.isArray(all) && all && (all as any).value) all = (all as any).value;
62105
if (!all) all = [];
63106
const result = { object: params.object, records: all, total: all.length };
@@ -122,12 +165,12 @@ export async function simulateBrowser() {
122165
}
123166
}),
124167

125-
// Metadata - Get all types (base route returns types)
168+
// Metadata - Get all types (merges SchemaRegistry + MetadataService types)
126169
http.get('http://localhost:3000/api/v1/meta', async () => {
127170
console.log('[VirtualNetwork] GET /meta (types)');
128171
try {
129-
const types = ql?.registry?.getRegisteredTypes?.() || [];
130-
return HttpResponse.json({ success: true, data: { types } });
172+
const result = await protocol?.getMetaTypes?.();
173+
return HttpResponse.json({ success: true, data: result || { types: [] } });
131174
} catch (err: any) {
132175
return HttpResponse.json({ error: err.message }, { status: 500 });
133176
}
@@ -137,43 +180,44 @@ export async function simulateBrowser() {
137180
http.get('http://localhost:3000/api/v1/meta/object', async () => {
138181
console.log('[VirtualNetwork] GET /meta/object');
139182
try {
140-
const objects = ql?.getObjects?.() || {};
141-
return HttpResponse.json({ success: true, data: objects });
183+
const result = await protocol?.getMetaItems?.({ type: 'object' });
184+
return HttpResponse.json({ success: true, data: result || { type: 'object', items: [] } });
142185
} catch (err: any) {
143186
return HttpResponse.json({ error: err.message }, { status: 500 });
144187
}
145188
}),
146189
http.get('http://localhost:3000/api/v1/meta/objects', async () => {
147190
console.log('[VirtualNetwork] GET /meta/objects');
148191
try {
149-
const objects = ql?.getObjects?.() || {};
150-
return HttpResponse.json({ success: true, data: objects });
192+
const result = await protocol?.getMetaItems?.({ type: 'object' });
193+
return HttpResponse.json({ success: true, data: result || { type: 'object', items: [] } });
151194
} catch (err: any) {
152195
return HttpResponse.json({ error: err.message }, { status: 500 });
153196
}
154197
}),
155198

156199
// Metadata - Object Detail (Singular & Plural support)
200+
// Returns the raw ServiceObject directly (with name, fields, etc.)
157201
http.get('http://localhost:3000/api/v1/meta/object/:name', async ({ params }) => {
158202
console.log(`[VirtualNetwork] GET /meta/object/${params.name}`);
159203
try {
160-
const result = ql?.registry?.getObject?.(params.name);
161-
if (!result) {
204+
const result = await protocol?.getMetaItem?.({ type: 'object', name: params.name as string });
205+
if (!result?.item) {
162206
return HttpResponse.json({ error: 'Not Found' }, { status: 404 });
163207
}
164-
return HttpResponse.json({ success: true, data: result });
208+
return HttpResponse.json({ success: true, data: result.item });
165209
} catch (err: any) {
166210
return HttpResponse.json({ error: err.message }, { status: 500 });
167211
}
168212
}),
169213
http.get('http://localhost:3000/api/v1/meta/objects/:name', async ({ params }) => {
170214
console.log(`[VirtualNetwork] GET /meta/objects/${params.name}`);
171215
try {
172-
const result = ql?.registry?.getObject?.(params.name);
173-
if (!result) {
216+
const result = await protocol?.getMetaItem?.({ type: 'object', name: params.name as string });
217+
if (!result?.item) {
174218
return HttpResponse.json({ error: 'Not Found' }, { status: 404 });
175219
}
176-
return HttpResponse.json({ success: true, data: result });
220+
return HttpResponse.json({ success: true, data: result.item });
177221
} catch (err: any) {
178222
return HttpResponse.json({ error: err.message }, { status: 500 });
179223
}
@@ -186,8 +230,8 @@ export async function simulateBrowser() {
186230
}
187231
console.log(`[VirtualNetwork] GET /meta/${params.type}`);
188232
try {
189-
const items = ql?.registry?.listItems?.(params.type) || [];
190-
return HttpResponse.json({ success: true, data: items });
233+
const result = await protocol?.getMetaItems?.({ type: params.type as string });
234+
return HttpResponse.json({ success: true, data: result || { type: params.type, items: [] } });
191235
} catch (err: any) {
192236
return HttpResponse.json({ error: err.message }, { status: 500 });
193237
}

0 commit comments

Comments
 (0)