Skip to content

Commit 53a9cae

Browse files
committed
refactor/fix: reconcile merge changes from new PR
1 parent f2380fe commit 53a9cae

File tree

5 files changed

+139
-115
lines changed

5 files changed

+139
-115
lines changed

site/src/api/queries/workspaces.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export const workspaceByOwnerAndName = (
2626
};
2727
};
2828

29-
type AutoCreateWorkspaceOptions = {
29+
export type AutoCreateWorkspaceOptions = {
3030
templateName: string;
3131
versionId?: string;
3232
organizationId: string;

site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx

Lines changed: 131 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -23,102 +23,87 @@ import {
2323
templateByName,
2424
templateVersionExternalAuth,
2525
} from "api/queries/templates";
26-
import { autoCreateWorkspace, createWorkspace } from "api/queries/workspaces";
26+
import {
27+
AutoCreateWorkspaceOptions,
28+
autoCreateWorkspace,
29+
createWorkspace,
30+
} from "api/queries/workspaces";
2731
import { checkAuthorization } from "api/queries/authCheck";
2832
import { CreateWSPermissions, createWorkspaceChecks } from "./permissions";
2933
import { richParameters } from "api/queries/templateVersions";
30-
import { paramsUsedToCreateWorkspace } from "utils/workspace";
3134
import { useEffectEvent } from "hooks/hookPolyfills";
3235

33-
type CreateWorkspaceMode = "form" | "auto";
36+
export const createWorkspaceModes = ["form", "auto", "duplicate"] as const;
37+
export type CreateWorkspaceMode = (typeof createWorkspaceModes)[number];
3438

35-
export type ExternalAuthPollingState = "idle" | "polling" | "abandoned";
39+
export type ExternalAuthPollingStatus = "idle" | "polling" | "abandoned";
3640

3741
const CreateWorkspacePage: FC = () => {
3842
const organizationId = useOrganizationId();
43+
const [searchParams] = useSearchParams();
3944
const { template: templateName } = useParams() as { template: string };
40-
const me = useMe();
4145
const navigate = useNavigate();
42-
const [searchParams, setSearchParams] = useSearchParams();
43-
const defaultBuildParameters = getDefaultBuildParameters(searchParams);
44-
const mode = (searchParams.get("mode") ?? "form") as CreateWorkspaceMode;
45-
const customVersionId = searchParams.get("version") ?? undefined;
46-
const defaultName =
47-
mode === "auto" ? generateUniqueName() : searchParams.get("name") ?? "";
46+
const me = useMe();
4847

4948
const queryClient = useQueryClient();
50-
const autoCreateWorkspaceMutation = useMutation(
51-
autoCreateWorkspace(queryClient),
52-
);
5349
const createWorkspaceMutation = useMutation(createWorkspace(queryClient));
5450

5551
const templateQuery = useQuery(templateByName(organizationId, templateName));
5652
const permissionsQuery = useQuery(
57-
checkAuthorization({
58-
checks: createWorkspaceChecks(organizationId),
59-
}),
53+
checkAuthorization({ checks: createWorkspaceChecks(organizationId) }),
6054
);
61-
const realizedVersionId =
62-
customVersionId ?? templateQuery.data?.active_version_id;
55+
56+
const versionId =
57+
searchParams.get("version") ?? templateQuery.data?.active_version_id;
58+
59+
const { authList, pollingStatus, startPolling } = useExternalAuth(versionId);
6360
const richParametersQuery = useQuery({
64-
...richParameters(realizedVersionId ?? ""),
65-
enabled: realizedVersionId !== undefined,
61+
...richParameters(versionId ?? ""),
62+
enabled: versionId !== undefined,
6663
});
67-
const realizedParameters = richParametersQuery.data
68-
? richParametersQuery.data.filter(paramsUsedToCreateWorkspace)
69-
: undefined;
7064

71-
const { externalAuth, externalAuthPollingState, startPollingExternalAuth } =
72-
useExternalAuth(realizedVersionId);
65+
const defaultBuildParameters = getDefaultBuildParameters(searchParams);
66+
const mode = getWorkspaceMode(searchParams);
67+
const defaultName =
68+
mode === "auto" ? generateUniqueName() : searchParams.get("name") ?? "";
69+
70+
const onCreateWorkspace = (workspace: Workspace) => {
71+
navigate(`/@${workspace.owner_name}/${workspace.name}`);
72+
};
73+
74+
const isAutoCreating = useAutomatedWorkspaceCreation({
75+
auto: mode === "auto",
76+
onSuccess: onCreateWorkspace,
77+
payload: {
78+
templateName,
79+
organizationId,
80+
defaultBuildParameters,
81+
defaultName,
82+
versionId,
83+
},
84+
});
7385

7486
const isLoadingFormData =
7587
templateQuery.isLoading ||
7688
permissionsQuery.isLoading ||
7789
richParametersQuery.isLoading;
90+
7891
const loadFormDataError =
7992
templateQuery.error ?? permissionsQuery.error ?? richParametersQuery.error;
8093

81-
const title = autoCreateWorkspaceMutation.isLoading
82-
? "Creating workspace..."
83-
: "Create workspace";
84-
85-
const onCreateWorkspace = useCallback(
86-
(workspace: Workspace) => {
87-
navigate(`/@${workspace.owner_name}/${workspace.name}`);
88-
},
89-
[navigate],
90-
);
91-
92-
const automateWorkspaceCreation = useEffectEvent(async () => {
93-
try {
94-
const newWorkspace = await autoCreateWorkspaceMutation.mutateAsync({
95-
templateName,
96-
organizationId,
97-
defaultBuildParameters,
98-
defaultName,
99-
versionId: realizedVersionId,
100-
});
101-
102-
onCreateWorkspace(newWorkspace);
103-
} catch (err) {
104-
searchParams.delete("mode");
105-
setSearchParams(searchParams);
106-
}
107-
});
108-
109-
useEffect(() => {
110-
if (mode === "auto") {
111-
void automateWorkspaceCreation();
112-
}
113-
}, [automateWorkspaceCreation, mode]);
114-
11594
return (
11695
<>
11796
<Helmet>
118-
<title>{pageTitle(title)}</title>
97+
<title>
98+
{pageTitle(
99+
isAutoCreating ? "Creating workspace..." : "Create workspace",
100+
)}
101+
</title>
119102
</Helmet>
103+
120104
{loadFormDataError && <ErrorAlert error={loadFormDataError} />}
121-
{isLoadingFormData || autoCreateWorkspaceMutation.isLoading ? (
105+
106+
{isLoadingFormData || isAutoCreating ? (
122107
<Loader />
123108
) : (
124109
<CreateWorkspacePageView
@@ -128,22 +113,22 @@ const CreateWorkspacePage: FC = () => {
128113
defaultBuildParameters={defaultBuildParameters}
129114
error={createWorkspaceMutation.error}
130115
template={templateQuery.data!}
131-
versionId={realizedVersionId}
132-
externalAuth={externalAuth ?? []}
133-
externalAuthPollingState={externalAuthPollingState}
134-
startPollingExternalAuth={startPollingExternalAuth}
116+
versionId={versionId}
117+
externalAuth={authList ?? []}
118+
externalAuthPollingStatus={pollingStatus}
119+
startPollingExternalAuth={startPolling}
135120
permissions={permissionsQuery.data as CreateWSPermissions}
136-
parameters={realizedParameters as TemplateVersionParameter[]}
137121
creatingWorkspace={createWorkspaceMutation.isLoading}
138-
onCancel={() => {
139-
navigate(-1);
140-
}}
122+
onCancel={() => navigate(-1)}
123+
parameters={richParametersQuery.data!.filter(
124+
(param) => !param.ephemeral,
125+
)}
141126
onSubmit={async (request, owner) => {
142-
if (realizedVersionId) {
127+
if (versionId) {
143128
request = {
144129
...request,
145130
template_id: undefined,
146-
template_version_id: realizedVersionId,
131+
template_version_id: versionId,
147132
};
148133
}
149134

@@ -161,64 +146,103 @@ const CreateWorkspacePage: FC = () => {
161146
};
162147

163148
const useExternalAuth = (versionId: string | undefined) => {
164-
const [externalAuthPollingState, setExternalAuthPollingState] =
165-
useState<ExternalAuthPollingState>("idle");
149+
const [pollingStatus, setPollingStatus] =
150+
useState<ExternalAuthPollingStatus>("idle");
166151

167-
const startPollingExternalAuth = useCallback(() => {
168-
setExternalAuthPollingState("polling");
152+
const startPolling = useCallback(() => {
153+
setPollingStatus("polling");
169154
}, []);
170155

171-
const { data: externalAuth } = useQuery(
156+
const { data: authList } = useQuery(
172157
versionId
173158
? {
174159
...templateVersionExternalAuth(versionId),
175-
refetchInterval:
176-
externalAuthPollingState === "polling" ? 1000 : false,
160+
refetchInterval: pollingStatus === "polling" ? 1000 : false,
177161
}
178162
: { enabled: false },
179163
);
180164

181-
const allSignedIn = externalAuth?.every((it) => it.authenticated);
182-
183165
useEffect(() => {
184-
if (allSignedIn) {
185-
setExternalAuthPollingState("idle");
186-
return;
187-
}
188-
189-
if (externalAuthPollingState !== "polling") {
166+
if (pollingStatus !== "polling") {
190167
return;
191168
}
192169

193-
// Poll for a maximum of one minute
194-
const quitPolling = setTimeout(
195-
() => setExternalAuthPollingState("abandoned"),
170+
const timeoutId = window.setTimeout(
171+
() => setPollingStatus("abandoned"),
196172
60_000,
197173
);
198-
return () => {
199-
clearTimeout(quitPolling);
200-
};
201-
}, [externalAuthPollingState, allSignedIn]);
202-
203-
return {
204-
startPollingExternalAuth,
205-
externalAuth,
206-
externalAuthPollingState,
207-
};
174+
175+
return () => clearTimeout(timeoutId);
176+
}, [pollingStatus]);
177+
178+
const isAllSignedIn = authList?.every((it) => it.authenticated) ?? false;
179+
180+
// Doing inline state sync to minimize extra re-renders that useEffect
181+
// approach would involve
182+
if (isAllSignedIn && pollingStatus === "polling") {
183+
setPollingStatus("idle");
184+
}
185+
186+
return { authList, isAllSignedIn, pollingStatus, startPolling } as const;
208187
};
209188

189+
function getWorkspaceMode(params: URLSearchParams): CreateWorkspaceMode {
190+
const paramMode = params.get("mode");
191+
if (createWorkspaceModes.includes(paramMode as CreateWorkspaceMode)) {
192+
return paramMode as CreateWorkspaceMode;
193+
}
194+
195+
return "form";
196+
}
197+
198+
type AutomatedWorkspaceConfig = {
199+
auto: boolean;
200+
payload: AutoCreateWorkspaceOptions;
201+
onSuccess: (newWorkspace: Workspace) => void;
202+
};
203+
204+
function useAutomatedWorkspaceCreation(config: AutomatedWorkspaceConfig) {
205+
// Duplicates some of the hook calls from the parent, but that was preferable
206+
// to having the function arguments balloon in complexity
207+
const [searchParams, setSearchParams] = useSearchParams();
208+
const queryClient = useQueryClient();
209+
const autoCreateWorkspaceMutation = useMutation(
210+
autoCreateWorkspace(queryClient),
211+
);
212+
213+
const automateWorkspaceCreation = useEffectEvent(async () => {
214+
try {
215+
const newWorkspace = await autoCreateWorkspaceMutation.mutateAsync(
216+
config.payload,
217+
);
218+
219+
config.onSuccess(newWorkspace);
220+
} catch (err) {
221+
searchParams.delete("mode");
222+
setSearchParams(searchParams);
223+
}
224+
});
225+
226+
useEffect(() => {
227+
if (config.auto) {
228+
void automateWorkspaceCreation();
229+
}
230+
}, [automateWorkspaceCreation, config.auto]);
231+
232+
return autoCreateWorkspaceMutation.isLoading;
233+
}
234+
210235
const getDefaultBuildParameters = (
211236
urlSearchParams: URLSearchParams,
212237
): WorkspaceBuildParameter[] => {
213-
const buildValues: WorkspaceBuildParameter[] = [];
214-
Array.from(urlSearchParams.keys())
238+
return [...urlSearchParams.keys()]
215239
.filter((key) => key.startsWith("param."))
216-
.forEach((key) => {
217-
const name = key.replace("param.", "");
218-
const value = urlSearchParams.get(key) ?? "";
219-
buildValues.push({ name, value });
240+
.map((key) => {
241+
return {
242+
name: key.replace("param.", ""),
243+
value: urlSearchParams.get(key) ?? "",
244+
};
220245
});
221-
return buildValues;
222246
};
223247

224248
export const orderedTemplateParameters = (

site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import { useTheme } from "@emotion/react";
3535
import { Alert } from "components/Alert/Alert";
3636
import { Margins } from "components/Margins/Margins";
3737
import {
38-
type ExternalAuthPollingState,
38+
type ExternalAuthPollingStatus,
3939
type CreateWorkspaceMode,
4040
} from "./CreateWorkspacePage";
4141

@@ -46,7 +46,7 @@ export interface CreateWorkspacePageViewProps {
4646
template: TypesGen.Template;
4747
versionId?: string;
4848
externalAuth: TypesGen.TemplateVersionExternalAuth[];
49-
externalAuthPollingState: ExternalAuthPollingState;
49+
externalAuthPollingStatus: ExternalAuthPollingStatus;
5050
startPollingExternalAuth: () => void;
5151
parameters: TypesGen.TemplateVersionParameter[];
5252
defaultBuildParameters: TypesGen.WorkspaceBuildParameter[];
@@ -67,7 +67,7 @@ export const CreateWorkspacePageView: FC<CreateWorkspacePageViewProps> = ({
6767
template,
6868
versionId,
6969
externalAuth,
70-
externalAuthPollingState,
70+
externalAuthPollingStatus,
7171
startPollingExternalAuth,
7272
parameters,
7373
defaultBuildParameters,
@@ -187,7 +187,7 @@ export const CreateWorkspacePageView: FC<CreateWorkspacePageViewProps> = ({
187187
key={auth.id}
188188
authenticateURL={auth.authenticate_url}
189189
authenticated={auth.authenticated}
190-
externalAuthPollingState={externalAuthPollingState}
190+
externalAuthPollingState={externalAuthPollingStatus}
191191
startPollingExternalAuth={startPollingExternalAuth}
192192
displayName={auth.display_name}
193193
displayIcon={auth.display_icon}

site/src/pages/CreateWorkspacePage/ExternalAuth.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@ import Tooltip from "@mui/material/Tooltip";
55
import { type FC } from "react";
66
import { LoadingButton } from "components/LoadingButton/LoadingButton";
77
import { Stack } from "components/Stack/Stack";
8-
import { type ExternalAuthPollingState } from "./CreateWorkspacePage";
8+
import { type ExternalAuthPollingStatus } from "./CreateWorkspacePage";
99

1010
export interface ExternalAuthProps {
1111
displayName: string;
1212
displayIcon: string;
1313
authenticated: boolean;
1414
authenticateURL: string;
15-
externalAuthPollingState: ExternalAuthPollingState;
15+
externalAuthPollingState: ExternalAuthPollingStatus;
1616
startPollingExternalAuth: () => void;
1717
error?: string;
1818
}

site/src/pages/WorkspacePage/WorkspaceReadyPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import { templateVersion, templateVersions } from "api/queries/templates";
3434
import { Alert } from "components/Alert/Alert";
3535
import { Stack } from "components/Stack/Stack";
3636
import { useWorkspaceBuildLogs } from "hooks/useWorkspaceBuildLogs";
37-
import { type CreateWorkspaceMode } from "xServices/createWorkspace/createWorkspaceXService";
37+
import { type CreateWorkspaceMode } from "../CreateWorkspacePage/CreateWorkspacePage";
3838

3939
interface WorkspaceReadyPageProps {
4040
workspaceState: StateFrom<typeof workspaceMachine>;

0 commit comments

Comments
 (0)