Skip to content

Commit 696597d

Browse files
authored
Merge pull request #392 from objectstack-ai/copilot/fix-build-and-test-again
2 parents 4ef2370 + 5f27d15 commit 696597d

2 files changed

Lines changed: 116 additions & 14 deletions

File tree

packages/foundation/core/src/app.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,8 @@ export class ObjectQL extends UpstreamObjectQL {
6363
// Store drivers for registration during init()
6464
if (config.datasources) {
6565
for (const [name, driver] of Object.entries(config.datasources)) {
66-
if (!(driver as any).name) {
67-
(driver as any).name = name;
68-
}
66+
// Always set driver.name to the config key so datasource(name) lookups work
67+
(driver as any).name = name;
6968
// Cast: local Driver interface is structurally compatible with upstream DriverInterface
7069
this.pendingDrivers.push({
7170
name,

packages/foundation/core/test/__mocks__/@objectstack/objectql.ts

Lines changed: 114 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,18 @@ export class ObjectQL {
2020

2121
async connect() {}
2222
async disconnect() {}
23-
async init() {}
23+
async init() {
24+
// Initialize drivers (connect + sync schema)
25+
for (const [_name, driver] of this.drivers) {
26+
if (driver.connect) {
27+
await driver.connect();
28+
}
29+
if (driver.init) {
30+
const objects = SchemaRegistry.getAllObjects();
31+
await driver.init(objects);
32+
}
33+
}
34+
}
2435

2536
registerDriver(driver: any, isDefault: boolean = false) {
2637
if (!driver.name) {
@@ -31,6 +42,18 @@ export class ObjectQL {
3142
this.defaultDriver = driver.name;
3243
}
3344
}
45+
46+
datasource(name: string): any {
47+
const driver = this.drivers.get(name);
48+
if (!driver) {
49+
throw new Error(`[ObjectQL] Datasource '${name}' not found`);
50+
}
51+
return driver;
52+
}
53+
54+
getDriverByName(name: string): any {
55+
return this.drivers.get(name);
56+
}
3457

3558
registerObject(schema: any, packageId: string = '__runtime__', namespace?: string): string {
3659
// Auto-assign field names from keys
@@ -68,6 +91,10 @@ export class ObjectQL {
6891
this.hooks.get(event)!.push({ handler, options });
6992
}
7093

94+
on(event: string, objectName: string, handler: any, packageId?: string) {
95+
this.registerHook(event, handler, { object: objectName, packageId });
96+
}
97+
7198
registerMiddleware(fn: any, options?: { object?: string }) {
7299
this.middlewares.push({ fn, object: options?.object });
73100
}
@@ -128,25 +155,101 @@ export class ObjectQL {
128155
});
129156
return opCtx.result;
130157
},
158+
create: async (data: any) => {
159+
const driver = this.drivers.get(this.defaultDriver || this.drivers.keys().next().value);
160+
const opCtx = { object: name, operation: 'insert', data, context: options, result: undefined as any };
161+
await this.executeWithMiddleware(opCtx, async () => {
162+
const hookContext: any = {
163+
object: name, event: 'beforeCreate',
164+
input: { data: opCtx.data }, session: options,
165+
data: opCtx.data, user: options.userId ? { id: options.userId } : options.user
166+
};
167+
await this.triggerHooks('beforeCreate', hookContext);
168+
const finalData = hookContext.data || hookContext.input.data;
169+
const result = driver?.create ? await driver.create(name, finalData, options) : finalData;
170+
hookContext.event = 'afterCreate';
171+
hookContext.result = result;
172+
await this.triggerHooks('afterCreate', hookContext);
173+
return hookContext.result;
174+
});
175+
return opCtx.result;
176+
},
131177
insert: async (data: any) => {
132178
const driver = this.drivers.get(this.defaultDriver || this.drivers.keys().next().value);
133-
if (driver && driver.insert) {
134-
return driver.insert(name, data);
135-
}
136-
return data;
179+
const opCtx = { object: name, operation: 'insert', data, context: options, result: undefined as any };
180+
await this.executeWithMiddleware(opCtx, async () => {
181+
const hookContext: any = {
182+
object: name, event: 'beforeCreate',
183+
input: { data: opCtx.data }, session: options,
184+
data: opCtx.data, user: options.userId ? { id: options.userId } : options.user
185+
};
186+
await this.triggerHooks('beforeCreate', hookContext);
187+
const finalData = hookContext.data || hookContext.input.data;
188+
const result = driver?.create ? await driver.create(name, finalData, options)
189+
: driver?.insert ? await driver.insert(name, finalData)
190+
: finalData;
191+
hookContext.event = 'afterCreate';
192+
hookContext.result = result;
193+
await this.triggerHooks('afterCreate', hookContext);
194+
return hookContext.result;
195+
});
196+
return opCtx.result;
137197
},
138198
update: async (id: string, data: any) => {
139199
const driver = this.drivers.get(this.defaultDriver || this.drivers.keys().next().value);
140-
if (driver && driver.update) {
141-
return driver.update(name, id, data);
142-
}
143-
return data;
200+
const opCtx = { object: name, operation: 'update', data, context: options, result: undefined as any };
201+
await this.executeWithMiddleware(opCtx, async () => {
202+
// Fetch previous data for hooks that need it
203+
let previousData: any;
204+
if (driver?.findOne) {
205+
try { previousData = await driver.findOne(name, { _id: id }); } catch (_e) { /* ignore */ }
206+
}
207+
const hookContext: any = {
208+
object: name, event: 'beforeUpdate',
209+
input: { id, data: opCtx.data }, session: options,
210+
data: opCtx.data, previousData, user: options.userId ? { id: options.userId } : options.user
211+
};
212+
await this.triggerHooks('beforeUpdate', hookContext);
213+
const finalData = hookContext.data || hookContext.input.data;
214+
const result = driver?.update ? await driver.update(name, id, finalData, options) : finalData;
215+
hookContext.event = 'afterUpdate';
216+
hookContext.result = result;
217+
await this.triggerHooks('afterUpdate', hookContext);
218+
return hookContext.result;
219+
});
220+
return opCtx.result;
144221
},
145222
delete: async (id: string) => {
146223
const driver = this.drivers.get(this.defaultDriver || this.drivers.keys().next().value);
147-
if (driver && driver.delete) {
148-
return driver.delete(name, id);
224+
const opCtx = { object: name, operation: 'delete', context: options, result: undefined as any };
225+
await this.executeWithMiddleware(opCtx, async () => {
226+
// Fetch current data for hooks that need it
227+
let previousData: any;
228+
if (driver?.findOne) {
229+
try { previousData = await driver.findOne(name, { _id: id }); } catch (_e) { /* ignore */ }
230+
}
231+
const hookContext: any = {
232+
object: name, event: 'beforeDelete',
233+
input: { id }, session: options,
234+
data: previousData, previousData, user: options.userId ? { id: options.userId } : options.user
235+
};
236+
await this.triggerHooks('beforeDelete', hookContext);
237+
const result = driver?.delete ? await driver.delete(name, id) : true;
238+
hookContext.event = 'afterDelete';
239+
hookContext.result = result;
240+
await this.triggerHooks('afterDelete', hookContext);
241+
return hookContext.result;
242+
});
243+
return opCtx.result;
244+
},
245+
count: async (filter?: any) => {
246+
const driver = this.drivers.get(this.defaultDriver || this.drivers.keys().next().value);
247+
if (driver?.count) {
248+
return driver.count(name, filter);
149249
}
250+
// Fallback: use find and count
251+
const results = driver?.find ? await driver.find(name, filter) : [];
252+
return Array.isArray(results) ? results.length : 0;
150253
}
151254
})
152255
};

0 commit comments

Comments
 (0)