refac: api 시스템 교체

- procDecorator의 타입 복잡도를 낮춤
- 타입 에러가 살짝 더 깔끔함
This commit is contained in:
2023-08-09 16:10:53 +00:00
parent 0c511d27c5
commit 81d16aaba2
12 changed files with 238 additions and 266 deletions
+29 -83
View File
@@ -3,7 +3,7 @@ import type { AnyAPIExecuter, APINamespace, ClassType } from './defs.js';
import { plainToInstance } from 'class-transformer';
import { validate } from 'class-validator';
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';
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;
}
// 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> {
const ctx = await preCtx(api.preDecorator, req, res);
if (Array.isArray(ctx)) {
const [invalidProc, errCtx, idx] = ctx;
if (idx == 0) {
res.json(invalidProc);
const [preResult, ctx] = await api.preDecorator({}, req, res);
if (!preResult.every((v) => v.result)) {
const lastErr = preResult[preResult.length - 1];
const [postResult,] = await api.postDecorator(ctx, preResult, req, res, false);
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;
}
const result = await postCtx(errCtx, api.postDecorator, req, res, idx);
if (result === undefined) {
res.json(invalidProc);
return;
}
const [postInvalidProc,] = result;
res.json(postInvalidProc);
res.json({
result: false,
path: req.path,
reason: `preDecorator: ${lastErr.type} ${lastErr.info}`,
})
return;
}
const result = await api(query, ctx, req, res);
const postResult = await postCtx(ctx, api.postDecorator, req, res);
if (postResult !== undefined) {
if (result === true) {
const [postResult,] = await api.postDecorator(ctx, preResult, req, res, true);
if(!postResult.every((v) => v.result)) {
if(result === true) {
//NOTE: 이미 api에서 response를 보낸 특이 케이스.
return;
}
const [invalidProc,] = postResult;
invalidProc.alreadyProcessed = true;
res.json(invalidProc);
const lastPostErr = postResult[postResult.length - 1];
res.json({
result: false,
path: req.path,
reason: `postDecorator: ${lastPostErr.type} ${lastPostErr.info}`,
originalResult: result,
});
return;
}