mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-16 04:27:21 +00:00
added readOneBy, updateMany, deleteMany, exists
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import type { DB } from "core";
|
||||
import type { EntityData, RepoQueryIn, RepositoryResponse } from "data";
|
||||
import { type BaseModuleApiOptions, ModuleApi, type PrimaryFieldType } from "modules";
|
||||
import type { FetchPromise, ResponseObject } from "modules/ModuleApi";
|
||||
|
||||
export type DataApiOptions = BaseModuleApiOptions & {
|
||||
queryLengthLimit: number;
|
||||
@@ -18,6 +19,12 @@ export class DataApi extends ModuleApi<DataApiOptions> {
|
||||
};
|
||||
}
|
||||
|
||||
private requireObjectSet(obj: any, message?: string) {
|
||||
if (!obj || typeof obj !== "object" || Object.keys(obj).length === 0) {
|
||||
throw new Error(message ?? "object is required");
|
||||
}
|
||||
}
|
||||
|
||||
readOne<E extends keyof DB | string, Data = E extends keyof DB ? DB[E] : EntityData>(
|
||||
entity: E,
|
||||
id: PrimaryFieldType,
|
||||
@@ -29,6 +36,18 @@ export class DataApi extends ModuleApi<DataApiOptions> {
|
||||
);
|
||||
}
|
||||
|
||||
readOneBy<E extends keyof DB | string, Data = E extends keyof DB ? DB[E] : EntityData>(
|
||||
entity: E,
|
||||
query: Omit<RepoQueryIn, "limit" | "offset" | "sort"> = {},
|
||||
) {
|
||||
type T = Pick<RepositoryResponse<Data>, "meta" | "data">;
|
||||
return this.readMany(entity, {
|
||||
...query,
|
||||
limit: 1,
|
||||
offset: 0,
|
||||
}).refine((data) => data[0]) as unknown as FetchPromise<ResponseObject<T>>;
|
||||
}
|
||||
|
||||
readMany<E extends keyof DB | string, Data = E extends keyof DB ? DB[E] : EntityData>(
|
||||
entity: E,
|
||||
query: RepoQueryIn = {},
|
||||
@@ -63,25 +82,64 @@ export class DataApi extends ModuleApi<DataApiOptions> {
|
||||
return this.post<RepositoryResponse<Data>>(["entity", entity as any], input);
|
||||
}
|
||||
|
||||
createMany<E extends keyof DB | string, Data = E extends keyof DB ? DB[E] : EntityData>(
|
||||
entity: E,
|
||||
input: Omit<Data, "id">[],
|
||||
) {
|
||||
if (!input || !Array.isArray(input) || input.length === 0) {
|
||||
throw new Error("input is required");
|
||||
}
|
||||
return this.post<RepositoryResponse<Data[]>>(["entity", entity as any], input);
|
||||
}
|
||||
|
||||
updateOne<E extends keyof DB | string, Data = E extends keyof DB ? DB[E] : EntityData>(
|
||||
entity: E,
|
||||
id: PrimaryFieldType,
|
||||
input: Partial<Omit<Data, "id">>,
|
||||
) {
|
||||
if (!id) throw new Error("ID is required");
|
||||
return this.patch<RepositoryResponse<Data>>(["entity", entity as any, id], input);
|
||||
}
|
||||
|
||||
updateMany<E extends keyof DB | string, Data = E extends keyof DB ? DB[E] : EntityData>(
|
||||
entity: E,
|
||||
where: RepoQueryIn["where"],
|
||||
update: Partial<Omit<Data, "id">>,
|
||||
) {
|
||||
this.requireObjectSet(where);
|
||||
return this.patch<RepositoryResponse<Data[]>>(["entity", entity as any], {
|
||||
update,
|
||||
where,
|
||||
});
|
||||
}
|
||||
|
||||
deleteOne<E extends keyof DB | string, Data = E extends keyof DB ? DB[E] : EntityData>(
|
||||
entity: E,
|
||||
id: PrimaryFieldType,
|
||||
) {
|
||||
if (!id) throw new Error("ID is required");
|
||||
return this.delete<RepositoryResponse<Data>>(["entity", entity as any, id]);
|
||||
}
|
||||
|
||||
deleteMany<E extends keyof DB | string, Data = E extends keyof DB ? DB[E] : EntityData>(
|
||||
entity: E,
|
||||
where: RepoQueryIn["where"],
|
||||
) {
|
||||
this.requireObjectSet(where);
|
||||
return this.delete<RepositoryResponse<Data>>(["entity", entity as any], where);
|
||||
}
|
||||
|
||||
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,
|
||||
);
|
||||
}
|
||||
|
||||
exists<E extends keyof DB | string>(entity: E, where: RepoQueryIn["where"] = {}) {
|
||||
return this.post<RepositoryResponse<{ entity: E; exists: boolean }>>(
|
||||
["entity", entity as any, "fn", "exists"],
|
||||
where,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,14 +343,20 @@ export class DataController extends Controller {
|
||||
"/:entity",
|
||||
permission(DataPermissions.entityCreate),
|
||||
tb("param", Type.Object({ entity: Type.String() })),
|
||||
tb("json", Type.Union([Type.Object({}), Type.Array(Type.Object({}))])),
|
||||
async (c) => {
|
||||
const { entity } = c.req.param();
|
||||
if (!this.entityExists(entity)) {
|
||||
return this.notFound(c);
|
||||
}
|
||||
const body = (await c.req.json()) as EntityData;
|
||||
const result = await this.em.mutator(entity).insertOne(body);
|
||||
const body = (await c.req.json()) as EntityData | EntityData[];
|
||||
|
||||
if (Array.isArray(body)) {
|
||||
const result = await this.em.mutator(entity).insertMany(body);
|
||||
return c.json(this.mutatorResult(result), 201);
|
||||
}
|
||||
|
||||
const result = await this.em.mutator(entity).insertOne(body);
|
||||
return c.json(this.mutatorResult(result), 201);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -270,9 +270,14 @@ export class Mutator<
|
||||
}
|
||||
|
||||
// @todo: decide whether entries should be deleted all at once or one by one (for events)
|
||||
async deleteWhere(where?: RepoQuery["where"]): Promise<MutatorResponse<Output[]>> {
|
||||
async deleteWhere(where: RepoQuery["where"]): Promise<MutatorResponse<Output[]>> {
|
||||
const entity = this.entity;
|
||||
|
||||
// @todo: add a way to delete all by adding force?
|
||||
if (!where || typeof where !== "object" || Object.keys(where).length === 0) {
|
||||
throw new Error("Where clause must be provided for mass deletion");
|
||||
}
|
||||
|
||||
const qb = this.appendWhere(this.conn.deleteFrom(entity.name), where).returning(
|
||||
entity.getSelect(),
|
||||
);
|
||||
@@ -282,11 +287,16 @@ 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");
|
||||
|
||||
// @todo: add a way to delete all by adding force?
|
||||
if (!where || typeof where !== "object" || Object.keys(where).length === 0) {
|
||||
throw new Error("Where clause must be provided for mass update");
|
||||
}
|
||||
|
||||
const query = this.appendWhere(this.conn.updateTable(entity.name), where)
|
||||
.set(validatedData as any)
|
||||
.returning(entity.getSelect());
|
||||
|
||||
@@ -148,9 +148,10 @@ export abstract class ModuleApi<Options extends BaseModuleApiOptions = BaseModul
|
||||
});
|
||||
}
|
||||
|
||||
delete<Data = any>(_input: TInput, _init?: RequestInit) {
|
||||
delete<Data = any>(_input: TInput, body?: any, _init?: RequestInit) {
|
||||
return this.request<Data>(_input, undefined, {
|
||||
..._init,
|
||||
body,
|
||||
method: "DELETE",
|
||||
});
|
||||
}
|
||||
@@ -171,7 +172,7 @@ export function createResponseProxy<Body = any, Data = any>(
|
||||
body: Body,
|
||||
data?: Data,
|
||||
): ResponseObject<Body, Data> {
|
||||
let actualData: any = data ?? body;
|
||||
let actualData: any = typeof data !== "undefined" ? data : body;
|
||||
const _props = ["raw", "body", "ok", "status", "res", "data", "toJSON"];
|
||||
|
||||
// that's okay, since you have to check res.ok anyway
|
||||
@@ -189,6 +190,7 @@ export function createResponseProxy<Body = any, Data = any>(
|
||||
if (prop === "toJSON") {
|
||||
return () => target;
|
||||
}
|
||||
|
||||
return Reflect.get(target, prop, receiver);
|
||||
},
|
||||
has(target, prop) {
|
||||
@@ -223,12 +225,19 @@ export class FetchPromise<T = ApiResponse<any>> implements Promise<T> {
|
||||
fetcher?: typeof fetch;
|
||||
verbose?: boolean;
|
||||
},
|
||||
protected refineData?: (data: T) => any,
|
||||
) {}
|
||||
|
||||
get verbose() {
|
||||
return this.options?.verbose ?? false;
|
||||
}
|
||||
|
||||
refine<N>(fn: (data: T) => N) {
|
||||
return new FetchPromise(this.request, this.options, fn) as unknown as FetchPromise<
|
||||
ApiResponse<N>
|
||||
>;
|
||||
}
|
||||
|
||||
async execute(): Promise<ResponseObject<T>> {
|
||||
// delay in dev environment
|
||||
isDebug() && (await new Promise((resolve) => setTimeout(resolve, 200)));
|
||||
@@ -265,6 +274,15 @@ export class FetchPromise<T = ApiResponse<any>> implements Promise<T> {
|
||||
resBody = res.body;
|
||||
}
|
||||
|
||||
if (this.refineData) {
|
||||
try {
|
||||
resData = this.refineData(resData);
|
||||
} catch (e) {
|
||||
console.warn("[FetchPromise] Error in refineData", e);
|
||||
resData = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
return createResponseProxy<T>(res, resBody, resData);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user