Skip to content

Commit

Permalink
Validators -> SpecValidator
Browse files Browse the repository at this point in the history
  • Loading branch information
mpppk committed Dec 11, 2024
1 parent 85bea66 commit 09edbed
Show file tree
Hide file tree
Showing 9 changed files with 114 additions and 92 deletions.
42 changes: 24 additions & 18 deletions src/core/validator/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const listDefinedRequestApiSpecKeys = <Spec extends AnyApiSpec>(
return apiSpecRequestKeys.filter((key) => spec[key] !== undefined);
};

export type Validators<
export type SpecValidator<
ParamsValidator extends AnyValidator | undefined,
QueryValidator extends AnyValidator | undefined,
BodyValidator extends AnyValidator | undefined,
Expand All @@ -31,22 +31,28 @@ export type Validators<
body: BodyValidator;
headers: HeadersValidator;
};
export type AnyValidators = Partial<
Validators<AnyValidator, AnyValidator, AnyValidator, AnyValidator>
export type AnySpecValidator = Partial<
SpecValidator<AnyValidator, AnyValidator, AnyValidator, AnyValidator>
>;
export type ValidatorsMap = {
[Path in string]: Partial<Record<Method, AnyValidators>>;
export type SpecValidatorMap = {
[Path in string]: Partial<Record<Method, AnySpecValidator>>;
};

export type ValidatorsRawInput<Path extends string, Method extends string> = {
export type SpecValidatorGeneratorRawInput<
Path extends string,
Method extends string,
> = {
path: Path;
method: Method;
params: Record<string, string | string[]>;
query?: ParsedQs;
body?: Record<string, string>;
headers: Record<string, string | string[] | undefined>;
};
export type ValidatorsInput<Path extends string, M extends Method> = {
export type SpecValidatorGeneratorInput<
Path extends string,
M extends Method,
> = {
path: Path;
method: M;
params: Record<string, string | string[]>;
Expand All @@ -55,8 +61,8 @@ export type ValidatorsInput<Path extends string, M extends Method> = {
headers: Record<string, string | string[] | undefined>;
};

export const runValidators = (
validators: AnyValidators | undefined,
export const runSpecValidator = (
validators: AnySpecValidator | undefined,
error: unknown,
) => {
const newD = () => Result.data(undefined);
Expand All @@ -69,27 +75,27 @@ export const runValidators = (
};
};

export type RequestValidator = (
input: ValidatorsRawInput<string, string>,
) => Result<AnyValidators, ValidatorInputError>;
export type RequestSpecValidatorGenerator = (
input: SpecValidatorGeneratorRawInput<string, string>,
) => Result<AnySpecValidator, ValidatorInputError>;
export type RequestValidatorGenerator = (
spec: AnyApiSpec,
input: ValidatorsInput<string, Method>,
input: SpecValidatorGeneratorInput<string, Method>,
key: ApiSpecRequestKey,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
) => any;
export const createRequestValidator = <E extends AnyApiEndpoints>(
export const createRequestSpecValidatorGenerator = <E extends AnyApiEndpoints>(
endpoints: E,
specValidatorGenerator: RequestValidatorGenerator,
): RequestValidator => {
): RequestSpecValidatorGenerator => {
return (
input: ValidatorsRawInput<string, string>,
): Result<AnyValidators, ValidatorInputError> => {
input: SpecValidatorGeneratorRawInput<string, string>,
): Result<AnySpecValidator, ValidatorInputError> => {
const { data: vInput, error } = checkValidatorsInput(endpoints, input);
if (error) {
return Result.error(error);
}
const validators: AnyValidators = {};
const validators: AnySpecValidator = {};
const spec = endpoints[vInput.path][vInput.method]!;
listDefinedRequestApiSpecKeys(spec).forEach((key) => {
validators[key] = () => specValidatorGenerator(spec, vInput, key);
Expand Down
37 changes: 18 additions & 19 deletions src/core/validator/response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,46 +13,45 @@ import {
checkValidatorsInput,
ValidatorInputError,
} from "./validate";
import { AnyValidators } from "./request";
import { AnySpecValidator } from "./request";

export const listDefinedResponseApiSpecKeys = <Response extends AnyResponse>(
res: Response,
): ApiSpecResponseKey[] => {
return apiSpecResponseKeys.filter((key) => res[key] !== undefined);
};

export type ResponseValidator = (
input: ResponseValidatorsRawInput<string, string, number>,
) => Result<AnyResponseValidators, ValidatorInputError>;
export type ResponseSpecValidatorGenerator = (
input: ResponseSpecValidatorGeneratorRawInput<string, string, number>,
) => Result<AnyResponseSpecValidator, ValidatorInputError>;
export type ResponseValidatorGenerator = (
spec: AnyApiSpec,
input: ResponseValidatorsInput<string, Method, StatusCode>,
input: ResponseSpecValidatorGeneratorInput<string, Method, StatusCode>,
key: ApiSpecResponseKey,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
) => any;
export const createResponseValidator = <E extends AnyApiEndpoints>(
export const createResponseSpecValidatorGenerator = <E extends AnyApiEndpoints>(
endpoints: E,
resValidatorGenerator: ResponseValidatorGenerator,
) => {
return <Validators extends AnyValidators>(
input: ResponseValidatorsInput<string, Method, StatusCode>,
): Result<Validators, ValidatorInputError> => {
return (
input: ResponseSpecValidatorGeneratorInput<string, Method, StatusCode>,
): Result<AnyResponseSpecValidator, ValidatorInputError> => {
const { data: vInput, error } = checkValidatorsInput(endpoints, input);
if (error) {
return Result.error(error);
}
const validator: AnyValidators = {};
const validator: AnySpecValidator = {};
const spec = endpoints[vInput.path][vInput.method]!;
// const spec = r.data! as AnyApiSpec;
const response = spec?.responses?.[input.statusCode as StatusCode] ?? {};
listDefinedResponseApiSpecKeys(response).forEach((key) => {
validator[key] = () => resValidatorGenerator(spec, input, key);
});
return Result.data(validator as Validators);
return Result.data(validator);
};
};

export type ResponseValidators<
export type ResponseSpecValidator<
// eslint-disable-next-line @typescript-eslint/no-explicit-any
BodyValidator extends AnyValidator | undefined,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand All @@ -61,11 +60,11 @@ export type ResponseValidators<
body: BodyValidator;
headers: HeadersValidator;
};
export type AnyResponseValidators = Partial<
ResponseValidators<AnyValidator, AnyValidator>
export type AnyResponseSpecValidator = Partial<
ResponseSpecValidator<AnyValidator, AnyValidator>
>;
export const runResponseValidators = (
r: Result<AnyResponseValidators, ValidatorInputError>,
export const runResponseSpecValidator = (
r: Result<AnyResponseSpecValidator, ValidatorInputError>,
) => {
const newD = () => Result.data(undefined);
return {
Expand All @@ -76,7 +75,7 @@ export const runResponseValidators = (
};
};

export type ResponseValidatorsRawInput<
export type ResponseSpecValidatorGeneratorRawInput<
Path extends string,
M extends string,
SC extends number,
Expand All @@ -87,7 +86,7 @@ export type ResponseValidatorsRawInput<
body?: unknown;
headers: Record<string, string | string[] | undefined>;
};
export type ResponseValidatorsInput<
export type ResponseSpecValidatorGeneratorInput<
Path extends string,
M extends Method,
SC extends StatusCode,
Expand Down
20 changes: 13 additions & 7 deletions src/core/validator/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ import {
newMethodInvalidError,
} from "../spec";
import {
createResponseValidator,
createResponseSpecValidatorGenerator,
ResponseValidatorGenerator,
} from "./response";
import {
createRequestValidator,
createRequestSpecValidatorGenerator,
RequestValidatorGenerator,
ValidatorsInput,
SpecValidatorGeneratorInput,
} from "./request";

export type Validator<Data, Error> = () => Result<Data, Error>;
Expand All @@ -28,7 +28,7 @@ export const checkValidatorsInput = <
>(
endpoints: E,
input: { path: string; method: string },
): Result<ValidatorsInput<Path, M>, ValidatorInputError> => {
): Result<SpecValidatorGeneratorInput<Path, M>, ValidatorInputError> => {
const method = input.method;
if (!isMethod(method)) {
return Result.error(newMethodInvalidError(method));
Expand All @@ -41,7 +41,7 @@ export const checkValidatorsInput = <
if (methodE) {
return Result.error(methodE);
}
return Result.data(input as ValidatorsInput<Path, M>);
return Result.data(input as SpecValidatorGeneratorInput<Path, M>);
};

export const validatePath = <E extends AnyApiEndpoints, Path extends string>(
Expand Down Expand Up @@ -93,7 +93,13 @@ export const createValidator = <E extends AnyApiEndpoints>(
reqV: RequestValidatorGenerator,
resV: ResponseValidatorGenerator,
) => {
const req = createRequestValidator<typeof endpoints>(endpoints, reqV);
const res = createResponseValidator<typeof endpoints>(endpoints, resV);
const req = createRequestSpecValidatorGenerator<typeof endpoints>(
endpoints,
reqV,
);
const res = createResponseSpecValidatorGenerator<typeof endpoints>(
endpoints,
resV,
);
return { req, res };
};
23 changes: 14 additions & 9 deletions src/express/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ import {
import { StatusCode } from "../core";
import { ParsedQs } from "qs";
import {
AnyValidators,
RequestValidator,
ValidatorsMap,
AnySpecValidator,
RequestSpecValidatorGenerator,
SpecValidatorMap,
} from "../core/validator/request";
import { Result } from "../utils";

Expand All @@ -41,15 +41,18 @@ export type Handler<

export type ToHandler<
Spec extends AnyApiSpec | undefined,
Validators extends AnyValidators | undefined,
Validators extends AnySpecValidator | undefined,
> = Handler<
Spec,
ValidateLocals<
Validators extends AnyValidators ? Validators : Record<string, never>
Validators extends AnySpecValidator ? Validators : Record<string, never>
>
>;

export type ToHandlers<E extends AnyApiEndpoints, V extends ValidatorsMap> = {
export type ToHandlers<
E extends AnyApiEndpoints,
V extends SpecValidatorMap,
> = {
[Path in keyof E & string]: {
[M in Method]: ToHandler<E[Path][M], V[Path][M]>;
};
Expand All @@ -69,7 +72,9 @@ export type ExpressResponse<
) => Response<ApiResBody<Responses, SC>, LocalsObj, SC>;
};

export type ValidateLocals<Vs extends AnyValidators | Record<string, never>> = {
export type ValidateLocals<
Vs extends AnySpecValidator | Record<string, never>,
> = {
validate: (req: Request<ParamsDictionary, unknown, unknown, unknown>) => Vs;
};

Expand All @@ -78,7 +83,7 @@ export type ValidateLocals<Vs extends AnyValidators | Record<string, never>> = {
*/
export type RouterT<
E extends AnyApiEndpoints,
V extends ValidatorsMap,
V extends SpecValidatorMap,
SC extends StatusCode = StatusCode,
> = Omit<IRouter, Method> & {
[M in Method]: <Path extends string & keyof E>(
Expand All @@ -93,7 +98,7 @@ export type RouterT<
};

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const validatorMiddleware = <V extends RequestValidator>(
export const validatorMiddleware = <V extends RequestSpecValidatorGenerator>(
validator: V,
) => {
return (_req: Request, res: Response, next: NextFunction) => {
Expand Down
Loading

0 comments on commit 09edbed

Please sign in to comment.