clonegur/backend/server/decorators/rest.ts
2023-06-30 13:19:21 +03:00

60 lines
2.5 KiB
TypeScript

// deno-lint-ignore-file ban-types
import { Reflect } from "https://deno.land/x/reflect_metadata@v0.1.12/mod.ts";
import RestResponse from "../RestResponse.ts";
import Router, { ProcessFunc, addMetaQuery } from "../Router.ts";
import serialize from "../serialize.ts";
export type HttpMethod = '*' | 'GET' | 'POST' | 'CHANGE' | 'DELETE' | 'PUT' | 'UPDATE' | 'OPTIONS';
type Base<KeyT extends string> = Router & { [X in KeyT]: Function; };
type ModArray<T extends Router> = ([ProcessFunc<T>, ...ProcessFunc<T>[]] | undefined)[];
export default function rest<KeyT extends keyof T & string, T extends Base<KeyT>>(method: HttpMethod, route?: string) {
return (target: T, key: KeyT) => {
const path = route ?? key;
addMetaQuery(target, (r) => ({
path, handler: {
async handle(req) {
if (method !== '*' && req.method.toUpperCase() !== method) return undefined;
const params: string[] = [];
const args: unknown[] = [];
const allMods: ModArray<T> = [];
let signature = r[key].toString();
signature = signature.substring(signature.indexOf('(') + 1, signature.indexOf(')'));
params.push(...signature.split(',').map(v => v.trim()).filter(v => v !== ''));
for (let proto = r; proto instanceof Router; proto = Object.getPrototypeOf(proto)) {
const data = Reflect.getOwnMetadata('router:params', proto, key) as ModArray<T>;
if (!data) continue;
for (let i = 0; i < data.length; i++) {
if (data[i] === undefined) continue;
if (allMods[i]) allMods[i]!.push(...data[i]!);
else allMods[i] ??= [...data[i]!];
}
}
for (let i = 0; i < params.length; i++) {
const param = params[i];
let arg: unknown = req.params[param];
for (const mod of allMods[i] ?? []) {
arg = await mod.call(r, req, arg, params[i]);
}
args.push(arg);
}
const res = await r[key].apply(r, args);
if (res instanceof RestResponse) return res;
return new RestResponse().body(await serialize(res));
}
},
}));
};
}