Skip to content

Commit 02a0870

Browse files
committed
Fix webapp type errors
1 parent d27234b commit 02a0870

File tree

5 files changed

+137
-59
lines changed

5 files changed

+137
-59
lines changed

apps/webapp/app/routes/api.v1.integrations.$integrationSlug.connections.$connectionId.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export async function loader({ request, params }: LoaderFunctionArgs) {
3333
id: parsedParams.data.connectionId,
3434
integration: {
3535
slug: parsedParams.data.integrationSlug,
36-
organization: authenticatedEnv.organization,
36+
organizationId: authenticatedEnv.organization.id,
3737
},
3838
},
3939
include: {
Lines changed: 22 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,36 @@
1-
import type { LoaderFunctionArgs } from "@remix-run/server-runtime";
21
import { json } from "@remix-run/server-runtime";
32
import { z } from "zod";
4-
import { ValidationError } from "zod-validation-error";
53
import { findProjectByRef } from "~/models/project.server";
6-
import { ApiRunListPresenter } from "~/presenters/v3/ApiRunListPresenter.server";
7-
import { authenticateApiRequestWithPersonalAccessToken } from "~/services/personalAccessToken.server";
8-
import { apiCors } from "~/utils/apiCors";
4+
import {
5+
ApiRunListPresenter,
6+
ApiRunListSearchParams,
7+
} from "~/presenters/v3/ApiRunListPresenter.server";
8+
import { createLoaderPATApiRoute } from "~/services/routeBuiilders/apiBuilder.server";
99

1010
const ParamsSchema = z.object({
1111
projectRef: z.string(),
1212
});
1313

14-
export async function loader({ request, params }: LoaderFunctionArgs) {
15-
if (request.method.toUpperCase() === "OPTIONS") {
16-
return apiCors(request, json({}));
17-
}
18-
19-
const authenticationResult = await authenticateApiRequestWithPersonalAccessToken(request);
20-
21-
if (!authenticationResult) {
22-
return apiCors(request, json({ error: "Invalid or Missing API key" }, { status: 401 }));
23-
}
24-
25-
const $params = ParamsSchema.safeParse(params);
26-
27-
if (!$params.success) {
28-
return json({ error: "Invalid params" }, { status: 400 });
29-
}
30-
31-
const project = await findProjectByRef($params.data.projectRef, authenticationResult.userId);
32-
33-
if (!project) {
34-
return json({ error: "Project not found" }, { status: 404 });
35-
}
36-
37-
const url = new URL(request.url);
38-
39-
const presenter = new ApiRunListPresenter();
14+
export const loader = createLoaderPATApiRoute(
15+
{
16+
params: ParamsSchema,
17+
searchParams: ApiRunListSearchParams,
18+
corsStrategy: "all",
19+
},
20+
async ({ searchParams, params, authentication }) => {
21+
const project = await findProjectByRef(params.projectRef, authentication.userId);
22+
23+
if (!project) {
24+
return json({ error: "Project not found" }, { status: 404 });
25+
}
4026

41-
try {
42-
const result = await presenter.call(project, url.searchParams);
27+
const presenter = new ApiRunListPresenter();
28+
const result = await presenter.call(project, searchParams);
4329

4430
if (!result) {
45-
return apiCors(request, json({ data: [] }));
31+
return json({ data: [] });
4632
}
4733

48-
return apiCors(request, json(result));
49-
} catch (error) {
50-
if (error instanceof ValidationError) {
51-
return apiCors(
52-
request,
53-
json({ error: "Query Error", details: error.details }, { status: 400 })
54-
);
55-
} else {
56-
return apiCors(
57-
request,
58-
json({ error: error instanceof Error ? error.message : String(error) }, { status: 400 })
59-
);
60-
}
34+
return json(result);
6135
}
62-
}
36+
);

apps/webapp/app/routes/api.v1.runs.$runParam.reschedule.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,7 @@ export async function action({ request, params }: ActionFunctionArgs) {
6262
}
6363

6464
const presenter = new ApiRetrieveRunPresenter();
65-
const result = await presenter.call(
66-
updatedRun.friendlyId,
67-
authenticationResult.environment,
68-
true
69-
);
65+
const result = await presenter.call(updatedRun.friendlyId, authenticationResult.environment);
7066

7167
if (!result) {
7268
return json({ error: "Run not found" }, { status: 404 });

apps/webapp/app/services/routeBuiilders/apiBuilder.server.ts

Lines changed: 112 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,12 @@ import {
99
checkAuthorization,
1010
} from "../authorization.server";
1111
import { logger } from "../logger.server";
12+
import {
13+
authenticateApiRequestWithPersonalAccessToken,
14+
PersonalAccessTokenAuthenticationResult,
15+
} from "../personalAccessToken.server";
1216

13-
type RouteBuilderOptions<
17+
type ApiKeyRouteBuilderOptions<
1418
TParamsSchema extends z.AnyZodObject | undefined = undefined,
1519
TSearchParamsSchema extends z.AnyZodObject | undefined = undefined
1620
> = {
@@ -30,7 +34,7 @@ type RouteBuilderOptions<
3034
};
3135
};
3236

33-
type HandlerFunction<
37+
type ApiKeyHandlerFunction<
3438
TParamsSchema extends z.AnyZodObject | undefined,
3539
TSearchParamsSchema extends z.AnyZodObject | undefined
3640
> = (args: {
@@ -46,8 +50,8 @@ export function createLoaderApiRoute<
4650
TParamsSchema extends z.AnyZodObject | undefined = undefined,
4751
TSearchParamsSchema extends z.AnyZodObject | undefined = undefined
4852
>(
49-
options: RouteBuilderOptions<TParamsSchema, TSearchParamsSchema>,
50-
handler: HandlerFunction<TParamsSchema, TSearchParamsSchema>
53+
options: ApiKeyRouteBuilderOptions<TParamsSchema, TSearchParamsSchema>,
54+
handler: ApiKeyHandlerFunction<TParamsSchema, TSearchParamsSchema>
5155
) {
5256
return async function loader({ request, params }: LoaderFunctionArgs) {
5357
const {
@@ -147,6 +151,110 @@ export function createLoaderApiRoute<
147151
};
148152
}
149153

154+
type PATRouteBuilderOptions<
155+
TParamsSchema extends z.AnyZodObject | undefined = undefined,
156+
TSearchParamsSchema extends z.AnyZodObject | undefined = undefined
157+
> = {
158+
params?: TParamsSchema;
159+
searchParams?: TSearchParamsSchema;
160+
corsStrategy?: "all" | "none";
161+
};
162+
163+
type PATHandlerFunction<
164+
TParamsSchema extends z.AnyZodObject | undefined,
165+
TSearchParamsSchema extends z.AnyZodObject | undefined
166+
> = (args: {
167+
params: TParamsSchema extends z.AnyZodObject ? z.infer<TParamsSchema> : undefined;
168+
searchParams: TSearchParamsSchema extends z.AnyZodObject
169+
? z.infer<TSearchParamsSchema>
170+
: undefined;
171+
authentication: PersonalAccessTokenAuthenticationResult;
172+
request: Request;
173+
}) => Promise<Response>;
174+
175+
export function createLoaderPATApiRoute<
176+
TParamsSchema extends z.AnyZodObject | undefined = undefined,
177+
TSearchParamsSchema extends z.AnyZodObject | undefined = undefined
178+
>(
179+
options: PATRouteBuilderOptions<TParamsSchema, TSearchParamsSchema>,
180+
handler: PATHandlerFunction<TParamsSchema, TSearchParamsSchema>
181+
) {
182+
return async function loader({ request, params }: LoaderFunctionArgs) {
183+
const {
184+
params: paramsSchema,
185+
searchParams: searchParamsSchema,
186+
corsStrategy = "none",
187+
} = options;
188+
189+
if (corsStrategy !== "none" && request.method.toUpperCase() === "OPTIONS") {
190+
return apiCors(request, json({}));
191+
}
192+
193+
const authenticationResult = await authenticateApiRequestWithPersonalAccessToken(request);
194+
195+
if (!authenticationResult) {
196+
return wrapResponse(
197+
request,
198+
json({ error: "Invalid or Missing API key" }, { status: 401 }),
199+
corsStrategy !== "none"
200+
);
201+
}
202+
203+
let parsedParams: any = undefined;
204+
if (paramsSchema) {
205+
const parsed = paramsSchema.safeParse(params);
206+
if (!parsed.success) {
207+
return wrapResponse(
208+
request,
209+
json(
210+
{ error: "Params Error", details: fromZodError(parsed.error).details },
211+
{ status: 400 }
212+
),
213+
corsStrategy !== "none"
214+
);
215+
}
216+
parsedParams = parsed.data;
217+
}
218+
219+
let parsedSearchParams: any = undefined;
220+
if (searchParamsSchema) {
221+
const searchParams = Object.fromEntries(new URL(request.url).searchParams);
222+
const parsed = searchParamsSchema.safeParse(searchParams);
223+
if (!parsed.success) {
224+
return wrapResponse(
225+
request,
226+
json(
227+
{ error: "Query Error", details: fromZodError(parsed.error).details },
228+
{ status: 400 }
229+
),
230+
corsStrategy !== "none"
231+
);
232+
}
233+
parsedSearchParams = parsed.data;
234+
}
235+
236+
try {
237+
const result = await handler({
238+
params: parsedParams,
239+
searchParams: parsedSearchParams,
240+
authentication: authenticationResult,
241+
request,
242+
});
243+
return wrapResponse(request, result, corsStrategy !== "none");
244+
} catch (error) {
245+
console.error("Error in API route:", error);
246+
if (error instanceof Response) {
247+
return wrapResponse(request, error, corsStrategy !== "none");
248+
}
249+
return wrapResponse(
250+
request,
251+
json({ error: "Internal Server Error" }, { status: 500 }),
252+
corsStrategy !== "none"
253+
);
254+
}
255+
};
256+
}
257+
150258
function wrapResponse(request: Request, response: Response, useCors: boolean) {
151259
return useCors ? apiCors(request, response) : response;
152260
}

apps/webapp/tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"isolatedModules": true,
88
"esModuleInterop": true,
99
"jsx": "react-jsx",
10-
"module": "preserve",
10+
"module": "esnext",
1111
"moduleResolution": "Bundler",
1212
"resolveJsonModule": true,
1313
"target": "ES2019",

0 commit comments

Comments
 (0)