Skip to content

Commit 0002bd6

Browse files
committed
Extract project settings loader into a presenter service
1 parent 0ac6722 commit 0002bd6

File tree

2 files changed

+128
-100
lines changed

2 files changed

+128
-100
lines changed

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsx

Lines changed: 14 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ import { useEnvironment } from "~/hooks/useEnvironment";
7979
import { DateTime } from "~/components/primitives/DateTime";
8080
import { TextLink } from "~/components/primitives/TextLink";
8181
import { cn } from "~/utils/cn";
82+
import { ProjectSettingsPresenter } from "~/services/projectSettingsPresenter.server";
8283

8384
export const meta: MetaFunction = () => {
8485
return [
@@ -89,114 +90,27 @@ export const meta: MetaFunction = () => {
8990
};
9091

9192
export const loader = async ({ request, params }: LoaderFunctionArgs) => {
92-
const githubAppEnabled = env.GITHUB_APP_ENABLED === "1";
93+
const userId = await requireUserId(request);
94+
const { projectParam, organizationSlug } = EnvironmentParamSchema.parse(params);
95+
96+
const projectSettingsPresenter = new ProjectSettingsPresenter();
97+
const { gitHubApp } = await projectSettingsPresenter.getProjectSettings(
98+
organizationSlug,
99+
projectParam,
100+
userId
101+
);
93102

94103
const session = await getSession(request.headers.get("Cookie"));
95104
const openGitHubRepoConnectionModal = session.get("gitHubAppInstalled") === true;
96105
const headers = new Headers({
97106
"Set-Cookie": await commitSession(session),
98107
});
99108

100-
if (!githubAppEnabled) {
101-
return typedjson(
102-
{
103-
githubAppEnabled,
104-
githubAppInstallations: undefined,
105-
connectedGithubRepository: undefined,
106-
openGitHubRepoConnectionModal,
107-
},
108-
{ headers }
109-
);
110-
}
111-
112-
const userId = await requireUserId(request);
113-
const { projectParam, organizationSlug } = EnvironmentParamSchema.parse(params);
114-
115-
const project = await findProjectBySlug(organizationSlug, projectParam, userId);
116-
if (!project) {
117-
throw new Response(undefined, {
118-
status: 404,
119-
statusText: "Project not found",
120-
});
121-
}
122-
123-
const connectedGithubRepository = await prisma.connectedGithubRepository.findFirst({
124-
where: {
125-
projectId: project.id,
126-
},
127-
select: {
128-
branchTracking: true,
129-
previewDeploymentsEnabled: true,
130-
createdAt: true,
131-
repository: {
132-
select: {
133-
id: true,
134-
name: true,
135-
fullName: true,
136-
htmlUrl: true,
137-
private: true,
138-
},
139-
},
140-
},
141-
});
142-
143-
if (connectedGithubRepository) {
144-
const branchTrackingOrFailure = BranchTrackingConfigSchema.safeParse(
145-
connectedGithubRepository.branchTracking
146-
);
147-
148-
return typedjson(
149-
{
150-
githubAppEnabled,
151-
connectedGithubRepository: {
152-
...connectedGithubRepository,
153-
branchTracking: branchTrackingOrFailure.success
154-
? branchTrackingOrFailure.data
155-
: undefined,
156-
},
157-
githubAppInstallations: undefined,
158-
openGitHubRepoConnectionModal,
159-
},
160-
{ headers }
161-
);
162-
}
163-
164-
const githubAppInstallations = await prisma.githubAppInstallation.findMany({
165-
where: {
166-
organizationId: project.organizationId,
167-
deletedAt: null,
168-
suspendedAt: null,
169-
},
170-
select: {
171-
id: true,
172-
accountHandle: true,
173-
targetType: true,
174-
appInstallationId: true,
175-
repositories: {
176-
select: {
177-
id: true,
178-
name: true,
179-
fullName: true,
180-
htmlUrl: true,
181-
private: true,
182-
},
183-
// Most installations will only have a couple of repos so loading them here should be fine.
184-
// However, there might be outlier organizations so it's best to expose the installation repos
185-
// via a resource endpoint and filter on user input.
186-
take: 200,
187-
},
188-
},
189-
take: 20,
190-
orderBy: {
191-
createdAt: "desc",
192-
},
193-
});
194-
195109
return typedjson(
196110
{
197-
githubAppEnabled,
198-
githubAppInstallations,
199-
connectedGithubRepository: undefined,
111+
githubAppEnabled: gitHubApp.enabled,
112+
githubAppInstallations: gitHubApp.installations,
113+
connectedGithubRepository: gitHubApp.connectedRepository,
200114
openGitHubRepoConnectionModal,
201115
},
202116
{ headers }
@@ -507,7 +421,7 @@ export default function Page() {
507421
<ConnectedGitHubRepoForm connectedGitHubRepo={connectedGithubRepository} />
508422
) : (
509423
<GitHubConnectionPrompt
510-
gitHubAppInstallations={githubAppInstallations}
424+
gitHubAppInstallations={githubAppInstallations ?? []}
511425
organizationSlug={organization.slug}
512426
projectSlug={project.slug}
513427
environmentSlug={environment.slug}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import { type PrismaClient } from "@trigger.dev/database";
2+
import { prisma } from "~/db.server";
3+
import { DeleteProjectService } from "~/services/deleteProject.server";
4+
import { BranchTrackingConfigSchema, type BranchTrackingConfig } from "~/v3/github";
5+
import { checkGitHubBranchExists } from "~/services/gitHub.server";
6+
import { tryCatch } from "@trigger.dev/core/utils";
7+
import { env } from "~/env.server";
8+
import { findProjectBySlug } from "~/models/project.server";
9+
10+
export class ProjectSettingsPresenter {
11+
#prismaClient: PrismaClient;
12+
13+
constructor(prismaClient: PrismaClient = prisma) {
14+
this.#prismaClient = prismaClient;
15+
}
16+
17+
async getProjectSettings(organizationSlug: string, projectSlug: string, userId: string) {
18+
const githubAppEnabled = env.GITHUB_APP_ENABLED === "1";
19+
20+
if (!githubAppEnabled) {
21+
return {
22+
gitHubApp: {
23+
enabled: false,
24+
connectedRepository: undefined,
25+
installations: undefined,
26+
},
27+
};
28+
}
29+
30+
const project = await findProjectBySlug(organizationSlug, projectSlug, userId);
31+
if (!project) {
32+
throw new Error("Project not found");
33+
}
34+
const connectedGithubRepository = await prisma.connectedGithubRepository.findFirst({
35+
where: {
36+
projectId: project.id,
37+
},
38+
select: {
39+
branchTracking: true,
40+
previewDeploymentsEnabled: true,
41+
createdAt: true,
42+
repository: {
43+
select: {
44+
id: true,
45+
name: true,
46+
fullName: true,
47+
htmlUrl: true,
48+
private: true,
49+
},
50+
},
51+
},
52+
});
53+
54+
if (connectedGithubRepository) {
55+
const branchTrackingOrFailure = BranchTrackingConfigSchema.safeParse(
56+
connectedGithubRepository.branchTracking
57+
);
58+
59+
return {
60+
gitHubApp: {
61+
enabled: true,
62+
connectedRepository: {
63+
...connectedGithubRepository,
64+
branchTracking: branchTrackingOrFailure.success
65+
? branchTrackingOrFailure.data
66+
: undefined,
67+
},
68+
// skip loading installations if there is a connected repository
69+
// a project can have only a single connected repository
70+
installations: undefined,
71+
},
72+
};
73+
}
74+
75+
const githubAppInstallations = await prisma.githubAppInstallation.findMany({
76+
where: {
77+
organizationId: project.organizationId,
78+
deletedAt: null,
79+
suspendedAt: null,
80+
},
81+
select: {
82+
id: true,
83+
accountHandle: true,
84+
targetType: true,
85+
appInstallationId: true,
86+
repositories: {
87+
select: {
88+
id: true,
89+
name: true,
90+
fullName: true,
91+
htmlUrl: true,
92+
private: true,
93+
},
94+
// Most installations will only have a couple of repos so loading them here should be fine.
95+
// However, there might be outlier organizations so it's best to expose the installation repos
96+
// via a resource endpoint and filter on user input.
97+
take: 200,
98+
},
99+
},
100+
take: 20,
101+
orderBy: {
102+
createdAt: "desc",
103+
},
104+
});
105+
106+
return {
107+
gitHubApp: {
108+
enabled: true,
109+
connectedRepository: undefined,
110+
installations: githubAppInstallations,
111+
},
112+
};
113+
}
114+
}

0 commit comments

Comments
 (0)