From e94e8d8bd1cb174b1ddcf261357821d7a851af82 Mon Sep 17 00:00:00 2001 From: dswbx Date: Fri, 10 Jan 2025 15:51:47 +0100 Subject: [PATCH] Refactor entity handling to preserve config while overriding type Reworked `ensureEntity` to replace entities while maintaining their configuration and allowing type adjustments. Updated tests to verify type persistence and synchronization of entity properties. --- app/__test__/modules/AppAuth.spec.ts | 15 ++++------- app/__test__/modules/AppMedia.spec.ts | 8 +++--- app/__test__/modules/Module.spec.ts | 31 ++++++++++++++++------- app/__test__/modules/module-test-suite.ts | 3 ++- app/package.json | 2 +- app/src/auth/AppAuth.ts | 12 ++++----- app/src/data/entities/Entity.ts | 2 +- app/src/data/entities/EntityManager.ts | 10 ++++++++ app/src/media/AppMedia.ts | 5 ++-- app/src/modules/Module.ts | 16 +++++++----- 10 files changed, 63 insertions(+), 41 deletions(-) diff --git a/app/__test__/modules/AppAuth.spec.ts b/app/__test__/modules/AppAuth.spec.ts index 849d8dd..11b1d03 100644 --- a/app/__test__/modules/AppAuth.spec.ts +++ b/app/__test__/modules/AppAuth.spec.ts @@ -121,15 +121,10 @@ describe("AppAuth", () => { await app.build(); - const userfields = app.modules.em.entity("users").fields.map((f) => f.name); - expect(userfields).toContain("additional"); - expect(userfields).toEqual([ - "id", - "additional", - "email", - "strategy", - "strategy_value", - "role" - ]); + const e = app.modules.em.entity("users"); + const fields = e.fields.map((f) => f.name); + expect(e.type).toBe("system"); + expect(fields).toContain("additional"); + expect(fields).toEqual(["id", "email", "strategy", "strategy_value", "role", "additional"]); }); }); diff --git a/app/__test__/modules/AppMedia.spec.ts b/app/__test__/modules/AppMedia.spec.ts index c1fc2f9..19fa73b 100644 --- a/app/__test__/modules/AppMedia.spec.ts +++ b/app/__test__/modules/AppMedia.spec.ts @@ -33,11 +33,12 @@ describe("AppMedia", () => { await app.build(); - const fields = app.modules.em.entity("media").fields.map((f) => f.name); + const e = app.modules.em.entity("media"); + const fields = e.fields.map((f) => f.name); + expect(e.type).toBe("system"); expect(fields).toContain("additional"); expect(fields).toEqual([ "id", - "additional", "path", "folder", "mime_type", @@ -47,7 +48,8 @@ describe("AppMedia", () => { "modified_at", "reference", "entity_id", - "metadata" + "metadata", + "additional" ]); }); }); diff --git a/app/__test__/modules/Module.spec.ts b/app/__test__/modules/Module.spec.ts index b53dcfa..572c5a1 100644 --- a/app/__test__/modules/Module.spec.ts +++ b/app/__test__/modules/Module.spec.ts @@ -68,7 +68,8 @@ describe("Module", async () => { return { entities: _em.entities.map((e) => ({ name: e.name, - fields: e.fields.map((f) => f.name) + fields: e.fields.map((f) => f.name), + type: e.type })), indices: _em.indices.map((i) => ({ name: i.name, @@ -105,7 +106,8 @@ describe("Module", async () => { entities: [ { name: "u", - fields: ["id", "name"] + fields: ["id", "name"], + type: "regular" } ], indices: [] @@ -124,7 +126,8 @@ describe("Module", async () => { entities: [ { name: "u", - fields: ["id", "name"] + fields: ["id", "name"], + type: "regular" } ], indices: [] @@ -139,9 +142,14 @@ describe("Module", async () => { // this should only add the field "important" m.prt.ensureEntity( - entity("u", { - important: text() - }) + entity( + "u", + { + important: text() + }, + undefined, + "system" + ) ); expect(m.ctx.flags.sync_required).toBe(true); @@ -149,11 +157,15 @@ describe("Module", async () => { entities: [ { name: "u", - fields: ["id", "name", "important"] + // ensured properties must come first + fields: ["id", "important", "name"], + // ensured type must be present + type: "system" }, { name: "p", - fields: ["id", "title"] + fields: ["id", "title"], + type: "regular" } ], indices: [] @@ -177,7 +189,8 @@ describe("Module", async () => { entities: [ { name: "u", - fields: ["id", "name", "title"] + fields: ["id", "name", "title"], + type: "regular" } ], indices: [ diff --git a/app/__test__/modules/module-test-suite.ts b/app/__test__/modules/module-test-suite.ts index 5c1cce7..cf4926a 100644 --- a/app/__test__/modules/module-test-suite.ts +++ b/app/__test__/modules/module-test-suite.ts @@ -5,7 +5,7 @@ import { Guard } from "../../src/auth"; import { EventManager } from "../../src/core/events"; import { Default, stripMark } from "../../src/core/utils"; import { EntityManager } from "../../src/data"; -import type { Module, ModuleBuildContext } from "../../src/modules/Module"; +import { Module, type ModuleBuildContext } from "../../src/modules/Module"; import { getDummyConnection } from "../helper"; export function makeCtx(overrides?: Partial): ModuleBuildContext { @@ -16,6 +16,7 @@ export function makeCtx(overrides?: Partial): ModuleBuildCon em: new EntityManager([], dummyConnection), emgr: new EventManager(), guard: new Guard(), + flags: Module.ctx_flags, ...overrides }; } diff --git a/app/package.json b/app/package.json index da85eac..1bb93d4 100644 --- a/app/package.json +++ b/app/package.json @@ -3,7 +3,7 @@ "type": "module", "sideEffects": false, "bin": "./dist/cli/index.js", - "version": "0.4.0", + "version": "0.5.0-rc5", "scripts": { "build:all": "NODE_ENV=production bun run build.ts --minify --types --clean && bun run build:cli", "dev": "vite", diff --git a/app/src/auth/AppAuth.ts b/app/src/auth/AppAuth.ts index 8899495..a0105a6 100644 --- a/app/src/auth/AppAuth.ts +++ b/app/src/auth/AppAuth.ts @@ -250,13 +250,11 @@ export class AppAuth extends Module { }; registerEntities() { - const name = this.config.entity_name as "users"; - const { - entities: { users } - } = this.ensureSchema( + const users = this.getUsersEntity(true); + this.ensureSchema( em( { - [name]: entity(name, AppAuth.usersFields) + [users.name as "users"]: users }, ({ index }, { users }) => { index(users).on(["email"], true).on(["strategy"]).on(["strategy_value"]); @@ -267,13 +265,13 @@ export class AppAuth extends Module { try { const roles = Object.keys(this.config.roles ?? {}); const field = make("role", enumm({ enum: roles })); - users.__experimental_replaceField("role", field); + users.__replaceField("role", field); } catch (e) {} try { const strategies = Object.keys(this.config.strategies ?? {}); const field = make("strategy", enumm({ enum: strategies })); - users.__experimental_replaceField("strategy", field); + users.__replaceField("strategy", field); } catch (e) {} } diff --git a/app/src/data/entities/Entity.ts b/app/src/data/entities/Entity.ts index aa3d75c..a87d609 100644 --- a/app/src/data/entities/Entity.ts +++ b/app/src/data/entities/Entity.ts @@ -140,7 +140,7 @@ export class Entity< return this.fields.find((field) => field.name === name); } - __experimental_replaceField(name: string, field: Field) { + __replaceField(name: string, field: Field) { const index = this.fields.findIndex((f) => f.name === name); if (index === -1) { throw new Error(`Field "${name}" not found on entity "${this.name}"`); diff --git a/app/src/data/entities/EntityManager.ts b/app/src/data/entities/EntityManager.ts index fea93aa..1e2030e 100644 --- a/app/src/data/entities/EntityManager.ts +++ b/app/src/data/entities/EntityManager.ts @@ -99,6 +99,16 @@ export class EntityManager { this.entities.push(entity); } + __replaceEntity(entity: Entity, name: string | undefined = entity.name) { + const entityIndex = this._entities.findIndex((e) => e.name === name); + + if (entityIndex === -1) { + throw new Error(`Entity "${name}" not found and cannot be replaced`); + } + + this._entities[entityIndex] = entity; + } + entity(e: Entity | keyof TBD | string): Entity { let entity: Entity | undefined; if (typeof e === "string") { diff --git a/app/src/media/AppMedia.ts b/app/src/media/AppMedia.ts index 97f0a9b..fc9ba42 100644 --- a/app/src/media/AppMedia.ts +++ b/app/src/media/AppMedia.ts @@ -48,10 +48,9 @@ export class AppMedia extends Module { this.setupListeners(); this.ctx.server.route(this.basepath, new MediaController(this).getController()); - const mediaEntity = this.getMediaEntity(true); - const name = mediaEntity.name as "media"; + const media = this.getMediaEntity(true); this.ensureSchema( - em({ [name]: mediaEntity }, ({ index }, { media }) => { + em({ [media.name as "media"]: media }, ({ index }, { media }) => { index(media).on(["path"], true).on(["reference"]); }) ); diff --git a/app/src/modules/Module.ts b/app/src/modules/Module.ts index e304c4a..2a4dad0 100644 --- a/app/src/modules/Module.ts +++ b/app/src/modules/Module.ts @@ -3,7 +3,8 @@ import type { Guard } from "auth"; import { SchemaObject } from "core"; import type { EventManager } from "core/events"; import type { Static, TSchema } from "core/utils"; -import type { Connection, Entity, EntityIndex, EntityManager, em as prototypeEm } from "data"; +import type { Connection, EntityIndex, EntityManager, em as prototypeEm } from "data"; +import { Entity } from "data"; import type { Hono } from "hono"; export type ServerEnv = { @@ -138,8 +139,6 @@ export abstract class Module