feat(objectql): auto-sync registered object schemas to database on startup#942
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…artup ObjectQLPlugin.start() now iterates all objects in SchemaRegistry after driver.connect() and calls driver.syncSchema() for each one. This ensures plugin-registered objects (e.g. sys_user from plugin-auth) have their database tables created/updated automatically. - Add syncRegisteredSchemas() to ObjectQLPlugin with per-object error tolerance - Add getDriverForObject() public method to ObjectQL engine - Add optional syncSchema to DriverInterface (aligns with IDataDriver) - Add integration tests for sync behavior, error tolerance, and edge cases Closes #940 Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com> Agent-Logs-Url: https://github.com/objectstack-ai/spec/sessions/a6a76ea8-5b90-40cb-88c8-26a9cb27cc6b
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com> Agent-Logs-Url: https://github.com/objectstack-ai/spec/sessions/a6a76ea8-5b90-40cb-88c8-26a9cb27cc6b
There was a problem hiding this comment.
Pull request overview
This PR ensures ObjectQL plugin-registered object schemas are automatically synchronized to the backing database during startup, preventing first-access failures due to missing tables/collections.
Changes:
- Invoke schema synchronization after
ObjectQLPlugin.start()finishes driver initialization. - Add
ObjectQL.getDriverForObject()as a tolerant driver resolver for per-object sync orchestration. - Extend the
DriverInterfacecontract with optionalsyncSchema?, and add integration tests covering sync behavior and resilience.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/objectql/src/plugin.ts | Calls new per-object schema sync after init(); adds syncRegisteredSchemas() implementation. |
| packages/objectql/src/engine.ts | Adds getDriverForObject() to resolve the driver responsible for an object with error tolerance. |
| packages/spec/src/contracts/data-engine.ts | Adds optional syncSchema? to DriverInterface to reflect schema sync capability. |
| packages/objectql/src/plugin.integration.test.ts | Adds/extends integration tests for startup schema sync behavior and resilience; resets static registry per test. |
| packages/objectql/CHANGELOG.md | Documents the new startup schema sync behavior and related API/contract additions. |
| getDriverForObject(objectName: string): DriverInterface | undefined { | ||
| try { | ||
| return this.getDriver(objectName); | ||
| } catch { |
There was a problem hiding this comment.
getDriverForObject() catches and suppresses all errors from getDriver() without logging. This makes datasource misconfiguration (e.g., object bound to an unregistered datasource) hard to debug because callers only see undefined. Consider catching the error, logging it at debug/warn with objectName + message, and then returning undefined for tolerance.
| } catch { | |
| } catch (error) { | |
| this.logger?.warn?.( | |
| `[ObjectQL] getDriverForObject: failed to resolve driver for object '${objectName}': ${ | |
| error instanceof Error ? error.message : String(error) | |
| }`, | |
| ); |
|
|
||
| ### Patch Changes | ||
|
|
||
| - Auto-sync all registered object schemas to database on startup: `ObjectQLPlugin.start()` now iterates every object in `SchemaRegistry` and calls `driver.syncSchema()` after driver connections are established. This ensures tables for plugin-registered objects (e.g. `sys_user` from plugin-auth) are created or updated automatically. |
There was a problem hiding this comment.
Changelog entry references sys_user, but system plugin objects in the sys namespace are registered as sys__user (FQN with double underscore). Updating the example name here would better match what users will actually see in logs/DB identifiers.
| - Auto-sync all registered object schemas to database on startup: `ObjectQLPlugin.start()` now iterates every object in `SchemaRegistry` and calls `driver.syncSchema()` after driver connections are established. This ensures tables for plugin-registered objects (e.g. `sys_user` from plugin-auth) are created or updated automatically. | |
| - Auto-sync all registered object schemas to database on startup: `ObjectQLPlugin.start()` now iterates every object in `SchemaRegistry` and calls `driver.syncSchema()` after driver connections are established. This ensures tables for plugin-registered objects (e.g. `sys__user` from plugin-auth) are created or updated automatically. |
|
|
||
| // Sync all registered object schemas to database | ||
| // This ensures tables/collections are created or updated for every | ||
| // object registered by plugins (e.g., sys_user from plugin-auth). |
There was a problem hiding this comment.
The example object name in this comment uses sys_user, but objects in the sys namespace are registered as FQNs using the sys__<name> pattern (double underscore). Using sys__user here would avoid confusion when troubleshooting schema sync/table names.
| // object registered by plugins (e.g., sys_user from plugin-auth). | |
| // object registered by plugins (e.g., sys__user from plugin-auth). |
| if (synced > 0 || skipped > 0) { | ||
| ctx.logger.info('Schema sync complete', { synced, skipped, total: allObjects.length }); | ||
| } |
There was a problem hiding this comment.
syncRegisteredSchemas() only emits the final "Schema sync complete" info log when synced > 0 || skipped > 0. If all objects have a driver+syncSchema but every call throws, this produces no summary log even though work was attempted. Consider tracking a failed counter and always logging a summary (or at least logging when failed > 0) so operators can tell startup schema sync ran but encountered errors.
| ctx.logger.warn('Failed to sync schema for object', { | ||
| object: obj.name, | ||
| driver: driver.name, | ||
| error: e instanceof Error ? e.message : String(e), | ||
| }); |
There was a problem hiding this comment.
When a schema sync fails, the warn log only captures e.message (or String(e)), which drops stack traces for Error instances and makes diagnosis harder. Consider including stack (when available) in the metadata or logging the full error details in a structured way.
ObjectQLPlugin.start()connects drivers viainit()but never syncs registered object schemas to the database. Plugin-registered objects (e.g.sys_userfrom plugin-auth) have metadata loaded but no backing tables — first access throwsSQLITE_ERROR: no such table.Changes
packages/objectql/src/plugin.ts— AddedsyncRegisteredSchemas()afterinit()instart(). IteratesSchemaRegistry.getAllObjects(), resolves each object's driver, callsdriver.syncSchema(name, schema)per-object. Tolerates missing drivers, unsupportedsyncSchema, and per-object failures with warn-level logging.packages/objectql/src/engine.ts— Added publicgetDriverForObject(objectName)that wraps the privategetDriver()with error tolerance (returnsundefinedinstead of throwing).packages/spec/src/contracts/data-engine.ts— Added optionalsyncSchema?toDriverInterface, aligning it with the already-declaredIDataDriverprotocol and Zod schema.packages/objectql/src/plugin.integration.test.ts— 4 new tests: normal sync, driver withoutsyncSchema, partial failure resilience, empty registry.Architecture
Sync orchestration lives in the Plugin layer (not
ObjectQL.init()), consistent with the boundary: Plugin orchestrates, Engine handles registry + connect.Original prompt
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.