8000 feat: handle update build for dynamic params by jaaydenh · Pull Request #18226 · coder/coder · GitHub
[go: up one dir, main page]

Skip to content

feat: handle update build for dynamic params #18226

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Jun 5, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat: check opt-in per template
  • Loading branch information
jaaydenh committed Jun 4, 2025
commit 561a0ebb069d1dc5bcd103ea6f048bb144e4a9ac
42 changes: 42 additions & 0 deletions site/src/hooks/useDynamicParametersOptOut.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { useQuery } from "react-query";

export const optOutKey = (id: string): string => `parameters.${id}.optOut`;

interface UseDynamicParametersOptOutOptions {
templateId: string | undefined;
templateUsesClassicParameters: boolean | undefined;
enabled: boolean;
}

export const useDynamicParametersOptOut = ({
templateId,
templateUsesClassicParameters,
enabled,
}: UseDynamicParametersOptOutOptions) => {
return useQuery({
enabled: !!templateId && enabled,
queryKey: ["dynamicParametersOptOut", templateId],
queryFn: () => {
if (!templateId) {
// This should not happen if enabled is working correctly,
// but as a type guard and sanity check.
throw new Error("templateId is required");
}
const localStorageKey = optOutKey(templateId);
const storedOptOutString = localStorage.getItem(localStorageKey);

let optedOut: boolean;

if (storedOptOutString !== null) {
optedOut = storedOptOutString === "true";
} else {
optedOut = Boolean(templateUsesClassicParameters);
}

return {
templateId,
optedOut,
};
},
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { UpdateBuildParametersDialog } from "./UpdateBuildParametersDialog";
import { UpdateBuildParametersDialogExperimental } from "./UpdateBuildParametersDialogExperimental";
import { WorkspaceDeleteDialog } from "./WorkspaceDeleteDialog";
import { useWorkspaceDuplication } from "./useWorkspaceDuplication";
import { useDynamicParametersOptOut } from "hooks/useDynamicParametersOptOut";

type WorkspaceMoreActionsProps = {
workspace: Workspace;
Expand All @@ -45,6 +46,13 @@ export const WorkspaceMoreActions: FC<WorkspaceMoreActionsProps> = ({
const { experiments } = useDashboard();
const isDynamicParametersEnabled = experiments.includes("dynamic-parameters");

const optOutQuery = useDynamicParametersOptOut({
templateId: workspace.template_id,
templateUsesClassicParameters:
workspace.template_use_classic_parameter_flow,
enabled: isDynamicParametersEnabled,
});

// Permissions
const { data: permissions } = useQuery(workspacePermissions(workspace));

Expand All @@ -54,7 +62,11 @@ export const WorkspaceMoreActions: FC<WorkspaceMoreActionsProps> = ({
// Change version
const [changeVersionDialogOpen, setChangeVersionDialogOpen] = useState(false);
const changeVersionMutation = useMutation(
changeVersion(workspace, queryClient, isDynamicParametersEnabled),
changeVersion(
workspace,
queryClient,
Boolean(!optOutQuery.data?.optedOut) ?? false,
),
);

// Delete
Expand Down Expand Up @@ -146,8 +158,8 @@ export const WorkspaceMoreActions: FC<WorkspaceMoreActionsProps> = ({
onClose={() => setIsDownloadDialogOpen(false)}
/>

{isDynamicParametersEnabled ? (
<UpdateBuildParametersDialogExperimental
{optOutQuery.data?.optedOut ? (
<UpdateBuildParametersDialog
missedParameters={
changeVersionMutation.error instanceof MissingBuildParameters
? changeVersionMutation.error.parameters
Expand All @@ -157,16 +169,17 @@ export const WorkspaceMoreActions: FC<WorkspaceMoreActionsProps> = ({
onClose={() => {
changeVersionMutation.reset();
}}
workspaceOwnerName={workspace.owner_name}
workspaceName={workspace.name}
templateVersionId={
changeVersionMutation.error instanceof MissingBuildParameters
? changeVersionMutation.error?.versionId
: undefined
}
onUpdate={(buildParameters) => {
if (changeVersionMutation.error instanceof MissingBuildParameters) {
changeVersionMutation.mutate({
versionId: changeVersionMutation.error.versionId,
buildParameters,
});
}
}}
8000 />
) : (
<UpdateBuildParametersDialog
<UpdateBuildParametersDialogExperimental
missedParameters={
changeVersionMutation.error instanceof MissingBuildParameters
? changeVersionMutation.error.parameters
Expand All @@ -176,14 +189,13 @@ export const WorkspaceMoreActions: FC<WorkspaceMoreActionsProps> = ({
onClose={() => {
changeVersionMutation.reset();
}}
onUpdate={(buildParameters) => {
if (changeVersionMutation.error instanceof MissingBuildParameters) {
changeVersionMutation.mutate({
versionId: changeVersionMutation.error.versionId,
buildParameters,
});
}
}}
workspaceOwnerName={workspace.owner_name}
workspaceName={workspace.name}
templateVersionId={
changeVersionMutation.error instanceof MissingBuildParameters
? changeVersionMutation.error?.versionId
: undefined
}
/>
)}

Expand Down
44 changes: 34 additions & 10 deletions site/src/modules/workspaces/WorkspaceUpdateDialogs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ import { UpdateBuildParametersDialog } from "modules/workspaces/WorkspaceMoreAct
import { UpdateBuildParametersDialogExperimental } from "modules/workspaces/WorkspaceMoreActions/UpdateBuildParametersDialogExperimental";
import { type FC, useState } from "react";
import { useMutation, useQueryClient } from "react-query";
import { useDynamicParametersOptOut } from "hooks/useDynamicParametersOptOut";
import { ErrorAlert } from "components/Alert/ErrorAlert";
import { Loader } from "components/Loader/Loader";

type UseWorkspaceUpdateOptions = {
workspace: Workspace;
Expand All @@ -36,8 +39,6 @@ export const useWorkspaceUpdate = ({
onSuccess,
onError,
}: UseWorkspaceUpdateOptions): UseWorkspaceUpdateResult => {
const { experiments } = useDashboard();
const isDynamicParametersEnabled = experiments.includes("dynamic-parameters");
const queryClient = useQueryClient();
const [isConfirmingUpdate, setIsConfirmingUpdate] = useState(false);

Expand All @@ -55,10 +56,20 @@ export const useWorkspaceUpdate = ({
setIsConfirmingUpdate(true);
};

const { experiments } = useDashboard();
const dynamicParametersEnabled = experiments.includes("dynamic-parameters");

const optOutQuery = useDynamicParametersOptOut({
templateId: workspace.template_id,
templateUsesClassicParameters:
workspace.template_use_classic_parameter_flow,
enabled: dynamicParametersEnabled,
});

const confirmUpdate = (buildParameters: WorkspaceBuildParameter[] = []) => {
updateWorkspaceMutation.mutate({
buildParameters,
isDynamicParametersEnabled,
isDynamicParametersEnabled: Boolean(!optOutQuery.data?.optedOut) ?? false,
});
setIsConfirmingUpdate(false);
};
Expand Down Expand Up @@ -155,14 +166,33 @@ const MissingBuildParametersDialog: FC<MissingBuildParametersDialogProps> = ({
}) => {
const { experiments } = useDashboard();
const isDynamicParametersEnabled = experiments.includes("dynamic-parameters");
const optOutQuery = useDynamicParametersOptOut({
templateId: workspace.template_id,
templateUsesClassicParameters:
workspace.template_use_classic_parameter_flow,
enabled: isDynamicParametersEnabled,
});

const missedParameters =
error instanceof MissingBuildParameters ? error.parameters : [];
const versionId =
error instanceof MissingBuildParameters ? error.versionId : undefined;
const isOpen = error instanceof MissingBuildParameters;

return isDynamicParametersEnabled ? (
if (optOutQuery.isError) {
return <ErrorAlert error={optOutQuery.error} />;
}
if (!optOutQuery.data) {
return <Loader />;
}

return optOutQuery.data?.optedOut ? (
<UpdateBuildParametersDialog
missedParameters={missedParameters}
open={isOpen}
{...dialogProps}
/>
) : (
<UpdateBuildParametersDialogExperimental
missedParameters={missedParameters}
open={isOpen}
Expand All @@ -171,11 +201,5 @@ const MissingBuildParametersDialog: FC<MissingBuildParametersDialogProps> = ({
workspaceName={workspace.name}
templateVersionId={versionId}
/>
) : (
<UpdateBuildParametersDialog
missedParameters={missedParameters}
open={isOpen}
{...dialogProps}
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,42 +8,33 @@ import { useParams } from "react-router-dom";
import CreateWorkspacePage from "./CreateWorkspacePage";
import CreateWorkspacePageExperimental from "./CreateWorkspacePageExperimental";
import { ExperimentalFormContext } from "./ExperimentalFormContext";
import {
optOutKey,
useDynamicParametersOptOut,
} from "hooks/useDynamicParametersOptOut";

const CreateWorkspaceExperimentRouter: FC = () => {
const { experiments } = useDashboard();
const dynamicParametersEnabled = experiments.includes("dynamic-parameters");
const isDynamicParametersEnabled = experiments.includes("dynamic-parameters");

const { organization: organizationName = "default", template: templateName } =
useParams() as { organization?: string; template: string };
const templateQuery = useQuery({
...templateByName(organizationName, templateName),
enabled: dynamicParametersEnabled,
enabled: isDynamicParametersEnabled,
});

const optOutQuery = useQuery({
const optOutQuery = useDynamicParametersOptOut({
templateId: templateQuery.data?.id,
templateUsesClassicParameters:
templateQuery.data?.use_classic_parameter_flow,
enabled: !!templateQuery.data,
queryKey: [organizationName, "template", templateQuery.data?.id, "optOut"],
queryFn: () => {
const templateId = templateQuery.data?.id;
const localStorageKey = optOutKey(templateId ?? "");
const storedOptOutString = localStorage.getItem(localStorageKey);

let optOutResult: boolean;

if (storedOptOutString !== null) {
optOutResult = storedOptOutString === "true";
} else {
optOutResult = !!templateQuery.data?.use_classic_parameter_flow;
}

return {
templateId: templateId,
optedOut: optOutResult,
};
},
});

if (dynamicParametersEnabled) {
if (isDynamicParametersEnabled) {
if (templateQuery.isError) {
return <ErrorAlert error={templateQuery.error} />;
}
if (optOutQuery.isError) {
return <ErrorAlert error={optOutQuery.error} />;
}
Expand Down Expand Up @@ -77,5 +68,3 @@ const CreateWorkspaceExperimentRouter: FC = () => {
};

export default CreateWorkspaceExperimentRouter;

const optOutKey = (id: string) => `parameters.${id}.optOut`;
Original file line number Diff line number Diff line change
Expand Up @@ -2,54 +2,33 @@ import { ErrorAlert } from "components/Alert/ErrorAlert";
import { Loader } from "components/Loader/Loader";
import { useDashboard } from "modules/dashboard/useDashboard";
import type { FC } from "react";
import { useQuery } from "react-query";
import { ExperimentalFormContext } from "../../CreateWorkspacePage/ExperimentalFormContext";
import { useWorkspaceSettings } from "../WorkspaceSettingsLayout";
import WorkspaceParametersPage from "./WorkspaceParametersPage";
import WorkspaceParametersPageExperimental from "./WorkspaceParametersPageExperimental";
import {
optOutKey,
useDynamicParametersOptOut,
} from "hooks/useDynamicParametersOptOut";

const WorkspaceParametersExperimentRouter: FC = () => {
const { experiments } = useDashboard();
const workspace = useWorkspaceSettings();
const dynamicParametersEnabled = experiments.includes("dynamic-parameters");

const optOutQuery = useQuery({
const optOutQuery = useDynamicParametersOptOut({
templateId: workspace.template_id,
templateUsesClassicParameters:
workspace.template_use_classic_parameter_flow,
enabled: dynamicParametersEnabled,
queryKey: [
"workspace",
workspace.id,
"template_id",
workspace.template_id,
"optOut",
],
queryFn: () => {
const templateId = workspace.template_id;
const workspaceId = workspace.id;
const localStorageKey = optOutKey(templateId);
const storedOptOutString = localStorage.getItem(localStorageKey);

let optOutResult: boolean;

if (storedOptOutString !== null) {
optOutResult = storedOptOutString === "true";
} else {
optOutResult = Boolean(workspace.template_use_classic_parameter_flow);
}

return {
templateId,
workspaceId,
optedOut: optOutResult,
};
},
});

if (dynamicParametersEnabled) {
if (optOutQuery.isLoading) {
return <Loader />;
if (optOutQuery.isError) {
return <ErrorAlert error={optOutQuery.error} />;
}
if (!optOutQuery.data) {
return <ErrorAlert error={optOutQuery.error} />;
return <Loader />;
}

const toggleOptedOut = () => {
Expand Down Expand Up @@ -79,5 +58,3 @@ const WorkspaceParametersExperimentRouter: FC = () => {
};

export default WorkspaceParametersExperimentRouter;

const optOutKey = (id: string) => `parameters.${id}.optOut`;
Original file line number Diff line number Diff line change
Expand Up @@ -155,11 +155,11 @@ export const WorkspaceParametersPageViewExperimental: FC<
</div>
)}

{(templateVersionId || workspace.template_active_version_id) && (
{(templateVersionId || workspace.latest_build.template_version_id) && (
<div className="flex flex-col gap-2">
<Label className="text-sm text-content-secondary">Version ID</Label>
<p className="m-0 text-sm font-medium">
{templateVersionId ?? workspace.template_active_version_id}
{templateVersionId ?? workspace.latest_build.template_version_id}
</p>
</div>
)}
Expand Down
Loading
0