Skip to content

Commit

Permalink
fix: Release channel policy form updates
Browse files Browse the repository at this point in the history
  • Loading branch information
adityachoudhari26 committed Dec 21, 2024
1 parent d2f9b02 commit 541aed7
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,8 @@ const View: React.FC<{
releaseChannels: SCHEMA.ReleaseChannel[];
};
deployments?: Deployment[];
}> = ({ activeTab, environmentPolicy, deployments }) => {
isLoading: boolean;
}> = ({ activeTab, environmentPolicy, deployments, isLoading }) => {
return {
[EnvironmentPolicyDrawerTab.Overview]: (
<Overview environmentPolicy={environmentPolicy} />
Expand All @@ -121,7 +122,11 @@ const View: React.FC<{
<RolloutAndTiming environmentPolicy={environmentPolicy} />
),
[EnvironmentPolicyDrawerTab.ReleaseChannels]: deployments != null && (
<ReleaseChannels policy={environmentPolicy} deployments={deployments} />
<ReleaseChannels
environmentPolicy={environmentPolicy}
deployments={deployments}
isLoading={isLoading}
/>
),
}[activeTab];
};
Expand Down Expand Up @@ -155,7 +160,7 @@ export const EnvironmentPolicyDrawer: React.FC = () => {
environmentPolicyId ?? "",
{ enabled: isOpen },
);
const environmentPolicy = environmentPolicyQ.data;
const { data: environmentPolicy, isLoading } = environmentPolicyQ;

const deploymentsQ = api.deployment.bySystemId.useQuery(
environmentPolicy?.systemId ?? "",
Expand Down Expand Up @@ -235,6 +240,7 @@ export const EnvironmentPolicyDrawer: React.FC = () => {
activeTab={tab ?? EnvironmentPolicyDrawerTab.Overview}
environmentPolicy={environmentPolicy}
deployments={deployments}
isLoading={isLoading}
/>
</div>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import type * as SCHEMA from "@ctrlplane/db/schema";
import { useState } from "react";
import Link from "next/link";
import { useParams } from "next/navigation";
import { IconPlus } from "@tabler/icons-react";
import { IconLoader2, IconPlus } from "@tabler/icons-react";

import { Button } from "@ctrlplane/ui/button";
import { Label } from "@ctrlplane/ui/label";
import {
Select,
Expand All @@ -15,6 +13,7 @@ import {
} from "@ctrlplane/ui/select";

import { api } from "~/trpc/react";
import { useInvalidatePolicy } from "./useInvalidatePolicy";

type Policy = SCHEMA.EnvironmentPolicy & {
releaseChannels: SCHEMA.ReleaseChannel[];
Expand All @@ -24,7 +23,11 @@ type Deployment = SCHEMA.Deployment & {
releaseChannels: SCHEMA.ReleaseChannel[];
};

type ReleaseChannelProps = { policy: Policy; deployments: Deployment[] };
type ReleaseChannelProps = {
environmentPolicy: Policy;
deployments: Deployment[];
isLoading: boolean;
};

type DeploymentSelectProps = {
deployment: Deployment;
Expand Down Expand Up @@ -90,43 +93,47 @@ const DeploymentSelect: React.FC<DeploymentSelectProps> = ({
};

export const ReleaseChannels: React.FC<ReleaseChannelProps> = ({
policy,
environmentPolicy,
deployments,
isLoading,
}) => {
const updateReleaseChannels =
api.environment.policy.updateReleaseChannels.useMutation();
const utils = api.useUtils();
const updatePolicy = api.environment.policy.update.useMutation();
const invalidatePolicy = useInvalidatePolicy(environmentPolicy);

const deploymentsWithReleaseChannels = deployments.filter(
(d) => d.releaseChannels.length > 0,
);

const { id, releaseChannels } = environmentPolicy;

const currReleaseChannels = Object.fromEntries(
deploymentsWithReleaseChannels.map((d) => [
d.id,
policy.releaseChannels.find((rc) => rc.deploymentId === d.id)?.id ?? null,
releaseChannels.find((rc) => rc.deploymentId === d.id)?.id ?? null,
]),
);

const [releaseChannels, setReleaseChannels] =
useState<Record<string, string | null>>(currReleaseChannels);

const updateReleaseChannel = (
deploymentId: string,
channelId: string | null,
) => setReleaseChannels((prev) => ({ ...prev, [deploymentId]: channelId }));

const onSubmit = () =>
updateReleaseChannels
.mutateAsync({
id: policy.id,
releaseChannels,
})
.then(() => utils.environment.policy.byId.invalidate(policy.id));
) => {
const newReleaseChannels = {
...currReleaseChannels,
[deploymentId]: channelId,
};
updatePolicy
.mutateAsync({ id, data: { releaseChannels: newReleaseChannels } })
.then(invalidatePolicy);
};

return (
<div className="space-y-4">
<Label>Release Channels</Label>
<Label className="flex items-center gap-2">
Release Channels{" "}
{(isLoading || updatePolicy.isPending) && (
<IconLoader2 className="h-4 w-4 animate-spin" />
)}
</Label>
<div className="space-y-2">
<div className="flex items-center gap-2 text-sm text-muted-foreground">
<span className="w-40">Deployment</span>
Expand All @@ -136,14 +143,11 @@ export const ReleaseChannels: React.FC<ReleaseChannelProps> = ({
<DeploymentSelect
key={d.id}
deployment={d}
releaseChannels={releaseChannels}
releaseChannels={currReleaseChannels}
updateReleaseChannel={updateReleaseChannel}
/>
))}
</div>
<Button onClick={onSubmit} disabled={updateReleaseChannels.isPending}>
Save
</Button>
</div>
);
};
64 changes: 57 additions & 7 deletions packages/api/src/router/environment-policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -366,14 +366,64 @@ export const policyRouter = createTRPCRouter({
.on({ type: "environmentPolicy", id: input.id }),
})
.input(z.object({ id: z.string().uuid(), data: updateEnvironmentPolicy }))
.mutation(({ ctx, input }) =>
ctx.db
.update(environmentPolicy)
.set(input.data)
.mutation(async ({ ctx, input }) => {
const { releaseChannels, ...data } = input.data;
const hasUpdates = Object.entries(data).length > 0;
if (hasUpdates)
await ctx.db
.update(environmentPolicy)
.set(data)
.where(eq(environmentPolicy.id, input.id))
.returning()
.then(takeFirst);

if (releaseChannels != null) {
const [nulled, set] = _.partition(
Object.entries(releaseChannels),
([_, channelId]) => channelId == null,
);

const nulledIds = nulled.map(([deploymentId]) => deploymentId);
const setChannels = set.map(([deploymentId, channelId]) => ({
policyId: input.id,
deploymentId,
channelId: channelId!,
}));

await ctx.db.transaction(async (db) => {
if (nulledIds.length > 0)
await db
.delete(environmentPolicyReleaseChannel)
.where(
inArray(
environmentPolicyReleaseChannel.deploymentId,
nulledIds,
),
);

if (setChannels.length > 0)
await db
.insert(environmentPolicyReleaseChannel)
.values(setChannels)
.onConflictDoUpdate({
target: [
environmentPolicyReleaseChannel.policyId,
environmentPolicyReleaseChannel.deploymentId,
],
set: buildConflictUpdateColumns(
environmentPolicyReleaseChannel,
["channelId"],
),
});
});
}

return ctx.db
.select()
.from(environmentPolicy)
.where(eq(environmentPolicy.id, input.id))
.returning()
.then(takeFirst),
),
.then(takeFirst);
}),

updateReleaseChannels: protectedProcedure
.input(
Expand Down
6 changes: 5 additions & 1 deletion packages/db/src/schema/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,11 @@ export const createEnvironmentPolicy = createInsertSchema(
environmentPolicy,
).omit({ id: true });

export const updateEnvironmentPolicy = createEnvironmentPolicy.partial();
export const updateEnvironmentPolicy = createEnvironmentPolicy
.partial()
.extend({
releaseChannels: z.record(z.string().uuid().nullable()).optional(),
});

export const environmentPolicyRelations = relations(
environmentPolicy,
Expand Down

0 comments on commit 541aed7

Please sign in to comment.