mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-16 04:27:21 +00:00
added new em() shorthand for prototyping, added insertMany mutator function for seeding
This commit is contained in:
@@ -16,7 +16,7 @@ describe("Mutator simple", async () => {
|
|||||||
new TextField("label", { required: true, minLength: 1 }),
|
new TextField("label", { required: true, minLength: 1 }),
|
||||||
new NumberField("count", { default_value: 0 })
|
new NumberField("count", { default_value: 0 })
|
||||||
]);
|
]);
|
||||||
const em = new EntityManager([items], connection);
|
const em = new EntityManager<any>([items], connection);
|
||||||
|
|
||||||
await em.connection.kysely.schema
|
await em.connection.kysely.schema
|
||||||
.createTable("items")
|
.createTable("items")
|
||||||
@@ -175,4 +175,18 @@ describe("Mutator simple", async () => {
|
|||||||
{ id: 8, label: "keep", count: 0 }
|
{ id: 8, label: "keep", count: 0 }
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("insertMany", async () => {
|
||||||
|
const oldCount = (await em.repo(items).count()).count;
|
||||||
|
const inserts = [{ label: "insert 1" }, { label: "insert 2" }];
|
||||||
|
const { data } = await em.mutator(items).insertMany(inserts);
|
||||||
|
|
||||||
|
expect(data.length).toBe(2);
|
||||||
|
expect(data.map((d) => ({ label: d.label }))).toEqual(inserts);
|
||||||
|
const newCount = (await em.repo(items).count()).count;
|
||||||
|
expect(newCount).toBe(oldCount + inserts.length);
|
||||||
|
|
||||||
|
const { data: data2 } = await em.repo(items).findMany();
|
||||||
|
expect(data2).toEqual(data);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
PolymorphicRelation,
|
PolymorphicRelation,
|
||||||
TextField
|
TextField
|
||||||
} from "../../src/data";
|
} from "../../src/data";
|
||||||
|
import { DummyConnection } from "../../src/data/connection/DummyConnection";
|
||||||
import {
|
import {
|
||||||
FieldPrototype,
|
FieldPrototype,
|
||||||
type FieldSchema,
|
type FieldSchema,
|
||||||
@@ -21,6 +22,7 @@ import {
|
|||||||
boolean,
|
boolean,
|
||||||
date,
|
date,
|
||||||
datetime,
|
datetime,
|
||||||
|
em,
|
||||||
entity,
|
entity,
|
||||||
enumm,
|
enumm,
|
||||||
json,
|
json,
|
||||||
@@ -272,4 +274,29 @@ describe("prototype", () => {
|
|||||||
|
|
||||||
const obj: Schema<typeof test> = {} as any;
|
const obj: Schema<typeof test> = {} as any;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("schema", async () => {
|
||||||
|
const _em = em(
|
||||||
|
{
|
||||||
|
posts: entity("posts", { name: text() }),
|
||||||
|
comments: entity("comments", { some: text() })
|
||||||
|
},
|
||||||
|
(relation, { posts, comments }) => {
|
||||||
|
relation(posts).manyToOne(comments);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
type LocalDb = (typeof _em)["DB"];
|
||||||
|
|
||||||
|
const es = [
|
||||||
|
new Entity("posts", [new TextField("name")]),
|
||||||
|
new Entity("comments", [new TextField("some")])
|
||||||
|
];
|
||||||
|
const _em2 = new EntityManager(es, new DummyConnection(), [
|
||||||
|
new ManyToOneRelation(es[0], es[1])
|
||||||
|
]);
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
expect(_em2.toJSON()).toEqual(_em.toJSON());
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ describe("[data] Mutator (base)", async () => {
|
|||||||
new TextField("hidden", { hidden: true }),
|
new TextField("hidden", { hidden: true }),
|
||||||
new TextField("not_fillable", { fillable: false })
|
new TextField("not_fillable", { fillable: false })
|
||||||
]);
|
]);
|
||||||
const em = new EntityManager([entity], dummyConnection);
|
const em = new EntityManager<any>([entity], dummyConnection);
|
||||||
await em.schema().sync({ force: true });
|
await em.schema().sync({ force: true });
|
||||||
|
|
||||||
const payload = { label: "item 1", count: 1 };
|
const payload = { label: "item 1", count: 1 };
|
||||||
@@ -61,7 +61,7 @@ describe("[data] Mutator (ManyToOne)", async () => {
|
|||||||
const posts = new Entity("posts", [new TextField("title")]);
|
const posts = new Entity("posts", [new TextField("title")]);
|
||||||
const users = new Entity("users", [new TextField("username")]);
|
const users = new Entity("users", [new TextField("username")]);
|
||||||
const relations = [new ManyToOneRelation(posts, users)];
|
const relations = [new ManyToOneRelation(posts, users)];
|
||||||
const em = new EntityManager([posts, users], dummyConnection, relations);
|
const em = new EntityManager<any>([posts, users], dummyConnection, relations);
|
||||||
await em.schema().sync({ force: true });
|
await em.schema().sync({ force: true });
|
||||||
|
|
||||||
test("RelationMutator", async () => {
|
test("RelationMutator", async () => {
|
||||||
@@ -192,7 +192,7 @@ describe("[data] Mutator (OneToOne)", async () => {
|
|||||||
const users = new Entity("users", [new TextField("username")]);
|
const users = new Entity("users", [new TextField("username")]);
|
||||||
const settings = new Entity("settings", [new TextField("theme")]);
|
const settings = new Entity("settings", [new TextField("theme")]);
|
||||||
const relations = [new OneToOneRelation(users, settings)];
|
const relations = [new OneToOneRelation(users, settings)];
|
||||||
const em = new EntityManager([users, settings], dummyConnection, relations);
|
const em = new EntityManager<any>([users, settings], dummyConnection, relations);
|
||||||
await em.schema().sync({ force: true });
|
await em.schema().sync({ force: true });
|
||||||
|
|
||||||
test("insertOne: missing ref", async () => {
|
test("insertOne: missing ref", async () => {
|
||||||
@@ -276,7 +276,7 @@ describe("[data] Mutator (ManyToMany)", async () => {
|
|||||||
|
|
||||||
describe("[data] Mutator (Events)", async () => {
|
describe("[data] Mutator (Events)", async () => {
|
||||||
const entity = new Entity("test", [new TextField("label")]);
|
const entity = new Entity("test", [new TextField("label")]);
|
||||||
const em = new EntityManager([entity], dummyConnection);
|
const em = new EntityManager<any>([entity], dummyConnection);
|
||||||
await em.schema().sync({ force: true });
|
await em.schema().sync({ force: true });
|
||||||
const events = new Map<string, any>();
|
const events = new Map<string, any>();
|
||||||
|
|
||||||
|
|||||||
7
app/src/data/connection/DummyConnection.ts
Normal file
7
app/src/data/connection/DummyConnection.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { Connection } from "./Connection";
|
||||||
|
|
||||||
|
export class DummyConnection extends Connection {
|
||||||
|
constructor() {
|
||||||
|
super(undefined as any);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -276,4 +276,39 @@ export class Mutator<DB = any, TB extends keyof DB = any, Data = Omit<DB[TB], "i
|
|||||||
|
|
||||||
return (await this.many(query)) as any;
|
return (await this.many(query)) as any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async insertMany(data: Data[]): Promise<MutatorResponse<DB[TB][]>> {
|
||||||
|
const entity = this.entity;
|
||||||
|
if (entity.type === "system" && this.__unstable_disable_system_entity_creation) {
|
||||||
|
throw new Error(`Creation of system entity "${entity.name}" is disabled`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const validated: any[] = [];
|
||||||
|
for (const row of data) {
|
||||||
|
const validatedData = {
|
||||||
|
...entity.getDefaultObject(),
|
||||||
|
...(await this.getValidatedData(row, "create"))
|
||||||
|
};
|
||||||
|
|
||||||
|
// check if required fields are present
|
||||||
|
const required = entity.getRequiredFields();
|
||||||
|
for (const field of required) {
|
||||||
|
if (
|
||||||
|
typeof validatedData[field.name] === "undefined" ||
|
||||||
|
validatedData[field.name] === null
|
||||||
|
) {
|
||||||
|
throw new Error(`Field "${field.name}" is required`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
validated.push(validatedData);
|
||||||
|
}
|
||||||
|
|
||||||
|
const query = this.conn
|
||||||
|
.insertInto(entity.name)
|
||||||
|
.values(validated)
|
||||||
|
.returning(entity.getSelect());
|
||||||
|
|
||||||
|
return (await this.many(query)) as any;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { DummyConnection } from "data/connection/DummyConnection";
|
||||||
|
import { EntityManager } from "data/entities/EntityManager";
|
||||||
import type { Generated } from "kysely";
|
import type { Generated } from "kysely";
|
||||||
import { MediaField, type MediaFieldConfig, type MediaItem } from "media/MediaField";
|
import { MediaField, type MediaFieldConfig, type MediaItem } from "media/MediaField";
|
||||||
import {
|
import {
|
||||||
@@ -7,6 +9,7 @@ import {
|
|||||||
type DateFieldConfig,
|
type DateFieldConfig,
|
||||||
Entity,
|
Entity,
|
||||||
type EntityConfig,
|
type EntityConfig,
|
||||||
|
type EntityRelation,
|
||||||
EnumField,
|
EnumField,
|
||||||
type EnumFieldConfig,
|
type EnumFieldConfig,
|
||||||
type Field,
|
type Field,
|
||||||
@@ -240,6 +243,57 @@ export function relation<Local extends Entity>(local: Local) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class EntityManagerPrototype<Entities extends Record<string, Entity>> extends EntityManager<
|
||||||
|
Schema<Entities>
|
||||||
|
> {
|
||||||
|
constructor(
|
||||||
|
public __entities: Entities,
|
||||||
|
relations: EntityRelation[]
|
||||||
|
) {
|
||||||
|
super(Object.values(__entities), new DummyConnection(), relations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function em<Entities extends Record<string, Entity>>(
|
||||||
|
entities: Entities,
|
||||||
|
schema?: (rel: typeof relation, entities: Entities) => void
|
||||||
|
) {
|
||||||
|
const relations: EntityRelation[] = [];
|
||||||
|
const relationProxy = (local: Entity) => {
|
||||||
|
return new Proxy(relation(local), {
|
||||||
|
get(target, prop) {
|
||||||
|
if (typeof target[prop] === "function") {
|
||||||
|
return (...args: any[]) => {
|
||||||
|
const result = target[prop](...args);
|
||||||
|
relations.push(result);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return target[prop];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (schema) {
|
||||||
|
schema(relationProxy, entities);
|
||||||
|
}
|
||||||
|
|
||||||
|
const e = new EntityManagerPrototype(entities, relations);
|
||||||
|
return {
|
||||||
|
DB: e.__entities as unknown as Schemas<Entities>,
|
||||||
|
entities: e.__entities,
|
||||||
|
relations,
|
||||||
|
indices: [],
|
||||||
|
toJSON: () => {
|
||||||
|
return e.toJSON() as unknown as {
|
||||||
|
entities: Schemas<Entities>;
|
||||||
|
relations: EntityRelation[];
|
||||||
|
indices: any[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export type InferEntityFields<T> = T extends Entity<infer _N, infer Fields>
|
export type InferEntityFields<T> = T extends Entity<infer _N, infer Fields>
|
||||||
? {
|
? {
|
||||||
[K in keyof Fields]: Fields[K] extends { _type: infer Type; _required: infer Required }
|
[K in keyof Fields]: Fields[K] extends { _type: infer Type; _required: infer Required }
|
||||||
@@ -291,18 +345,9 @@ export type InferField<Field> = Field extends { _type: infer Type; _required: in
|
|||||||
: Type | undefined
|
: Type | undefined
|
||||||
: never;
|
: never;
|
||||||
|
|
||||||
const n = number();
|
export type Schemas<T extends Record<string, Entity>> = {
|
||||||
type T2 = InferField<typeof n>;
|
[K in keyof T]: Schema<T[K]>;
|
||||||
|
};
|
||||||
const users = entity("users", {
|
|
||||||
name: text(),
|
|
||||||
email: text(),
|
|
||||||
created_at: datetime(),
|
|
||||||
updated_at: datetime()
|
|
||||||
});
|
|
||||||
type TUsersFields = InferEntityFields<typeof users>;
|
|
||||||
type TUsers = Schema<typeof users>;
|
|
||||||
type TUsers2 = Simplify<OptionalUndefined<Schema<typeof users>>>;
|
|
||||||
|
|
||||||
export type InsertSchema<T> = Simplify<OptionalUndefined<InferEntityFields<T>>>;
|
export type InsertSchema<T> = Simplify<OptionalUndefined<InferEntityFields<T>>>;
|
||||||
export type Schema<T> = Simplify<{ id: Generated<number> } & InsertSchema<T>>;
|
export type Schema<T> = Simplify<{ id: Generated<number> } & InsertSchema<T>>;
|
||||||
|
|||||||
Reference in New Issue
Block a user