refac: api 시스템 교체
- procDecorator의 타입 복잡도를 낮춤 - 타입 에러가 살짝 더 깔끔함
This commit is contained in:
@@ -1,16 +1,17 @@
|
|||||||
import { GET } from "../defs";
|
import { GET } from "../defs";
|
||||||
import type { structure } from "../../apiStructure/sammoRootAPI";
|
import type { structure } from "../../apiStructure/sammoRootAPI";
|
||||||
import type { ExtractError, ExtractQuery, ExtractResponse } from "../../apiStructure/defs";
|
import type { ExtractError, ExtractQuery, ExtractResponse } from "../../apiStructure/defs";
|
||||||
|
import { declProcDecorators } from "../ProcDecorator/base";
|
||||||
|
|
||||||
type BaseAPI = typeof structure.Login.ReqNonce;
|
type BaseAPI = typeof structure.Login.ReqNonce;
|
||||||
type RType = ExtractResponse<BaseAPI>;
|
type RType = ExtractResponse<BaseAPI>;
|
||||||
type EType = ExtractError<BaseAPI>;
|
type EType = ExtractError<BaseAPI>;
|
||||||
type QType = ExtractQuery<BaseAPI>;
|
type QType = ExtractQuery<BaseAPI>;
|
||||||
const argValidator = undefined;
|
const argValidator = undefined;
|
||||||
const procDecorator = [] as const;
|
|
||||||
|
|
||||||
export const ReqNonce = GET<RType, EType, QType>(argValidator)(procDecorator)(
|
export const ReqNonce = GET<RType, EType, QType>
|
||||||
(query, ctx, req, res) => {
|
(argValidator)
|
||||||
|
(declProcDecorators([] as const))
|
||||||
|
(() => {
|
||||||
throw new Error("Method not implemented.");
|
throw new Error("Method not implemented.");
|
||||||
}
|
});
|
||||||
);
|
|
||||||
@@ -4,6 +4,7 @@ import type { ExtractError, ExtractQuery, ExtractResponse } from "../../apiStruc
|
|||||||
import { StartSession } from "../ProcDecorator/StartSession";
|
import { StartSession } from "../ProcDecorator/StartSession";
|
||||||
import { IsString } from "class-validator";
|
import { IsString } from "class-validator";
|
||||||
import { delay } from "../../util/delay";
|
import { delay } from "../../util/delay";
|
||||||
|
import { declProcDecorators } from "../ProcDecorator/base";
|
||||||
|
|
||||||
type BaseAPI = typeof structure.Login.LoginByID;
|
type BaseAPI = typeof structure.Login.LoginByID;
|
||||||
type RType = ExtractResponse<BaseAPI>;
|
type RType = ExtractResponse<BaseAPI>;
|
||||||
@@ -15,19 +16,20 @@ class ArgValidator implements QType {
|
|||||||
@IsString()
|
@IsString()
|
||||||
password!: string;
|
password!: string;
|
||||||
}
|
}
|
||||||
const procDecorator = [
|
|
||||||
StartSession,
|
|
||||||
] as const;
|
|
||||||
|
|
||||||
export const LoginByID = POST<RType, EType, QType>(ArgValidator)(procDecorator)(
|
export const LoginByID = POST<RType, EType, QType>
|
||||||
async (query, ctx, req, res) => {
|
(ArgValidator)
|
||||||
|
(declProcDecorators([
|
||||||
|
StartSession,
|
||||||
|
] as const))
|
||||||
|
(async (query, ctx, req, res) => {
|
||||||
const username = query.username;
|
const username = query.username;
|
||||||
const password = query.password;
|
const password = query.password;
|
||||||
|
|
||||||
//TODO: DB에서 뭔가 가져와야 함
|
//TODO: DB에서 뭔가 가져와야 함
|
||||||
await delay(1);
|
await delay(1);
|
||||||
|
|
||||||
if(Math.random() < 0.3){
|
if (Math.random() < 0.3) {
|
||||||
return {
|
return {
|
||||||
result: false,
|
result: false,
|
||||||
reason: "로그인 실패",
|
reason: "로그인 실패",
|
||||||
@@ -35,7 +37,7 @@ export const LoginByID = POST<RType, EType, QType>(ArgValidator)(procDecorator)(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(Math.random() < 0.5){
|
if (Math.random() < 0.5) {
|
||||||
return {
|
return {
|
||||||
result: false,
|
result: false,
|
||||||
reason: "OTP 인증 필요",
|
reason: "OTP 인증 필요",
|
||||||
@@ -51,5 +53,4 @@ export const LoginByID = POST<RType, EType, QType>(ArgValidator)(procDecorator)(
|
|||||||
result: true,
|
result: true,
|
||||||
nextToken,
|
nextToken,
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
);
|
|
||||||
@@ -4,22 +4,25 @@ import type { ExtractError, ExtractQuery, ExtractResponse } from "../../apiStruc
|
|||||||
import { StartSession } from "../ProcDecorator/StartSession";
|
import { StartSession } from "../ProcDecorator/StartSession";
|
||||||
import { IsNumber, IsString } from "class-validator";
|
import { IsNumber, IsString } from "class-validator";
|
||||||
import { delay } from "../../util/delay";
|
import { delay } from "../../util/delay";
|
||||||
|
import { PackChain, ParseInType, ParseOutType, declProcDecorators } from "../ProcDecorator/base";
|
||||||
type BaseAPI = typeof structure.Login.LoginByToken;
|
type BaseAPI = typeof structure.Login.LoginByToken;
|
||||||
type RType = ExtractResponse<BaseAPI>;
|
type RType = ExtractResponse<BaseAPI>;
|
||||||
type EType = ExtractError<BaseAPI>;
|
type EType = ExtractError<BaseAPI>;
|
||||||
type QType = ExtractQuery<BaseAPI>;
|
type QType = ExtractQuery<BaseAPI>;
|
||||||
|
|
||||||
class ArgValidator implements QType {
|
class ArgValidator implements QType {
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
token_id!: number;
|
token_id!: number;
|
||||||
@IsString()
|
@IsString()
|
||||||
hashedToken!: string;
|
hashedToken!: string;
|
||||||
}
|
}
|
||||||
const procDecorator = [
|
|
||||||
StartSession,
|
|
||||||
] as const;
|
|
||||||
|
|
||||||
export const LoginByToken = POST<RType, EType, QType>(ArgValidator)(procDecorator)(
|
export const LoginByToken = POST<RType, EType, QType>
|
||||||
async (query, ctx) => {
|
(ArgValidator)
|
||||||
|
(declProcDecorators([
|
||||||
|
StartSession,
|
||||||
|
] as const))
|
||||||
|
(async (query, ctx) => {
|
||||||
query.hashedToken;
|
query.hashedToken;
|
||||||
ctx.clearSession();
|
ctx.clearSession();
|
||||||
|
|
||||||
@@ -38,5 +41,4 @@ export const LoginByToken = POST<RType, EType, QType>(ArgValidator)(procDecorato
|
|||||||
nextToken,
|
nextToken,
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
});
|
||||||
);
|
|
||||||
@@ -1,21 +1,22 @@
|
|||||||
import { type APINamespaceType, GET as GET } from "../defs";
|
import { GET } from "../defs";
|
||||||
import type { structure } from "../../apiStructure/sammoRootAPI";
|
import type { structure } from "../../apiStructure/sammoRootAPI";
|
||||||
import type { DefAPINamespace, ExtractError, ExtractQuery, ExtractResponse } from "../../apiStructure/defs";
|
import type { ExtractError, ExtractQuery, ExtractResponse } from "../../apiStructure/defs";
|
||||||
import { StartSession } from "../ProcDecorator/StartSession";
|
import { StartSession } from "../ProcDecorator/StartSession";
|
||||||
|
import { declProcDecorators } from "../ProcDecorator/base";
|
||||||
|
|
||||||
type BaseAPI = typeof structure.Login.ReqNonce;
|
type BaseAPI = typeof structure.Login.ReqNonce;
|
||||||
type RType = ExtractResponse<BaseAPI>;
|
type RType = ExtractResponse<BaseAPI>;
|
||||||
type EType = ExtractError<BaseAPI>;
|
type EType = ExtractError<BaseAPI>;
|
||||||
type QType = ExtractQuery<BaseAPI>;
|
type QType = ExtractQuery<BaseAPI>;
|
||||||
const argValidator = undefined;
|
|
||||||
const procDecorator = [
|
|
||||||
StartSession,
|
|
||||||
] as const;
|
|
||||||
|
|
||||||
export const ReqNonce = GET<RType, EType, QType>(argValidator)(procDecorator)(
|
export const ReqNonce = GET<RType, EType, QType>
|
||||||
async (query, ctx) => {
|
(undefined)
|
||||||
|
(declProcDecorators([
|
||||||
|
StartSession,
|
||||||
|
] as const))
|
||||||
|
(async (query, ctx) => {
|
||||||
const nonce = await ctx.getValue<string>("nonce");
|
const nonce = await ctx.getValue<string>("nonce");
|
||||||
if(nonce !== undefined){
|
if (nonce !== undefined) {
|
||||||
return {
|
return {
|
||||||
loginNonce: nonce,
|
loginNonce: nonce,
|
||||||
result: true,
|
result: true,
|
||||||
@@ -28,8 +29,4 @@ export const ReqNonce = GET<RType, EType, QType>(argValidator)(procDecorator)(
|
|||||||
loginNonce: newNonce,
|
loginNonce: newNonce,
|
||||||
result: true,
|
result: true,
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
type BaseAPI2 = APINamespaceType<typeof structure.Login>['ReqNonce'];
|
|
||||||
type A = typeof ReqNonce;
|
|
||||||
@@ -1,14 +1,16 @@
|
|||||||
import { GET } from "../defs";
|
import { GET } from "../defs";
|
||||||
import type { structure } from "../../apiStructure/sammoRootAPI";
|
import type { structure } from "../../apiStructure/sammoRootAPI";
|
||||||
import type { ExtractError, ExtractQuery, ExtractResponse } from "../../apiStructure/defs";
|
import type { ExtractError, ExtractQuery, ExtractResponse } from "../../apiStructure/defs";
|
||||||
|
import { declProcDecorators } from "../ProcDecorator/base";
|
||||||
|
|
||||||
type BaseAPI = typeof structure.Login.test;
|
type BaseAPI = typeof structure.Login.test;
|
||||||
type RType = ExtractResponse<BaseAPI>;
|
type RType = ExtractResponse<BaseAPI>;
|
||||||
type EType = ExtractError<BaseAPI>;
|
type EType = ExtractError<BaseAPI>;
|
||||||
type QType = ExtractQuery<BaseAPI>;
|
type QType = ExtractQuery<BaseAPI>;
|
||||||
|
|
||||||
export const test = GET<RType, EType, QType>(undefined)(undefined)(
|
export const test = GET<RType, EType, QType>
|
||||||
(query, ctx, req, res) => {
|
(undefined)
|
||||||
|
(declProcDecorators([] as const))
|
||||||
|
(() => {
|
||||||
throw new Error("Method not implemented.");
|
throw new Error("Method not implemented.");
|
||||||
}
|
});
|
||||||
);
|
|
||||||
@@ -9,12 +9,14 @@ export interface UserLevelCtx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function ParseUserLevel<Q extends LoginCtx & SessionCtx>(): ProcDecoratorGenerator<UserLevelCtx, Q> {
|
export function ParseUserLevel<Q extends LoginCtx & SessionCtx>(): ProcDecoratorGenerator<UserLevelCtx, Q> {
|
||||||
return (inCtx) => {
|
return (ctx) => {
|
||||||
const userLevel = 123;
|
const userLevel = 123;
|
||||||
inCtx.setValue('userLevel', userLevel);
|
ctx.setValue('userLevel', userLevel);
|
||||||
return {
|
return [{
|
||||||
|
result: true,
|
||||||
|
}, {
|
||||||
userLevel,
|
userLevel,
|
||||||
...inCtx,
|
...ctx,
|
||||||
}
|
}];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,10 +12,14 @@ export function ReqGameLogin<Q extends LoginCtx & SessionCtx>(): ProcDecoratorGe
|
|||||||
return async (inCtx) => {
|
return async (inCtx) => {
|
||||||
const ctx: Q & Partial<GameLoginCtx> = inCtx;
|
const ctx: Q & Partial<GameLoginCtx> = inCtx;
|
||||||
if (ctx.generalID !== undefined) {
|
if (ctx.generalID !== undefined) {
|
||||||
return {
|
return [{
|
||||||
|
result: true,
|
||||||
|
type: 'GameLogin',
|
||||||
|
info: 'UseSession',
|
||||||
|
}, {
|
||||||
generalID: ctx.generalID,
|
generalID: ctx.generalID,
|
||||||
...inCtx,
|
...inCtx,
|
||||||
};
|
}, ];
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Something GameLogin');
|
console.log('Something GameLogin');
|
||||||
@@ -23,9 +27,13 @@ export function ReqGameLogin<Q extends LoginCtx & SessionCtx>(): ProcDecoratorGe
|
|||||||
|
|
||||||
inCtx.setValue('generalID', ctx.userID * 4);
|
inCtx.setValue('generalID', ctx.userID * 4);
|
||||||
|
|
||||||
return {
|
return [{
|
||||||
|
result: true,
|
||||||
|
type: 'GameLogin',
|
||||||
|
info: 'UseDB',
|
||||||
|
}, {
|
||||||
generalID: ctx.userID * 4,
|
generalID: ctx.userID * 4,
|
||||||
...inCtx,
|
...inCtx,
|
||||||
}
|
}, ]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,23 +1,23 @@
|
|||||||
import type { SessionCtx } from "./StartSession";
|
import type { SessionCtx } from "./StartSession";
|
||||||
import type { InvalidProc, ProcDecoratorGenerator } from "./base";
|
import type { DecoratorResultFalse, DecoratorResultTrue, ProcDecorator, ProcDecoratorGenerator } from "./base";
|
||||||
|
|
||||||
export type LoginCtx = {
|
export type LoginCtx = {
|
||||||
userID: number;
|
userID: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ReqLogin<Q extends SessionCtx>(): ProcDecoratorGenerator<LoginCtx, Q> {
|
export function ReqLogin<Q extends SessionCtx>(): ProcDecorator<LoginCtx & Q, Q> {
|
||||||
return (inCtx) => {
|
return (ctx) => {
|
||||||
const ctx: Q & Partial<LoginCtx> = inCtx;
|
|
||||||
const userID = ctx.userID;
|
const userID = ctx.userID;
|
||||||
|
|
||||||
if (userID === undefined) {
|
if (userID === undefined) {
|
||||||
return {
|
return [{
|
||||||
result: false,
|
result: false,
|
||||||
reason: 'Required Login',
|
type: 'Required Login',
|
||||||
invalidProcSymbol: 'ReqLogin'
|
info: 'ReqLogin'
|
||||||
} satisfies InvalidProc;
|
}, ctx];
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctx as LoginCtx & Q;
|
return [{ result: true }, ctx as Q & LoginCtx];
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -14,7 +14,7 @@ export type SessionCtx = {
|
|||||||
export function StartSession<Q extends object = Empty>(): ProcDecoratorGenerator<SessionCtx, Q> {
|
export function StartSession<Q extends object = Empty>(): ProcDecoratorGenerator<SessionCtx, Q> {
|
||||||
return (inCtx) => {
|
return (inCtx) => {
|
||||||
const raw: Record<string, unknown> = {};
|
const raw: Record<string, unknown> = {};
|
||||||
return {
|
return [{ result: true }, {
|
||||||
clearSession: () => Promise.resolve(),
|
clearSession: () => Promise.resolve(),
|
||||||
deleteValue: async (key: string) => {
|
deleteValue: async (key: string) => {
|
||||||
console.log('deleteValue', key);
|
console.log('deleteValue', key);
|
||||||
@@ -34,6 +34,6 @@ export function StartSession<Q extends object = Empty>(): ProcDecoratorGenerator
|
|||||||
changed: false,
|
changed: false,
|
||||||
},
|
},
|
||||||
...inCtx,
|
...inCtx,
|
||||||
};
|
}];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,131 +1,153 @@
|
|||||||
import type { Request, Response } from "express";
|
import type { Request, Response } from "express";
|
||||||
import type { InvalidResponse } from "../../apiStructure/defs";
|
|
||||||
|
|
||||||
type MayBePromise<T> = T | Promise<T>;
|
type MayBePromise<T> = T | Promise<T>;
|
||||||
|
|
||||||
export type Empty = Record<string, never>;
|
export type Empty = Record<string, never>;
|
||||||
export type DecoratorStack = {
|
export type DecoratorResultTrue = {
|
||||||
result: boolean,
|
result: true;
|
||||||
type: string,
|
type?: string;
|
||||||
info: string,
|
info?: string;
|
||||||
}[];
|
};
|
||||||
|
export type DecoratorResultFalse = {
|
||||||
|
result: false;
|
||||||
|
type: string;
|
||||||
|
info: string;
|
||||||
|
}
|
||||||
|
export type DecoratorResult = DecoratorResultTrue | DecoratorResultFalse;
|
||||||
|
export type DecoratorStack = DecoratorResult[];
|
||||||
|
|
||||||
export interface ProcDecorator<Out extends object, In extends object = Record<string, never>> {
|
export interface ProcDecorator<Out extends object, In extends object = Record<string, never>> {
|
||||||
(inCtx: In & Partial<Out>, req: Request, res: Response): MayBePromise<[Out, DecoratorStack]>;
|
(inCtx: In & Partial<Out>, req: Request, res: Response)
|
||||||
|
: MayBePromise<[DecoratorResultTrue, Out]>
|
||||||
|
| MayBePromise<[DecoratorResultFalse, In & Partial<Out>]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PostProcDecorator<T extends object>{
|
export interface PostProcDecorator<T extends object> {
|
||||||
(ctx: T, stack: DecoratorStack, req: Request, res: Response, isValidRoute: boolean): MayBePromise<[T, DecoratorStack]>;
|
(ctx: T, preResult: DecoratorResult, req: Request, res: Response, isValidRoute: boolean): MayBePromise<[DecoratorResult, T]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ProcDecoratorRunner<Out extends object, In extends object> {
|
||||||
|
(inCtx: In & Partial<Out>, req: Request, res: Response): MayBePromise<[DecoratorStack, Out]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PostProcDecoratorRunner<T extends object> {
|
||||||
|
(ctx: T, preResult: DecoratorStack, req: Request, res: Response, isValidRoute: boolean): MayBePromise<[DecoratorStack, T]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
type PlainDecorator = ProcDecorator<any, any>;
|
type PlainDecorator = ProcDecorator<any, any>;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
type PlainPostDecorator = PostProcDecorator<any>;
|
type PlainPostDecorator = PostProcDecorator<any>;
|
||||||
|
|
||||||
|
|
||||||
export type ProcDecoratorGenerator<B extends object, A extends object> = ProcDecorator<B & A, A>;
|
export type ProcDecoratorGenerator<B extends object, A extends object> = ProcDecorator<B & A, A>;
|
||||||
export type ProcDecoratorPrePostGenerator<B extends object, A extends object> = [ProcDecorator<B & A, A>, PostProcDecorator<B & A>];
|
export type ProcDecoratorPrePostGenerator<B extends object, A extends object> = [ProcDecorator<B & A, A>, PostProcDecorator<B & A>];
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
export type ProcDecoratorChain = readonly ((() => PlainDecorator) | (() => [PlainDecorator, PlainPostDecorator]))[];
|
||||||
export type ProcDecoratorChain = undefined | readonly ((() => PlainDecorator) | (() => [PlainDecorator, PlainPostDecorator]))[];
|
|
||||||
|
|
||||||
export type ResolveChain<T> = T extends undefined ? Empty : T extends ProcDecoratorChain ? Resolve<PackChain<T>> : never;
|
export type ResolveChain<T> = T extends undefined ? Empty : T extends ProcDecoratorChain ? Resolve<PackChain<T>> : never;
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
|
|
||||||
type ParseInType<T> = T extends ProcDecorator<infer B, infer A> ? A : never;
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
type ParseOutType<T> = T extends ProcDecorator<infer B, infer A> ? B : never;
|
export type ParseInType<T> = T extends ProcDecorator<any, infer A> ? A : never;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
export type ParseOutType<T> = T extends ProcDecorator<infer B, any> ? B : never;
|
||||||
|
|
||||||
export function declProcDecorators<T extends ProcDecoratorChain>(decorators: T) {
|
export function declProcDecorators<T extends ProcDecoratorChain>(decorators: T) {
|
||||||
type InType = ParseInType<PackChain<T>>;
|
type InType = ParseInType<PackChain<T>>;
|
||||||
type OutType = ParseOutType<PackChain<T>>;
|
type OutType = ParseOutType<PackChain<T>>;
|
||||||
|
|
||||||
const preDecorator: PlainDecorator[] = [];
|
const preDecorator: PlainDecorator[] = [];
|
||||||
const postDecorator: (PlainDecorator | undefined)[] = [];
|
const postDecorator: (PlainPostDecorator | undefined)[] = [];
|
||||||
if (decorators) {
|
if (decorators) {
|
||||||
for (const procGen of decorators) {
|
for (const procGen of decorators) {
|
||||||
const proc = procGen();
|
const proc = procGen();
|
||||||
if (Array.isArray(proc)) {
|
if (Array.isArray(proc)) {
|
||||||
preDecorator.push(proc[0]);
|
preDecorator.push(proc[0]);
|
||||||
postDecorator.push(undefined);
|
postDecorator.push(proc[1]);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
preDecorator.push(proc);
|
preDecorator.push(proc);
|
||||||
postDecorator.push(proc);
|
postDecorator.push(undefined);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const packedDecorators: readonly [ProcDecorator<OutType, InType>, PostProcDecorator<OutType>] = [
|
const packedDecorators: readonly [ProcDecoratorRunner<OutType, InType>, PostProcDecoratorRunner<OutType>] = [
|
||||||
async (ctx, req, res) => {
|
async (ctx, req, res) => {
|
||||||
let rctx = ctx as unknown as OutType;
|
let rctx = ctx as unknown as OutType;
|
||||||
const decoratorStack: DecoratorStack = [];
|
const decoratorStack: DecoratorStack = [];
|
||||||
if (!preDecorator) {
|
if (!preDecorator) {
|
||||||
return [rctx, decoratorStack];
|
return [decoratorStack, rctx];
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const [idx, proc] of preDecorator.entries()) {
|
for (const [idx, proc] of preDecorator.entries()) {
|
||||||
try {
|
try {
|
||||||
const [newCtx, [stackResult]] = await proc(rctx, req, res);
|
const [stackResult, newCtx] = await proc(rctx, req, res);
|
||||||
decoratorStack.push(stackResult);
|
decoratorStack.push(stackResult);
|
||||||
|
|
||||||
if(stackResult.result){
|
if (stackResult.result) {
|
||||||
rctx = newCtx;
|
rctx = newCtx;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return [newCtx, decoratorStack];
|
return [decoratorStack, newCtx];
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
|
while (decoratorStack.length > idx) {
|
||||||
return [{
|
decoratorStack.pop();
|
||||||
|
}
|
||||||
|
decoratorStack.push({
|
||||||
result: false,
|
result: false,
|
||||||
reason: `internal error[${idx}]: ${e}`,
|
type: 'PreThrow',
|
||||||
invalidProcInfo: [['PreThrow', idx]],
|
info: `internal error: ${e}`,
|
||||||
}, rctx];
|
});
|
||||||
|
|
||||||
|
return [decoratorStack, rctx];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return [rctx, decoratorStack];
|
return [decoratorStack, rctx];
|
||||||
},
|
},
|
||||||
async (ctx, stack, req, res) => {
|
async (ctx, stack, req, res) => {
|
||||||
|
if (!postDecorator) {
|
||||||
|
return [stack, ctx];
|
||||||
|
}
|
||||||
|
const isValidRoute = stack.length === postDecorator.length && stack.every(v => v.result);
|
||||||
|
while (stack.length > 0) {
|
||||||
|
const preStackResult = stack.pop() as DecoratorResult;
|
||||||
|
const idx = stack.length;
|
||||||
|
const proc = postDecorator[idx];
|
||||||
|
|
||||||
return [ctx, stack];
|
if (!proc) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const [postStackResult, nextCtx] = await proc(ctx, preStackResult, req, res, isValidRoute);
|
||||||
|
if (!postStackResult.result) {
|
||||||
|
stack.push(postStackResult);
|
||||||
|
return [stack, nextCtx];
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = nextCtx;
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
stack.push({
|
||||||
|
result: false,
|
||||||
|
type: 'PostThrow',
|
||||||
|
info: `internal error: ${e}`,
|
||||||
|
});
|
||||||
|
return [stack, ctx];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return [stack, ctx];
|
||||||
},
|
},
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
return packedDecorators;
|
return packedDecorators;
|
||||||
|
|
||||||
return [
|
|
||||||
async (ctx: InType & Partial<OutType>, req: Request, res: Response) => {
|
|
||||||
return ctx as unknown as OutType;
|
|
||||||
/*if (!preDecorator) {
|
|
||||||
return ctx as Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const [idx, proc] of preDecorator.entries()) {
|
|
||||||
try {
|
|
||||||
const result = await proc(ctx, req, res, true) as Result | [InvalidProc, object];
|
|
||||||
if (Array.isArray(result)) {
|
|
||||||
const [invalidProc, erroredCtx] = result;
|
|
||||||
invalidProc.invalidProcInfo.push(['Pre', idx]);
|
|
||||||
throw [invalidProc, erroredCtx as Partial<Result>]
|
|
||||||
return ;
|
|
||||||
}
|
|
||||||
ctx = result;
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
return [{
|
|
||||||
result: false,
|
|
||||||
reason: `internal error[${idx}]: ${e}`,
|
|
||||||
invalidProcSymbol: ['preCtx', idx],
|
|
||||||
}, ctx, idx];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ctx as ResolveChain<T>;*/
|
|
||||||
},
|
|
||||||
async (ctx, req, res) => ctx
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Compose2<B extends object, A extends object, D, C> = D & B extends A & C ? B extends C ? ProcDecorator<D & B, A> : never : never;
|
type Compose2<B extends object, A extends object, D, C> = D & B extends A & C ? B extends C ? ProcDecorator<D & B, A> : never : never;
|
||||||
@@ -134,7 +156,7 @@ type PD1<B extends object, A extends object> = () => ProcDecorator<B, A>;
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
type PD2<B extends object, A extends object> = () => [ProcDecorator<B, A>, PostProcDecorator<any>];
|
type PD2<B extends object, A extends object> = () => [ProcDecorator<B, A>, PostProcDecorator<any>];
|
||||||
|
|
||||||
type PackChain<T> =
|
export type PackChain<T> =
|
||||||
T extends readonly [] ? ProcDecorator<Empty, Empty> :
|
T extends readonly [] ? ProcDecorator<Empty, Empty> :
|
||||||
T extends readonly [PD1<infer B, infer A>] ? ProcDecorator<B, A> :
|
T extends readonly [PD1<infer B, infer A>] ? ProcDecorator<B, A> :
|
||||||
T extends readonly [PD2<infer B, infer A>] ? ProcDecorator<B, A> :
|
T extends readonly [PD2<infer B, infer A>] ? ProcDecorator<B, A> :
|
||||||
|
|||||||
+52
-61
@@ -6,7 +6,7 @@ import {
|
|||||||
type ValidatorOptions,
|
type ValidatorOptions,
|
||||||
} from "class-validator";
|
} from "class-validator";
|
||||||
import { type ClassTransformOptions, plainToInstance } from "class-transformer";
|
import { type ClassTransformOptions, plainToInstance } from "class-transformer";
|
||||||
import type { InvalidProc, ProcDecorator, ProcDecoratorChain, ResolveChain } from './ProcDecorator/base.js';
|
import type { Empty, ParseOutType, PostProcDecoratorRunner, ProcDecorator, ProcDecoratorChain, ProcDecoratorRunner, ResolveChain } from './ProcDecorator/base.js';
|
||||||
import { clamp } from 'lodash-es';
|
import { clamp } from 'lodash-es';
|
||||||
|
|
||||||
export type APINamespace = {
|
export type APINamespace = {
|
||||||
@@ -31,59 +31,44 @@ type ValidatorType<T> = T extends undefined ? undefined : ClassType<T>;
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
export type AnyAPIExecuter = APIExecuter<any, any, any, any>;
|
export type AnyAPIExecuter = APIExecuter<any, any, any, any>;
|
||||||
|
|
||||||
interface APIExecuter<R extends ValidResponse, E extends InvalidResponse, Q extends RawArgType, PD extends ProcDecoratorChain> {
|
interface APIExecuter<R extends ValidResponse, E extends InvalidResponse, Q extends RawArgType, CTX extends object> {
|
||||||
(query: Q, ctx: ResolveChain<PD>, expressReq: Request, expressRes: Response): Promise<R | E | true>;
|
(query: Q, ctx: CTX, expressReq: Request, expressRes: Response): Promise<R | E | true>;
|
||||||
httpMethod: HttpMethod;
|
httpMethod: HttpMethod;
|
||||||
argValidator?: ValidatorType<Q>;
|
argValidator?: ValidatorType<Q>;
|
||||||
preDecorator: ProcDecorator<any, any>[];
|
preDecorator: ProcDecoratorRunner<CTX, Empty>;
|
||||||
postDecorator: (ProcDecorator<any, any> | undefined)[];
|
postDecorator: PostProcDecoratorRunner<CTX>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface iAPI_GET<R extends ValidResponse, E extends InvalidResponse, Q extends RawArgType, PD extends ProcDecoratorChain> extends APIExecuter<R, E, Q, PD> {
|
export interface iAPI_GET<R extends ValidResponse, E extends InvalidResponse, Q extends RawArgType, CTX extends object> extends APIExecuter<R, E, Q, CTX> {
|
||||||
httpMethod: 'get';
|
httpMethod: 'get';
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface iAPI_POST<R extends ValidResponse, E extends InvalidResponse, Q extends RawArgType, PD extends ProcDecoratorChain> extends APIExecuter<R, E, Q, PD> {
|
export interface iAPI_POST<R extends ValidResponse, E extends InvalidResponse, Q extends RawArgType, CTX extends object> extends APIExecuter<R, E, Q, CTX> {
|
||||||
httpMethod: 'post';
|
httpMethod: 'post';
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface iAPI_PUT<R extends ValidResponse, E extends InvalidResponse, Q extends RawArgType, PD extends ProcDecoratorChain> extends APIExecuter<R, E, Q, PD> {
|
export interface iAPI_PUT<R extends ValidResponse, E extends InvalidResponse, Q extends RawArgType, CTX extends object> extends APIExecuter<R, E, Q, CTX> {
|
||||||
httpMethod: 'put';
|
httpMethod: 'put';
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface iAPI_DELETE<R extends ValidResponse, E extends InvalidResponse, Q extends RawArgType, PD extends ProcDecoratorChain> extends APIExecuter<R, E, Q, PD> {
|
export interface iAPI_DELETE<R extends ValidResponse, E extends InvalidResponse, Q extends RawArgType, CTX extends object> extends APIExecuter<R, E, Q, CTX> {
|
||||||
httpMethod: 'delete';
|
httpMethod: 'delete';
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface iAPI_PATCH<R extends ValidResponse, E extends InvalidResponse, Q extends RawArgType, PD extends ProcDecoratorChain> extends APIExecuter<R, E, Q, PD> {
|
export interface iAPI_PATCH<R extends ValidResponse, E extends InvalidResponse, Q extends RawArgType, CTX extends object> extends APIExecuter<R, E, Q, CTX> {
|
||||||
httpMethod: 'patch';
|
httpMethod: 'patch';
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface iAPI_HEAD<R extends ValidResponse, E extends InvalidResponse, Q extends RawArgType, PD extends ProcDecoratorChain> extends APIExecuter<R, E, Q, PD> {
|
export interface iAPI_HEAD<R extends ValidResponse, E extends InvalidResponse, Q extends RawArgType, CTX extends object> extends APIExecuter<R, E, Q, CTX> {
|
||||||
httpMethod: 'head';
|
httpMethod: 'head';
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateAPI<R extends ValidResponse, E extends InvalidResponse, Q extends RawArgType, PD extends ProcDecoratorChain>(httpMethod: HttpMethod,
|
function generateAPI<R extends ValidResponse, E extends InvalidResponse, Q extends RawArgType, CTX extends object>(httpMethod: HttpMethod,
|
||||||
argValidator: ValidatorType<Q> | undefined,
|
argValidator: ValidatorType<Q> | undefined,
|
||||||
procDecoratorChain: PD,
|
preDecorator: ProcDecoratorRunner<CTX, Empty>,
|
||||||
callback: (query: Q, ctx: ResolveChain<PD>, expressReq: Request, expressRes: Response) => Promise<R | E | true>,
|
postDecorator: PostProcDecoratorRunner<CTX>,
|
||||||
): APIExecuter<R, E, Q, PD> {
|
callback: (query: Q, ctx: CTX, expressReq: Request, expressRes: Response) => Promise<R | E | true>,
|
||||||
const preDecorator: ProcDecorator<any, any>[] = [];
|
): APIExecuter<R, E, Q, CTX> {
|
||||||
const postDecorator: (ProcDecorator<any, any> | undefined)[] = [];
|
|
||||||
if (procDecoratorChain) {
|
|
||||||
for (const procGen of procDecoratorChain) {
|
|
||||||
const proc = procGen();
|
|
||||||
if (Array.isArray(proc)) {
|
|
||||||
preDecorator.push(proc[0]);
|
|
||||||
postDecorator.push(undefined);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
preDecorator.push(proc);
|
|
||||||
postDecorator.push(proc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Object.assign(
|
return Object.assign(
|
||||||
callback,
|
callback,
|
||||||
{
|
{
|
||||||
@@ -96,79 +81,85 @@ function generateAPI<R extends ValidResponse, E extends InvalidResponse, Q exten
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function GET<R extends ValidResponse, E extends InvalidResponse, Q extends RawArgType>(argValidator?: ValidatorType<Q>) {
|
export function GET<R extends ValidResponse, E extends InvalidResponse, Q extends RawArgType>(argValidator?: ValidatorType<Q>) {
|
||||||
return <PD extends ProcDecoratorChain>(procDecoratorChain: PD) => {
|
return <CTX extends object>([preDecorator, postDecorator]: readonly [ProcDecoratorRunner<CTX, Empty>, PostProcDecoratorRunner<CTX>]) => {
|
||||||
return (callback: (query: Q, ctx: ResolveChain<PD>, expressReq: Request, expressRes: Response) => Promise<R | E | true>) => {
|
return (callback: (query: Q, ctx: CTX, expressReq: Request, expressRes: Response) => Promise<R | E | true>) => {
|
||||||
return generateAPI<R, E, Q, PD>(
|
return generateAPI<R, E, Q, CTX>(
|
||||||
'get',
|
'get',
|
||||||
argValidator,
|
argValidator,
|
||||||
procDecoratorChain,
|
preDecorator,
|
||||||
|
postDecorator,
|
||||||
callback,
|
callback,
|
||||||
) as iAPI_GET<R, E, Q, PD>;
|
) as iAPI_GET<R, E, Q, CTX>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function POST<R extends ValidResponse, E extends InvalidResponse, Q extends RawArgType>(argValidator?: ValidatorType<Q>) {
|
export function POST<R extends ValidResponse, E extends InvalidResponse, Q extends RawArgType>(argValidator?: ValidatorType<Q>) {
|
||||||
return <PD extends ProcDecoratorChain>(procDecoratorChain: PD) => {
|
return <CTX extends object>([preDecorator, postDecorator]: readonly [ProcDecoratorRunner<CTX, Empty>, PostProcDecoratorRunner<CTX>]) => {
|
||||||
return (callback: (query: Q, ctx: ResolveChain<PD>, expressReq: Request, expressRes: Response) => Promise<R | E | true>) => {
|
return (callback: (query: Q, ctx: CTX, expressReq: Request, expressRes: Response) => Promise<R | E | true>) => {
|
||||||
return generateAPI<R, E, Q, PD>(
|
return generateAPI<R, E, Q, CTX>(
|
||||||
'post',
|
'post',
|
||||||
argValidator,
|
argValidator,
|
||||||
procDecoratorChain,
|
preDecorator,
|
||||||
|
postDecorator,
|
||||||
callback,
|
callback,
|
||||||
) as iAPI_POST<R, E, Q, PD>;
|
) as iAPI_POST<R, E, Q, CTX>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PUT<R extends ValidResponse, E extends InvalidResponse, Q extends RawArgType>(argValidator?: ValidatorType<Q>) {
|
export function PUT<R extends ValidResponse, E extends InvalidResponse, Q extends RawArgType>(argValidator?: ValidatorType<Q>) {
|
||||||
return <PD extends ProcDecoratorChain>(procDecoratorChain: PD) => {
|
return <CTX extends object>([preDecorator, postDecorator]: readonly [ProcDecoratorRunner<CTX, Empty>, PostProcDecoratorRunner<CTX>]) => {
|
||||||
return (callback: (query: Q, ctx: ResolveChain<PD>, expressReq: Request, expressRes: Response) => Promise<R | E | true>) => {
|
return (callback: (query: Q, ctx: CTX, expressReq: Request, expressRes: Response) => Promise<R | E | true>) => {
|
||||||
return generateAPI<R, E, Q, PD>(
|
return generateAPI<R, E, Q, CTX>(
|
||||||
'put',
|
'put',
|
||||||
argValidator,
|
argValidator,
|
||||||
procDecoratorChain,
|
preDecorator,
|
||||||
|
postDecorator,
|
||||||
callback,
|
callback,
|
||||||
) as iAPI_PUT<R, E, Q, PD>;
|
) as iAPI_PUT<R, E, Q, CTX>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DELETE<R extends ValidResponse, E extends InvalidResponse, Q extends RawArgType>(argValidator?: ValidatorType<Q>) {
|
export function DELETE<R extends ValidResponse, E extends InvalidResponse, Q extends RawArgType>(argValidator?: ValidatorType<Q>) {
|
||||||
return <PD extends ProcDecoratorChain>(procDecoratorChain: PD) => {
|
return <CTX extends object>([preDecorator, postDecorator]: readonly [ProcDecoratorRunner<CTX, Empty>, PostProcDecoratorRunner<CTX>]) => {
|
||||||
return (callback: (query: Q, ctx: ResolveChain<PD>, expressReq: Request, expressRes: Response) => Promise<R | E | true>) => {
|
return (callback: (query: Q, ctx: CTX, expressReq: Request, expressRes: Response) => Promise<R | E | true>) => {
|
||||||
return generateAPI<R, E, Q, PD>(
|
return generateAPI<R, E, Q, CTX>(
|
||||||
'delete',
|
'delete',
|
||||||
argValidator,
|
argValidator,
|
||||||
procDecoratorChain,
|
preDecorator,
|
||||||
|
postDecorator,
|
||||||
callback,
|
callback,
|
||||||
) as iAPI_DELETE<R, E, Q, PD>;
|
) as iAPI_DELETE<R, E, Q, CTX>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PATCH<R extends ValidResponse, E extends InvalidResponse, Q extends RawArgType>(argValidator?: ValidatorType<Q>) {
|
export function PATCH<R extends ValidResponse, E extends InvalidResponse, Q extends RawArgType>(argValidator?: ValidatorType<Q>) {
|
||||||
return <PD extends ProcDecoratorChain>(procDecoratorChain: PD) => {
|
return <CTX extends object>([preDecorator, postDecorator]: readonly [ProcDecoratorRunner<CTX, Empty>, PostProcDecoratorRunner<CTX>]) => {
|
||||||
return (callback: (query: Q, ctx: ResolveChain<PD>, expressReq: Request, expressRes: Response) => Promise<R | E | true>) => {
|
return (callback: (query: Q, ctx: CTX, expressReq: Request, expressRes: Response) => Promise<R | E | true>) => {
|
||||||
return generateAPI<R, E, Q, PD>(
|
return generateAPI<R, E, Q, CTX>(
|
||||||
'patch',
|
'patch',
|
||||||
argValidator,
|
argValidator,
|
||||||
procDecoratorChain,
|
preDecorator,
|
||||||
|
postDecorator,
|
||||||
callback,
|
callback,
|
||||||
) as iAPI_PATCH<R, E, Q, PD>;
|
) as iAPI_PATCH<R, E, Q, CTX>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function HEAD<R extends ValidResponse, E extends InvalidResponse, Q extends RawArgType>(argValidator?: ValidatorType<Q>) {
|
export function HEAD<R extends ValidResponse, E extends InvalidResponse, Q extends RawArgType>(argValidator?: ValidatorType<Q>) {
|
||||||
return <PD extends ProcDecoratorChain>(procDecoratorChain: PD) => {
|
return <CTX extends object>([preDecorator, postDecorator]: readonly [ProcDecoratorRunner<CTX, Empty>, PostProcDecoratorRunner<CTX>]) => {
|
||||||
return (callback: (query: Q, ctx: ResolveChain<PD>, expressReq: Request, expressRes: Response) => Promise<R | E | true>) => {
|
return (callback: (query: Q, ctx: CTX, expressReq: Request, expressRes: Response) => Promise<R | E | true>) => {
|
||||||
return generateAPI<R, E, Q, PD>(
|
return generateAPI<R, E, Q, CTX>(
|
||||||
'head',
|
'head',
|
||||||
argValidator,
|
argValidator,
|
||||||
procDecoratorChain,
|
preDecorator,
|
||||||
|
postDecorator,
|
||||||
callback,
|
callback,
|
||||||
) as iAPI_HEAD<R, E, Q, PD>;
|
) as iAPI_HEAD<R, E, Q, CTX>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+29
-83
@@ -3,7 +3,7 @@ import type { AnyAPIExecuter, APINamespace, ClassType } from './defs.js';
|
|||||||
import { plainToInstance } from 'class-transformer';
|
import { plainToInstance } from 'class-transformer';
|
||||||
import { validate } from 'class-validator';
|
import { validate } from 'class-validator';
|
||||||
import type { ExtractQuery, RawArgType } from '../apiStructure/defs.js';
|
import type { ExtractQuery, RawArgType } from '../apiStructure/defs.js';
|
||||||
import type { Empty, InvalidProc, ProcDecorator, ProcDecoratorChain, ResolveChain } from './ProcDecorator/base.js';
|
import type { DecoratorResult, Empty, ProcDecorator, ProcDecoratorChain, ResolveChain } from './ProcDecorator/base.js';
|
||||||
import clamp from 'lodash-es/clamp.js';
|
import clamp from 'lodash-es/clamp.js';
|
||||||
|
|
||||||
async function parseParam<Q extends Exclude<RawArgType, undefined>>(req: Request, argValidator?: ClassType<Q>): Promise<Q> {
|
async function parseParam<Q extends Exclude<RawArgType, undefined>>(req: Request, argValidator?: ClassType<Q>): Promise<Q> {
|
||||||
@@ -36,97 +36,43 @@ async function parseBody<Q extends Exclude<RawArgType, undefined>>(req: Request,
|
|||||||
return classObject as Q;
|
return classObject as Q;
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
type PlainDecorator = ProcDecorator<any, any>;
|
|
||||||
|
|
||||||
async function preCtx<PD extends ProcDecorator<any, any>>(preDecorator: PlainDecorator[], req: Request, res: Response): Promise<ResolveChain<PD> | [InvalidProc, ResolveChain<PD>, number]> {
|
|
||||||
if (!preDecorator) {
|
|
||||||
return {} as ResolveChain<PD>;
|
|
||||||
}
|
|
||||||
|
|
||||||
let ctx = {} as ResolveChain<PD>;
|
|
||||||
for (const [idx, proc] of preDecorator.entries()) {
|
|
||||||
try {
|
|
||||||
const result = await proc(ctx, req, res, true);
|
|
||||||
if ('invalidProcSymbol' in result) {
|
|
||||||
return [result, ctx, idx];
|
|
||||||
}
|
|
||||||
ctx = result;
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
return [{
|
|
||||||
result: false,
|
|
||||||
reason: `internal error[${idx}]: ${e}`,
|
|
||||||
invalidProcSymbol: ['preCtx', idx],
|
|
||||||
}, ctx, idx];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ctx;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function postCtx<PD extends ProcDecoratorChain>(ctx: ResolveChain<PD>, postDecorator: (undefined | PlainDecorator)[], req: Request, res: Response, index = -1): Promise<[InvalidProc, number] | undefined> {
|
|
||||||
if (!postDecorator) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let isValidRoute = true;
|
|
||||||
if (index !== -1 && index !== postDecorator.length) {
|
|
||||||
isValidRoute = false;
|
|
||||||
index = clamp(index, 0, postDecorator.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = index - 1; i >= 0; i--) {
|
|
||||||
const proc = postDecorator[i];
|
|
||||||
if (!proc) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const result = await proc(ctx, req, res, isValidRoute);
|
|
||||||
if ('invalidProcSymbol' in result) {
|
|
||||||
result.invalidProcSymbol = ['postCtx', i, result.invalidProcSymbol];
|
|
||||||
return [result, i];
|
|
||||||
}
|
|
||||||
ctx = result;
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
return [{
|
|
||||||
result: false,
|
|
||||||
reason: `internal error[${i}]: ${e}`,
|
|
||||||
invalidProcSymbol: ['postCtx', i],
|
|
||||||
}, i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function apiRun(query: object, req: Request, res: Response, api: AnyAPIExecuter): Promise<void> {
|
async function apiRun(query: object, req: Request, res: Response, api: AnyAPIExecuter): Promise<void> {
|
||||||
const ctx = await preCtx(api.preDecorator, req, res);
|
const [preResult, ctx] = await api.preDecorator({}, req, res);
|
||||||
if (Array.isArray(ctx)) {
|
if (!preResult.every((v) => v.result)) {
|
||||||
const [invalidProc, errCtx, idx] = ctx;
|
const lastErr = preResult[preResult.length - 1];
|
||||||
if (idx == 0) {
|
const [postResult,] = await api.postDecorator(ctx, preResult, req, res, false);
|
||||||
res.json(invalidProc);
|
if (!postResult.every((v) => v.result)) {
|
||||||
|
//회수조차 불가능?
|
||||||
|
const lastPostErr = postResult[postResult.length - 1];
|
||||||
|
res.json({
|
||||||
|
result: false,
|
||||||
|
path: req.path,
|
||||||
|
reason: `preDecorator: ${lastErr.type} ${lastErr.info}, postDecorator: ${lastPostErr.type} ${lastPostErr.info}`,
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const result = await postCtx(errCtx, api.postDecorator, req, res, idx);
|
|
||||||
if (result === undefined) {
|
res.json({
|
||||||
res.json(invalidProc);
|
result: false,
|
||||||
return;
|
path: req.path,
|
||||||
}
|
reason: `preDecorator: ${lastErr.type} ${lastErr.info}`,
|
||||||
const [postInvalidProc,] = result;
|
})
|
||||||
res.json(postInvalidProc);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const result = await api(query, ctx, req, res);
|
const result = await api(query, ctx, req, res);
|
||||||
const postResult = await postCtx(ctx, api.postDecorator, req, res);
|
const [postResult,] = await api.postDecorator(ctx, preResult, req, res, true);
|
||||||
if (postResult !== undefined) {
|
if(!postResult.every((v) => v.result)) {
|
||||||
if (result === true) {
|
if(result === true) {
|
||||||
//NOTE: 이미 api에서 response를 보낸 특이 케이스.
|
//NOTE: 이미 api에서 response를 보낸 특이 케이스.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const [invalidProc,] = postResult;
|
const lastPostErr = postResult[postResult.length - 1];
|
||||||
invalidProc.alreadyProcessed = true;
|
res.json({
|
||||||
res.json(invalidProc);
|
result: false,
|
||||||
|
path: req.path,
|
||||||
|
reason: `postDecorator: ${lastPostErr.type} ${lastPostErr.info}`,
|
||||||
|
originalResult: result,
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user