Skip to content

Commit dfd312d

Browse files
committed
feat: enhance deployment and environment management with environmentIds
- Added `environmentIds` as a required field in OpenAPI schemas and related TypeScript interfaces for user approval records. - Updated the logic in deployment routes to utilize `environmentIds` directly from the request body, improving clarity and consistency. - Refactored related components to remove unnecessary environment ID fetching logic, streamlining the deployment process. - Adjusted tests and integration points to ensure proper handling of the new `environmentIds` field across the codebase.
1 parent 35b70f8 commit dfd312d

21 files changed

+133
-137
lines changed

apps/api/openapi/openapi.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2485,7 +2485,8 @@
24852485
}
24862486
},
24872487
"required": [
2488-
"status"
2488+
"status",
2489+
"environmentIds"
24892490
],
24902491
"type": "object"
24912492
},

apps/api/openapi/schemas/userapprovalrecord.jsonnet

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ local openapi = import '../lib/openapi.libsonnet';
33
{
44
UpsertUserApprovalRecordRequest: {
55
type: 'object',
6-
required: ['status'],
6+
required: ['status', 'environmentIds'],
77
properties: {
88
environmentIds: { type: 'array', items: { type: 'string' } },
99
status: openapi.schemaRef('ApprovalStatus'),

apps/api/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"build": "tsc && tsc-alias",
1313
"clean": "rm -rf .turbo node_modules",
1414
"dev": "pnpm with-env tsx watch --clear-screen=false src/index.ts",
15+
"typecheck": "tsc --noEmit --emitDeclarationOnly false",
1516
"format": "prettier --check . --ignore-path ../../.gitignore --ignore-path ./src/types/openapi.ts --ignore-path ./openapi/openapi.json && bash -c 'jsonnetfmt -i ./**/*.jsonnet'",
1617
"format:fix": "prettier --write . --ignore-path ../../.gitignore --ignore-path ./src/types/openapi.ts --ignore-path ./openapi/openapi.json && bash -c 'jsonnetfmt -i ./**/*.jsonnet'",
1718
"generate": "jsonnet openapi/main.jsonnet > openapi/openapi.json && pnpm generate:types",

apps/api/src/routes/v1/workspaces/deployment-versions.ts

Lines changed: 1 addition & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -2,58 +2,9 @@ import type { AsyncTypedHandler } from "@/types/api.js";
22
import type { WorkspaceEngine } from "@ctrlplane/workspace-engine-sdk";
33
import { ApiError, asyncHandler } from "@/types/api.js";
44
import { Router } from "express";
5-
65
import { Event, sendGoEvent } from "@ctrlplane/events";
76
import { getClientFor } from "@ctrlplane/workspace-engine-sdk";
87

9-
const getEnvironmentIds = async (
10-
workspaceId: string,
11-
deploymentVersionId: string,
12-
) => {
13-
const deploymentVersionResponse = await getClientFor(workspaceId).GET(
14-
"/v1/workspaces/{workspaceId}/deploymentversions/{deploymentVersionId}",
15-
{ params: { path: { workspaceId, deploymentVersionId } } },
16-
);
17-
18-
if (deploymentVersionResponse.error != null)
19-
throw new ApiError(
20-
deploymentVersionResponse.error.error ?? "Deployment version not found",
21-
deploymentVersionResponse.response.status,
22-
);
23-
const { deploymentId } = deploymentVersionResponse.data;
24-
25-
const deploymentResponse = await getClientFor(workspaceId).GET(
26-
"/v1/workspaces/{workspaceId}/deployments/{deploymentId}",
27-
{ params: { path: { workspaceId, deploymentId } } },
28-
);
29-
if (deploymentResponse.error != null)
30-
throw new ApiError(
31-
deploymentResponse.error.error ?? "Deployment not found",
32-
deploymentResponse.response.status,
33-
);
34-
35-
const { deployment } = deploymentResponse.data;
36-
const { systemIds } = deployment;
37-
38-
const environmentIds: string[] = [];
39-
for (const systemId of systemIds) {
40-
const systemResponse = await getClientFor(workspaceId).GET(
41-
"/v1/workspaces/{workspaceId}/systems/{systemId}",
42-
{ params: { path: { workspaceId, systemId } } },
43-
);
44-
45-
if (systemResponse.error != null)
46-
throw new ApiError(
47-
systemResponse.error.error ?? "System not found",
48-
systemResponse.response.status,
49-
);
50-
const { environments } = systemResponse.data;
51-
environmentIds.push(...environments.map((environment) => environment.id));
52-
}
53-
54-
return environmentIds;
55-
};
56-
578
const upsertUserApprovalRecord: AsyncTypedHandler<
589
"/v1/workspaces/{workspaceId}/deployment-versions/{deploymentVersionId}/user-approval-records",
5910
"put"
@@ -71,12 +22,8 @@ const upsertUserApprovalRecord: AsyncTypedHandler<
7122
reason: req.body.reason,
7223
};
7324

74-
const environmentIds =
75-
req.body.environmentIds ??
76-
(await getEnvironmentIds(workspaceId, deploymentVersionId));
77-
7825
try {
79-
for (const environmentId of environmentIds)
26+
for (const environmentId of req.body.environmentIds)
8027
await sendGoEvent({
8128
workspaceId,
8229
eventType: Event.UserApprovalRecordCreated,

apps/api/src/types/openapi.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1767,7 +1767,7 @@ export interface components {
17671767
slug?: string;
17681768
};
17691769
UpsertUserApprovalRecordRequest: {
1770-
environmentIds?: string[];
1770+
environmentIds: string[];
17711771
reason?: string;
17721772
status: components["schemas"]["ApprovalStatus"];
17731773
};

apps/web/app/routes/ws/deployments/_components/DeploymentProvider.tsx

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,25 @@
1-
import type { WorkspaceEngine } from "@ctrlplane/workspace-engine-sdk";
21
import type { ReactNode } from "react";
32
import { createContext, useContext } from "react";
43

5-
type Deployment = WorkspaceEngine["schemas"]["Deployment"] & {
6-
variables: WorkspaceEngine["schemas"]["DeploymentVariableWithValues"][];
4+
type Deployment = {
5+
id: string;
6+
name: string;
7+
resourceSelector?: string | null;
8+
description?: string | null;
9+
jobAgentId?: string | null;
10+
jobAgentConfig?: Record<string, any> | null;
11+
systemDeployments: Array<{
12+
systemId: string;
13+
system: {
14+
id: string;
15+
name: string;
16+
17+
systemEnvironments: Array<{
18+
systemId: string;
19+
environmentId: string;
20+
}>;
21+
};
22+
}>;
723
};
824

925
type DeploymentContextType = {

apps/web/app/routes/ws/deployments/_components/RedeployDialog.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,10 @@ const useRedeploy = (releaseTarget: ReleaseTarget, onClose: () => void) => {
4242
};
4343

4444
const useDeployment = (deploymentId: string) => {
45-
const { workspace } = useWorkspace();
4645
const { data: deployment, isLoading } = trpc.deployment.get.useQuery({
47-
workspaceId: workspace.id,
4846
deploymentId,
4947
});
50-
return { deployment: deployment?.deployment, isLoading };
48+
return { deployment, isLoading };
5149
};
5250

5351
function DeploymentBadge({ deploymentId }: { deploymentId: string }) {

apps/web/app/routes/ws/deployments/_components/variables/DeploymentVariableSection.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,6 @@ export function DeploymentVariableSection({
110110
onSuccess: () => {
111111
toast.success(`Variable "${variable.variable.key}" deleted`);
112112
utils.deployment.get.invalidate({
113-
workspaceId: workspace.id,
114113
deploymentId: deploymentId ?? "",
115114
});
116115
},

apps/web/app/routes/ws/deployments/_layout.tsx

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,26 @@ import { Outlet, useParams } from "react-router";
22

33
import { trpc } from "~/api/trpc";
44
import { Spinner } from "~/components/ui/spinner";
5-
import { useWorkspace } from "~/components/WorkspaceProvider";
65
import { DeploymentProvider } from "./_components/DeploymentProvider";
76

87
export default function DeploymentsLayout() {
9-
const { workspace } = useWorkspace();
108
const { deploymentId } = useParams();
119

12-
const { data: { deployment, variables } = {}, isLoading } =
13-
trpc.deployment.get.useQuery(
14-
{ workspaceId: workspace.id, deploymentId: deploymentId ?? "" },
15-
{ enabled: deploymentId != null },
16-
);
10+
const { data: deployment, isLoading } = trpc.deployment.get.useQuery(
11+
{ deploymentId: deploymentId ?? "" },
12+
{ enabled: deploymentId != null },
13+
);
1714

1815
if (isLoading) {
1916
return <Spinner />;
2017
}
2118

22-
if (deployment == null || variables == null) {
19+
if (deployment == null) {
2320
throw new Error("Deployment not found");
2421
}
2522

2623
return (
27-
<DeploymentProvider deployment={{ ...deployment, variables }}>
24+
<DeploymentProvider deployment={deployment}>
2825
<Outlet />
2926
</DeploymentProvider>
3027
);

apps/web/app/routes/ws/deployments/page.$deploymentId.release-targets.tsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useState } from "react";
1+
import { useMemo, useState } from "react";
22
import _ from "lodash";
33
import { Search } from "lucide-react";
44
import { Link, useSearchParams } from "react-router";
@@ -103,11 +103,17 @@ export default function ReleaseTargetsPage() {
103103
workspaceId: workspace.id,
104104
});
105105

106+
const environmentIds = useMemo(() => {
107+
return new Set(
108+
deployment.systemDeployments.flatMap((sd) =>
109+
sd.system.systemEnvironments.map((se) => se.environmentId),
110+
),
111+
);
112+
}, [deployment.systemDeployments]);
113+
106114
const environments =
107115
environmentsQuery.data?.filter((environment) =>
108-
environment.systemEnvironments.some((se) =>
109-
deployment.systemIds.includes(se.systemId),
110-
),
116+
environmentIds.has(environment.id),
111117
) ?? [];
112118

113119
const releaseTargets = releaseTargetsQuery.data?.items ?? [];

0 commit comments

Comments
 (0)