1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00
lens/packages/core/src/main/router/route.ts
Sebastian Malton 1675c56e59 chore: Turn on @typescript-eslint/strict-boolean-expressions
- Fix some bugs about bad conditions

Signed-off-by: Sebastian Malton <sebastian@malton.name>
2023-06-01 09:27:44 -04:00

154 lines
4.5 KiB
TypeScript

/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { Cluster } from "../../common/cluster/cluster";
import type http from "http";
import type httpProxy from "http-proxy";
import type { LensApiResultContentType } from "./router-content-types";
import type { URLSearchParams } from "url";
import type { SafeParseReturnType } from "zod";
declare const emptyObjectSymbol: unique symbol;
export interface EmptyObject {[emptyObjectSymbol]?: never}
export type InferParam<
T extends string,
PathParams,
> =
T extends `{${infer P}?}`
? PathParams & Partial<Record<P, string>>
: T extends `{${infer P}}`
? PathParams & Record<P, string>
: PathParams;
export type InferParamFromPath<P extends string> =
P extends `${string}/{${infer B}*}${infer Tail}`
? Tail extends ""
? Record<B, string>
: never
: P extends `${infer A}/${infer B}`
? InferParam<A, InferParamFromPath<B>>
: InferParam<P, EmptyObject>;
export interface LensApiRequest<Path extends string> {
path: Path;
payload: unknown;
params: string extends Path
? Partial<Record<string, string>>
: InferParamFromPath<Path>;
cluster: Cluster | undefined;
query: URLSearchParams;
raw: {
req: http.IncomingMessage;
res: http.ServerResponse;
};
}
export interface ClusterLensApiRequest<Path extends string> extends LensApiRequest<Path> {
cluster: Cluster;
}
export interface LensApiResult<Response> {
statusCode?: number;
response?: Response;
error?: string | Error;
contentType?: LensApiResultContentType;
headers?: Partial<Record<string, string>>;
proxy?: httpProxy;
}
export type RouteResponse<Response> =
| LensApiResult<Response>
| void;
export interface RouteHandler<TResponse, Path extends string>{
(request: LensApiRequest<Path>): RouteResponse<TResponse> | Promise<RouteResponse<TResponse>>;
}
export interface BaseRoutePaths<Path extends string> {
path: Path;
method: "get" | "post" | "put" | "patch" | "delete";
}
export interface PayloadValidator<Payload> {
safeParse(payload: unknown): SafeParseReturnType<unknown, Payload>;
}
export interface ValidatorBaseRoutePaths<Path extends string, Payload> extends BaseRoutePaths<Path> {
payloadSchema: PayloadValidator<Payload>;
}
export interface Route<TResponse, Path extends string> extends BaseRoutePaths<Path> {
handler: RouteHandler<TResponse, Path>;
}
export interface BindHandler<Path extends string> {
<TResponse>(handler: RouteHandler<TResponse, Path>): Route<TResponse, Path>;
}
export function route<Path extends string>(parts: BaseRoutePaths<Path>): BindHandler<Path> {
return (handler) => ({
...parts,
handler,
});
}
export interface ClusterRouteHandler<Response, Path extends string>{
(request: ClusterLensApiRequest<Path>): RouteResponse<Response> | Promise<RouteResponse<Response>>;
}
export interface BindClusterHandler<Path extends string> {
<TResponse>(handler: ClusterRouteHandler<TResponse, Path>): Route<TResponse, Path>;
}
export function clusterRoute<Path extends string>(parts: BaseRoutePaths<Path>): BindClusterHandler<Path> {
return (handler) => ({
...parts,
handler: ({ cluster, ...rest }) => {
if (!cluster) {
return {
error: "Cluster missing",
statusCode: 400,
};
}
return handler({ cluster, ...rest });
},
});
}
export interface ValidatedClusterLensApiRequest<Path extends string, Payload> extends ClusterLensApiRequest<Path> {
payload: Payload;
}
export interface ValidatedClusterRouteHandler<Payload, Response, Path extends string> {
(request: ValidatedClusterLensApiRequest<Path, Payload>): RouteResponse<Response> | Promise<RouteResponse<Response>>;
}
export interface BindValidatedClusterHandler<Path extends string, Payload> {
<Response>(handler: ValidatedClusterRouteHandler<Payload, Response, Path>): Route<Response, Path>;
}
export function payloadWithSchemaClusterRoute<Path extends string, Payload>({ payloadSchema: payloadValidator, ...parts }: ValidatorBaseRoutePaths<Path, Payload>): BindValidatedClusterHandler<Path, Payload> {
const boundClusterRoute = clusterRoute(parts);
return (handler) => boundClusterRoute(({ payload, ...rest }) => {
const validationResult = payloadValidator.safeParse(payload);
if (!validationResult.success) {
return {
error: validationResult.error,
statusCode: 400,
};
}
return handler({
payload: validationResult.data,
...rest,
});
});
}