Skip to content

Commit 7e6cb66

Browse files
authored
feat(site): allow creating a workspace without connecting optional external auth providers (#12251)
1 parent b8a5323 commit 7e6cb66

5 files changed

+117
-29
lines changed

site/src/pages/CreateWorkspacePage/CreateWorkspacePage.test.tsx

+1-14
Original file line numberDiff line numberDiff line change
@@ -233,19 +233,6 @@ describe("CreateWorkspacePage", () => {
233233
);
234234
});
235235

236-
it("external auth errors if unauthenticated", async () => {
237-
jest
238-
.spyOn(API, "getTemplateVersionExternalAuth")
239-
.mockResolvedValueOnce([MockTemplateVersionExternalAuthGithub]);
240-
241-
renderCreateWorkspacePage();
242-
await waitForLoaderToBeRemoved();
243-
244-
await screen.findByText(
245-
"To create a workspace using the selected template, please ensure you are authenticated with all the external providers listed below.",
246-
);
247-
});
248-
249236
it("auto create a workspace if uses mode=auto", async () => {
250237
const param = "first_parameter";
251238
const paramValue = "It works!";
@@ -312,7 +299,7 @@ describe("CreateWorkspacePage", () => {
312299
route: `/templates/${MockWorkspace.name}/workspace?${params.toString()}`,
313300
});
314301

315-
const warningMessage = await screen.findByRole("alert");
302+
const warningMessage = await screen.findByTestId("duplication-warning");
316303
const nameInput = await screen.findByRole("textbox", {
317304
name: "Workspace Name",
318305
});

site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx

+74
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,80 @@ export const ExternalAuth: Story = {
126126
authenticate_url: "",
127127
display_icon: "/icon/gitlab.svg",
128128
display_name: "GitLab",
129+
optional: true,
130+
},
131+
],
132+
},
133+
};
134+
135+
export const ExternalAuthError: Story = {
136+
args: {
137+
error: true,
138+
externalAuth: [
139+
{
140+
id: "github",
141+
type: "github",
142+
authenticated: false,
143+
authenticate_url: "",
144+
display_icon: "/icon/github.svg",
145+
display_name: "GitHub",
146+
},
147+
{
148+
id: "gitlab",
149+
type: "gitlab",
150+
authenticated: false,
151+
authenticate_url: "",
152+
display_icon: "/icon/gitlab.svg",
153+
display_name: "GitLab",
154+
optional: true,
155+
},
156+
],
157+
},
158+
};
159+
160+
export const ExternalAuthAllRequiredConnected: Story = {
161+
args: {
162+
externalAuth: [
163+
{
164+
id: "github",
165+
type: "github",
166+
authenticated: true,
167+
authenticate_url: "",
168+
display_icon: "/icon/github.svg",
169+
display_name: "GitHub",
170+
},
171+
{
172+
id: "gitlab",
173+
type: "gitlab",
174+
authenticated: false,
175+
authenticate_url: "",
176+
display_icon: "/icon/gitlab.svg",
177+
display_name: "GitLab",
178+
optional: true,
179+
},
180+
],
181+
},
182+
};
183+
184+
export const ExternalAuthAllConnected: Story = {
185+
args: {
186+
externalAuth: [
187+
{
188+
id: "github",
189+
type: "github",
190+
authenticated: true,
191+
authenticate_url: "",
192+
display_icon: "/icon/github.svg",
193+
display_name: "GitHub",
194+
},
195+
{
196+
id: "gitlab",
197+
type: "gitlab",
198+
authenticated: true,
199+
authenticate_url: "",
200+
display_icon: "/icon/gitlab.svg",
201+
display_name: "GitLab",
202+
optional: true,
129203
},
130204
],
131205
},

site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx

+12-9
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,10 @@ export const CreateWorkspacePageView: FC<CreateWorkspacePageViewProps> = ({
147147
);
148148
}, [autofillParameters]);
149149

150+
const hasAllRequiredExternalAuth = externalAuth.every(
151+
(auth) => auth.optional || auth.authenticated,
152+
);
153+
150154
return (
151155
<Margins size="medium">
152156
<PageHeader actions={<Button onClick={onCancel}>Cancel</Button>}>
@@ -179,7 +183,7 @@ export const CreateWorkspacePageView: FC<CreateWorkspacePageViewProps> = ({
179183
{Boolean(error) && <ErrorAlert error={error} />}
180184

181185
{mode === "duplicate" && (
182-
<Alert severity="info" dismissible>
186+
<Alert severity="info" dismissible data-testid="duplication-warning">
183187
{Language.duplicationWarning}
184188
</Alert>
185189
)}
@@ -248,21 +252,19 @@ export const CreateWorkspacePageView: FC<CreateWorkspacePageViewProps> = ({
248252
{externalAuth && externalAuth.length > 0 && (
249253
<FormSection
250254
title="External Authentication"
251-
description="This template requires authentication to external services."
255+
description="This template uses external services for authentication."
252256
>
253257
<FormFields>
254-
{requiresExternalAuth && (
255-
// This should really be a `notice` but `severity` is a MUI prop, and we'd need
256-
// to basically make our own `Alert` component.
257-
<Alert severity="info">
258-
To create a workspace using the selected template, please
259-
ensure you are authenticated with all the external providers
260-
listed below.
258+
{Boolean(error) && !hasAllRequiredExternalAuth && (
259+
<Alert severity="error">
260+
To create a workspace using this template, please connect to
261+
all required external authentication providers listed below.
261262
</Alert>
262263
)}
263264
{externalAuth.map((auth) => (
264265
<ExternalAuthButton
265266
key={auth.id}
267+
error={error}
266268
auth={auth}
267269
isLoading={externalAuthPollingState === "polling"}
268270
onStartPolling={startPollingExternalAuth}
@@ -313,6 +315,7 @@ export const CreateWorkspacePageView: FC<CreateWorkspacePageViewProps> = ({
313315
<FormFooter
314316
onCancel={onCancel}
315317
isLoading={creatingWorkspace}
318+
submitDisabled={!hasAllRequiredExternalAuth}
316319
submitLabel="Create Workspace"
317320
/>
318321
</HorizontalForm>

site/src/pages/CreateWorkspacePage/ExternalAuthButton.stories.tsx

+13-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const MockExternalAuth: TemplateVersionExternalAuth = {
1212
};
1313

1414
const meta: Meta<typeof ExternalAuthButton> = {
15-
title: "pages/CreateWorkspacePage/ExternalAuth",
15+
title: "pages/CreateWorkspacePage/ExternalAuthButton",
1616
component: ExternalAuthButton,
1717
};
1818

@@ -25,6 +25,15 @@ export const Github: Story = {
2525
},
2626
};
2727

28+
export const GithubOptional: Story = {
29+
args: {
30+
auth: {
31+
...MockExternalAuth,
32+
optional: true,
33+
},
34+
},
35+
};
36+
2837
export const GithubWithRetry: Story = {
2938
args: {
3039
auth: MockExternalAuth,
@@ -48,6 +57,7 @@ export const Gitlab: Story = {
4857
display_icon: "/icon/gitlab.svg",
4958
display_name: "GitLab",
5059
authenticated: false,
60+
optional: true,
5161
},
5262
},
5363
};
@@ -70,6 +80,7 @@ export const AzureDevOps: Story = {
7080
display_icon: "/icon/azure-devops.svg",
7181
display_name: "Azure DevOps",
7282
authenticated: false,
83+
optional: true,
7384
},
7485
},
7586
};
@@ -92,6 +103,7 @@ export const Bitbucket: Story = {
92103
display_icon: "/icon/bitbucket.svg",
93104
display_name: "Bitbucket",
94105
authenticated: false,
106+
optional: true,
95107
},
96108
},
97109
};

site/src/pages/CreateWorkspacePage/ExternalAuthButton.tsx

+17-5
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,27 @@
11
import ReplayIcon from "@mui/icons-material/Replay";
22
import Button from "@mui/material/Button";
33
import Tooltip from "@mui/material/Tooltip";
4-
import { type FC } from "react";
54
import LoadingButton from "@mui/lab/LoadingButton";
65
import { visuallyHidden } from "@mui/utils";
6+
import { type FC } from "react";
7+
import type { TemplateVersionExternalAuth } from "api/typesGenerated";
78
import { ExternalImage } from "components/ExternalImage/ExternalImage";
8-
import { TemplateVersionExternalAuth } from "api/typesGenerated";
9+
import { Pill } from "components/Pill/Pill";
910

1011
export interface ExternalAuthButtonProps {
1112
auth: TemplateVersionExternalAuth;
1213
displayRetry: boolean;
1314
isLoading: boolean;
1415
onStartPolling: () => void;
16+
error?: unknown;
1517
}
1618

1719
export const ExternalAuthButton: FC<ExternalAuthButtonProps> = ({
1820
auth,
1921
displayRetry,
2022
isLoading,
2123
onStartPolling,
24+
error,
2225
}) => {
2326
return (
2427
<>
@@ -48,9 +51,18 @@ export const ExternalAuthButton: FC<ExternalAuthButtonProps> = ({
4851
onStartPolling();
4952
}}
5053
>
51-
{auth.authenticated
52-
? `Authenticated with ${auth.display_name}`
53-
: `Login with ${auth.display_name}`}
54+
{auth.authenticated ? (
55+
`Authenticated with ${auth.display_name}`
56+
) : (
57+
<>
58+
Login with {auth.display_name}
59+
{!auth.optional && (
60+
<Pill type={error ? "error" : "info"} css={{ marginLeft: 12 }}>
61+
Required
62+
</Pill>
63+
)}
64+
</>
65+
)}
5466
</LoadingButton>
5567

5668
{displayRetry && (

0 commit comments

Comments
 (0)