mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-17 04:46:05 +00:00
added format command and added trailing commas to reduce conflicts
This commit is contained in:
@@ -5,7 +5,7 @@ import {
|
||||
EntityIndex,
|
||||
type EntityManager,
|
||||
constructEntity,
|
||||
constructRelation
|
||||
constructRelation,
|
||||
} from "data";
|
||||
import { Module } from "modules/Module";
|
||||
import { DataController } from "./api/DataController";
|
||||
@@ -16,7 +16,7 @@ export class AppData extends Module<typeof dataConfigSchema> {
|
||||
const {
|
||||
entities: _entities = {},
|
||||
relations: _relations = {},
|
||||
indices: _indices = {}
|
||||
indices: _indices = {},
|
||||
} = this.config;
|
||||
|
||||
this.ctx.logger.context("AppData").log("building with entities", Object.keys(_entities));
|
||||
@@ -33,7 +33,7 @@ export class AppData extends Module<typeof dataConfigSchema> {
|
||||
};
|
||||
|
||||
const relations = transformObject(_relations, (relation) =>
|
||||
constructRelation(relation, _entity)
|
||||
constructRelation(relation, _entity),
|
||||
);
|
||||
|
||||
const indices = transformObject(_indices, (index, name) => {
|
||||
@@ -56,7 +56,7 @@ export class AppData extends Module<typeof dataConfigSchema> {
|
||||
|
||||
this.ctx.server.route(
|
||||
this.basepath,
|
||||
new DataController(this.ctx, this.config).getController()
|
||||
new DataController(this.ctx, this.config).getController(),
|
||||
);
|
||||
this.ctx.guard.registerPermissions(Object.values(DataPermissions));
|
||||
|
||||
@@ -84,7 +84,7 @@ export class AppData extends Module<typeof dataConfigSchema> {
|
||||
override toJSON(secrets?: boolean): AppDataConfig {
|
||||
return {
|
||||
...this.config,
|
||||
...this.em.toJSON()
|
||||
...this.em.toJSON(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,25 +13,25 @@ export class DataApi extends ModuleApi<DataApiOptions> {
|
||||
basepath: "/api/data",
|
||||
queryLengthLimit: 1000,
|
||||
defaultQuery: {
|
||||
limit: 10
|
||||
}
|
||||
limit: 10,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
readOne<E extends keyof DB | string, Data = E extends keyof DB ? DB[E] : EntityData>(
|
||||
entity: E,
|
||||
id: PrimaryFieldType,
|
||||
query: Omit<RepoQueryIn, "where" | "limit" | "offset"> = {}
|
||||
query: Omit<RepoQueryIn, "where" | "limit" | "offset"> = {},
|
||||
) {
|
||||
return this.get<Pick<RepositoryResponse<Data>, "meta" | "data">>(
|
||||
["entity", entity as any, id],
|
||||
query
|
||||
query,
|
||||
);
|
||||
}
|
||||
|
||||
readMany<E extends keyof DB | string, Data = E extends keyof DB ? DB[E] : EntityData>(
|
||||
entity: E,
|
||||
query: RepoQueryIn = {}
|
||||
query: RepoQueryIn = {},
|
||||
) {
|
||||
type T = Pick<RepositoryResponse<Data[]>, "meta" | "data">;
|
||||
|
||||
@@ -48,17 +48,17 @@ export class DataApi extends ModuleApi<DataApiOptions> {
|
||||
readManyByReference<
|
||||
E extends keyof DB | string,
|
||||
R extends keyof DB | string,
|
||||
Data = R extends keyof DB ? DB[R] : EntityData
|
||||
Data = R extends keyof DB ? DB[R] : EntityData,
|
||||
>(entity: E, id: PrimaryFieldType, reference: R, query: RepoQueryIn = {}) {
|
||||
return this.get<Pick<RepositoryResponse<Data[]>, "meta" | "data">>(
|
||||
["entity", entity as any, id, reference],
|
||||
query ?? this.options.defaultQuery
|
||||
query ?? this.options.defaultQuery,
|
||||
);
|
||||
}
|
||||
|
||||
createOne<E extends keyof DB | string, Data = E extends keyof DB ? DB[E] : EntityData>(
|
||||
entity: E,
|
||||
input: Omit<Data, "id">
|
||||
input: Omit<Data, "id">,
|
||||
) {
|
||||
return this.post<RepositoryResponse<Data>>(["entity", entity as any], input);
|
||||
}
|
||||
@@ -66,14 +66,14 @@ export class DataApi extends ModuleApi<DataApiOptions> {
|
||||
updateOne<E extends keyof DB | string, Data = E extends keyof DB ? DB[E] : EntityData>(
|
||||
entity: E,
|
||||
id: PrimaryFieldType,
|
||||
input: Partial<Omit<Data, "id">>
|
||||
input: Partial<Omit<Data, "id">>,
|
||||
) {
|
||||
return this.patch<RepositoryResponse<Data>>(["entity", entity as any, id], input);
|
||||
}
|
||||
|
||||
deleteOne<E extends keyof DB | string, Data = E extends keyof DB ? DB[E] : EntityData>(
|
||||
entity: E,
|
||||
id: PrimaryFieldType
|
||||
id: PrimaryFieldType,
|
||||
) {
|
||||
return this.delete<RepositoryResponse<Data>>(["entity", entity as any, id]);
|
||||
}
|
||||
@@ -81,7 +81,7 @@ export class DataApi extends ModuleApi<DataApiOptions> {
|
||||
count<E extends keyof DB | string>(entity: E, where: RepoQueryIn["where"] = {}) {
|
||||
return this.post<RepositoryResponse<{ entity: E; count: number }>>(
|
||||
["entity", entity as any, "fn", "count"],
|
||||
where
|
||||
where,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
type MutatorResponse,
|
||||
type RepoQuery,
|
||||
type RepositoryResponse,
|
||||
querySchema
|
||||
querySchema,
|
||||
} from "data";
|
||||
import type { Handler } from "hono/types";
|
||||
import type { ModuleBuildContext } from "modules";
|
||||
@@ -18,7 +18,7 @@ import type { AppDataConfig } from "../data-schema";
|
||||
export class DataController extends Controller {
|
||||
constructor(
|
||||
private readonly ctx: ModuleBuildContext,
|
||||
private readonly config: AppDataConfig
|
||||
private readonly config: AppDataConfig,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
@@ -32,7 +32,7 @@ export class DataController extends Controller {
|
||||
}
|
||||
|
||||
repoResult<T extends RepositoryResponse<any> = RepositoryResponse>(
|
||||
res: T
|
||||
res: T,
|
||||
): Pick<T, "meta" | "data"> {
|
||||
let meta: Partial<RepositoryResponse["meta"]> = {};
|
||||
|
||||
@@ -48,7 +48,7 @@ export class DataController extends Controller {
|
||||
//return objectCleanEmpty(template) as any;
|
||||
// filter empty
|
||||
return Object.fromEntries(
|
||||
Object.entries(template).filter(([_, v]) => typeof v !== "undefined" && v !== null)
|
||||
Object.entries(template).filter(([_, v]) => typeof v !== "undefined" && v !== null),
|
||||
) as any;
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ export class DataController extends Controller {
|
||||
handler("data info", (c) => {
|
||||
// sample implementation
|
||||
return c.json(this.em.toJSON());
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
// sync endpoint
|
||||
@@ -103,7 +103,7 @@ export class DataController extends Controller {
|
||||
//console.log("tables", tables);
|
||||
const changes = await this.em.schema().sync({
|
||||
force,
|
||||
drop
|
||||
drop,
|
||||
});
|
||||
return c.json({ tables: tables.map((t) => t.name), changes });
|
||||
});
|
||||
@@ -118,14 +118,14 @@ export class DataController extends Controller {
|
||||
this.em.entities.map((e) => [
|
||||
e.name,
|
||||
{
|
||||
$ref: `${this.config.basepath}/schemas/${e.name}`
|
||||
}
|
||||
])
|
||||
$ref: `${this.config.basepath}/schemas/${e.name}`,
|
||||
},
|
||||
]),
|
||||
);
|
||||
return c.json({
|
||||
$schema: "https://json-schema.org/draft/2020-12/schema",
|
||||
$id,
|
||||
properties: schemas
|
||||
properties: schemas,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -137,8 +137,8 @@ export class DataController extends Controller {
|
||||
"param",
|
||||
Type.Object({
|
||||
entity: Type.String(),
|
||||
context: Type.Optional(StringEnum(["create", "update"]))
|
||||
})
|
||||
context: Type.Optional(StringEnum(["create", "update"])),
|
||||
}),
|
||||
),
|
||||
async (c) => {
|
||||
//console.log("request", c.req.raw);
|
||||
@@ -157,9 +157,9 @@ export class DataController extends Controller {
|
||||
$id,
|
||||
title: _entity.label,
|
||||
$comment: _entity.config.description,
|
||||
...schema
|
||||
...schema,
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// entity endpoints
|
||||
@@ -178,7 +178,7 @@ export class DataController extends Controller {
|
||||
const $rels = (r: any) =>
|
||||
r.map((r: any) => ({
|
||||
entity: r.other(_entity).entity.name,
|
||||
ref: r.other(_entity).reference
|
||||
ref: r.other(_entity).reference,
|
||||
}));
|
||||
|
||||
return c.json({
|
||||
@@ -188,8 +188,8 @@ export class DataController extends Controller {
|
||||
all: $rels(this.em.relations.relationsOf(_entity)),
|
||||
listable: $rels(this.em.relations.listableRelationsOf(_entity)),
|
||||
source: $rels(this.em.relations.sourceRelationsOf(_entity)),
|
||||
target: $rels(this.em.relations.targetRelationsOf(_entity))
|
||||
}
|
||||
target: $rels(this.em.relations.targetRelationsOf(_entity)),
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -222,7 +222,7 @@ export class DataController extends Controller {
|
||||
const where = (await c.req.json()) as any;
|
||||
const result = await this.em.repository(entity).count(where);
|
||||
return c.json({ entity, count: result.count });
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// fn: exists
|
||||
@@ -239,7 +239,7 @@ export class DataController extends Controller {
|
||||
const where = c.req.json() as any;
|
||||
const result = await this.em.repository(entity).exists(where);
|
||||
return c.json({ entity, exists: result.exists });
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -263,7 +263,7 @@ export class DataController extends Controller {
|
||||
const result = await this.em.repository(entity).findMany(options);
|
||||
|
||||
return c.json(this.repoResult(result), { status: result.data ? 200 : 404 });
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// read one
|
||||
@@ -274,8 +274,8 @@ export class DataController extends Controller {
|
||||
"param",
|
||||
Type.Object({
|
||||
entity: Type.String(),
|
||||
id: tbNumber
|
||||
})
|
||||
id: tbNumber,
|
||||
}),
|
||||
),
|
||||
tb("query", querySchema),
|
||||
async (c) => {
|
||||
@@ -287,7 +287,7 @@ export class DataController extends Controller {
|
||||
const result = await this.em.repository(entity).findId(Number(id), options);
|
||||
|
||||
return c.json(this.repoResult(result), { status: result.data ? 200 : 404 });
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// read many by reference
|
||||
@@ -299,8 +299,8 @@ export class DataController extends Controller {
|
||||
Type.Object({
|
||||
entity: Type.String(),
|
||||
id: tbNumber,
|
||||
reference: Type.String()
|
||||
})
|
||||
reference: Type.String(),
|
||||
}),
|
||||
),
|
||||
tb("query", querySchema),
|
||||
async (c) => {
|
||||
@@ -315,7 +315,7 @@ export class DataController extends Controller {
|
||||
.findManyByReference(Number(id), reference, options);
|
||||
|
||||
return c.json(this.repoResult(result), { status: result.data ? 200 : 404 });
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// func query
|
||||
@@ -334,7 +334,7 @@ export class DataController extends Controller {
|
||||
const result = await this.em.repository(entity).findMany(options);
|
||||
|
||||
return c.json(this.repoResult(result), { status: result.data ? 200 : 404 });
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -354,7 +354,7 @@ export class DataController extends Controller {
|
||||
const result = await this.em.mutator(entity).insertOne(body);
|
||||
|
||||
return c.json(this.mutatorResult(result), 201);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// update many
|
||||
@@ -366,8 +366,8 @@ export class DataController extends Controller {
|
||||
"json",
|
||||
Type.Object({
|
||||
update: Type.Object({}),
|
||||
where: querySchema.properties.where
|
||||
})
|
||||
where: querySchema.properties.where,
|
||||
}),
|
||||
),
|
||||
async (c) => {
|
||||
const { entity } = c.req.param();
|
||||
@@ -381,7 +381,7 @@ export class DataController extends Controller {
|
||||
const result = await this.em.mutator(entity).updateWhere(update, where);
|
||||
|
||||
return c.json(this.mutatorResult(result));
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// update one
|
||||
@@ -398,7 +398,7 @@ export class DataController extends Controller {
|
||||
const result = await this.em.mutator(entity).updateOne(Number(id), body);
|
||||
|
||||
return c.json(this.mutatorResult(result));
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// delete one
|
||||
@@ -414,7 +414,7 @@ export class DataController extends Controller {
|
||||
const result = await this.em.mutator(entity).deleteOne(Number(id));
|
||||
|
||||
return c.json(this.mutatorResult(result));
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// delete many
|
||||
@@ -432,7 +432,7 @@ export class DataController extends Controller {
|
||||
const result = await this.em.mutator(entity).deleteWhere(where);
|
||||
|
||||
return c.json(this.mutatorResult(result));
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return hono;
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
type SelectQueryBuilder,
|
||||
type SelectQueryNode,
|
||||
type Simplify,
|
||||
sql
|
||||
sql,
|
||||
} from "kysely";
|
||||
|
||||
export type QB = SelectQueryBuilder<any, any, any>;
|
||||
@@ -33,7 +33,7 @@ export type DbFunctions = {
|
||||
jsonObjectFrom<O>(expr: SelectQueryBuilderExpression<O>): RawBuilder<Simplify<O> | null>;
|
||||
jsonArrayFrom<O>(expr: SelectQueryBuilderExpression<O>): RawBuilder<Simplify<O>[]>;
|
||||
jsonBuildObject<O extends Record<string, Expression<unknown>>>(
|
||||
obj: O
|
||||
obj: O,
|
||||
): RawBuilder<
|
||||
Simplify<{
|
||||
[K in keyof O]: O[K] extends Expression<infer V> ? V : never;
|
||||
@@ -49,7 +49,7 @@ export abstract class Connection<DB = any> {
|
||||
constructor(
|
||||
kysely: Kysely<DB>,
|
||||
public fn: Partial<DbFunctions> = {},
|
||||
protected plugins: KyselyPlugin[] = []
|
||||
protected plugins: KyselyPlugin[] = [],
|
||||
) {
|
||||
this.kysely = kysely;
|
||||
this[CONN_SYMBOL] = true;
|
||||
@@ -84,7 +84,7 @@ export abstract class Connection<DB = any> {
|
||||
}
|
||||
|
||||
protected async batch<Queries extends QB[]>(
|
||||
queries: [...Queries]
|
||||
queries: [...Queries],
|
||||
): Promise<{
|
||||
[K in keyof Queries]: Awaited<ReturnType<Queries[K]["execute"]>>;
|
||||
}> {
|
||||
@@ -92,7 +92,7 @@ export abstract class Connection<DB = any> {
|
||||
}
|
||||
|
||||
async batchQuery<Queries extends QB[]>(
|
||||
queries: [...Queries]
|
||||
queries: [...Queries],
|
||||
): Promise<{
|
||||
[K in keyof Queries]: Awaited<ReturnType<Queries[K]["execute"]>>;
|
||||
}> {
|
||||
|
||||
@@ -15,7 +15,7 @@ export type LibSqlCredentials = Config & {
|
||||
class CustomLibsqlDialect extends LibsqlDialect {
|
||||
override createIntrospector(db: Kysely<any>): DatabaseIntrospector {
|
||||
return new SqliteIntrospector(db, {
|
||||
excludeTables: ["libsql_wasm_func_table"]
|
||||
excludeTables: ["libsql_wasm_func_table"],
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,7 @@ export class LibsqlConnection extends SqliteConnection {
|
||||
const kysely = new Kysely({
|
||||
// @ts-expect-error libsql has type issues
|
||||
dialect: new CustomLibsqlDialect({ client }),
|
||||
plugins
|
||||
plugins,
|
||||
});
|
||||
|
||||
super(kysely, {}, plugins);
|
||||
@@ -64,7 +64,7 @@ export class LibsqlConnection extends SqliteConnection {
|
||||
}
|
||||
|
||||
protected override async batch<Queries extends QB[]>(
|
||||
queries: [...Queries]
|
||||
queries: [...Queries],
|
||||
): Promise<{
|
||||
[K in keyof Queries]: Awaited<ReturnType<Queries[K]["execute"]>>;
|
||||
}> {
|
||||
@@ -72,7 +72,7 @@ export class LibsqlConnection extends SqliteConnection {
|
||||
const compiled = q.compile();
|
||||
return {
|
||||
sql: compiled.sql,
|
||||
args: compiled.parameters as any[]
|
||||
args: compiled.parameters as any[],
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -10,9 +10,9 @@ export class SqliteConnection extends Connection {
|
||||
...fn,
|
||||
jsonArrayFrom,
|
||||
jsonObjectFrom,
|
||||
jsonBuildObject
|
||||
jsonBuildObject,
|
||||
},
|
||||
plugins
|
||||
plugins,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import type {
|
||||
ExpressionBuilder,
|
||||
Kysely,
|
||||
SchemaMetadata,
|
||||
TableMetadata
|
||||
TableMetadata,
|
||||
} from "kysely";
|
||||
import { DEFAULT_MIGRATION_LOCK_TABLE, DEFAULT_MIGRATION_TABLE, sql } from "kysely";
|
||||
import type { ConnectionIntrospector, IndexMetadata } from "./Connection";
|
||||
@@ -62,7 +62,7 @@ export class SqliteIntrospector implements DatabaseIntrospector, ConnectionIntro
|
||||
seqno: number;
|
||||
cid: number;
|
||||
name: string;
|
||||
}>`pragma_index_info(${index})`.as("index_info")
|
||||
}>`pragma_index_info(${index})`.as("index_info"),
|
||||
)
|
||||
.select(["seqno", "cid", "name"])
|
||||
.orderBy("cid")
|
||||
@@ -74,8 +74,8 @@ export class SqliteIntrospector implements DatabaseIntrospector, ConnectionIntro
|
||||
isUnique: isUnique,
|
||||
columns: columns.map((col) => ({
|
||||
name: col.name,
|
||||
order: col.seqno
|
||||
}))
|
||||
order: col.seqno,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ export class SqliteIntrospector implements DatabaseIntrospector, ConnectionIntro
|
||||
}
|
||||
|
||||
async getTables(
|
||||
options: DatabaseMetadataOptions = { withInternalKyselyTables: false }
|
||||
options: DatabaseMetadataOptions = { withInternalKyselyTables: false },
|
||||
): Promise<TableMetadata[]> {
|
||||
let query = this.#db
|
||||
.selectFrom("sqlite_master")
|
||||
@@ -99,7 +99,7 @@ export class SqliteIntrospector implements DatabaseIntrospector, ConnectionIntro
|
||||
|
||||
if (!options.withInternalKyselyTables) {
|
||||
query = query.where(
|
||||
this.excludeTables([DEFAULT_MIGRATION_TABLE, DEFAULT_MIGRATION_LOCK_TABLE])
|
||||
this.excludeTables([DEFAULT_MIGRATION_TABLE, DEFAULT_MIGRATION_LOCK_TABLE]),
|
||||
);
|
||||
}
|
||||
if (this._excludeTables.length > 0) {
|
||||
@@ -112,7 +112,7 @@ export class SqliteIntrospector implements DatabaseIntrospector, ConnectionIntro
|
||||
|
||||
async getMetadata(options?: DatabaseMetadataOptions): Promise<DatabaseMetadata> {
|
||||
return {
|
||||
tables: await this.getTables(options)
|
||||
tables: await this.getTables(options),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ export class SqliteIntrospector implements DatabaseIntrospector, ConnectionIntro
|
||||
type: string;
|
||||
notnull: 0 | 1;
|
||||
dflt_value: any;
|
||||
}>`pragma_table_info(${table})`.as("table_info")
|
||||
}>`pragma_table_info(${table})`.as("table_info"),
|
||||
)
|
||||
.select(["name", "type", "notnull", "dflt_value"])
|
||||
.orderBy("cid")
|
||||
@@ -157,8 +157,8 @@ export class SqliteIntrospector implements DatabaseIntrospector, ConnectionIntro
|
||||
isNullable: !col.notnull,
|
||||
isAutoIncrementing: col.name === autoIncrementCol,
|
||||
hasDefaultValue: col.dflt_value != null,
|
||||
comment: undefined
|
||||
}))
|
||||
comment: undefined,
|
||||
})),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { SqliteIntrospector } from "./SqliteIntrospector";
|
||||
class CustomSqliteDialect extends SqliteDialect {
|
||||
override createIntrospector(db: Kysely<any>): DatabaseIntrospector {
|
||||
return new SqliteIntrospector(db, {
|
||||
excludeTables: ["test_table"]
|
||||
excludeTables: ["test_table"],
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ export class SqliteLocalConnection extends SqliteConnection {
|
||||
const plugins = [new ParseJSONResultsPlugin()];
|
||||
const kysely = new Kysely({
|
||||
dialect: new CustomSqliteDialect({ database }),
|
||||
plugins
|
||||
plugins,
|
||||
//log: ["query"],
|
||||
});
|
||||
|
||||
|
||||
@@ -4,14 +4,14 @@ import {
|
||||
RelationClassMap,
|
||||
RelationFieldClassMap,
|
||||
entityConfigSchema,
|
||||
entityTypes
|
||||
entityTypes,
|
||||
} from "data";
|
||||
import { MediaField, mediaFieldConfigSchema } from "../media/MediaField";
|
||||
|
||||
export const FIELDS = {
|
||||
...FieldClassMap,
|
||||
...RelationFieldClassMap,
|
||||
media: { schema: mediaFieldConfigSchema, field: MediaField }
|
||||
media: { schema: mediaFieldConfigSchema, field: MediaField },
|
||||
};
|
||||
export type FieldType = keyof typeof FIELDS;
|
||||
|
||||
@@ -21,11 +21,11 @@ export const fieldsSchemaObject = objectTransform(FIELDS, (field, name) => {
|
||||
return Type.Object(
|
||||
{
|
||||
type: Type.Const(name, { default: name, readOnly: true }),
|
||||
config: Type.Optional(field.schema)
|
||||
config: Type.Optional(field.schema),
|
||||
},
|
||||
{
|
||||
title: name
|
||||
}
|
||||
title: name,
|
||||
},
|
||||
);
|
||||
});
|
||||
export const fieldsSchema = Type.Union(Object.values(fieldsSchemaObject));
|
||||
@@ -37,7 +37,7 @@ export const entitiesSchema = Type.Object({
|
||||
//name: Type.String(),
|
||||
type: Type.Optional(Type.String({ enum: entityTypes, default: "regular", readOnly: true })),
|
||||
config: Type.Optional(entityConfigSchema),
|
||||
fields: Type.Optional(entityFields)
|
||||
fields: Type.Optional(entityFields),
|
||||
});
|
||||
export type TAppDataEntity = Static<typeof entitiesSchema>;
|
||||
|
||||
@@ -47,11 +47,11 @@ export const relationsSchema = Object.entries(RelationClassMap).map(([name, rela
|
||||
type: Type.Const(name, { default: name, readOnly: true }),
|
||||
source: Type.String(),
|
||||
target: Type.String(),
|
||||
config: Type.Optional(relationClass.schema)
|
||||
config: Type.Optional(relationClass.schema),
|
||||
},
|
||||
{
|
||||
title: name
|
||||
}
|
||||
title: name,
|
||||
},
|
||||
);
|
||||
});
|
||||
export type TAppDataRelation = Static<(typeof relationsSchema)[number]>;
|
||||
@@ -61,11 +61,11 @@ export const indicesSchema = Type.Object(
|
||||
entity: Type.String(),
|
||||
fields: Type.Array(Type.String(), { minItems: 1 }),
|
||||
//name: Type.Optional(Type.String()),
|
||||
unique: Type.Optional(Type.Boolean({ default: false }))
|
||||
unique: Type.Optional(Type.Boolean({ default: false })),
|
||||
},
|
||||
{
|
||||
additionalProperties: false
|
||||
}
|
||||
additionalProperties: false,
|
||||
},
|
||||
);
|
||||
|
||||
export const dataConfigSchema = Type.Object(
|
||||
@@ -73,11 +73,11 @@ export const dataConfigSchema = Type.Object(
|
||||
basepath: Type.Optional(Type.String({ default: "/api/data" })),
|
||||
entities: Type.Optional(StringRecord(entitiesSchema, { default: {} })),
|
||||
relations: Type.Optional(StringRecord(Type.Union(relationsSchema), { default: {} })),
|
||||
indices: Type.Optional(StringRecord(indicesSchema, { default: {} }))
|
||||
indices: Type.Optional(StringRecord(indicesSchema, { default: {} })),
|
||||
},
|
||||
{
|
||||
additionalProperties: false
|
||||
}
|
||||
additionalProperties: false,
|
||||
},
|
||||
);
|
||||
|
||||
export type AppDataConfig = Static<typeof dataConfigSchema>;
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
Type,
|
||||
parse,
|
||||
snakeToPascalWithSpaces,
|
||||
transformObject
|
||||
transformObject,
|
||||
} from "core/utils";
|
||||
import { type Field, PrimaryField, type TActionContext, type TRenderContext } from "../fields";
|
||||
|
||||
@@ -16,11 +16,11 @@ export const entityConfigSchema = Type.Object(
|
||||
name_singular: Type.Optional(Type.String()),
|
||||
description: Type.Optional(Type.String()),
|
||||
sort_field: Type.Optional(Type.String({ default: config.data.default_primary_field })),
|
||||
sort_dir: Type.Optional(StringEnum(["asc", "desc"], { default: "asc" }))
|
||||
sort_dir: Type.Optional(StringEnum(["asc", "desc"], { default: "asc" })),
|
||||
},
|
||||
{
|
||||
additionalProperties: false
|
||||
}
|
||||
additionalProperties: false,
|
||||
},
|
||||
);
|
||||
|
||||
export type EntityConfig = Static<typeof entityConfigSchema>;
|
||||
@@ -42,7 +42,7 @@ export type TEntityType = (typeof entityTypes)[number];
|
||||
*/
|
||||
export class Entity<
|
||||
EntityName extends string = string,
|
||||
Fields extends Record<string, Field<any, any, any>> = Record<string, Field<any, any, any>>
|
||||
Fields extends Record<string, Field<any, any, any>> = Record<string, Field<any, any, any>>,
|
||||
> {
|
||||
readonly #_name!: EntityName;
|
||||
readonly #_fields!: Fields; // only for types
|
||||
@@ -99,14 +99,14 @@ export class Entity<
|
||||
getDefaultSort() {
|
||||
return {
|
||||
by: this.config.sort_field ?? "id",
|
||||
dir: this.config.sort_dir ?? "asc"
|
||||
dir: this.config.sort_dir ?? "asc",
|
||||
};
|
||||
}
|
||||
|
||||
getAliasedSelectFrom(
|
||||
select: string[],
|
||||
_alias?: string,
|
||||
context?: TActionContext | TRenderContext
|
||||
context?: TActionContext | TRenderContext,
|
||||
): string[] {
|
||||
const alias = _alias ?? this.name;
|
||||
return this.getFields()
|
||||
@@ -114,7 +114,7 @@ export class Entity<
|
||||
(field) =>
|
||||
!field.isVirtual() &&
|
||||
!field.isHidden(context ?? "read") &&
|
||||
select.includes(field.name)
|
||||
select.includes(field.name),
|
||||
)
|
||||
.map((field) => (alias ? `${alias}.${field.name} as ${field.name}` : field.name));
|
||||
}
|
||||
@@ -206,7 +206,7 @@ export class Entity<
|
||||
options?: {
|
||||
explain?: boolean;
|
||||
ignoreUnknown?: boolean;
|
||||
}
|
||||
},
|
||||
): boolean {
|
||||
if (typeof data !== "object") {
|
||||
if (options?.explain) {
|
||||
@@ -224,7 +224,7 @@ export class Entity<
|
||||
if (unknown_keys.length > 0) {
|
||||
if (options?.explain) {
|
||||
throw new Error(
|
||||
`Entity "${this.name}" data must only contain known keys, unknown: "${unknown_keys}"`
|
||||
`Entity "${this.name}" data must only contain known keys, unknown: "${unknown_keys}"`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -265,10 +265,10 @@ export class Entity<
|
||||
$comment: field.config.description,
|
||||
$field: field.type,
|
||||
readOnly: !fillable ? true : undefined,
|
||||
...field.toJsonSchema()
|
||||
...field.toJsonSchema(),
|
||||
};
|
||||
}),
|
||||
{ additionalProperties: false }
|
||||
{ additionalProperties: false },
|
||||
);
|
||||
|
||||
return options?.clean ? JSON.parse(JSON.stringify(schema)) : schema;
|
||||
@@ -280,7 +280,7 @@ export class Entity<
|
||||
type: this.type,
|
||||
//fields: transformObject(this.fields, (field) => field.toJSON()),
|
||||
fields: Object.fromEntries(this.fields.map((field) => [field.name, field.toJSON()])),
|
||||
config: this.config
|
||||
config: this.config,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Connection } from "../connection/Connection";
|
||||
import {
|
||||
EntityNotDefinedException,
|
||||
TransformRetrieveFailedException,
|
||||
UnableToConnectException
|
||||
UnableToConnectException,
|
||||
} from "../errors";
|
||||
import { MutatorEvents, RepositoryEvents } from "../events";
|
||||
import type { Field } from "../fields/Field";
|
||||
@@ -18,7 +18,7 @@ import { type EntityData, Mutator, Repository } from "./index";
|
||||
|
||||
type EntitySchema<
|
||||
TBD extends object = DefaultDB,
|
||||
E extends Entity | keyof TBD | string = string
|
||||
E extends Entity | keyof TBD | string = string,
|
||||
> = E extends Entity<infer Name>
|
||||
? Name extends keyof TBD
|
||||
? Name
|
||||
@@ -42,7 +42,7 @@ export class EntityManager<TBD extends object = DefaultDB> {
|
||||
connection: Connection,
|
||||
relations: EntityRelation[] = [],
|
||||
indices: EntityIndex[] = [],
|
||||
emgr?: EventManager<any>
|
||||
emgr?: EventManager<any>,
|
||||
) {
|
||||
// add entities & relations
|
||||
entities.forEach((entity) => this.addEntity(entity));
|
||||
@@ -114,11 +114,11 @@ export class EntityManager<TBD extends object = DefaultDB> {
|
||||
|
||||
entity<Silent extends true | false = false>(
|
||||
e: Entity | keyof TBD | string,
|
||||
silent?: Silent
|
||||
silent?: Silent,
|
||||
): Silent extends true ? Entity | undefined : Entity {
|
||||
// make sure to always retrieve by name
|
||||
const entity = this.entities.find((entity) =>
|
||||
e instanceof Entity ? entity.name === e.name : entity.name === e
|
||||
e instanceof Entity ? entity.name === e.name : entity.name === e,
|
||||
);
|
||||
|
||||
if (!entity) {
|
||||
@@ -177,7 +177,7 @@ export class EntityManager<TBD extends object = DefaultDB> {
|
||||
if (found) {
|
||||
throw new Error(
|
||||
`Relation "${relation.type}" between "${relation.source.entity.name}" ` +
|
||||
`and "${relation.target.entity.name}" already exists`
|
||||
`and "${relation.target.entity.name}" already exists`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -206,7 +206,7 @@ export class EntityManager<TBD extends object = DefaultDB> {
|
||||
}
|
||||
|
||||
repository<E extends Entity | keyof TBD | string>(
|
||||
entity: E
|
||||
entity: E,
|
||||
): Repository<TBD, EntitySchema<TBD, E>> {
|
||||
return this.repo(entity);
|
||||
}
|
||||
@@ -277,7 +277,7 @@ export class EntityManager<TBD extends object = DefaultDB> {
|
||||
row[key] = field.transformRetrieve(value as any);
|
||||
} catch (e: any) {
|
||||
throw new TransformRetrieveFailedException(
|
||||
`"${field.type}" field "${key}" on entity "${entity.name}": ${e.message}`
|
||||
`"${field.type}" field "${key}" on entity "${entity.name}": ${e.message}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -293,7 +293,7 @@ export class EntityManager<TBD extends object = DefaultDB> {
|
||||
entities: Object.fromEntries(this.entities.map((e) => [e.name, e.toJSON()])),
|
||||
relations: Object.fromEntries(this.relations.all.map((r) => [r.getName(), r.toJSON()])),
|
||||
//relations: this.relations.all.map((r) => r.toJSON()),
|
||||
indices: Object.fromEntries(this.indices.map((i) => [i.name, i.toJSON()]))
|
||||
indices: Object.fromEntries(this.indices.map((i) => [i.name, i.toJSON()])),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ export class Mutator<
|
||||
TBD extends object = DefaultDB,
|
||||
TB extends keyof TBD = any,
|
||||
Output = TBD[TB],
|
||||
Input = Omit<Output, "id">
|
||||
Input = Omit<Output, "id">,
|
||||
> implements EmitsEvents
|
||||
{
|
||||
em: EntityManager<TBD>;
|
||||
@@ -85,7 +85,7 @@ export class Mutator<
|
||||
`Field "${key}" not found on entity "${entity.name}". Fields: ${entity
|
||||
.getFillableFields()
|
||||
.map((f) => f.name)
|
||||
.join(", ")}`
|
||||
.join(", ")}`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ export class Mutator<
|
||||
sql,
|
||||
parameters: [...parameters],
|
||||
result: result,
|
||||
data
|
||||
data,
|
||||
};
|
||||
} catch (e) {
|
||||
// @todo: redact
|
||||
@@ -139,14 +139,14 @@ export class Mutator<
|
||||
}
|
||||
|
||||
const result = await this.emgr.emit(
|
||||
new Mutator.Events.MutatorInsertBefore({ entity, data: data as any })
|
||||
new Mutator.Events.MutatorInsertBefore({ entity, data: data as any }),
|
||||
);
|
||||
|
||||
// if listener returned, take what's returned
|
||||
const _data = result.returned ? result.params.data : data;
|
||||
const validatedData = {
|
||||
...entity.getDefaultObject(),
|
||||
...(await this.getValidatedData(_data, "create"))
|
||||
...(await this.getValidatedData(_data, "create")),
|
||||
};
|
||||
|
||||
// check if required fields are present
|
||||
@@ -182,8 +182,8 @@ export class Mutator<
|
||||
new Mutator.Events.MutatorUpdateBefore({
|
||||
entity,
|
||||
entityId: id,
|
||||
data
|
||||
})
|
||||
data,
|
||||
}),
|
||||
);
|
||||
|
||||
const _data = result.returned ? result.params.data : data;
|
||||
@@ -198,7 +198,7 @@ export class Mutator<
|
||||
const res = await this.single(query);
|
||||
|
||||
await this.emgr.emit(
|
||||
new Mutator.Events.MutatorUpdateAfter({ entity, entityId: id, data: res.data })
|
||||
new Mutator.Events.MutatorUpdateAfter({ entity, entityId: id, data: res.data }),
|
||||
);
|
||||
|
||||
return res as any;
|
||||
@@ -220,7 +220,7 @@ export class Mutator<
|
||||
const res = await this.single(query);
|
||||
|
||||
await this.emgr.emit(
|
||||
new Mutator.Events.MutatorDeleteAfter({ entity, entityId: id, data: res.data })
|
||||
new Mutator.Events.MutatorDeleteAfter({ entity, entityId: id, data: res.data }),
|
||||
);
|
||||
|
||||
return res as any;
|
||||
@@ -274,7 +274,7 @@ export class Mutator<
|
||||
const entity = this.entity;
|
||||
|
||||
const qb = this.appendWhere(this.conn.deleteFrom(entity.name), where).returning(
|
||||
entity.getSelect()
|
||||
entity.getSelect(),
|
||||
);
|
||||
|
||||
return (await this.many(qb)) as any;
|
||||
@@ -282,7 +282,7 @@ export class Mutator<
|
||||
|
||||
async updateWhere(
|
||||
data: Partial<Input>,
|
||||
where?: RepoQuery["where"]
|
||||
where?: RepoQuery["where"],
|
||||
): Promise<MutatorResponse<Output[]>> {
|
||||
const entity = this.entity;
|
||||
const validatedData = await this.getValidatedData(data, "update");
|
||||
@@ -304,7 +304,7 @@ export class Mutator<
|
||||
for (const row of data) {
|
||||
const validatedData = {
|
||||
...entity.getDefaultObject(),
|
||||
...(await this.getValidatedData(row, "create"))
|
||||
...(await this.getValidatedData(row, "create")),
|
||||
};
|
||||
|
||||
// check if required fields are present
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
type EntityData,
|
||||
type EntityManager,
|
||||
WhereBuilder,
|
||||
WithBuilder
|
||||
WithBuilder,
|
||||
} from "../index";
|
||||
import { JoinBuilder } from "./JoinBuilder";
|
||||
|
||||
@@ -79,7 +79,7 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
|
||||
const validated = {
|
||||
...cloneDeep(defaultQuerySchema),
|
||||
sort: entity.getDefaultSort(),
|
||||
select: entity.getSelect()
|
||||
select: entity.getSelect(),
|
||||
};
|
||||
|
||||
if (!options) return validated;
|
||||
@@ -101,10 +101,10 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
|
||||
|
||||
if (invalid.length > 0) {
|
||||
throw new InvalidSearchParamsException(
|
||||
`Invalid select field(s): ${invalid.join(", ")}`
|
||||
`Invalid select field(s): ${invalid.join(", ")}`,
|
||||
).context({
|
||||
entity: entity.name,
|
||||
valid: validated.select
|
||||
valid: validated.select,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
|
||||
const related = this.em.relationOf(entity.name, entry);
|
||||
if (!related) {
|
||||
throw new InvalidSearchParamsException(
|
||||
`JOIN: "${entry}" is not a relation of "${entity.name}"`
|
||||
`JOIN: "${entry}" is not a relation of "${entity.name}"`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -155,7 +155,7 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
|
||||
|
||||
if (invalid.length > 0) {
|
||||
throw new InvalidSearchParamsException(
|
||||
`Invalid where field(s): ${invalid.join(", ")}`
|
||||
`Invalid where field(s): ${invalid.join(", ")}`,
|
||||
).context({ aliases, entity: entity.name });
|
||||
}
|
||||
|
||||
@@ -189,7 +189,7 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
|
||||
const [_count, _total, result] = await this.em.connection.batchQuery([
|
||||
countQuery,
|
||||
totalQuery,
|
||||
qb
|
||||
qb,
|
||||
]);
|
||||
//$console.log("result", { _count, _total });
|
||||
|
||||
@@ -207,8 +207,8 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
|
||||
count: _count[0]?.count ?? 0, // @todo: better graceful method
|
||||
items: result.length,
|
||||
time,
|
||||
query: { sql: compiled.sql, parameters: compiled.parameters }
|
||||
}
|
||||
query: { sql: compiled.sql, parameters: compiled.parameters },
|
||||
},
|
||||
};
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
@@ -221,10 +221,10 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
|
||||
|
||||
protected async single(
|
||||
qb: RepositoryQB,
|
||||
options: RepoQuery
|
||||
options: RepoQuery,
|
||||
): Promise<RepositoryResponse<EntityData>> {
|
||||
await this.emgr.emit(
|
||||
new Repository.Events.RepositoryFindOneBefore({ entity: this.entity, options })
|
||||
new Repository.Events.RepositoryFindOneBefore({ entity: this.entity, options }),
|
||||
);
|
||||
|
||||
const { data, ...response } = await this.performQuery(qb);
|
||||
@@ -233,8 +233,8 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
|
||||
new Repository.Events.RepositoryFindOneAfter({
|
||||
entity: this.entity,
|
||||
options,
|
||||
data: data[0]!
|
||||
})
|
||||
data: data[0]!,
|
||||
}),
|
||||
);
|
||||
|
||||
return { ...response, data: data[0]! };
|
||||
@@ -248,7 +248,7 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
|
||||
ignore?: (keyof RepoQuery)[];
|
||||
alias?: string;
|
||||
defaults?: Pick<RepoQuery, "limit" | "offset">;
|
||||
}
|
||||
},
|
||||
) {
|
||||
const entity = this.entity;
|
||||
let qb = _qb ?? (this.conn.selectFrom(entity.name) as RepositoryQB);
|
||||
@@ -262,7 +262,7 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
|
||||
const defaults = {
|
||||
limit: 10,
|
||||
offset: 0,
|
||||
...config?.defaults
|
||||
...config?.defaults,
|
||||
};
|
||||
|
||||
if (!ignore.includes("select") && options.select) {
|
||||
@@ -295,7 +295,7 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
|
||||
|
||||
private buildQuery(
|
||||
_options?: Partial<RepoQuery>,
|
||||
ignore: (keyof RepoQuery)[] = []
|
||||
ignore: (keyof RepoQuery)[] = [],
|
||||
): { qb: RepositoryQB; options: RepoQuery } {
|
||||
const entity = this.entity;
|
||||
const options = this.getValidOptions(_options);
|
||||
@@ -305,23 +305,23 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
|
||||
ignore,
|
||||
alias: entity.name,
|
||||
// already done
|
||||
validate: false
|
||||
validate: false,
|
||||
}),
|
||||
options
|
||||
options,
|
||||
};
|
||||
}
|
||||
|
||||
async findId(
|
||||
id: PrimaryFieldType,
|
||||
_options?: Partial<Omit<RepoQuery, "where" | "limit" | "offset">>
|
||||
_options?: Partial<Omit<RepoQuery, "where" | "limit" | "offset">>,
|
||||
): Promise<RepositoryResponse<TBD[TB] | undefined>> {
|
||||
const { qb, options } = this.buildQuery(
|
||||
{
|
||||
..._options,
|
||||
where: { [this.entity.getPrimaryField().name]: id },
|
||||
limit: 1
|
||||
limit: 1,
|
||||
},
|
||||
["offset", "sort"]
|
||||
["offset", "sort"],
|
||||
);
|
||||
|
||||
return this.single(qb, options) as any;
|
||||
@@ -329,12 +329,12 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
|
||||
|
||||
async findOne(
|
||||
where: RepoQuery["where"],
|
||||
_options?: Partial<Omit<RepoQuery, "where" | "limit" | "offset">>
|
||||
_options?: Partial<Omit<RepoQuery, "where" | "limit" | "offset">>,
|
||||
): Promise<RepositoryResponse<TBD[TB] | undefined>> {
|
||||
const { qb, options } = this.buildQuery({
|
||||
..._options,
|
||||
where,
|
||||
limit: 1
|
||||
limit: 1,
|
||||
});
|
||||
|
||||
return this.single(qb, options) as any;
|
||||
@@ -344,7 +344,7 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
|
||||
const { qb, options } = this.buildQuery(_options);
|
||||
|
||||
await this.emgr.emit(
|
||||
new Repository.Events.RepositoryFindManyBefore({ entity: this.entity, options })
|
||||
new Repository.Events.RepositoryFindManyBefore({ entity: this.entity, options }),
|
||||
);
|
||||
|
||||
const res = await this.performQuery(qb);
|
||||
@@ -353,8 +353,8 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
|
||||
new Repository.Events.RepositoryFindManyAfter({
|
||||
entity: this.entity,
|
||||
options,
|
||||
data: res.data
|
||||
})
|
||||
data: res.data,
|
||||
}),
|
||||
);
|
||||
|
||||
return res as any;
|
||||
@@ -364,7 +364,7 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
|
||||
async findManyByReference(
|
||||
id: PrimaryFieldType,
|
||||
reference: string,
|
||||
_options?: Partial<Omit<RepoQuery, "limit" | "offset">>
|
||||
_options?: Partial<Omit<RepoQuery, "limit" | "offset">>,
|
||||
): Promise<RepositoryResponse<EntityData>> {
|
||||
const entity = this.entity;
|
||||
const listable_relations = this.em.relations.listableRelationsOf(entity);
|
||||
@@ -372,7 +372,7 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
|
||||
|
||||
if (!relation) {
|
||||
throw new Error(
|
||||
`Relation "${reference}" not found or not listable on entity "${entity.name}"`
|
||||
`Relation "${reference}" not found or not listable on entity "${entity.name}"`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -380,7 +380,7 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
|
||||
const refQueryOptions = relation.getReferenceQuery(newEntity, id as number, reference);
|
||||
if (!("where" in refQueryOptions) || Object.keys(refQueryOptions.where as any).length === 0) {
|
||||
throw new Error(
|
||||
`Invalid reference query for "${reference}" on entity "${newEntity.name}"`
|
||||
`Invalid reference query for "${reference}" on entity "${newEntity.name}"`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -389,8 +389,8 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
|
||||
...refQueryOptions,
|
||||
where: {
|
||||
...refQueryOptions.where,
|
||||
..._options?.where
|
||||
}
|
||||
..._options?.where,
|
||||
},
|
||||
};
|
||||
|
||||
return this.cloneFor(newEntity).findMany(findManyOptions);
|
||||
@@ -415,7 +415,7 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
|
||||
sql: compiled.sql,
|
||||
parameters: [...compiled.parameters],
|
||||
result,
|
||||
count: result[0]?.count ?? 0
|
||||
count: result[0]?.count ?? 0,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -441,7 +441,7 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
|
||||
sql: compiled.sql,
|
||||
parameters: [...compiled.parameters],
|
||||
result,
|
||||
exists: result[0]!.count > 0
|
||||
exists: result[0]!.count > 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,14 +6,14 @@ import {
|
||||
exp,
|
||||
isBooleanLike,
|
||||
isPrimitive,
|
||||
makeValidator
|
||||
makeValidator,
|
||||
} from "core";
|
||||
import type {
|
||||
DeleteQueryBuilder,
|
||||
ExpressionBuilder,
|
||||
ExpressionWrapper,
|
||||
SelectQueryBuilder,
|
||||
UpdateQueryBuilder
|
||||
UpdateQueryBuilder,
|
||||
} from "kysely";
|
||||
|
||||
type Builder = ExpressionBuilder<any, any>;
|
||||
@@ -34,58 +34,58 @@ const expressions = [
|
||||
exp(
|
||||
"$eq",
|
||||
(v: Primitive) => isPrimitive(v),
|
||||
(v, k, eb: Builder) => eb(key(k), "=", v)
|
||||
(v, k, eb: Builder) => eb(key(k), "=", v),
|
||||
),
|
||||
exp(
|
||||
"$ne",
|
||||
(v: Primitive) => isPrimitive(v),
|
||||
(v, k, eb: Builder) => eb(key(k), "!=", v)
|
||||
(v, k, eb: Builder) => eb(key(k), "!=", v),
|
||||
),
|
||||
exp(
|
||||
"$gt",
|
||||
(v: Primitive) => isPrimitive(v),
|
||||
(v, k, eb: Builder) => eb(key(k), ">", v)
|
||||
(v, k, eb: Builder) => eb(key(k), ">", v),
|
||||
),
|
||||
exp(
|
||||
"$gte",
|
||||
(v: Primitive) => isPrimitive(v),
|
||||
(v, k, eb: Builder) => eb(key(k), ">=", v)
|
||||
(v, k, eb: Builder) => eb(key(k), ">=", v),
|
||||
),
|
||||
exp(
|
||||
"$lt",
|
||||
(v: Primitive) => isPrimitive(v),
|
||||
(v, k, eb: Builder) => eb(key(k), "<", v)
|
||||
(v, k, eb: Builder) => eb(key(k), "<", v),
|
||||
),
|
||||
exp(
|
||||
"$lte",
|
||||
(v: Primitive) => isPrimitive(v),
|
||||
(v, k, eb: Builder) => eb(key(k), "<=", v)
|
||||
(v, k, eb: Builder) => eb(key(k), "<=", v),
|
||||
),
|
||||
exp(
|
||||
"$isnull",
|
||||
(v: BooleanLike) => isBooleanLike(v),
|
||||
(v, k, eb: Builder) => eb(key(k), v ? "is" : "is not", null)
|
||||
(v, k, eb: Builder) => eb(key(k), v ? "is" : "is not", null),
|
||||
),
|
||||
exp(
|
||||
"$in",
|
||||
(v: any[]) => Array.isArray(v),
|
||||
(v, k, eb: Builder) => eb(key(k), "in", v)
|
||||
(v, k, eb: Builder) => eb(key(k), "in", v),
|
||||
),
|
||||
exp(
|
||||
"$notin",
|
||||
(v: any[]) => Array.isArray(v),
|
||||
(v, k, eb: Builder) => eb(key(k), "not in", v)
|
||||
(v, k, eb: Builder) => eb(key(k), "not in", v),
|
||||
),
|
||||
exp(
|
||||
"$between",
|
||||
(v: [number, number]) => Array.isArray(v) && v.length === 2,
|
||||
(v, k, eb: Builder) => eb.between(key(k), v[0], v[1])
|
||||
(v, k, eb: Builder) => eb.between(key(k), v[0], v[1]),
|
||||
),
|
||||
exp(
|
||||
"$like",
|
||||
(v: Primitive) => isPrimitive(v),
|
||||
(v, k, eb: Builder) => eb(key(k), "like", String(v).replace(/\*/g, "%"))
|
||||
)
|
||||
(v, k, eb: Builder) => eb(key(k), "like", String(v).replace(/\*/g, "%")),
|
||||
),
|
||||
];
|
||||
|
||||
export type WhereQuery = FilterQuery<typeof expressions>;
|
||||
@@ -103,7 +103,7 @@ export class WhereBuilder {
|
||||
const fns = validator.build(query, {
|
||||
value_is_kv: true,
|
||||
exp_ctx: eb,
|
||||
convert: true
|
||||
convert: true,
|
||||
});
|
||||
|
||||
if (fns.$or.length > 0 && fns.$and.length > 0) {
|
||||
@@ -124,7 +124,7 @@ export class WhereBuilder {
|
||||
const { keys } = validator.build(query, {
|
||||
value_is_kv: true,
|
||||
exp_ctx: () => null,
|
||||
convert: true
|
||||
convert: true,
|
||||
});
|
||||
return Array.from(keys);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ export class WithBuilder {
|
||||
em: EntityManager<any>,
|
||||
qb: RepositoryQB,
|
||||
entity: Entity,
|
||||
withs: RepoQuery["with"]
|
||||
withs: RepoQuery["with"],
|
||||
) {
|
||||
if (!withs || !isObject(withs)) {
|
||||
console.warn(`'withs' undefined or invalid, given: ${JSON.stringify(withs)}`);
|
||||
@@ -36,8 +36,8 @@ export class WithBuilder {
|
||||
if (query) {
|
||||
subQuery = em.repo(other.entity).addOptionsToQueryBuilder(subQuery, query as any, {
|
||||
ignore: ["with", "join", cardinality === 1 ? "limit" : undefined].filter(
|
||||
Boolean
|
||||
) as any
|
||||
Boolean,
|
||||
) as any,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ export class WithBuilder {
|
||||
const related = em.relationOf(entity, ref);
|
||||
if (!related) {
|
||||
throw new InvalidSearchParamsException(
|
||||
`WITH: "${ref}" is not a relation of "${entity}"`
|
||||
`WITH: "${ref}" is not a relation of "${entity}"`,
|
||||
);
|
||||
}
|
||||
depth++;
|
||||
|
||||
@@ -42,11 +42,11 @@ export class InvalidFieldConfigException extends Exception {
|
||||
constructor(
|
||||
field: Field<any, any, any>,
|
||||
public given: any,
|
||||
error: TypeInvalidError
|
||||
error: TypeInvalidError,
|
||||
) {
|
||||
console.error("InvalidFieldConfigException", {
|
||||
given,
|
||||
error: error.firstToString()
|
||||
error: error.firstToString(),
|
||||
});
|
||||
super(`Invalid Field config given for field "${field.name}": ${error.firstToString()}`);
|
||||
}
|
||||
@@ -71,7 +71,7 @@ export class EntityNotFoundException extends Exception {
|
||||
|
||||
constructor(entity: Entity | string, id: any) {
|
||||
super(
|
||||
`Entity "${typeof entity !== "string" ? entity.name : entity}" with id "${id}" not found`
|
||||
`Entity "${typeof entity !== "string" ? entity.name : entity}" with id "${id}" not found`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ export class MutatorInsertBefore extends Event<{ entity: Entity; data: EntityDat
|
||||
|
||||
return this.clone({
|
||||
entity,
|
||||
data
|
||||
data,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ export class MutatorUpdateBefore extends Event<
|
||||
return this.clone({
|
||||
...rest,
|
||||
entity,
|
||||
data
|
||||
data,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -68,7 +68,7 @@ export const MutatorEvents = {
|
||||
MutatorUpdateBefore,
|
||||
MutatorUpdateAfter,
|
||||
MutatorDeleteBefore,
|
||||
MutatorDeleteAfter
|
||||
MutatorDeleteAfter,
|
||||
};
|
||||
|
||||
export class RepositoryFindOneBefore extends Event<{ entity: Entity; options: RepoQuery }> {
|
||||
@@ -98,5 +98,5 @@ export const RepositoryEvents = {
|
||||
RepositoryFindOneBefore,
|
||||
RepositoryFindOneAfter,
|
||||
RepositoryFindManyBefore,
|
||||
RepositoryFindManyAfter
|
||||
RepositoryFindManyAfter,
|
||||
};
|
||||
|
||||
@@ -5,9 +5,9 @@ import { Field, type TActionContext, type TRenderContext, baseFieldConfigSchema
|
||||
|
||||
export const booleanFieldConfigSchema = Type.Composite([
|
||||
Type.Object({
|
||||
default_value: Type.Optional(Type.Boolean({ default: false }))
|
||||
default_value: Type.Optional(Type.Boolean({ default: false })),
|
||||
}),
|
||||
baseFieldConfigSchema
|
||||
baseFieldConfigSchema,
|
||||
]);
|
||||
|
||||
export type BooleanFieldConfig = Static<typeof booleanFieldConfigSchema>;
|
||||
@@ -40,7 +40,7 @@ export class BooleanField<Required extends true | false = false> extends Field<
|
||||
override getHtmlConfig() {
|
||||
return {
|
||||
...super.getHtmlConfig(),
|
||||
element: "boolean"
|
||||
element: "boolean",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ export class BooleanField<Required extends true | false = false> extends Field<
|
||||
override async transformPersist(
|
||||
val: unknown,
|
||||
em: EntityManager<any>,
|
||||
context: TActionContext
|
||||
context: TActionContext,
|
||||
): Promise<boolean | undefined> {
|
||||
const value = await super.transformPersist(val, em, context);
|
||||
if (this.nullish(value)) {
|
||||
|
||||
@@ -9,13 +9,13 @@ export const dateFieldConfigSchema = Type.Composite(
|
||||
type: StringEnum(["date", "datetime", "week"] as const, { default: "date" }),
|
||||
timezone: Type.Optional(Type.String()),
|
||||
min_date: Type.Optional(Type.String()),
|
||||
max_date: Type.Optional(Type.String())
|
||||
max_date: Type.Optional(Type.String()),
|
||||
}),
|
||||
baseFieldConfigSchema
|
||||
baseFieldConfigSchema,
|
||||
],
|
||||
{
|
||||
additionalProperties: false
|
||||
}
|
||||
additionalProperties: false,
|
||||
},
|
||||
);
|
||||
|
||||
export type DateFieldConfig = Static<typeof dateFieldConfigSchema>;
|
||||
@@ -43,8 +43,8 @@ export class DateField<Required extends true | false = false> extends Field<
|
||||
...super.getHtmlConfig(),
|
||||
element: "date",
|
||||
props: {
|
||||
type: htmlType
|
||||
}
|
||||
type: htmlType,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ export class DateField<Required extends true | false = false> extends Field<
|
||||
if (this.config.type === "week" && value.includes("-W")) {
|
||||
const [year, week] = value.split("-W").map((n) => Number.parseInt(n, 10)) as [
|
||||
number,
|
||||
number
|
||||
number,
|
||||
];
|
||||
//console.log({ year, week });
|
||||
// @ts-ignore causes errors on build?
|
||||
@@ -129,7 +129,7 @@ export class DateField<Required extends true | false = false> extends Field<
|
||||
override async transformPersist(
|
||||
_value: any,
|
||||
em: EntityManager<any>,
|
||||
context: TActionContext
|
||||
context: TActionContext,
|
||||
): Promise<string | undefined> {
|
||||
const value = await super.transformPersist(_value, em, context);
|
||||
if (this.nullish(value)) return value;
|
||||
|
||||
@@ -12,9 +12,9 @@ export const enumFieldConfigSchema = Type.Composite(
|
||||
Type.Object(
|
||||
{
|
||||
type: Const("strings"),
|
||||
values: Type.Array(Type.String())
|
||||
values: Type.Array(Type.String()),
|
||||
},
|
||||
{ title: "Strings" }
|
||||
{ title: "Strings" },
|
||||
),
|
||||
Type.Object(
|
||||
{
|
||||
@@ -22,23 +22,23 @@ export const enumFieldConfigSchema = Type.Composite(
|
||||
values: Type.Array(
|
||||
Type.Object({
|
||||
label: Type.String(),
|
||||
value: Type.String()
|
||||
})
|
||||
)
|
||||
value: Type.String(),
|
||||
}),
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Objects",
|
||||
additionalProperties: false
|
||||
}
|
||||
)
|
||||
])
|
||||
)
|
||||
additionalProperties: false,
|
||||
},
|
||||
),
|
||||
]),
|
||||
),
|
||||
}),
|
||||
baseFieldConfigSchema
|
||||
baseFieldConfigSchema,
|
||||
],
|
||||
{
|
||||
additionalProperties: false
|
||||
}
|
||||
additionalProperties: false,
|
||||
},
|
||||
);
|
||||
|
||||
export type EnumFieldConfig = Static<typeof enumFieldConfigSchema>;
|
||||
@@ -123,7 +123,7 @@ export class EnumField<Required extends true | false = false, TypeOverride = str
|
||||
override async transformPersist(
|
||||
_value: any,
|
||||
em: EntityManager<any>,
|
||||
context: TActionContext
|
||||
context: TActionContext,
|
||||
): Promise<string | undefined> {
|
||||
const value = await super.transformPersist(_value, em, context);
|
||||
if (this.nullish(value)) return value;
|
||||
@@ -132,7 +132,7 @@ export class EnumField<Required extends true | false = false, TypeOverride = str
|
||||
throw new TransformPersistFailedException(
|
||||
`Field "${this.name}" must be one of the following values: ${this.getOptions()
|
||||
.map((o) => o.value)
|
||||
.join(", ")}`
|
||||
.join(", ")}`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -146,8 +146,8 @@ export class EnumField<Required extends true | false = false, TypeOverride = str
|
||||
[];
|
||||
return this.toSchemaWrapIfRequired(
|
||||
StringEnum(values, {
|
||||
default: this.getDefault()
|
||||
})
|
||||
default: this.getDefault(),
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
Type,
|
||||
TypeInvalidError,
|
||||
parse,
|
||||
snakeToPascalWithSpaces
|
||||
snakeToPascalWithSpaces,
|
||||
} from "core/utils";
|
||||
import type { ColumnBuilderCallback, ColumnDataType, ColumnDefinitionBuilder } from "kysely";
|
||||
import type { HTMLInputTypeAttribute, InputHTMLAttributes } from "react";
|
||||
@@ -38,32 +38,32 @@ export const baseFieldConfigSchema = Type.Object(
|
||||
Type.Union(
|
||||
[
|
||||
Type.Boolean({ title: "Boolean", default: DEFAULT_FILLABLE }),
|
||||
Type.Array(StringEnum(ActionContext), { title: "Context", uniqueItems: true })
|
||||
Type.Array(StringEnum(ActionContext), { title: "Context", uniqueItems: true }),
|
||||
],
|
||||
{
|
||||
default: DEFAULT_FILLABLE
|
||||
}
|
||||
)
|
||||
default: DEFAULT_FILLABLE,
|
||||
},
|
||||
),
|
||||
),
|
||||
hidden: Type.Optional(
|
||||
Type.Union(
|
||||
[
|
||||
Type.Boolean({ title: "Boolean", default: DEFAULT_HIDDEN }),
|
||||
// @todo: tmp workaround
|
||||
Type.Array(StringEnum(TmpContext), { title: "Context", uniqueItems: true })
|
||||
Type.Array(StringEnum(TmpContext), { title: "Context", uniqueItems: true }),
|
||||
],
|
||||
{
|
||||
default: DEFAULT_HIDDEN
|
||||
}
|
||||
)
|
||||
default: DEFAULT_HIDDEN,
|
||||
},
|
||||
),
|
||||
),
|
||||
// if field is virtual, it will not call transformPersist & transformRetrieve
|
||||
virtual: Type.Optional(Type.Boolean()),
|
||||
default_value: Type.Optional(Type.Any())
|
||||
default_value: Type.Optional(Type.Any()),
|
||||
},
|
||||
{
|
||||
additionalProperties: false
|
||||
}
|
||||
additionalProperties: false,
|
||||
},
|
||||
);
|
||||
export type BaseFieldConfig = Static<typeof baseFieldConfigSchema>;
|
||||
|
||||
@@ -72,7 +72,7 @@ export type SchemaResponse = [string, ColumnDataType, ColumnBuilderCallback] | u
|
||||
export abstract class Field<
|
||||
Config extends BaseFieldConfig = BaseFieldConfig,
|
||||
Type = any,
|
||||
Required extends true | false = false
|
||||
Required extends true | false = false,
|
||||
> {
|
||||
_required!: Required;
|
||||
_type!: Type;
|
||||
@@ -108,7 +108,7 @@ export abstract class Field<
|
||||
|
||||
protected useSchemaHelper(
|
||||
type: ColumnDataType,
|
||||
builder?: (col: ColumnDefinitionBuilder) => ColumnDefinitionBuilder
|
||||
builder?: (col: ColumnDefinitionBuilder) => ColumnDefinitionBuilder,
|
||||
): SchemaResponse {
|
||||
return [
|
||||
this.name,
|
||||
@@ -116,7 +116,7 @@ export abstract class Field<
|
||||
(col: ColumnDefinitionBuilder) => {
|
||||
if (builder) return builder(col);
|
||||
return col;
|
||||
}
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -189,7 +189,7 @@ export abstract class Field<
|
||||
getHtmlConfig(): { element: HTMLInputTypeAttribute | string; props?: InputHTMLAttributes<any> } {
|
||||
return {
|
||||
element: "input",
|
||||
props: { type: "text" }
|
||||
props: { type: "text" },
|
||||
};
|
||||
}
|
||||
|
||||
@@ -217,7 +217,7 @@ export abstract class Field<
|
||||
async transformPersist(
|
||||
value: unknown,
|
||||
em: EntityManager<any>,
|
||||
context: TActionContext
|
||||
context: TActionContext,
|
||||
): Promise<any> {
|
||||
if (this.nullish(value)) {
|
||||
if (this.isRequired() && !this.hasDefault()) {
|
||||
@@ -245,7 +245,7 @@ export abstract class Field<
|
||||
return {
|
||||
// @todo: current workaround because of fixed string type
|
||||
type: this.type as any,
|
||||
config: this.config
|
||||
config: this.config,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ export class JsonField<Required extends true | false = false, TypeOverride = obj
|
||||
override async transformPersist(
|
||||
_value: any,
|
||||
em: EntityManager<any>,
|
||||
context: TActionContext
|
||||
context: TActionContext,
|
||||
): Promise<string | undefined> {
|
||||
const value = await super.transformPersist(_value, em, context);
|
||||
//console.log("value", value);
|
||||
@@ -91,7 +91,7 @@ export class JsonField<Required extends true | false = false, TypeOverride = obj
|
||||
|
||||
if (!this.isSerializable(value)) {
|
||||
throw new TransformPersistFailedException(
|
||||
`Field "${this.name}" must be serializable to JSON.`
|
||||
`Field "${this.name}" must be serializable to JSON.`,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,20 +9,20 @@ export const jsonSchemaFieldConfigSchema = Type.Composite(
|
||||
Type.Object({
|
||||
schema: Type.Object({}, { default: {} }),
|
||||
ui_schema: Type.Optional(Type.Object({})),
|
||||
default_from_schema: Type.Optional(Type.Boolean())
|
||||
default_from_schema: Type.Optional(Type.Boolean()),
|
||||
}),
|
||||
baseFieldConfigSchema
|
||||
baseFieldConfigSchema,
|
||||
],
|
||||
{
|
||||
additionalProperties: false
|
||||
}
|
||||
additionalProperties: false,
|
||||
},
|
||||
);
|
||||
|
||||
export type JsonSchemaFieldConfig = Static<typeof jsonSchemaFieldConfigSchema>;
|
||||
|
||||
export class JsonSchemaField<
|
||||
Required extends true | false = false,
|
||||
TypeOverride = object
|
||||
TypeOverride = object,
|
||||
> extends Field<JsonSchemaFieldConfig, TypeOverride, Required> {
|
||||
override readonly type = "jsonschema";
|
||||
private validator: Validator;
|
||||
@@ -107,7 +107,7 @@ export class JsonSchemaField<
|
||||
override async transformPersist(
|
||||
_value: any,
|
||||
em: EntityManager<any>,
|
||||
context: TActionContext
|
||||
context: TActionContext,
|
||||
): Promise<string | undefined> {
|
||||
const value = await super.transformPersist(_value, em, context);
|
||||
if (this.nullish(value)) return value;
|
||||
@@ -130,8 +130,8 @@ export class JsonSchemaField<
|
||||
return this.toSchemaWrapIfRequired(
|
||||
FromSchema({
|
||||
default: this.getDefault(),
|
||||
...schema
|
||||
})
|
||||
...schema,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,13 +11,13 @@ export const numberFieldConfigSchema = Type.Composite(
|
||||
maximum: Type.Optional(Type.Number()),
|
||||
exclusiveMinimum: Type.Optional(Type.Number()),
|
||||
exclusiveMaximum: Type.Optional(Type.Number()),
|
||||
multipleOf: Type.Optional(Type.Number())
|
||||
multipleOf: Type.Optional(Type.Number()),
|
||||
}),
|
||||
baseFieldConfigSchema
|
||||
baseFieldConfigSchema,
|
||||
],
|
||||
{
|
||||
additionalProperties: false
|
||||
}
|
||||
additionalProperties: false,
|
||||
},
|
||||
);
|
||||
|
||||
export type NumberFieldConfig = Static<typeof numberFieldConfigSchema>;
|
||||
@@ -39,8 +39,8 @@ export class NumberField<Required extends true | false = false> extends Field<
|
||||
props: {
|
||||
type: "number",
|
||||
pattern: "d*",
|
||||
inputMode: "numeric"
|
||||
} as any // @todo: react expects "inputMode", but type dictates "inputmode"
|
||||
inputMode: "numeric",
|
||||
} as any, // @todo: react expects "inputMode", but type dictates "inputmode"
|
||||
};
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ export class NumberField<Required extends true | false = false> extends Field<
|
||||
override async transformPersist(
|
||||
_value: unknown,
|
||||
em: EntityManager<any>,
|
||||
context: TActionContext
|
||||
context: TActionContext,
|
||||
): Promise<number | undefined> {
|
||||
const value = await super.transformPersist(_value, em, context);
|
||||
|
||||
@@ -72,13 +72,13 @@ export class NumberField<Required extends true | false = false> extends Field<
|
||||
|
||||
if (this.config.maximum && (value as number) > this.config.maximum) {
|
||||
throw new TransformPersistFailedException(
|
||||
`Field "${this.name}" cannot be greater than ${this.config.maximum}`
|
||||
`Field "${this.name}" cannot be greater than ${this.config.maximum}`,
|
||||
);
|
||||
}
|
||||
|
||||
if (this.config.minimum && (value as number) < this.config.minimum) {
|
||||
throw new TransformPersistFailedException(
|
||||
`Field "${this.name}" cannot be less than ${this.config.minimum}`
|
||||
`Field "${this.name}" cannot be less than ${this.config.minimum}`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -93,8 +93,8 @@ export class NumberField<Required extends true | false = false> extends Field<
|
||||
maximum: this.config?.maximum,
|
||||
exclusiveMinimum: this.config?.exclusiveMinimum,
|
||||
exclusiveMaximum: this.config?.exclusiveMaximum,
|
||||
multipleOf: this.config?.multipleOf
|
||||
})
|
||||
multipleOf: this.config?.multipleOf,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ import { Field, baseFieldConfigSchema } from "./Field";
|
||||
export const primaryFieldConfigSchema = Type.Composite([
|
||||
Type.Omit(baseFieldConfigSchema, ["required"]),
|
||||
Type.Object({
|
||||
required: Type.Optional(Type.Literal(false))
|
||||
})
|
||||
required: Type.Optional(Type.Literal(false)),
|
||||
}),
|
||||
]);
|
||||
|
||||
export type PrimaryFieldConfig = Static<typeof primaryFieldConfigSchema>;
|
||||
|
||||
@@ -19,19 +19,19 @@ export const textFieldConfigSchema = Type.Composite(
|
||||
{
|
||||
additionalProperties: Type.Union([
|
||||
Type.String({ title: "String" }),
|
||||
Type.Number({ title: "Number" })
|
||||
])
|
||||
}
|
||||
)
|
||||
)
|
||||
})
|
||||
)
|
||||
Type.Number({ title: "Number" }),
|
||||
]),
|
||||
},
|
||||
),
|
||||
),
|
||||
}),
|
||||
),
|
||||
}),
|
||||
baseFieldConfigSchema
|
||||
baseFieldConfigSchema,
|
||||
],
|
||||
{
|
||||
additionalProperties: false
|
||||
}
|
||||
additionalProperties: false,
|
||||
},
|
||||
);
|
||||
|
||||
export type TextFieldConfig = Static<typeof textFieldConfigSchema>;
|
||||
@@ -81,7 +81,7 @@ export class TextField<Required extends true | false = false> extends Field<
|
||||
override async transformPersist(
|
||||
_value: any,
|
||||
em: EntityManager<any>,
|
||||
context: TActionContext
|
||||
context: TActionContext,
|
||||
): Promise<string | undefined> {
|
||||
let value = await super.transformPersist(_value, em, context);
|
||||
|
||||
@@ -94,19 +94,19 @@ export class TextField<Required extends true | false = false> extends Field<
|
||||
|
||||
if (this.config.maxLength && value?.length > this.config.maxLength) {
|
||||
throw new TransformPersistFailedException(
|
||||
`Field "${this.name}" must be at most ${this.config.maxLength} character(s)`
|
||||
`Field "${this.name}" must be at most ${this.config.maxLength} character(s)`,
|
||||
);
|
||||
}
|
||||
|
||||
if (this.config.minLength && value?.length < this.config.minLength) {
|
||||
throw new TransformPersistFailedException(
|
||||
`Field "${this.name}" must be at least ${this.config.minLength} character(s)`
|
||||
`Field "${this.name}" must be at least ${this.config.minLength} character(s)`,
|
||||
);
|
||||
}
|
||||
|
||||
if (this.config.pattern && value && !new RegExp(this.config.pattern).test(value)) {
|
||||
throw new TransformPersistFailedException(
|
||||
`Field "${this.name}" must match the pattern ${this.config.pattern}`
|
||||
`Field "${this.name}" must match the pattern ${this.config.pattern}`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -119,8 +119,8 @@ export class TextField<Required extends true | false = false> extends Field<
|
||||
default: this.getDefault(),
|
||||
minLength: this.config?.minLength,
|
||||
maxLength: this.config?.maxLength,
|
||||
pattern: this.config?.pattern
|
||||
})
|
||||
pattern: this.config?.pattern,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,8 +25,8 @@ export class VirtualField extends Field<VirtualFieldConfig> {
|
||||
return this.toSchemaWrapIfRequired(
|
||||
Type.Any({
|
||||
default: this.getDefault(),
|
||||
readOnly: true
|
||||
})
|
||||
readOnly: true,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { JsonField, type JsonFieldConfig, jsonFieldConfigSchema } from "./JsonFi
|
||||
import {
|
||||
JsonSchemaField,
|
||||
type JsonSchemaFieldConfig,
|
||||
jsonSchemaFieldConfigSchema
|
||||
jsonSchemaFieldConfigSchema,
|
||||
} from "./JsonSchemaField";
|
||||
import { NumberField, type NumberFieldConfig, numberFieldConfigSchema } from "./NumberField";
|
||||
import { PrimaryField, type PrimaryFieldConfig, primaryFieldConfigSchema } from "./PrimaryField";
|
||||
@@ -35,7 +35,7 @@ export {
|
||||
type NumberFieldConfig,
|
||||
TextField,
|
||||
textFieldConfigSchema,
|
||||
type TextFieldConfig
|
||||
type TextFieldConfig,
|
||||
};
|
||||
|
||||
export * from "./Field";
|
||||
@@ -51,5 +51,5 @@ export const FieldClassMap = {
|
||||
date: { schema: dateFieldConfigSchema, field: DateField },
|
||||
enum: { schema: enumFieldConfigSchema, field: EnumField },
|
||||
json: { schema: jsonFieldConfigSchema, field: JsonField },
|
||||
jsonschema: { schema: jsonSchemaFieldConfigSchema, field: JsonSchemaField }
|
||||
jsonschema: { schema: jsonSchemaFieldConfigSchema, field: JsonSchemaField },
|
||||
} as const;
|
||||
|
||||
@@ -6,7 +6,7 @@ export class EntityIndex {
|
||||
public entity: Entity,
|
||||
public fields: Field[],
|
||||
public unique: boolean = false,
|
||||
public name?: string
|
||||
public name?: string,
|
||||
) {
|
||||
if (fields.length === 0) {
|
||||
throw new Error("Indices must contain at least one field");
|
||||
@@ -21,7 +21,7 @@ export class EntityIndex {
|
||||
throw new Error(
|
||||
`Unique indices must have first field as required: ${fields
|
||||
.map((f) => f.name)
|
||||
.join(", ")}`
|
||||
.join(", ")}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ export class EntityIndex {
|
||||
this.name = [
|
||||
unique ? "idx_unique" : "idx",
|
||||
entity.name,
|
||||
...fields.map((f) => f.name)
|
||||
...fields.map((f) => f.name),
|
||||
].join("_");
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ export class EntityIndex {
|
||||
entity: this.entity.name,
|
||||
fields: this.fields.map((f) => f.name),
|
||||
//name: this.name,
|
||||
unique: this.unique
|
||||
unique: this.unique,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ export function getDefaultValues(fields: Field[], data: EntityData): EntityData
|
||||
// form fields don't like "null" or "undefined", so return empty string
|
||||
acc[field.name] = field.getValue(data?.[field.name], "form") ?? "";
|
||||
},
|
||||
{} as EntityData
|
||||
{} as EntityData,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ export function getChangeSet(
|
||||
action: string,
|
||||
formData: EntityData,
|
||||
data: EntityData,
|
||||
fields: Field[]
|
||||
fields: Field[],
|
||||
): EntityData {
|
||||
//console.log("getChangeSet", formData, data);
|
||||
return transform(
|
||||
@@ -45,7 +45,7 @@ export function getChangeSet(
|
||||
//console.log("no change", key, value, data[key]);
|
||||
}
|
||||
},
|
||||
{} as typeof formData
|
||||
{} as typeof formData,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -54,17 +54,17 @@ export function readableEmJson(_em: EntityManager) {
|
||||
entities: _em.entities.map((e) => ({
|
||||
name: e.name,
|
||||
fields: e.fields.map((f) => f.name),
|
||||
type: e.type
|
||||
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
|
||||
unique: i.unique,
|
||||
})),
|
||||
relations: _em.relations.all.map((r) => ({
|
||||
name: r.getName(),
|
||||
...r.toJSON()
|
||||
}))
|
||||
...r.toJSON(),
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ export {
|
||||
type RepoQueryIn,
|
||||
defaultQuerySchema,
|
||||
querySchema,
|
||||
whereSchema
|
||||
whereSchema,
|
||||
} from "./server/data-query-impl";
|
||||
|
||||
export { Connection } from "./connection/Connection";
|
||||
@@ -25,7 +25,7 @@ export { constructEntity, constructRelation } from "./schema/constructor";
|
||||
|
||||
export const DatabaseEvents = {
|
||||
...MutatorEvents,
|
||||
...RepositoryEvents
|
||||
...RepositoryEvents,
|
||||
};
|
||||
export { MutatorEvents, RepositoryEvents };
|
||||
|
||||
|
||||
@@ -13,9 +13,7 @@ export class DeserializeJsonValuesPlugin implements KyselyPlugin {
|
||||
transformQuery(args: PluginTransformQueryArgs): RootOperationNode {
|
||||
return args.node;
|
||||
}
|
||||
transformResult(
|
||||
args: PluginTransformResultArgs
|
||||
): Promise<QueryResult<UnknownRow>> {
|
||||
transformResult(args: PluginTransformResultArgs): Promise<QueryResult<UnknownRow>> {
|
||||
return Promise.resolve({
|
||||
...args.result,
|
||||
rows: args.result.rows.map((row: KeyValueObject) => {
|
||||
|
||||
@@ -31,7 +31,7 @@ import {
|
||||
type PolymorphicRelationConfig,
|
||||
type TEntityType,
|
||||
TextField,
|
||||
type TextFieldConfig
|
||||
type TextFieldConfig,
|
||||
} from "../index";
|
||||
|
||||
type Options<Config = any> = {
|
||||
@@ -56,44 +56,44 @@ const FieldMap = {
|
||||
media: (o: Options) =>
|
||||
new MediaField(o.field_name, { ...o.config, entity: o.entity.name, required: o.is_required }),
|
||||
medium: (o: Options) =>
|
||||
new MediaField(o.field_name, { ...o.config, entity: o.entity.name, required: o.is_required })
|
||||
new MediaField(o.field_name, { ...o.config, entity: o.entity.name, required: o.is_required }),
|
||||
} as const;
|
||||
type TFieldType = keyof typeof FieldMap;
|
||||
|
||||
export function text(
|
||||
config?: Omit<TextFieldConfig, "required">
|
||||
config?: Omit<TextFieldConfig, "required">,
|
||||
): TextField<false> & { required: () => TextField<true> } {
|
||||
return new FieldPrototype("text", config, false) as any;
|
||||
}
|
||||
export function number(
|
||||
config?: Omit<NumberFieldConfig, "required">
|
||||
config?: Omit<NumberFieldConfig, "required">,
|
||||
): NumberField<false> & { required: () => NumberField<true> } {
|
||||
return new FieldPrototype("number", config, false) as any;
|
||||
}
|
||||
export function date(
|
||||
config?: Omit<DateFieldConfig, "required" | "type">
|
||||
config?: Omit<DateFieldConfig, "required" | "type">,
|
||||
): DateField<false> & { required: () => DateField<true> } {
|
||||
return new FieldPrototype("date", { ...config, type: "date" }, false) as any;
|
||||
}
|
||||
export function datetime(
|
||||
config?: Omit<DateFieldConfig, "required" | "type">
|
||||
config?: Omit<DateFieldConfig, "required" | "type">,
|
||||
): DateField<false> & { required: () => DateField<true> } {
|
||||
return new FieldPrototype("date", { ...config, type: "datetime" }, false) as any;
|
||||
}
|
||||
export function week(
|
||||
config?: Omit<DateFieldConfig, "required" | "type">
|
||||
config?: Omit<DateFieldConfig, "required" | "type">,
|
||||
): DateField<false> & { required: () => DateField<true> } {
|
||||
return new FieldPrototype("date", { ...config, type: "week" }, false) as any;
|
||||
}
|
||||
export function boolean(
|
||||
config?: Omit<BooleanFieldConfig, "required">
|
||||
config?: Omit<BooleanFieldConfig, "required">,
|
||||
): BooleanField<false> & { required: () => BooleanField<true> } {
|
||||
return new FieldPrototype("boolean", config, false) as any;
|
||||
}
|
||||
export function enumm<TypeOverride = string>(
|
||||
config?: Omit<EnumFieldConfig, "required" | "options"> & {
|
||||
enum: string[] | { label: string; value: string }[];
|
||||
}
|
||||
},
|
||||
): EnumField<false, TypeOverride> & {
|
||||
required: () => EnumField<true, TypeOverride>;
|
||||
} {
|
||||
@@ -101,18 +101,18 @@ export function enumm<TypeOverride = string>(
|
||||
const actual_config = {
|
||||
options: {
|
||||
type,
|
||||
values: config?.enum ?? []
|
||||
}
|
||||
values: config?.enum ?? [],
|
||||
},
|
||||
};
|
||||
return new FieldPrototype("enumm", actual_config, false) as any;
|
||||
}
|
||||
export function json<TypeOverride = object>(
|
||||
config?: Omit<JsonFieldConfig, "required">
|
||||
config?: Omit<JsonFieldConfig, "required">,
|
||||
): JsonField<false, TypeOverride> & { required: () => JsonField<true, TypeOverride> } {
|
||||
return new FieldPrototype("json", config, false) as any;
|
||||
}
|
||||
export function jsonSchema<TypeOverride = object>(
|
||||
config?: Omit<JsonSchemaFieldConfig, "required">
|
||||
config?: Omit<JsonSchemaFieldConfig, "required">,
|
||||
): JsonField<false, TypeOverride> & { required: () => JsonSchemaField<true, TypeOverride> } {
|
||||
return new FieldPrototype("jsonSchema", config, false) as any;
|
||||
}
|
||||
@@ -120,7 +120,7 @@ export function media(config?: Omit<MediaFieldConfig, "entity">): MediaField<fal
|
||||
return new FieldPrototype("media", config, false) as any;
|
||||
}
|
||||
export function medium(
|
||||
config?: Omit<MediaFieldConfig, "required" | "entity" | "max_items">
|
||||
config?: Omit<MediaFieldConfig, "required" | "entity" | "max_items">,
|
||||
): MediaField<false, MediaItem> {
|
||||
return new FieldPrototype("media", { ...config, max_items: 1 }, false) as any;
|
||||
}
|
||||
@@ -135,7 +135,7 @@ export class FieldPrototype {
|
||||
constructor(
|
||||
public type: TFieldType,
|
||||
public config: any,
|
||||
public is_required: boolean
|
||||
public is_required: boolean,
|
||||
) {}
|
||||
|
||||
required() {
|
||||
@@ -163,7 +163,7 @@ export class FieldPrototype {
|
||||
entity: { name: "unknown", fields: {} },
|
||||
field_name,
|
||||
config: this.config,
|
||||
is_required: this.is_required
|
||||
is_required: this.is_required,
|
||||
}) as unknown as Field;
|
||||
} catch (e) {
|
||||
throw new Error(`Faild to construct field "${this.type}": ${e}`);
|
||||
@@ -175,12 +175,12 @@ export class FieldPrototype {
|
||||
|
||||
export function entity<
|
||||
EntityName extends string,
|
||||
Fields extends Record<string, Field<any, any, any>>
|
||||
Fields extends Record<string, Field<any, any, any>>,
|
||||
>(
|
||||
name: EntityName,
|
||||
fields: Fields,
|
||||
config?: EntityConfig,
|
||||
type?: TEntityType
|
||||
type?: TEntityType,
|
||||
): Entity<EntityName, Fields> {
|
||||
const _fields: Field[] = [];
|
||||
for (const [field_name, field] of Object.entries(fields)) {
|
||||
@@ -189,7 +189,7 @@ export function entity<
|
||||
entity: { name, fields },
|
||||
field_name,
|
||||
config: f.config,
|
||||
is_required: f.is_required
|
||||
is_required: f.is_required,
|
||||
};
|
||||
_fields.push(f.getField(o));
|
||||
}
|
||||
@@ -207,7 +207,7 @@ export function relation<Local extends Entity>(local: Local) {
|
||||
manyToMany: <Foreign extends Entity>(
|
||||
foreign: Foreign,
|
||||
config?: ManyToManyRelationConfig,
|
||||
additionalFields?: Record<string, Field<any, any, any>>
|
||||
additionalFields?: Record<string, Field<any, any, any>>,
|
||||
) => {
|
||||
const add_fields: Field[] = [];
|
||||
if (additionalFields) {
|
||||
@@ -221,7 +221,7 @@ export function relation<Local extends Entity>(local: Local) {
|
||||
entity: { name: entity_name, fields },
|
||||
field_name,
|
||||
config: f.config,
|
||||
is_required: f.is_required
|
||||
is_required: f.is_required,
|
||||
};
|
||||
_fields.push(f.getField(o));
|
||||
}
|
||||
@@ -232,16 +232,16 @@ export function relation<Local extends Entity>(local: Local) {
|
||||
},
|
||||
polyToOne: <Foreign extends Entity>(
|
||||
foreign: Foreign,
|
||||
config?: Omit<PolymorphicRelationConfig, "targetCardinality">
|
||||
config?: Omit<PolymorphicRelationConfig, "targetCardinality">,
|
||||
) => {
|
||||
return new PolymorphicRelation(local, foreign, { ...config, targetCardinality: 1 });
|
||||
},
|
||||
polyToMany: <Foreign extends Entity>(
|
||||
foreign: Foreign,
|
||||
config?: PolymorphicRelationConfig
|
||||
config?: PolymorphicRelationConfig,
|
||||
) => {
|
||||
return new PolymorphicRelation(local, foreign, config);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -256,7 +256,7 @@ export function index<E extends Entity>(entity: E) {
|
||||
return field;
|
||||
});
|
||||
return new EntityIndex(entity, _fields, unique);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -266,7 +266,7 @@ class EntityManagerPrototype<Entities extends Record<string, Entity>> extends En
|
||||
constructor(
|
||||
public __entities: Entities,
|
||||
relations: EntityRelation[] = [],
|
||||
indices: EntityIndex[] = []
|
||||
indices: EntityIndex[] = [],
|
||||
) {
|
||||
super(Object.values(__entities), new DummyConnection(), relations, indices);
|
||||
}
|
||||
@@ -279,7 +279,7 @@ type Chained<R extends Record<string, (...args: any[]) => any>> = {
|
||||
};
|
||||
type ChainedFn<
|
||||
Fn extends (...args: any[]) => Record<string, (...args: any[]) => any>,
|
||||
Return extends ReturnType<Fn> = ReturnType<Fn>
|
||||
Return extends ReturnType<Fn> = ReturnType<Fn>,
|
||||
> = (e: Entity) => {
|
||||
[K in keyof Return]: (...args: Parameters<Return[K]>) => Chained<Return>;
|
||||
};
|
||||
@@ -288,8 +288,8 @@ export function em<Entities extends Record<string, Entity>>(
|
||||
entities: Entities,
|
||||
schema?: (
|
||||
fns: { relation: ChainedFn<typeof relation>; index: ChainedFn<typeof index> },
|
||||
entities: Entities
|
||||
) => void
|
||||
entities: Entities,
|
||||
) => void,
|
||||
) {
|
||||
const relations: EntityRelation[] = [];
|
||||
const indices: EntityIndex[] = [];
|
||||
@@ -301,7 +301,7 @@ export function em<Entities extends Record<string, Entity>>(
|
||||
relations.push(target[prop](...args));
|
||||
return relationProxy(e);
|
||||
};
|
||||
}
|
||||
},
|
||||
}) as any;
|
||||
};
|
||||
|
||||
@@ -312,7 +312,7 @@ export function em<Entities extends Record<string, Entity>>(
|
||||
indices.push(target[prop](...args));
|
||||
return indexProxy(e);
|
||||
};
|
||||
}
|
||||
},
|
||||
}) as any;
|
||||
};
|
||||
|
||||
@@ -327,7 +327,7 @@ export function em<Entities extends Record<string, Entity>>(
|
||||
relations,
|
||||
indices,
|
||||
toJSON: () =>
|
||||
e.toJSON() as unknown as Pick<ModuleConfigs["data"], "entities" | "relations" | "indices">
|
||||
e.toJSON() as unknown as Pick<ModuleConfigs["data"], "entities" | "relations" | "indices">,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -367,7 +367,7 @@ type OptionalUndefined<
|
||||
? undefined extends T[Props]
|
||||
? Props
|
||||
: never
|
||||
: never
|
||||
: never,
|
||||
> = Merge<
|
||||
{
|
||||
[K in OptionsProps]?: T[K];
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { Entity, EntityData, EntityManager } from "../entities";
|
||||
import {
|
||||
type EntityRelationAnchor,
|
||||
type MutationInstructionResponse,
|
||||
RelationHelper
|
||||
RelationHelper,
|
||||
} from "../relations";
|
||||
import type { RepoQuery } from "../server/data-query-impl";
|
||||
import type { RelationType } from "./relation-types";
|
||||
@@ -25,7 +25,7 @@ export type BaseRelationConfig = Static<typeof EntityRelation.schema>;
|
||||
|
||||
// @todo: add generic type for relation config
|
||||
export abstract class EntityRelation<
|
||||
Schema extends typeof EntityRelation.schema = typeof EntityRelation.schema
|
||||
Schema extends typeof EntityRelation.schema = typeof EntityRelation.schema,
|
||||
> {
|
||||
config: Static<Schema>;
|
||||
|
||||
@@ -39,14 +39,14 @@ export abstract class EntityRelation<
|
||||
static schema = Type.Object({
|
||||
mappedBy: Type.Optional(Type.String()),
|
||||
inversedBy: Type.Optional(Type.String()),
|
||||
required: Type.Optional(Type.Boolean())
|
||||
required: Type.Optional(Type.Boolean()),
|
||||
});
|
||||
|
||||
// don't make protected, App requires it to instantiatable
|
||||
constructor(
|
||||
source: EntityRelationAnchor,
|
||||
target: EntityRelationAnchor,
|
||||
config: Partial<Static<Schema>> = {}
|
||||
config: Partial<Static<Schema>> = {},
|
||||
) {
|
||||
this.source = source;
|
||||
this.target = target;
|
||||
@@ -67,13 +67,13 @@ export abstract class EntityRelation<
|
||||
*/
|
||||
abstract buildWith(
|
||||
entity: Entity,
|
||||
reference: string
|
||||
reference: string,
|
||||
): (eb: ExpressionBuilder<any, any>) => KyselyQueryBuilder;
|
||||
|
||||
abstract buildJoin(
|
||||
entity: Entity,
|
||||
qb: KyselyQueryBuilder,
|
||||
reference: string
|
||||
reference: string,
|
||||
): KyselyQueryBuilder;
|
||||
|
||||
getReferenceQuery(entity: Entity, id: number, reference: string): Partial<RepoQuery> {
|
||||
@@ -105,7 +105,7 @@ export abstract class EntityRelation<
|
||||
|
||||
throw new Error(
|
||||
`Entity "${entity_name}" is not part of the relation ` +
|
||||
`"${this.source.entity.name} <-> ${this.target.entity.name}"`
|
||||
`"${this.source.entity.name} <-> ${this.target.entity.name}"`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ export abstract class EntityRelation<
|
||||
if (Array.isArray(hydrated) && hydrated.length > 1) {
|
||||
throw new Error(
|
||||
`Failed to hydrate "${anchor.entity.name}" ` +
|
||||
`with value: ${JSON.stringify(value)} (cardinality: 1)`
|
||||
`with value: ${JSON.stringify(value)} (cardinality: 1)`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ export abstract class EntityRelation<
|
||||
if (!hydrated) {
|
||||
throw new Error(
|
||||
`Failed to hydrate "${anchor.entity.name}" ` +
|
||||
`with value: ${JSON.stringify(value)} (cardinality: -)`
|
||||
`with value: ${JSON.stringify(value)} (cardinality: -)`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -178,7 +178,7 @@ export abstract class EntityRelation<
|
||||
async $set(
|
||||
em: EntityManager<any>,
|
||||
key: string,
|
||||
value: unknown
|
||||
value: unknown,
|
||||
): Promise<void | MutationInstructionResponse> {
|
||||
throw new Error("$set is not allowed");
|
||||
}
|
||||
@@ -186,7 +186,7 @@ export abstract class EntityRelation<
|
||||
async $create(
|
||||
em: EntityManager<any>,
|
||||
key: string,
|
||||
value: unknown
|
||||
value: unknown,
|
||||
): Promise<void | MutationInstructionResponse> {
|
||||
throw new Error("$create is not allowed");
|
||||
}
|
||||
@@ -194,7 +194,7 @@ export abstract class EntityRelation<
|
||||
async $attach(
|
||||
em: EntityManager<any>,
|
||||
key: string,
|
||||
value: unknown
|
||||
value: unknown,
|
||||
): Promise<void | MutationInstructionResponse> {
|
||||
throw new Error("$attach is not allowed");
|
||||
}
|
||||
@@ -202,7 +202,7 @@ export abstract class EntityRelation<
|
||||
async $detach(
|
||||
em: EntityManager<any>,
|
||||
key: string,
|
||||
value: unknown
|
||||
value: unknown,
|
||||
): Promise<void | MutationInstructionResponse> {
|
||||
throw new Error("$detach is not allowed");
|
||||
}
|
||||
@@ -213,7 +213,7 @@ export abstract class EntityRelation<
|
||||
this.source.entity.name,
|
||||
this.target.entity.name,
|
||||
this.config.mappedBy,
|
||||
this.config.inversedBy
|
||||
this.config.inversedBy,
|
||||
].filter(Boolean);
|
||||
return parts.join("_");
|
||||
}
|
||||
@@ -223,7 +223,7 @@ export abstract class EntityRelation<
|
||||
type: this.type(),
|
||||
source: this.source.entity.name,
|
||||
target: this.target.entity.name,
|
||||
config: this.config
|
||||
config: this.config,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,19 +21,19 @@ export class ManyToManyRelation extends EntityRelation<typeof ManyToManyRelation
|
||||
EntityRelation.schema,
|
||||
Type.Object({
|
||||
connectionTable: Type.Optional(Type.String()),
|
||||
connectionTableMappedName: Type.Optional(Type.String())
|
||||
})
|
||||
connectionTableMappedName: Type.Optional(Type.String()),
|
||||
}),
|
||||
],
|
||||
{
|
||||
additionalProperties: false
|
||||
}
|
||||
additionalProperties: false,
|
||||
},
|
||||
);
|
||||
|
||||
constructor(
|
||||
source: Entity,
|
||||
target: Entity,
|
||||
config?: ManyToManyRelationConfig,
|
||||
additionalFields?: Field[]
|
||||
additionalFields?: Field[],
|
||||
) {
|
||||
const connectionTable =
|
||||
config?.connectionTable || ManyToManyRelation.defaultConnectionTable(source, target);
|
||||
@@ -67,12 +67,12 @@ export class ManyToManyRelation extends EntityRelation<typeof ManyToManyRelation
|
||||
getField(entity: Entity): RelationField {
|
||||
const conn = this.connectionEntity;
|
||||
const selfField = conn.fields.find(
|
||||
(f) => f instanceof RelationField && f.target() === entity.name
|
||||
(f) => f instanceof RelationField && f.target() === entity.name,
|
||||
)!;
|
||||
|
||||
if (!selfField || !(selfField instanceof RelationField)) {
|
||||
throw new Error(
|
||||
`Connection entity "${conn.name}" does not have a relation to "${entity.name}"`
|
||||
`Connection entity "${conn.name}" does not have a relation to "${entity.name}"`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ export class ManyToManyRelation extends EntityRelation<typeof ManyToManyRelation
|
||||
const join = [
|
||||
conn.name,
|
||||
`${other.entity.name}.${other.entity.getPrimaryField().name}`,
|
||||
`${conn.name}.${otherField.name}`
|
||||
`${conn.name}.${otherField.name}`,
|
||||
] as const;
|
||||
|
||||
const entityRef = `${entity.name}.${entity.getPrimaryField().name}`;
|
||||
@@ -100,7 +100,7 @@ export class ManyToManyRelation extends EntityRelation<typeof ManyToManyRelation
|
||||
join,
|
||||
entityRef,
|
||||
otherRef,
|
||||
groupBy
|
||||
groupBy,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -109,9 +109,9 @@ export class ManyToManyRelation extends EntityRelation<typeof ManyToManyRelation
|
||||
|
||||
return {
|
||||
where: {
|
||||
[otherRef]: id
|
||||
[otherRef]: id,
|
||||
},
|
||||
join: [other.reference]
|
||||
join: [other.reference],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ export class ManyToManyRelation extends EntityRelation<typeof ManyToManyRelation
|
||||
const limit = 5;
|
||||
const { other, join, entityRef, otherRef } = this.getQueryInfo(entity);
|
||||
const additionalFields = this.connectionEntity.fields.filter(
|
||||
(f) => !(f instanceof RelationField || f instanceof PrimaryField)
|
||||
(f) => !(f instanceof RelationField || f instanceof PrimaryField),
|
||||
);
|
||||
|
||||
return (eb: ExpressionBuilder<any, any>) =>
|
||||
@@ -149,9 +149,9 @@ export class ManyToManyRelation extends EntityRelation<typeof ManyToManyRelation
|
||||
select.push(
|
||||
jsonBuildObject(
|
||||
Object.fromEntries(
|
||||
additionalFields.map((f) => [f.name, eb2.ref(`${conn}.${f.name}`)])
|
||||
)
|
||||
).as(this.connectionTableMappedName)
|
||||
additionalFields.map((f) => [f.name, eb2.ref(`${conn}.${f.name}`)]),
|
||||
),
|
||||
).as(this.connectionTableMappedName),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -186,7 +186,7 @@ export class ManyToManyRelation extends EntityRelation<typeof ManyToManyRelation
|
||||
override getName(): string {
|
||||
return [
|
||||
super.getName(),
|
||||
[this.connectionEntity.name, this.connectionTableMappedName].filter(Boolean)
|
||||
[this.connectionEntity.name, this.connectionTableMappedName].filter(Boolean),
|
||||
].join("_");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ export type ManyToOneRelationConfig = Static<typeof ManyToOneRelation.schema>;
|
||||
export class ManyToOneRelation extends EntityRelation<typeof ManyToOneRelation.schema> {
|
||||
private fieldConfig?: RelationFieldBaseConfig;
|
||||
static DEFAULTS = {
|
||||
with_limit: 5
|
||||
with_limit: 5,
|
||||
};
|
||||
|
||||
static override schema = Type.Composite(
|
||||
@@ -32,24 +32,24 @@ export class ManyToOneRelation extends EntityRelation<typeof ManyToOneRelation.s
|
||||
Type.Object({
|
||||
sourceCardinality: Type.Optional(Type.Number()),
|
||||
with_limit: Type.Optional(
|
||||
Type.Number({ default: ManyToOneRelation.DEFAULTS.with_limit })
|
||||
Type.Number({ default: ManyToOneRelation.DEFAULTS.with_limit }),
|
||||
),
|
||||
fieldConfig: Type.Optional(
|
||||
Type.Object({
|
||||
label: Type.String()
|
||||
})
|
||||
)
|
||||
})
|
||||
label: Type.String(),
|
||||
}),
|
||||
),
|
||||
}),
|
||||
],
|
||||
{
|
||||
additionalProperties: false
|
||||
}
|
||||
additionalProperties: false,
|
||||
},
|
||||
);
|
||||
|
||||
constructor(
|
||||
source: Entity,
|
||||
target: Entity,
|
||||
config: Partial<Static<typeof ManyToOneRelation.schema>> = {}
|
||||
config: Partial<Static<typeof ManyToOneRelation.schema>> = {},
|
||||
) {
|
||||
const mappedBy = config.mappedBy || target.name;
|
||||
const inversedBy = config.inversedBy || source.name;
|
||||
@@ -78,15 +78,15 @@ export class ManyToOneRelation extends EntityRelation<typeof ManyToOneRelation.s
|
||||
// add required mapping field on source
|
||||
const field = RelationField.create(this, this.target, {
|
||||
label: defaultLabel,
|
||||
...this.fieldConfig
|
||||
...this.fieldConfig,
|
||||
});
|
||||
|
||||
if (!this.source.entity.field(field.name)) {
|
||||
this.source.entity.addField(
|
||||
RelationField.create(this, this.target, {
|
||||
label: defaultLabel,
|
||||
...this.fieldConfig
|
||||
})
|
||||
...this.fieldConfig,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -100,7 +100,7 @@ export class ManyToOneRelation extends EntityRelation<typeof ManyToOneRelation.s
|
||||
|
||||
if (!(field instanceof RelationField)) {
|
||||
throw new Error(
|
||||
`Field "${this.target.reference}_${id}" not found on entity "${this.source.entity.name}"`
|
||||
`Field "${this.target.reference}_${id}" not found on entity "${this.source.entity.name}"`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ export class ManyToOneRelation extends EntityRelation<typeof ManyToOneRelation.s
|
||||
relationRef,
|
||||
entityRef,
|
||||
otherRef,
|
||||
groupBy
|
||||
groupBy,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -145,9 +145,9 @@ export class ManyToOneRelation extends EntityRelation<typeof ManyToOneRelation.s
|
||||
|
||||
return {
|
||||
where: {
|
||||
[otherRef]: id
|
||||
[otherRef]: id,
|
||||
},
|
||||
join: other.entity.name === self.entity.name ? [] : [other.entity.name]
|
||||
join: other.entity.name === self.entity.name ? [] : [other.entity.name],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -176,7 +176,7 @@ export class ManyToOneRelation extends EntityRelation<typeof ManyToOneRelation.s
|
||||
override async $set(
|
||||
em: EntityManager<any>,
|
||||
key: string,
|
||||
value: object
|
||||
value: object,
|
||||
): Promise<void | MutationInstructionResponse> {
|
||||
if (typeof value !== "object") {
|
||||
throw new Error(`Invalid value for relation field "${key}" given, expected object.`);
|
||||
@@ -204,14 +204,14 @@ export class ManyToOneRelation extends EntityRelation<typeof ManyToOneRelation.s
|
||||
}
|
||||
|
||||
const query = await em.repository(field.target()).exists({
|
||||
[field.targetField()]: primaryReference as any
|
||||
[field.targetField()]: primaryReference as any,
|
||||
});
|
||||
|
||||
if (!query.exists) {
|
||||
const idProp = field.targetField();
|
||||
throw new Error(
|
||||
`Cannot connect "${entity.name}.${key}" to ` +
|
||||
`"${field.target()}.${idProp}" = "${primaryReference}": not found.`
|
||||
`"${field.target()}.${idProp}" = "${primaryReference}": not found.`,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ export class OneToOneRelation extends ManyToOneRelation {
|
||||
mappedBy,
|
||||
inversedBy,
|
||||
sourceCardinality: 1,
|
||||
required
|
||||
required,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ export class OneToOneRelation extends ManyToOneRelation {
|
||||
override async $set(
|
||||
em: EntityManager<any>,
|
||||
key: string,
|
||||
value: object
|
||||
value: object,
|
||||
): Promise<MutationInstructionResponse> {
|
||||
throw new Error("$set is not allowed");
|
||||
}
|
||||
@@ -49,7 +49,7 @@ export class OneToOneRelation extends ManyToOneRelation {
|
||||
override async $create(
|
||||
em: EntityManager<any>,
|
||||
key: string,
|
||||
value: unknown
|
||||
value: unknown,
|
||||
): Promise<void | MutationInstructionResponse> {
|
||||
if (value === null || typeof value !== "object") {
|
||||
throw new Error(`Invalid value for relation field "${key}" given, expected object.`);
|
||||
|
||||
@@ -15,12 +15,12 @@ export class PolymorphicRelation extends EntityRelation<typeof PolymorphicRelati
|
||||
[
|
||||
EntityRelation.schema,
|
||||
Type.Object({
|
||||
targetCardinality: Type.Optional(Type.Number())
|
||||
})
|
||||
targetCardinality: Type.Optional(Type.Number()),
|
||||
}),
|
||||
],
|
||||
{
|
||||
additionalProperties: false
|
||||
}
|
||||
additionalProperties: false,
|
||||
},
|
||||
);
|
||||
|
||||
constructor(source: Entity, target: Entity, config: Partial<PolymorphicRelationConfig> = {}) {
|
||||
@@ -63,7 +63,7 @@ export class PolymorphicRelation extends EntityRelation<typeof PolymorphicRelati
|
||||
reference_other,
|
||||
entityRef,
|
||||
otherRef,
|
||||
groupBy
|
||||
groupBy,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ export class PolymorphicRelation extends EntityRelation<typeof PolymorphicRelati
|
||||
|
||||
return qb
|
||||
.innerJoin(other.entity.name, (join) =>
|
||||
join.onRef(entityRef, "=", otherRef).on(whereLhs, "=", reference)
|
||||
join.onRef(entityRef, "=", otherRef).on(whereLhs, "=", reference),
|
||||
)
|
||||
.groupBy(groupBy);
|
||||
}
|
||||
@@ -83,8 +83,8 @@ export class PolymorphicRelation extends EntityRelation<typeof PolymorphicRelati
|
||||
return {
|
||||
where: {
|
||||
[this.getReferenceField().name]: info.reference_other,
|
||||
[this.getEntityIdField().name]: id
|
||||
}
|
||||
[this.getEntityIdField().name]: id,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -12,8 +12,8 @@ export const relationFieldConfigSchema = Type.Composite([
|
||||
reference: Type.String(),
|
||||
target: Type.String(), // @todo: potentially has to be an instance!
|
||||
target_field: Type.Optional(Type.String({ default: "id" })),
|
||||
on_delete: Type.Optional(StringEnum(CASCADES, { default: "set null" }))
|
||||
})
|
||||
on_delete: Type.Optional(StringEnum(CASCADES, { default: "set null" })),
|
||||
}),
|
||||
]);
|
||||
/*export const relationFieldConfigSchema = baseFieldConfigSchema.extend({
|
||||
reference: z.string(),
|
||||
@@ -44,11 +44,11 @@ export class RelationField extends Field<RelationFieldConfig> {
|
||||
static create(
|
||||
relation: EntityRelation,
|
||||
target: EntityRelationAnchor,
|
||||
config?: RelationFieldBaseConfig
|
||||
config?: RelationFieldBaseConfig,
|
||||
) {
|
||||
const name = [
|
||||
target.reference ?? target.entity.name,
|
||||
target.entity.getPrimaryField().name
|
||||
target.entity.getPrimaryField().name,
|
||||
].join("_");
|
||||
//console.log('name', name);
|
||||
return new RelationField(name, {
|
||||
@@ -56,7 +56,7 @@ export class RelationField extends Field<RelationFieldConfig> {
|
||||
required: relation.required,
|
||||
reference: target.reference,
|
||||
target: target.entity.name,
|
||||
target_field: target.entity.getPrimaryField().name
|
||||
target_field: target.entity.getPrimaryField().name,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -94,8 +94,8 @@ export class RelationField extends Field<RelationFieldConfig> {
|
||||
override toJsonSchema() {
|
||||
return this.toSchemaWrapIfRequired(
|
||||
Type.Number({
|
||||
$ref: `${this.config?.target}#/properties/${this.config?.target_field}`
|
||||
})
|
||||
$ref: `${this.config?.target}#/properties/${this.config?.target_field}`,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
ManyToManyRelation,
|
||||
type MutationOperation,
|
||||
MutationOperations,
|
||||
RelationField
|
||||
RelationField,
|
||||
} from "../relations";
|
||||
|
||||
export type MutationInstructionResponse = [string, PrimaryFieldType | null];
|
||||
@@ -13,7 +13,7 @@ export type MutationInstructionResponse = [string, PrimaryFieldType | null];
|
||||
export class RelationMutator {
|
||||
constructor(
|
||||
protected entity: Entity,
|
||||
protected em: EntityManager<any>
|
||||
protected em: EntityManager<any>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -32,11 +32,11 @@ export class RelationMutator {
|
||||
// @todo: improve later
|
||||
if (this.entity.type === "generated") {
|
||||
const relation = this.em.relations.all.find(
|
||||
(r) => r instanceof ManyToManyRelation && r.connectionEntity.name === this.entity.name
|
||||
(r) => r instanceof ManyToManyRelation && r.connectionEntity.name === this.entity.name,
|
||||
);
|
||||
if (relation instanceof ManyToManyRelation) {
|
||||
references.push(
|
||||
...this.entity.fields.filter((f) => f.type === "relation").map((f) => f.name)
|
||||
...this.entity.fields.filter((f) => f.type === "relation").map((f) => f.name),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -53,7 +53,7 @@ export class RelationMutator {
|
||||
async persistRelationField(
|
||||
field: RelationField,
|
||||
key: string,
|
||||
value: PrimaryFieldType
|
||||
value: PrimaryFieldType,
|
||||
): Promise<MutationInstructionResponse> {
|
||||
// allow empty if field is not required
|
||||
if (value === null && !field.isRequired()) {
|
||||
@@ -68,14 +68,14 @@ export class RelationMutator {
|
||||
}
|
||||
|
||||
const query = await this.em.repository(field.target()).exists({
|
||||
[field.targetField()]: value
|
||||
[field.targetField()]: value,
|
||||
});
|
||||
|
||||
if (!query.exists) {
|
||||
const idProp = field.targetField();
|
||||
throw new Error(
|
||||
`Cannot connect "${this.entity.name}.${key}" to ` +
|
||||
`"${field.target()}.${idProp}" = "${value}": not found.`
|
||||
`"${field.target()}.${idProp}" = "${value}": not found.`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -85,11 +85,11 @@ export class RelationMutator {
|
||||
async persistReference(
|
||||
relation: EntityRelation,
|
||||
key: string,
|
||||
value: unknown
|
||||
value: unknown,
|
||||
): Promise<void | MutationInstructionResponse> {
|
||||
if (typeof value !== "object" || value === null || typeof value === "undefined") {
|
||||
throw new Error(
|
||||
`Invalid value for relation "${key}" given, expected object to persist reference. Like '{$set: {id: 1}}'.`
|
||||
`Invalid value for relation "${key}" given, expected object to persist reference. Like '{$set: {id: 1}}'.`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ export class RelationMutator {
|
||||
if (!MutationOperations.includes(operation)) {
|
||||
throw new Error(
|
||||
`Invalid operation "${operation}" for relation "${key}". ` +
|
||||
`Allowed: ${MutationOperations.join(", ")}`
|
||||
`Allowed: ${MutationOperations.join(", ")}`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ export class RelationMutator {
|
||||
|
||||
throw new Error(
|
||||
`Relation "${key}" failed to resolve on entity "${this.entity.name}": ` +
|
||||
"Unable to resolve relation origin."
|
||||
"Unable to resolve relation origin.",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
RelationField,
|
||||
type RelationFieldBaseConfig,
|
||||
type RelationFieldConfig,
|
||||
relationFieldConfigSchema
|
||||
relationFieldConfigSchema,
|
||||
} from "./RelationField";
|
||||
|
||||
export {
|
||||
@@ -32,7 +32,7 @@ export {
|
||||
RelationField,
|
||||
relationFieldConfigSchema,
|
||||
type RelationFieldBaseConfig,
|
||||
type RelationFieldConfig
|
||||
type RelationFieldConfig,
|
||||
};
|
||||
|
||||
export const RelationClassMap = {
|
||||
@@ -41,10 +41,10 @@ export const RelationClassMap = {
|
||||
[RelationTypes.ManyToMany]: { schema: ManyToManyRelation.schema, cls: ManyToManyRelation },
|
||||
[RelationTypes.Polymorphic]: {
|
||||
schema: PolymorphicRelation.schema,
|
||||
cls: PolymorphicRelation
|
||||
}
|
||||
cls: PolymorphicRelation,
|
||||
},
|
||||
} as const;
|
||||
|
||||
export const RelationFieldClassMap = {
|
||||
relation: { schema: relationFieldConfigSchema, field: RelationField }
|
||||
relation: { schema: relationFieldConfigSchema, field: RelationField },
|
||||
} as const;
|
||||
|
||||
@@ -58,7 +58,7 @@ export class SchemaManager {
|
||||
|
||||
async introspect(): Promise<IntrospectedTable[]> {
|
||||
const tables = await this.getIntrospector().getTables({
|
||||
withInternalKyselyTables: false
|
||||
withInternalKyselyTables: false,
|
||||
});
|
||||
|
||||
const indices = await this.getIntrospector().getIndices();
|
||||
@@ -71,7 +71,7 @@ export class SchemaManager {
|
||||
|
||||
cleanTables.push({
|
||||
...table,
|
||||
indices: indices.filter((index) => index.table === table.name)
|
||||
indices: indices.filter((index) => index.table === table.name),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ export class SchemaManager {
|
||||
isNullable: true, // managed by the field
|
||||
isAutoIncrementing: field instanceof PrimaryField,
|
||||
hasDefaultValue: false, // managed by the field
|
||||
comment: undefined
|
||||
comment: undefined,
|
||||
})),
|
||||
indices: indices.map((index) => ({
|
||||
name: index.name,
|
||||
@@ -102,9 +102,9 @@ export class SchemaManager {
|
||||
isUnique: index.unique,
|
||||
columns: index.fields.map((f) => ({
|
||||
name: f.name,
|
||||
order: 0 // doesn't matter
|
||||
}))
|
||||
})) as any
|
||||
order: 0, // doesn't matter
|
||||
})),
|
||||
})) as any,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -131,12 +131,12 @@ export class SchemaManager {
|
||||
columns: {
|
||||
add: [],
|
||||
drop: [],
|
||||
change: []
|
||||
change: [],
|
||||
},
|
||||
indices: {
|
||||
add: [],
|
||||
drop: []
|
||||
}
|
||||
drop: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -151,22 +151,22 @@ export class SchemaManager {
|
||||
columns: {
|
||||
add: entity.columns.map(namesFn),
|
||||
drop: [],
|
||||
change: []
|
||||
change: [],
|
||||
},
|
||||
indices: {
|
||||
add: entity.indices.map(namesFn),
|
||||
drop: []
|
||||
}
|
||||
drop: [],
|
||||
},
|
||||
});
|
||||
} else {
|
||||
// If the table exists, check for new columns
|
||||
const newColumns = entity.columns.filter(
|
||||
(newColumn) => !table.columns.map(namesFn).includes(newColumn.name)
|
||||
(newColumn) => !table.columns.map(namesFn).includes(newColumn.name),
|
||||
);
|
||||
|
||||
// check for columns to drop
|
||||
const dropColumns = table.columns.filter(
|
||||
(oldColumn) => !entity.columns.map(namesFn).includes(oldColumn.name)
|
||||
(oldColumn) => !entity.columns.map(namesFn).includes(oldColumn.name),
|
||||
);
|
||||
|
||||
// check for changed columns
|
||||
@@ -179,25 +179,25 @@ export class SchemaManager {
|
||||
col_diffs.push({
|
||||
attribute: key,
|
||||
prev: db_col[key],
|
||||
next: value
|
||||
next: value,
|
||||
});
|
||||
}
|
||||
}
|
||||
if (Object.keys(col_diffs).length > 0) {
|
||||
columnDiffs.push({
|
||||
name: entity_col.name,
|
||||
changes: col_diffs
|
||||
changes: col_diffs,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// new indices
|
||||
const newIndices = entity.indices.filter(
|
||||
(newIndex) => !table.indices.map((i) => i.name).includes(newIndex.name)
|
||||
(newIndex) => !table.indices.map((i) => i.name).includes(newIndex.name),
|
||||
);
|
||||
|
||||
const dropIndices = table.indices.filter(
|
||||
(oldIndex) => !entity.indices.map((i) => i.name).includes(oldIndex.name)
|
||||
(oldIndex) => !entity.indices.map((i) => i.name).includes(oldIndex.name),
|
||||
);
|
||||
|
||||
const anythingChanged = [
|
||||
@@ -205,7 +205,7 @@ export class SchemaManager {
|
||||
dropColumns,
|
||||
//columnDiffs, // ignored
|
||||
newIndices,
|
||||
dropIndices
|
||||
dropIndices,
|
||||
].some((arr) => arr.length > 0);
|
||||
|
||||
if (anythingChanged) {
|
||||
@@ -217,12 +217,12 @@ export class SchemaManager {
|
||||
drop: dropColumns.map(namesFn),
|
||||
// @todo: this is ignored for now
|
||||
//change: columnDiffs.map(namesFn),
|
||||
change: []
|
||||
change: [],
|
||||
},
|
||||
indices: {
|
||||
add: newIndices.map(namesFn),
|
||||
drop: dropIndices.map(namesFn)
|
||||
}
|
||||
drop: dropIndices.map(namesFn),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,17 +18,17 @@ export function constructEntity(name: string, entityConfig: TAppDataEntity) {
|
||||
name,
|
||||
Object.values(fields),
|
||||
entityConfig.config as any,
|
||||
entityConfig.type as any
|
||||
entityConfig.type as any,
|
||||
);
|
||||
}
|
||||
|
||||
export function constructRelation(
|
||||
relationConfig: TAppDataRelation,
|
||||
resolver: (name: Entity | string) => Entity
|
||||
resolver: (name: Entity | string) => Entity,
|
||||
) {
|
||||
return new RELATIONS[relationConfig.type].cls(
|
||||
resolver(relationConfig.source),
|
||||
resolver(relationConfig.target),
|
||||
relationConfig.config
|
||||
relationConfig.config,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
StringEnum,
|
||||
Type,
|
||||
Value,
|
||||
isObject
|
||||
isObject,
|
||||
} from "core/utils";
|
||||
import { WhereBuilder, type WhereQuery } from "../entities";
|
||||
|
||||
@@ -23,9 +23,9 @@ const sort = Type.Transform(
|
||||
Type.Union(
|
||||
[Type.String(), Type.Object({ by: Type.String(), dir: StringEnum(["asc", "desc"]) })],
|
||||
{
|
||||
default: sort_default
|
||||
}
|
||||
)
|
||||
default: sort_default,
|
||||
},
|
||||
),
|
||||
)
|
||||
.Decode((value): { by: string; dir: "asc" | "desc" } => {
|
||||
if (typeof value === "string") {
|
||||
@@ -43,7 +43,7 @@ const sort = Type.Transform(
|
||||
.Encode((value) => value);
|
||||
|
||||
const stringArray = Type.Transform(
|
||||
Type.Union([Type.String(), Type.Array(Type.String())], { default: [] })
|
||||
Type.Union([Type.String(), Type.Array(Type.String())], { default: [] }),
|
||||
)
|
||||
.Decode((value) => {
|
||||
if (Array.isArray(value)) {
|
||||
@@ -56,7 +56,7 @@ const stringArray = Type.Transform(
|
||||
.Encode((value) => (Array.isArray(value) ? value : [value]));
|
||||
|
||||
export const whereSchema = Type.Transform(
|
||||
Type.Union([Type.String(), Type.Object({})], { default: {} })
|
||||
Type.Union([Type.String(), Type.Object({})], { default: {} }),
|
||||
)
|
||||
.Decode((value) => {
|
||||
const q = typeof value === "string" ? JSON.parse(value) : value;
|
||||
@@ -73,7 +73,7 @@ export type RepoWithSchema = Record<
|
||||
|
||||
export const withSchema = <TSelf extends TThis>(Self: TSelf) =>
|
||||
Type.Transform(
|
||||
Type.Union([Type.String(), Type.Array(Type.String()), Type.Record(Type.String(), Self)])
|
||||
Type.Union([Type.String(), Type.Array(Type.String()), Type.Record(Type.String(), Self)]),
|
||||
)
|
||||
.Decode((value) => {
|
||||
// images
|
||||
@@ -130,15 +130,15 @@ export const querySchema = Type.Recursive(
|
||||
select: stringArray,
|
||||
with: withSchema(Self),
|
||||
join: stringArray,
|
||||
where: whereSchema
|
||||
where: whereSchema,
|
||||
},
|
||||
{
|
||||
// @todo: determine if unknown is allowed, it's ignore anyway
|
||||
additionalProperties: false
|
||||
}
|
||||
)
|
||||
additionalProperties: false,
|
||||
},
|
||||
),
|
||||
),
|
||||
{ $id: "query-schema" }
|
||||
{ $id: "query-schema" },
|
||||
);
|
||||
|
||||
export type RepoQueryIn = {
|
||||
|
||||
Reference in New Issue
Block a user