Skip to content

Commit 422fa77

Browse files
committed
Add @objectstack/driver-memory dependency and refactor createMockHandlers for improved data handling
1 parent 141e634 commit 422fa77

File tree

4 files changed

+60
-99
lines changed

4 files changed

+60
-99
lines changed

examples/msw-react-crud/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
},
1212
"dependencies": {
1313
"@objectstack/client": "workspace:*",
14+
"@objectstack/driver-memory": "workspace:*",
1415
"@objectstack/spec": "workspace:*",
1516
"react": "^18.3.1",
1617
"react-dom": "^18.3.1"

examples/msw-react-crud/src/mocks/createMockHandlers.ts

Lines changed: 53 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -2,46 +2,37 @@
22
* Auto-generate MSW handlers from ObjectStack configuration
33
*
44
* This helper creates all necessary MSW handlers based on your
5-
* objectstack.config.ts without requiring the full runtime.
5+
* objectstack.config.ts using the in-memory driver.
66
*/
77

88
import { http, HttpResponse } from 'msw';
9+
import { InMemoryDriver } from '@objectstack/driver-memory';
910

10-
// Simple in-memory store
11-
const stores = new Map<string, Map<string, any>>();
12-
13-
let idCounters = new Map<string, number>();
14-
15-
function getStore(objectName: string): Map<string, any> {
16-
if (!stores.has(objectName)) {
17-
stores.set(objectName, new Map());
18-
}
19-
return stores.get(objectName)!;
20-
}
21-
22-
function getNextId(objectName: string): string {
23-
const current = idCounters.get(objectName) || 0;
24-
const next = current + 1;
25-
idCounters.set(objectName, next);
26-
return String(next);
27-
}
11+
// Shared driver instance
12+
const driver = new InMemoryDriver();
13+
driver.connect();
2814

2915
/**
3016
* Seed initial data for an object
3117
*/
32-
export function seedData(objectName: string, records: any[]) {
33-
const store = getStore(objectName);
34-
records.forEach((record) => {
35-
const id = record.id || getNextId(objectName);
36-
store.set(id, { ...record, id });
37-
});
18+
export async function seedData(objectName: string, records: any[]) {
19+
// Ensure table exists
20+
await driver.syncSchema(objectName, {});
3821

39-
// Initialize ID counter
40-
const maxId = Math.max(
41-
0,
42-
...Array.from(store.values()).map(r => parseInt(r.id) || 0)
43-
);
44-
idCounters.set(objectName, maxId);
22+
// Get existing records to determine insert vs update
23+
const existing = await driver.find(objectName, { object: objectName });
24+
25+
for (const record of records) {
26+
const id = record.id;
27+
// Check if record exists by ID
28+
const found = id ? existing.find((r: any) => r.id === id) : null;
29+
30+
if (found) {
31+
await driver.update(objectName, id, record);
32+
} else {
33+
await driver.create(objectName, record);
34+
}
35+
}
4536
}
4637

4738
/**
@@ -64,9 +55,12 @@ export function createMockHandlers(baseUrl: string = '/api/v1', metadata: any =
6455
}
6556
};
6657

67-
// Generate handlers for both correct paths and doubled paths (client compatibility)
68-
const paths = [baseUrl, `${baseUrl}/api/v1`];
69-
58+
// Generate handlers for both correct paths and potential variations
59+
const paths = [baseUrl];
60+
if (!baseUrl.endsWith('/api/v1')) {
61+
paths.push(`${baseUrl}/api/v1`); // fallback compatibility
62+
}
63+
7064
const handlers = [];
7165

7266
for (const path of paths) {
@@ -110,30 +104,34 @@ export function createMockHandlers(baseUrl: string = '/api/v1', metadata: any =
110104
}),
111105

112106
// Data: Find/List
113-
http.get(`${path}/data/:object`, ({ params, request }) => {
107+
http.get(`${path}/data/:object`, async ({ params, request }) => {
114108
const objectName = params.object as string;
115-
const store = getStore(objectName);
116109
const url = new URL(request.url);
117110

118111
const top = parseInt(url.searchParams.get('top') || '100');
119112
const skip = parseInt(url.searchParams.get('$skip') || '0');
120113

121-
const records = Array.from(store.values());
122-
const paginatedRecords = records.slice(skip, skip + top);
114+
// Fetch all and slice manually since driver lacks skip/filtering
115+
await driver.syncSchema(objectName, {});
116+
const allRecords = await driver.find(objectName, { object: objectName });
117+
118+
const paginatedRecords = allRecords.slice(skip, skip + top);
123119

124120
return HttpResponse.json({
125121
'@odata.context': `${baseUrl}/data/$metadata#${objectName}`,
126122
value: paginatedRecords,
127-
count: records.length
123+
count: allRecords.length
128124
});
129125
}),
130126

131127
// Data: Get by ID
132-
http.get(`${path}/data/:object/:id`, ({ params }) => {
128+
http.get(`${path}/data/:object/:id`, async ({ params }) => {
133129
const objectName = params.object as string;
134130
const id = params.id as string;
135-
const store = getStore(objectName);
136-
const record = store.get(id);
131+
132+
await driver.syncSchema(objectName, {});
133+
const allRecords = await driver.find(objectName, { object: objectName });
134+
const record = allRecords.find((r: any) => r.id === id);
137135

138136
if (!record) {
139137
return HttpResponse.json({ error: 'Record not found' }, { status: 404 });
@@ -146,16 +144,9 @@ export function createMockHandlers(baseUrl: string = '/api/v1', metadata: any =
146144
http.post(`${path}/data/:object`, async ({ params, request }) => {
147145
const objectName = params.object as string;
148146
const body = await request.json() as any;
149-
const store = getStore(objectName);
150147

151-
const id = body.id || getNextId(objectName);
152-
const newRecord = {
153-
...body,
154-
id,
155-
createdAt: body.createdAt || new Date().toISOString()
156-
};
157-
158-
store.set(id, newRecord);
148+
await driver.syncSchema(objectName, {});
149+
const newRecord = await driver.create(objectName, body);
159150

160151
return HttpResponse.json(newRecord, { status: 201 });
161152
}),
@@ -165,31 +156,28 @@ export function createMockHandlers(baseUrl: string = '/api/v1', metadata: any =
165156
const objectName = params.object as string;
166157
const id = params.id as string;
167158
const updates = await request.json() as any;
168-
const store = getStore(objectName);
169-
const record = store.get(id);
170159

171-
if (!record) {
172-
return HttpResponse.json({ error: 'Record not found' }, { status: 404 });
160+
try {
161+
await driver.syncSchema(objectName, {});
162+
const updatedRecord = await driver.update(objectName, id, updates);
163+
return HttpResponse.json(updatedRecord);
164+
} catch (e) {
165+
return HttpResponse.json({ error: 'Record not found' }, { status: 404 });
173166
}
174-
175-
const updatedRecord = { ...record, ...updates };
176-
store.set(id, updatedRecord);
177-
178-
return HttpResponse.json(updatedRecord);
179167
}),
180168

181169
// Data: Delete
182-
http.delete(`${path}/data/:object/:id`, ({ params }) => {
170+
http.delete(`${path}/data/:object/:id`, async ({ params }) => {
183171
const objectName = params.object as string;
184172
const id = params.id as string;
185-
const store = getStore(objectName);
186173

187-
if (!store.has(id)) {
174+
await driver.syncSchema(objectName, {});
175+
const success = await driver.delete(objectName, id);
176+
177+
if (!success) {
188178
return HttpResponse.json({ error: 'Record not found' }, { status: 404 });
189179
}
190180

191-
store.delete(id);
192-
193181
return HttpResponse.json({ success: true }, { status: 204 });
194182
})
195183
);

packages/driver-memory/src/memory-driver.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,10 +110,10 @@ export class InMemoryDriver implements DriverInterface {
110110

111111
// COMPATIBILITY: Driver must return 'id' as string
112112
const newRecord = {
113-
id: this.generateId(),
113+
id: data.id || this.generateId(),
114114
...data,
115-
created_at: new Date(),
116-
updated_at: new Date(),
115+
created_at: data.created_at || new Date(),
116+
updated_at: data.updated_at || new Date(),
117117
};
118118

119119
table.push(newRecord);

pnpm-lock.yaml

Lines changed: 3 additions & 31 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)