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 NumberField("count", { default_value: 0 })
|
||||
]);
|
||||
const em = new EntityManager([items], connection);
|
||||
const em = new EntityManager<any>([items], connection);
|
||||
|
||||
await em.connection.kysely.schema
|
||||
.createTable("items")
|
||||
@@ -175,4 +175,18 @@ describe("Mutator simple", async () => {
|
||||
{ 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,
|
||||
TextField
|
||||
} from "../../src/data";
|
||||
import { DummyConnection } from "../../src/data/connection/DummyConnection";
|
||||
import {
|
||||
FieldPrototype,
|
||||
type FieldSchema,
|
||||
@@ -21,6 +22,7 @@ import {
|
||||
boolean,
|
||||
date,
|
||||
datetime,
|
||||
em,
|
||||
entity,
|
||||
enumm,
|
||||
json,
|
||||
@@ -272,4 +274,29 @@ describe("prototype", () => {
|
||||
|
||||
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("not_fillable", { fillable: false })
|
||||
]);
|
||||
const em = new EntityManager([entity], dummyConnection);
|
||||
const em = new EntityManager<any>([entity], dummyConnection);
|
||||
await em.schema().sync({ force: true });
|
||||
|
||||
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 users = new Entity("users", [new TextField("username")]);
|
||||
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 });
|
||||
|
||||
test("RelationMutator", async () => {
|
||||
@@ -192,7 +192,7 @@ describe("[data] Mutator (OneToOne)", async () => {
|
||||
const users = new Entity("users", [new TextField("username")]);
|
||||
const settings = new Entity("settings", [new TextField("theme")]);
|
||||
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 });
|
||||
|
||||
test("insertOne: missing ref", async () => {
|
||||
@@ -276,7 +276,7 @@ describe("[data] Mutator (ManyToMany)", async () => {
|
||||
|
||||
describe("[data] Mutator (Events)", async () => {
|
||||
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 });
|
||||
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;
|
||||
}
|
||||
|
||||
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 { MediaField, type MediaFieldConfig, type MediaItem } from "media/MediaField";
|
||||
import {
|
||||
@@ -7,6 +9,7 @@ import {
|
||||
type DateFieldConfig,
|
||||
Entity,
|
||||
type EntityConfig,
|
||||
type EntityRelation,
|
||||
EnumField,
|
||||
type EnumFieldConfig,
|
||||
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>
|
||||
? {
|
||||
[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
|
||||
: never;
|
||||
|
||||
const n = number();
|
||||
type T2 = InferField<typeof n>;
|
||||
|
||||
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 Schemas<T extends Record<string, Entity>> = {
|
||||
[K in keyof T]: Schema<T[K]>;
|
||||
};
|
||||
|
||||
export type InsertSchema<T> = Simplify<OptionalUndefined<InferEntityFields<T>>>;
|
||||
export type Schema<T> = Simplify<{ id: Generated<number> } & InsertSchema<T>>;
|
||||
|
||||
Reference in New Issue
Block a user