Skip to content

Commit

Permalink
src add
Browse files Browse the repository at this point in the history
  • Loading branch information
leomerida15 committed Sep 20, 2023
1 parent e1cedfe commit 9df611b
Show file tree
Hide file tree
Showing 24 changed files with 782 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
node_modules
dist
index.js
src
!src
11 changes: 11 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { ConfigObject, OasKeys, ResolverKeys } from ".";

export const createConfig = <K extends ResolverKeys, O extends OasKeys = "3.1">(
params?: ConfigObject<K, O>,
) => {
const resolver = (params?.resolver || "zod") as ResolverKeys;

const oas = (params?.oas || "3.1") as OasKeys;

return { resolver, oas };
};
27 changes: 27 additions & 0 deletions src/createRocket.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { resolverObj } from "./resolvers/resolverObj";
import { ConfigObject, OasKeys } from "./types";
import { ResolverKeys } from "./resolvers";
import { Http } from "./httpStatus";
import { createConfig } from "./config";
import { Oas } from "./docs";

/**
* CreateRocket: generates a kit to workr back end in next.js with app folder
*
*/
export const createRocket = <
K extends ResolverKeys = "zod",
O extends OasKeys = "3.1",
>(
params?: ConfigObject<K, O>,
) => {
const config = createConfig(params);

const Route = resolverObj[config.resolver] as K extends "yup"
? typeof resolverObj.yup
: typeof resolverObj.zod;

const OpenApi = Oas[config.oas];

return { Route, Http, OpenApi };
};
18 changes: 18 additions & 0 deletions src/docs/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { oas31 } from "openapi3-ts";

export const Oas = {
"3.0": (doc?: oas31.OpenAPIObject) => {
if (!doc) return oas31.OpenApiBuilder.create(doc);

if (!doc.openapi) doc.openapi = "3.0.3";

return oas31.OpenApiBuilder.create(doc);
},
"3.1": (doc?: oas31.OpenAPIObject) => {
if (!doc) return oas31.OpenApiBuilder.create(doc);

if (!doc.openapi) doc.openapi = "3.1.0";

return oas31.OpenApiBuilder.create(doc);
},
};
3 changes: 3 additions & 0 deletions src/docs/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Oas } from ".";

export type OasKeys = keyof typeof Oas;
1 change: 1 addition & 0 deletions src/httpStatus/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * as Http from "http-status-codes";
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./types";
export { createRocket } from "./createRocket";
4 changes: 4 additions & 0 deletions src/resolvers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from "./resolverObj";
export type * from "./types";
export * as YubResolver from "./yup";
export * as ZodResolver from "./zod";
24 changes: 24 additions & 0 deletions src/resolvers/methods/formatParams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
export const formatParams = (params: Record<any, any>) => {
const paramsEntry = Object.entries(params).map(([key, value]) => {
// en caso de que sea array
if (Array.isArray(value)) {
return value.map((vItem) => {
const vItemoNumber = Number(vItem);

if (vItemoNumber !== 0 && !vItemoNumber) {
return [key, value];
} else {
return [key, vItemoNumber];
}
});
}

const valueToNumber = Number(value);
if (valueToNumber !== 0 && !valueToNumber) {
return [key, value];
} else {
return [key, valueToNumber];
}
});
return Object.fromEntries(paramsEntry);
};
7 changes: 7 additions & 0 deletions src/resolvers/resolverObj.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { yupRoute } from "./yup/route";
import { zodRoute } from "./zod/route";

export const resolverObj = {
yup: yupRoute,
zod: zodRoute,
};
10 changes: 10 additions & 0 deletions src/resolvers/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { StatusCodes } from "http-status-codes";
import { resolverObj } from "./resolverObj";
export * from "./zod/types";
export * from "./yup/types";

export type ResolverKeys = keyof typeof resolverObj;

export interface ReplyInit extends Omit<ResponseInit, "status"> {
status?: StatusCodes;
}
119 changes: 119 additions & 0 deletions src/resolvers/yup/ValidAndFormat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { NextRequest } from "next/server";
import { ObjectSchema, AnyObject, InferType } from "yup";
import { IYupSchemasValid } from "../types";
import { headers as Headers } from "next/headers";
import { ReadonlyHeaders } from "next/dist/server/web/spec-extension/adapters/headers";

export default class ValidAndFormat<
B extends ObjectSchema<AnyObject>,
C extends ObjectSchema<AnyObject>,
Q extends ObjectSchema<AnyObject>,
H extends ObjectSchema<AnyObject>,
R extends ObjectSchema<AnyObject>,
> {
Schemas?: IYupSchemasValid<B, C, Q, H, R>;
NativeContext: InferType<C>;
nativeRequest: NextRequest;
bodyNative: InferType<B> = {};

constructor(
nativeRequest: NextRequest,
context: InferType<C>,
Schemas?: IYupSchemasValid<B, C, Q, H, R>,
) {
this.Schemas = Schemas;
this.NativeContext = context;
this.nativeRequest = nativeRequest;
}

headers(): InferType<H> | ReadonlyHeaders {
if (!this.Schemas?.headers) return Headers();

return this.Schemas.headers.validateSync(Headers());
}

context(): InferType<C> {
if (!this.Schemas?.context) return this.NativeContext;

return this.Schemas.context.validateSync(this.NativeContext);
}

private getQueryWhoNoHasSchema(
queriesArray: Array<keyof InferType<Q>>,
): Partial<InferType<Q>> {
//
const resQueries: any = {};

const symbolsReq = Object.getOwnPropertySymbols(this.nativeRequest);

const UrlNative = symbolsReq
.filter((S) => {
//@ts-ignore
const item = nativeRequest[S];

return item?.url;
})
.map<URL>((S) => {
//@ts-ignore
const item = nativeRequest[S];

return item?.url;
})[0];

const validUrlNative = Object.keys(this.nativeRequest).includes("url");

const url = validUrlNative ? new URL(this.nativeRequest.url) : UrlNative;

queriesArray.map((q) => {
resQueries[q] = url.searchParams.get(String(q));
});

return resQueries;
}

private createGetQueryWhoHasSchema(queryFormat: InferType<Q>) {
return (queriesArray: Array<keyof InferType<Q>>): Partial<InferType<Q>> => {
const querysEntrys = Object.entries(queryFormat);

const queryFilter = querysEntrys.filter(([k]) =>
queriesArray.includes(k),
);

const queryObj = Object.fromEntries(queryFilter) as Partial<InferType<Q>>;

return queryObj;
};
}

query() {
if (!this.Schemas?.query) return this.getQueryWhoNoHasSchema;

const keys = Object.keys(this.Schemas.query.shape);

const query = this.getQueryWhoNoHasSchema(keys);

const queryFormat = this.Schemas.query.validateSync(query) as InferType<Q>;

const getQueryWhoHasSchema = this.createGetQueryWhoHasSchema(queryFormat);

return getQueryWhoHasSchema;
}

private async defineBody() {
const valid_methods = !["DELETE", "GET"].includes(
this.nativeRequest.method,
);

if (valid_methods && this.Schemas?.body) {
this.bodyNative = await this.nativeRequest.json();
}
}

async body(): Promise<InferType<B>> {
await this.defineBody();

if (!this.Schemas?.body) return this.bodyNative;

return this.Schemas.body.validateSync(this.bodyNative);
}
}
5 changes: 5 additions & 0 deletions src/resolvers/yup/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from "./requestFactory";
export * from "./responseFactory";
export * from "./route";
export * from "./types";
export * from "./ValidAndFormat";
40 changes: 40 additions & 0 deletions src/resolvers/yup/requestFactory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { NextRequest } from "next/server";
import { IYupSchemasValid } from "./types";
import { IYupRequestFactoryResp } from "./types";
import { AnyObject, InferType, ObjectSchema } from "yup";
import ValidAndFormat from "./ValidAndFormat";

export const requestFactory = async <
B extends ObjectSchema<AnyObject>,
C extends ObjectSchema<AnyObject>,
Q extends ObjectSchema<AnyObject>,
H extends ObjectSchema<AnyObject>,
R extends ObjectSchema<AnyObject>,
>(
nativeRequest: NextRequest,
context: InferType<C>,
Schemas?: IYupSchemasValid<B, C, Q, H, R>,
) => {
const validAndFormat = new ValidAndFormat<B, C, Q, H, R>(
nativeRequest,
context,
Schemas,
);

const Headers = validAndFormat.headers();

const Context = validAndFormat.context();

const Query = validAndFormat.query();

const body = await validAndFormat.body();

const resp = {
getHeaders: () => Headers,
getContext: () => Context,
getQuery: (keys: Array<keyof InferType<Q> | string>) => Query(keys),
getBody: () => body,
};

return { ...resp, ...nativeRequest } as IYupRequestFactoryResp<B, C, Q>;
};
26 changes: 26 additions & 0 deletions src/resolvers/yup/responseFactory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { getReasonPhrase } from "http-status-codes";
import { NextResponse } from "next/server";
import { ISchema, InferType } from "yup";
import { ReplyInit } from "../types";

export const responseFactory = <R extends ISchema<any>,>(
respnseSchemas?: R,
) => {
return {
rewrite: NextResponse.redirect,

next: NextResponse.next,

redirect: NextResponse.redirect,

json: (body: InferType<R>, init?: ReplyInit) => {
if (respnseSchemas) respnseSchemas.validate(body);

if (init?.status && !init?.statusText) {
init.statusText = getReasonPhrase(init.status);
}

return NextResponse.json<InferType<R>>(body, init);
},
};
};
43 changes: 43 additions & 0 deletions src/resolvers/yup/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { NextRequest, NextResponse } from "next/server";
import { requestFactory } from "./requestFactory";
import { responseFactory } from "./responseFactory";
import { IYupRouteParams, YupActionReturnType } from "./types";
import { AnyObject, InferType, ObjectSchema } from "yup";

export const yupRoute = <
B extends ObjectSchema<AnyObject>,
C extends ObjectSchema<AnyObject>,
Q extends ObjectSchema<AnyObject>,
H extends ObjectSchema<AnyObject>,
R extends ObjectSchema<AnyObject>,
>(
P: IYupRouteParams<B, C, Q, H, R> | IYupRouteParams<B, C, Q, H, R>["Handler"],
) => {
return async (
nextRequest: NextRequest,
context: InferType<C>,
): Promise<YupActionReturnType<B, C, Q, H, R>> => {
try {
if (typeof P === "object") {
const { schemas, Handler } = P;
const req = await requestFactory<B, C, Q, H, R>(
nextRequest,
context,
schemas,
);

const reply = responseFactory(schemas?.response);

return Handler(req, reply, context);
}

const req = await requestFactory<B, C, Q, H, R>(nextRequest, context);

const reply = responseFactory();

return P(req, reply, context);
} catch (error) {
return NextResponse.json((error as any).errors, { status: 400 });
}
};
};
Loading

0 comments on commit 9df611b

Please sign in to comment.