mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-16 04:27:21 +00:00
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.
This commit is contained in:
@@ -121,15 +121,10 @@ describe("AppAuth", () => {
|
|||||||
|
|
||||||
await app.build();
|
await app.build();
|
||||||
|
|
||||||
const userfields = app.modules.em.entity("users").fields.map((f) => f.name);
|
const e = app.modules.em.entity("users");
|
||||||
expect(userfields).toContain("additional");
|
const fields = e.fields.map((f) => f.name);
|
||||||
expect(userfields).toEqual([
|
expect(e.type).toBe("system");
|
||||||
"id",
|
expect(fields).toContain("additional");
|
||||||
"additional",
|
expect(fields).toEqual(["id", "email", "strategy", "strategy_value", "role", "additional"]);
|
||||||
"email",
|
|
||||||
"strategy",
|
|
||||||
"strategy_value",
|
|
||||||
"role"
|
|
||||||
]);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -33,11 +33,12 @@ describe("AppMedia", () => {
|
|||||||
|
|
||||||
await app.build();
|
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).toContain("additional");
|
||||||
expect(fields).toEqual([
|
expect(fields).toEqual([
|
||||||
"id",
|
"id",
|
||||||
"additional",
|
|
||||||
"path",
|
"path",
|
||||||
"folder",
|
"folder",
|
||||||
"mime_type",
|
"mime_type",
|
||||||
@@ -47,7 +48,8 @@ describe("AppMedia", () => {
|
|||||||
"modified_at",
|
"modified_at",
|
||||||
"reference",
|
"reference",
|
||||||
"entity_id",
|
"entity_id",
|
||||||
"metadata"
|
"metadata",
|
||||||
|
"additional"
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -68,7 +68,8 @@ describe("Module", async () => {
|
|||||||
return {
|
return {
|
||||||
entities: _em.entities.map((e) => ({
|
entities: _em.entities.map((e) => ({
|
||||||
name: e.name,
|
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) => ({
|
indices: _em.indices.map((i) => ({
|
||||||
name: i.name,
|
name: i.name,
|
||||||
@@ -105,7 +106,8 @@ describe("Module", async () => {
|
|||||||
entities: [
|
entities: [
|
||||||
{
|
{
|
||||||
name: "u",
|
name: "u",
|
||||||
fields: ["id", "name"]
|
fields: ["id", "name"],
|
||||||
|
type: "regular"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
indices: []
|
indices: []
|
||||||
@@ -124,7 +126,8 @@ describe("Module", async () => {
|
|||||||
entities: [
|
entities: [
|
||||||
{
|
{
|
||||||
name: "u",
|
name: "u",
|
||||||
fields: ["id", "name"]
|
fields: ["id", "name"],
|
||||||
|
type: "regular"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
indices: []
|
indices: []
|
||||||
@@ -139,9 +142,14 @@ describe("Module", async () => {
|
|||||||
|
|
||||||
// this should only add the field "important"
|
// this should only add the field "important"
|
||||||
m.prt.ensureEntity(
|
m.prt.ensureEntity(
|
||||||
entity("u", {
|
entity(
|
||||||
important: text()
|
"u",
|
||||||
})
|
{
|
||||||
|
important: text()
|
||||||
|
},
|
||||||
|
undefined,
|
||||||
|
"system"
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(m.ctx.flags.sync_required).toBe(true);
|
expect(m.ctx.flags.sync_required).toBe(true);
|
||||||
@@ -149,11 +157,15 @@ describe("Module", async () => {
|
|||||||
entities: [
|
entities: [
|
||||||
{
|
{
|
||||||
name: "u",
|
name: "u",
|
||||||
fields: ["id", "name", "important"]
|
// ensured properties must come first
|
||||||
|
fields: ["id", "important", "name"],
|
||||||
|
// ensured type must be present
|
||||||
|
type: "system"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "p",
|
name: "p",
|
||||||
fields: ["id", "title"]
|
fields: ["id", "title"],
|
||||||
|
type: "regular"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
indices: []
|
indices: []
|
||||||
@@ -177,7 +189,8 @@ describe("Module", async () => {
|
|||||||
entities: [
|
entities: [
|
||||||
{
|
{
|
||||||
name: "u",
|
name: "u",
|
||||||
fields: ["id", "name", "title"]
|
fields: ["id", "name", "title"],
|
||||||
|
type: "regular"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
indices: [
|
indices: [
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { Guard } from "../../src/auth";
|
|||||||
import { EventManager } from "../../src/core/events";
|
import { EventManager } from "../../src/core/events";
|
||||||
import { Default, stripMark } from "../../src/core/utils";
|
import { Default, stripMark } from "../../src/core/utils";
|
||||||
import { EntityManager } from "../../src/data";
|
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";
|
import { getDummyConnection } from "../helper";
|
||||||
|
|
||||||
export function makeCtx(overrides?: Partial<ModuleBuildContext>): ModuleBuildContext {
|
export function makeCtx(overrides?: Partial<ModuleBuildContext>): ModuleBuildContext {
|
||||||
@@ -16,6 +16,7 @@ export function makeCtx(overrides?: Partial<ModuleBuildContext>): ModuleBuildCon
|
|||||||
em: new EntityManager([], dummyConnection),
|
em: new EntityManager([], dummyConnection),
|
||||||
emgr: new EventManager(),
|
emgr: new EventManager(),
|
||||||
guard: new Guard(),
|
guard: new Guard(),
|
||||||
|
flags: Module.ctx_flags,
|
||||||
...overrides
|
...overrides
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
"bin": "./dist/cli/index.js",
|
"bin": "./dist/cli/index.js",
|
||||||
"version": "0.4.0",
|
"version": "0.5.0-rc5",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build:all": "NODE_ENV=production bun run build.ts --minify --types --clean && bun run build:cli",
|
"build:all": "NODE_ENV=production bun run build.ts --minify --types --clean && bun run build:cli",
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -250,13 +250,11 @@ export class AppAuth extends Module<typeof authConfigSchema> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
registerEntities() {
|
registerEntities() {
|
||||||
const name = this.config.entity_name as "users";
|
const users = this.getUsersEntity(true);
|
||||||
const {
|
this.ensureSchema(
|
||||||
entities: { users }
|
|
||||||
} = this.ensureSchema(
|
|
||||||
em(
|
em(
|
||||||
{
|
{
|
||||||
[name]: entity(name, AppAuth.usersFields)
|
[users.name as "users"]: users
|
||||||
},
|
},
|
||||||
({ index }, { users }) => {
|
({ index }, { users }) => {
|
||||||
index(users).on(["email"], true).on(["strategy"]).on(["strategy_value"]);
|
index(users).on(["email"], true).on(["strategy"]).on(["strategy_value"]);
|
||||||
@@ -267,13 +265,13 @@ export class AppAuth extends Module<typeof authConfigSchema> {
|
|||||||
try {
|
try {
|
||||||
const roles = Object.keys(this.config.roles ?? {});
|
const roles = Object.keys(this.config.roles ?? {});
|
||||||
const field = make("role", enumm({ enum: roles }));
|
const field = make("role", enumm({ enum: roles }));
|
||||||
users.__experimental_replaceField("role", field);
|
users.__replaceField("role", field);
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const strategies = Object.keys(this.config.strategies ?? {});
|
const strategies = Object.keys(this.config.strategies ?? {});
|
||||||
const field = make("strategy", enumm({ enum: strategies }));
|
const field = make("strategy", enumm({ enum: strategies }));
|
||||||
users.__experimental_replaceField("strategy", field);
|
users.__replaceField("strategy", field);
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ export class Entity<
|
|||||||
return this.fields.find((field) => field.name === name);
|
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);
|
const index = this.fields.findIndex((f) => f.name === name);
|
||||||
if (index === -1) {
|
if (index === -1) {
|
||||||
throw new Error(`Field "${name}" not found on entity "${this.name}"`);
|
throw new Error(`Field "${name}" not found on entity "${this.name}"`);
|
||||||
|
|||||||
@@ -99,6 +99,16 @@ export class EntityManager<TBD extends object = DefaultDB> {
|
|||||||
this.entities.push(entity);
|
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 {
|
entity(e: Entity | keyof TBD | string): Entity {
|
||||||
let entity: Entity | undefined;
|
let entity: Entity | undefined;
|
||||||
if (typeof e === "string") {
|
if (typeof e === "string") {
|
||||||
|
|||||||
@@ -48,10 +48,9 @@ export class AppMedia extends Module<typeof mediaConfigSchema> {
|
|||||||
this.setupListeners();
|
this.setupListeners();
|
||||||
this.ctx.server.route(this.basepath, new MediaController(this).getController());
|
this.ctx.server.route(this.basepath, new MediaController(this).getController());
|
||||||
|
|
||||||
const mediaEntity = this.getMediaEntity(true);
|
const media = this.getMediaEntity(true);
|
||||||
const name = mediaEntity.name as "media";
|
|
||||||
this.ensureSchema(
|
this.ensureSchema(
|
||||||
em({ [name]: mediaEntity }, ({ index }, { media }) => {
|
em({ [media.name as "media"]: media }, ({ index }, { media }) => {
|
||||||
index(media).on(["path"], true).on(["reference"]);
|
index(media).on(["path"], true).on(["reference"]);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ import type { Guard } from "auth";
|
|||||||
import { SchemaObject } from "core";
|
import { SchemaObject } from "core";
|
||||||
import type { EventManager } from "core/events";
|
import type { EventManager } from "core/events";
|
||||||
import type { Static, TSchema } from "core/utils";
|
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";
|
import type { Hono } from "hono";
|
||||||
|
|
||||||
export type ServerEnv = {
|
export type ServerEnv = {
|
||||||
@@ -138,8 +139,6 @@ export abstract class Module<Schema extends TSchema = TSchema, ConfigSchema = St
|
|||||||
return this.config;
|
return this.config;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @todo: add a method to signal the requirement of database sync!!!
|
|
||||||
|
|
||||||
protected ensureEntity(entity: Entity) {
|
protected ensureEntity(entity: Entity) {
|
||||||
// check fields
|
// check fields
|
||||||
if (!this.ctx.em.hasEntity(entity.name)) {
|
if (!this.ctx.em.hasEntity(entity.name)) {
|
||||||
@@ -152,13 +151,18 @@ export abstract class Module<Schema extends TSchema = TSchema, ConfigSchema = St
|
|||||||
|
|
||||||
// if exists, check all fields required are there
|
// if exists, check all fields required are there
|
||||||
// @todo: check if the field also equal
|
// @todo: check if the field also equal
|
||||||
for (const field of entity.fields) {
|
for (const field of instance.fields) {
|
||||||
const _field = instance.field(field.name);
|
const _field = entity.field(field.name);
|
||||||
if (!_field) {
|
if (!_field) {
|
||||||
instance.addField(field);
|
entity.addField(field);
|
||||||
this.ctx.flags.sync_required = true;
|
this.ctx.flags.sync_required = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// replace entity (mainly to keep the ensured type)
|
||||||
|
this.ctx.em.__replaceEntity(
|
||||||
|
new Entity(entity.name, entity.fields, instance.config, entity.type)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ensureIndex(index: EntityIndex) {
|
protected ensureIndex(index: EntityIndex) {
|
||||||
|
|||||||
Reference in New Issue
Block a user