Skip to content

Commit

Permalink
Started making NextApiAdapter
Browse files Browse the repository at this point in the history
  • Loading branch information
renatodellosso committed Dec 20, 2024
1 parent 3c9f9af commit 578c66a
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 55 deletions.
28 changes: 14 additions & 14 deletions lib/api/AccessLevels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ namespace AccessLevels {

export async function IfSignedIn(
req: NextApiRequest,
res: ApiLib.ApiResponse<any>,
res: ApiLib.NextResponse<any>,
{ userPromise }: UserAndDb,
) {
return {
Expand All @@ -49,7 +49,7 @@ namespace AccessLevels {

export async function IfDeveloper(
req: NextApiRequest,
res: ApiLib.ApiResponse<any>,
res: ApiLib.NextResponse<any>,
{ userPromise }: UserAndDb,
) {
const user = await userPromise;
Expand All @@ -58,7 +58,7 @@ namespace AccessLevels {

export async function IfOnTeam(
req: NextApiRequest,
res: ApiLib.ApiResponse<any>,
res: ApiLib.NextResponse<any>,
{ userPromise, db }: UserAndDb,
teamId: string,
) {
Expand All @@ -82,7 +82,7 @@ namespace AccessLevels {

export async function IfTeamOwner(
req: NextApiRequest,
res: ApiLib.ApiResponse<any>,
res: ApiLib.NextResponse<any>,
{ userPromise, db }: UserAndDb,
teamId: string,
) {
Expand All @@ -106,7 +106,7 @@ namespace AccessLevels {

export async function IfCompOwner(
req: NextApiRequest,
res: ApiLib.ApiResponse<any>,
res: ApiLib.NextResponse<any>,
{ userPromise, db }: UserAndDb,
compId: string,
) {
Expand Down Expand Up @@ -138,7 +138,7 @@ namespace AccessLevels {

export async function IfSeasonOwner(
req: NextApiRequest,
res: ApiLib.ApiResponse<any>,
res: ApiLib.NextResponse<any>,
{ userPromise, db }: UserAndDb,
seasonId: string,
) {
Expand Down Expand Up @@ -167,7 +167,7 @@ namespace AccessLevels {

export async function IfMatchOwner(
req: NextApiRequest,
res: ApiLib.ApiResponse<any>,
res: ApiLib.NextResponse<any>,
{ userPromise, db }: UserAndDb,
matchId: string,
) {
Expand Down Expand Up @@ -201,7 +201,7 @@ namespace AccessLevels {

export async function IfReportOwner(
req: NextApiRequest,
res: ApiLib.ApiResponse<any>,
res: ApiLib.NextResponse<any>,
{ userPromise, db }: UserAndDb,
reportId: string,
) {
Expand Down Expand Up @@ -230,7 +230,7 @@ namespace AccessLevels {

export async function IfOnTeamThatOwnsComp(
req: NextApiRequest,
res: ApiLib.ApiResponse<any>,
res: ApiLib.NextResponse<any>,
{ userPromise, db }: UserAndDb,
compId: string,
) {
Expand Down Expand Up @@ -262,7 +262,7 @@ namespace AccessLevels {

export async function IfOnTeamThatOwnsMatch(
req: NextApiRequest,
res: ApiLib.ApiResponse<any>,
res: ApiLib.NextResponse<any>,
{ userPromise, db }: UserAndDb,
matchId: string,
) {
Expand Down Expand Up @@ -296,7 +296,7 @@ namespace AccessLevels {

export async function IfOnTeamThatOwnsPitReport(
req: NextApiRequest,
res: ApiLib.ApiResponse<any>,
res: ApiLib.NextResponse<any>,
{ userPromise, db }: UserAndDb,
pitReportId: string,
) {
Expand Down Expand Up @@ -333,7 +333,7 @@ namespace AccessLevels {

export async function IfOnTeamThatOwnsReport(
req: NextApiRequest,
res: ApiLib.ApiResponse<any>,
res: ApiLib.NextResponse<any>,
{ userPromise, db }: UserAndDb,
reportId: string,
) {
Expand Down Expand Up @@ -362,7 +362,7 @@ namespace AccessLevels {

export async function IfOnTeamThatOwnsSubjectiveReport(
req: NextApiRequest,
res: ApiLib.ApiResponse<any>,
res: ApiLib.NextResponse<any>,
{ userPromise, db }: UserAndDb,
reportId: string,
) {
Expand Down Expand Up @@ -394,7 +394,7 @@ namespace AccessLevels {

export async function IfOnTeamThatOwnsPicklist(
req: NextApiRequest,
res: ApiLib.ApiResponse<any>,
res: ApiLib.NextResponse<any>,
{ userPromise, db }: UserAndDb,
picklistId: string,
) {
Expand Down
73 changes: 37 additions & 36 deletions lib/api/ApiLib.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { NextApiRequest, NextApiResponse } from "next";
import { NextApiResponse } from "next";
import { OmitCallSignature } from "@/lib/Types";
import toast from "react-hot-toast";

Expand Down Expand Up @@ -59,47 +59,41 @@ namespace ApiLib {
}
}

export class ApiResponse<TSend> {
constructor(public innerRes: NextApiResponse) {}

send(data: TSend | Errors.ErrorType) {
this.innerRes.send(data);
return this;
}

status(code: number) {
this.innerRes.status(code);
return this;
}
export interface HttpRequest {
url?: string;
body: any;
}

error(code: number, message: string) {
this.innerRes.status(code).send({ error: message });
return this;
}
export interface ApiResponse<TSend> {
send(data: TSend | Errors.ErrorType): ApiResponse<TSend>;
status(code: number): ApiResponse<TSend>;
error(code: number, message: string): ApiResponse<TSend>;
}

export type Route<
TArgs extends Array<any>,
TReturn,
TDependencies,
TDataFetchedDuringAuth,
TRequest extends HttpRequest = HttpRequest,
TResponse extends ApiResponse<TReturn> = ApiResponse<TReturn>,
> = {
subUrl: string;

(...args: TArgs): Promise<TReturn>;

isAuthorized: (
req: NextApiRequest,
res: ApiResponse<TReturn>,
req: TRequest,
res: TResponse,
deps: TDependencies,
args: TArgs,
) => Promise<{
authorized: boolean;
authData: TDataFetchedDuringAuth | undefined;
}>;
handler: (
req: NextApiRequest,
res: ApiResponse<TReturn>,
req: TRequest,
res: TResponse,
deps: TDependencies,
authData: TDataFetchedDuringAuth,
args: TArgs,
Expand Down Expand Up @@ -149,15 +143,16 @@ namespace ApiLib {
TReturn,
TDependencies,
TFetchedDuringAuth,
TRequest extends HttpRequest = HttpRequest,
>(
server: Omit<
OmitCallSignature<
Route<TArgs, TReturn, TDependencies, TFetchedDuringAuth>
Route<TArgs, TReturn, TDependencies, TFetchedDuringAuth, TRequest>
>,
"subUrl"
>,
clientHandler?: (...args: any) => Promise<any>,
): Route<TArgs, TReturn, TDependencies, TFetchedDuringAuth> {
): Route<TArgs, TReturn, TDependencies, TFetchedDuringAuth, TRequest> {
return Object.assign(
clientHandler ?? { subUrl: "newRoute" },
server,
Expand All @@ -170,16 +165,17 @@ namespace ApiLib {
| Route<any, any, TDependencies, any>;
};

export abstract class ApiTemplate<TDependencies> {
// [route: string]: any;

export abstract class ApiTemplate<
TDependencies,
TRequest extends HttpRequest = HttpRequest,
> {
private initSegment(segment: Segment<any>, subUrl: string) {
for (const [key, value] of Object.entries(segment)) {
if (typeof value === "function") {
value.subUrl = subUrl + "/" + key;
} else if (
(value as unknown as Route<any, any, TDependencies, any>).subUrl ===
"newRoute"
(value as unknown as Route<any, any, TDependencies, any, TRequest>)
.subUrl === "newRoute"
) {
const route = value as unknown as Route<any, any, TDependencies, any>;
route.subUrl = subUrl + "/" + key;
Expand Down Expand Up @@ -214,15 +210,19 @@ namespace ApiLib {
None,
}

export abstract class ServerApi<TDependencies> {
export abstract class ServerApi<
TDependencies,
TRequest extends HttpRequest = HttpRequest,
TResponse extends ApiResponse<any> = ApiResponse<any>,
> {
constructor(
private api: ApiTemplate<TDependencies>,
private urlPrefix: string,
private errorLogMode: ErrorLogMode = ErrorLogMode.Log,
) {}

async handle(req: NextApiRequest, rawRes: NextApiResponse) {
const res = new ApiResponse(rawRes);
async handle(req: TRequest, rawRes: any) {
const res = this.parseRawResponse(rawRes);

if (!req.url) {
throw new Errors.InvalidRequestError(res);
Expand Down Expand Up @@ -266,14 +266,15 @@ namespace ApiLib {
return;
}

new Errors.InternalServerError(new ApiResponse(rawRes));
new Errors.InternalServerError(res);
}
}

abstract getDependencies(
req: NextApiRequest,
res: ApiResponse<any>,
): TDependencies;
protected parseRawResponse(rawRes: any): TResponse {
return rawRes;
}

abstract getDependencies(req: TRequest, res: TResponse): TDependencies;
}
}

Expand Down
7 changes: 5 additions & 2 deletions lib/api/ClientApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,17 @@ import {
import { games } from "../games";
import { Statbotics } from "../Statbotics";
import { TheBlueAlliance } from "../TheBlueAlliance";
import { request } from "http";
import { SlackNotLinkedError } from "./Errors";
import { _id } from "@next-auth/mongodb-adapter";
import { NextApiRequest } from "next";

/**
* @tested_by tests/lib/api/ClientApi.test.ts
*/
export default class ClientApi extends ApiLib.ApiTemplate<ApiDependencies> {
export default class ClientApi extends ApiLib.ApiTemplate<
ApiDependencies,
NextApiRequest
> {
constructor() {
super(false);
this.init();
Expand Down
2 changes: 1 addition & 1 deletion lib/api/Errors.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import ApiLib from "./ApiLib";

export class SlackNotLinkedError extends ApiLib.Errors.Error {
constructor(res: ApiLib.ApiResponse<any>) {
constructor(res: ApiLib.NextResponse<any>) {
super(res, 400, "Team has not provided a Slack webhook");
}
}
63 changes: 63 additions & 0 deletions lib/api/NextApiAdapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { NextApiRequest, NextApiResponse } from "next";
import ApiLib from "./ApiLib";
import { OmitCallSignature } from "../Types";

namespace NextApiAdapter {
export class NextResponse<TSend> implements ApiLib.ApiResponse<TSend> {
constructor(public innerRes: NextApiResponse) {}

send(data: TSend | ApiLib.Errors.ErrorType) {
this.innerRes.send(data);
return this;
}

status(code: number) {
this.innerRes.status(code);
return this;
}

error(code: number, message: string) {
this.innerRes.status(code).send({ error: message });
return this;
}
}

export function createRoute<
TArgs extends Array<any>,
TReturn,
TDependencies,
TFetchedDuringAuth,
>(
server: Omit<
OmitCallSignature<
ApiLib.Route<
TArgs,
TReturn,
TDependencies,
TFetchedDuringAuth,
NextApiRequest
>
>,
"subUrl"
>,
clientHandler?: (...args: any) => Promise<any>,
): ApiLib.Route<
TArgs,
TReturn,
TDependencies,
TFetchedDuringAuth,
NextApiRequest
> {
return ApiLib.createRoute(server, clientHandler);
}

export abstract class ServerApi<TDependencies> extends ApiLib.ServerApi<
TDependencies,
NextApiRequest,
NextResponse<unknown>
> {
protected parseRawResponse(rawRes: any): NextResponse<unknown> {
return new NextResponse(rawRes);
}
}
}
2 changes: 1 addition & 1 deletion lib/api/ServerApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export default class ServerApi extends ApiLib.ServerApi<ApiDependencies> {

getDependencies(
req: NextApiRequest,
res: ApiLib.ApiResponse<any>,
res: ApiLib.NextResponse<any>,
): ApiDependencies {
return {
db: getDatabase(),
Expand Down
2 changes: 1 addition & 1 deletion lib/testutils/TestUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import InMemoryDbInterface from "../client/dbinterfaces/InMemoryDbInterface";
import { ResendInterface } from "../ResendUtils";
import { SlackInterface } from "../SlackClient";

export class TestRes extends ApiLib.ApiResponse<any> {
export class TestRes extends ApiLib.NextResponse<any> {
status = jest.fn((code) => this);
send = jest.fn((obj) => this);
error = jest.fn((code, message) => {
Expand Down

0 comments on commit 578c66a

Please sign in to comment.