Fix entity referencing issue during post-seeded relational fetch

This commit is contained in:
dswbx
2025-01-10 17:28:43 +01:00
parent e94e8d8bd1
commit bb756548a6
11 changed files with 95 additions and 29 deletions

1
.gitignore vendored
View File

@@ -18,6 +18,7 @@ packages/media/.env
**/*/vite.config.ts.timestamp* **/*/vite.config.ts.timestamp*
.history .history
**/*/.db/* **/*/.db/*
**/*/.configs/*
**/*/*.db **/*/*.db
**/*/*.db-shm **/*/*.db-shm
**/*/*.db-wal **/*/*.db-wal

View File

@@ -3,7 +3,7 @@
"type": "module", "type": "module",
"sideEffects": false, "sideEffects": false,
"bin": "./dist/cli/index.js", "bin": "./dist/cli/index.js",
"version": "0.5.0-rc5", "version": "0.5.0-rc6",
"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",

View File

@@ -1,6 +1,7 @@
export class Exception extends Error { export class Exception extends Error {
code = 400; code = 400;
override name = "Exception"; override name = "Exception";
protected _context = undefined;
constructor(message: string, code?: number) { constructor(message: string, code?: number) {
super(message); super(message);
@@ -9,11 +10,16 @@ export class Exception extends Error {
} }
} }
context(context: any) {
this._context = context;
return this;
}
toJSON() { toJSON() {
return { return {
error: this.message, error: this.message,
type: this.name type: this.name,
//message: this.message context: this._context
}; };
} }
} }

View File

@@ -107,15 +107,15 @@ export class EntityManager<TBD extends object = DefaultDB> {
} }
this._entities[entityIndex] = entity; this._entities[entityIndex] = entity;
// caused issues because this.entity() was using a reference (for when initial config was given)
} }
entity(e: Entity | keyof TBD | string): Entity { entity(e: Entity | keyof TBD | string): Entity {
let entity: Entity | undefined; // make sure to always retrieve by name
if (typeof e === "string") { const entity = this.entities.find((entity) =>
entity = this.entities.find((entity) => entity.name === e); e instanceof Entity ? entity.name === e.name : entity.name === e
} else if (e instanceof Entity) { );
entity = e;
}
if (!entity) { if (!entity) {
// @ts-ignore // @ts-ignore

View File

@@ -58,7 +58,7 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
} }
private cloneFor(entity: Entity) { private cloneFor(entity: Entity) {
return new Repository(this.em, entity, this.emgr); return new Repository(this.em, this.em.entity(entity), this.emgr);
} }
private get conn() { private get conn() {
@@ -94,7 +94,10 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
if (invalid.length > 0) { if (invalid.length > 0) {
throw new InvalidSearchParamsException( throw new InvalidSearchParamsException(
`Invalid select field(s): ${invalid.join(", ")}` `Invalid select field(s): ${invalid.join(", ")}`
); ).context({
entity: entity.name,
valid: validated.select
});
} }
validated.select = options.select; validated.select = options.select;

View File

@@ -1,4 +1,4 @@
import type { EntityData, Field } from "data"; import type { EntityData, EntityManager, Field } from "data";
import { transform } from "lodash-es"; import { transform } from "lodash-es";
export function getDefaultValues(fields: Field[], data: EntityData): EntityData { export function getDefaultValues(fields: Field[], data: EntityData): EntityData {
@@ -48,3 +48,23 @@ export function getChangeSet(
{} as typeof formData {} as typeof formData
); );
} }
export function readableEmJson(_em: EntityManager) {
return {
entities: _em.entities.map((e) => ({
name: e.name,
fields: e.fields.map((f) => f.name),
type: e.type
})),
indices: _em.indices.map((i) => ({
name: i.name,
entity: i.entity.name,
fields: i.fields.map((f) => f.name),
unique: i.unique
})),
relations: _em.relations.all.map((r) => ({
name: r.getName(),
...r.toJSON()
}))
};
}

View File

@@ -26,7 +26,6 @@ export class AppMedia extends Module<typeof mediaConfigSchema> {
private _storage?: Storage; private _storage?: Storage;
override async build() { override async build() {
console.log("building");
if (!this.config.enabled) { if (!this.config.enabled) {
this.setBuilt(); this.setBuilt();
return; return;

View File

@@ -46,10 +46,12 @@ export abstract class Module<Schema extends TSchema = TSchema, ConfigSchema = St
} }
static ctx_flags = { static ctx_flags = {
sync_required: false sync_required: false,
ctx_reload_required: false
} as { } as {
// signal that a sync is required at the end of build // signal that a sync is required at the end of build
sync_required: boolean; sync_required: boolean;
ctx_reload_required: boolean;
}; };
onBeforeUpdate(from: ConfigSchema, to: ConfigSchema): ConfigSchema | Promise<ConfigSchema> { onBeforeUpdate(from: ConfigSchema, to: ConfigSchema): ConfigSchema | Promise<ConfigSchema> {

View File

@@ -400,8 +400,8 @@ export class ModuleManager {
}); });
} }
private async buildModules(options?: { graceful?: boolean }) { private async buildModules(options?: { graceful?: boolean; ignoreFlags?: boolean }) {
this.logger.log("buildModules() triggered", options?.graceful, this._built); this.logger.log("buildModules() triggered", options, this._built);
if (options?.graceful && this._built) { if (options?.graceful && this._built) {
this.logger.log("skipping build (graceful)"); this.logger.log("skipping build (graceful)");
return; return;
@@ -417,12 +417,25 @@ export class ModuleManager {
this._built = true; this._built = true;
this.logger.log("modules built", ctx.flags); this.logger.log("modules built", ctx.flags);
if (options?.ignoreFlags !== true) {
if (ctx.flags.sync_required) { if (ctx.flags.sync_required) {
ctx.flags.sync_required = false;
this.logger.log("db sync requested"); this.logger.log("db sync requested");
// sync db
await ctx.em.schema().sync({ force: true }); await ctx.em.schema().sync({ force: true });
await this.save(); await this.save();
ctx.flags.sync_required = false; // reset
} }
if (ctx.flags.ctx_reload_required) {
ctx.flags.ctx_reload_required = false;
this.logger.log("ctx reload requested");
this.ctx(true);
}
}
// reset all falgs
ctx.flags = Module.ctx_flags;
} }
async build() { async build() {

View File

@@ -125,12 +125,18 @@ export function DataTable<Data extends Record<string, any> = Record<string, any>
</thead> </thead>
) : null} ) : null}
<tbody> <tbody>
{!data || data.length === 0 ? ( {!data || !Array.isArray(data) || data.length === 0 ? (
<tr> <tr>
<td colSpan={select.length + (checkable ? 1 : 0)}> <td colSpan={select.length + (checkable ? 1 : 0)}>
<div className="flex flex-col gap-2 p-8 justify-center items-center border-t border-muted"> <div className="flex flex-col gap-2 p-8 justify-center items-center border-t border-muted">
<i className="opacity-50"> <i className="opacity-50">
{Array.isArray(data) ? "No data to show" : "Loading..."} {Array.isArray(data) ? (
"No data to show"
) : !data ? (
"Loading..."
) : (
<pre>{JSON.stringify(data, null, 2)}</pre>
)}
</i> </i>
</div> </div>
</td> </td>

View File

@@ -1,3 +1,4 @@
import { readFile } from "node:fs/promises";
import { serveStatic } from "@hono/node-server/serve-static"; import { serveStatic } from "@hono/node-server/serve-static";
import { createClient } from "@libsql/client/node"; import { createClient } from "@libsql/client/node";
import { App, registries } from "./src"; import { App, registries } from "./src";
@@ -6,7 +7,15 @@ import { StorageLocalAdapter } from "./src/media/storage/adapters/StorageLocalAd
registries.media.register("local", StorageLocalAdapter); registries.media.register("local", StorageLocalAdapter);
const credentials = { const run_example: string | boolean = false;
//run_example = "ex-admin-rich";
const credentials = run_example
? {
url: `file:.configs/${run_example}.db`
//url: ":memory:"
}
: {
url: import.meta.env.VITE_DB_URL!, url: import.meta.env.VITE_DB_URL!,
authToken: import.meta.env.VITE_DB_TOKEN! authToken: import.meta.env.VITE_DB_TOKEN!
}; };
@@ -16,10 +25,17 @@ if (!credentials.url) {
const connection = new LibsqlConnection(createClient(credentials)); const connection = new LibsqlConnection(createClient(credentials));
let initialConfig: any = undefined;
if (run_example) {
const { version, ...config } = JSON.parse(
await readFile(`.configs/${run_example}.json`, "utf-8")
);
initialConfig = config;
}
export default { export default {
async fetch(request: Request) { async fetch(request: Request) {
const app = App.create({ connection }); const app = App.create({ connection, initialConfig });
app.emgr.onEvent( app.emgr.onEvent(
App.Events.AppBuiltEvent, App.Events.AppBuiltEvent,
async () => { async () => {