mirror of
https://github.com/coder/coder.git
synced 2025-07-15 22:20:27 +00:00
feat(site): allow creating a workspace without connecting optional external auth providers (#12251)
This commit is contained in:
committed by
GitHub
parent
b8a53230c7
commit
7e6cb66a50
@ -233,19 +233,6 @@ describe("CreateWorkspacePage", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("external auth errors if unauthenticated", async () => {
|
||||
jest
|
||||
.spyOn(API, "getTemplateVersionExternalAuth")
|
||||
.mockResolvedValueOnce([MockTemplateVersionExternalAuthGithub]);
|
||||
|
||||
renderCreateWorkspacePage();
|
||||
await waitForLoaderToBeRemoved();
|
||||
|
||||
await screen.findByText(
|
||||
"To create a workspace using the selected template, please ensure you are authenticated with all the external providers listed below.",
|
||||
);
|
||||
});
|
||||
|
||||
it("auto create a workspace if uses mode=auto", async () => {
|
||||
const param = "first_parameter";
|
||||
const paramValue = "It works!";
|
||||
@ -312,7 +299,7 @@ describe("CreateWorkspacePage", () => {
|
||||
route: `/templates/${MockWorkspace.name}/workspace?${params.toString()}`,
|
||||
});
|
||||
|
||||
const warningMessage = await screen.findByRole("alert");
|
||||
const warningMessage = await screen.findByTestId("duplication-warning");
|
||||
const nameInput = await screen.findByRole("textbox", {
|
||||
name: "Workspace Name",
|
||||
});
|
||||
|
@ -126,6 +126,80 @@ export const ExternalAuth: Story = {
|
||||
authenticate_url: "",
|
||||
display_icon: "/icon/gitlab.svg",
|
||||
display_name: "GitLab",
|
||||
optional: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export const ExternalAuthError: Story = {
|
||||
args: {
|
||||
error: true,
|
||||
externalAuth: [
|
||||
{
|
||||
id: "github",
|
||||
type: "github",
|
||||
authenticated: false,
|
||||
authenticate_url: "",
|
||||
display_icon: "/icon/github.svg",
|
||||
display_name: "GitHub",
|
||||
},
|
||||
{
|
||||
id: "gitlab",
|
||||
type: "gitlab",
|
||||
authenticated: false,
|
||||
authenticate_url: "",
|
||||
display_icon: "/icon/gitlab.svg",
|
||||
display_name: "GitLab",
|
||||
optional: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export const ExternalAuthAllRequiredConnected: Story = {
|
||||
args: {
|
||||
externalAuth: [
|
||||
{
|
||||
id: "github",
|
||||
type: "github",
|
||||
authenticated: true,
|
||||
authenticate_url: "",
|
||||
display_icon: "/icon/github.svg",
|
||||
display_name: "GitHub",
|
||||
},
|
||||
{
|
||||
id: "gitlab",
|
||||
type: "gitlab",
|
||||
authenticated: false,
|
||||
authenticate_url: "",
|
||||
display_icon: "/icon/gitlab.svg",
|
||||
display_name: "GitLab",
|
||||
optional: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export const ExternalAuthAllConnected: Story = {
|
||||
args: {
|
||||
externalAuth: [
|
||||
{
|
||||
id: "github",
|
||||
type: "github",
|
||||
authenticated: true,
|
||||
authenticate_url: "",
|
||||
display_icon: "/icon/github.svg",
|
||||
display_name: "GitHub",
|
||||
},
|
||||
{
|
||||
id: "gitlab",
|
||||
type: "gitlab",
|
||||
authenticated: true,
|
||||
authenticate_url: "",
|
||||
display_icon: "/icon/gitlab.svg",
|
||||
display_name: "GitLab",
|
||||
optional: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -147,6 +147,10 @@ export const CreateWorkspacePageView: FC<CreateWorkspacePageViewProps> = ({
|
||||
);
|
||||
}, [autofillParameters]);
|
||||
|
||||
const hasAllRequiredExternalAuth = externalAuth.every(
|
||||
(auth) => auth.optional || auth.authenticated,
|
||||
);
|
||||
|
||||
return (
|
||||
<Margins size="medium">
|
||||
<PageHeader actions={<Button onClick={onCancel}>Cancel</Button>}>
|
||||
@ -179,7 +183,7 @@ export const CreateWorkspacePageView: FC<CreateWorkspacePageViewProps> = ({
|
||||
{Boolean(error) && <ErrorAlert error={error} />}
|
||||
|
||||
{mode === "duplicate" && (
|
||||
<Alert severity="info" dismissible>
|
||||
<Alert severity="info" dismissible data-testid="duplication-warning">
|
||||
{Language.duplicationWarning}
|
||||
</Alert>
|
||||
)}
|
||||
@ -248,21 +252,19 @@ export const CreateWorkspacePageView: FC<CreateWorkspacePageViewProps> = ({
|
||||
{externalAuth && externalAuth.length > 0 && (
|
||||
<FormSection
|
||||
title="External Authentication"
|
||||
description="This template requires authentication to external services."
|
||||
description="This template uses external services for authentication."
|
||||
>
|
||||
<FormFields>
|
||||
{requiresExternalAuth && (
|
||||
// This should really be a `notice` but `severity` is a MUI prop, and we'd need
|
||||
// to basically make our own `Alert` component.
|
||||
<Alert severity="info">
|
||||
To create a workspace using the selected template, please
|
||||
ensure you are authenticated with all the external providers
|
||||
listed below.
|
||||
{Boolean(error) && !hasAllRequiredExternalAuth && (
|
||||
<Alert severity="error">
|
||||
To create a workspace using this template, please connect to
|
||||
all required external authentication providers listed below.
|
||||
</Alert>
|
||||
)}
|
||||
{externalAuth.map((auth) => (
|
||||
<ExternalAuthButton
|
||||
key={auth.id}
|
||||
error={error}
|
||||
auth={auth}
|
||||
isLoading={externalAuthPollingState === "polling"}
|
||||
onStartPolling={startPollingExternalAuth}
|
||||
@ -313,6 +315,7 @@ export const CreateWorkspacePageView: FC<CreateWorkspacePageViewProps> = ({
|
||||
<FormFooter
|
||||
onCancel={onCancel}
|
||||
isLoading={creatingWorkspace}
|
||||
submitDisabled={!hasAllRequiredExternalAuth}
|
||||
submitLabel="Create Workspace"
|
||||
/>
|
||||
</HorizontalForm>
|
||||
|
@ -12,7 +12,7 @@ const MockExternalAuth: TemplateVersionExternalAuth = {
|
||||
};
|
||||
|
||||
const meta: Meta<typeof ExternalAuthButton> = {
|
||||
title: "pages/CreateWorkspacePage/ExternalAuth",
|
||||
title: "pages/CreateWorkspacePage/ExternalAuthButton",
|
||||
component: ExternalAuthButton,
|
||||
};
|
||||
|
||||
@ -25,6 +25,15 @@ export const Github: Story = {
|
||||
},
|
||||
};
|
||||
|
||||
export const GithubOptional: Story = {
|
||||
args: {
|
||||
auth: {
|
||||
...MockExternalAuth,
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const GithubWithRetry: Story = {
|
||||
args: {
|
||||
auth: MockExternalAuth,
|
||||
@ -48,6 +57,7 @@ export const Gitlab: Story = {
|
||||
display_icon: "/icon/gitlab.svg",
|
||||
display_name: "GitLab",
|
||||
authenticated: false,
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
@ -70,6 +80,7 @@ export const AzureDevOps: Story = {
|
||||
display_icon: "/icon/azure-devops.svg",
|
||||
display_name: "Azure DevOps",
|
||||
authenticated: false,
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
@ -92,6 +103,7 @@ export const Bitbucket: Story = {
|
||||
display_icon: "/icon/bitbucket.svg",
|
||||
display_name: "Bitbucket",
|
||||
authenticated: false,
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -1,17 +1,19 @@
|
||||
import ReplayIcon from "@mui/icons-material/Replay";
|
||||
import Button from "@mui/material/Button";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import { type FC } from "react";
|
||||
import LoadingButton from "@mui/lab/LoadingButton";
|
||||
import { visuallyHidden } from "@mui/utils";
|
||||
import { type FC } from "react";
|
||||
import type { TemplateVersionExternalAuth } from "api/typesGenerated";
|
||||
import { ExternalImage } from "components/ExternalImage/ExternalImage";
|
||||
import { TemplateVersionExternalAuth } from "api/typesGenerated";
|
||||
import { Pill } from "components/Pill/Pill";
|
||||
|
||||
export interface ExternalAuthButtonProps {
|
||||
auth: TemplateVersionExternalAuth;
|
||||
displayRetry: boolean;
|
||||
isLoading: boolean;
|
||||
onStartPolling: () => void;
|
||||
error?: unknown;
|
||||
}
|
||||
|
||||
export const ExternalAuthButton: FC<ExternalAuthButtonProps> = ({
|
||||
@ -19,6 +21,7 @@ export const ExternalAuthButton: FC<ExternalAuthButtonProps> = ({
|
||||
displayRetry,
|
||||
isLoading,
|
||||
onStartPolling,
|
||||
error,
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
@ -48,9 +51,18 @@ export const ExternalAuthButton: FC<ExternalAuthButtonProps> = ({
|
||||
onStartPolling();
|
||||
}}
|
||||
>
|
||||
{auth.authenticated
|
||||
? `Authenticated with ${auth.display_name}`
|
||||
: `Login with ${auth.display_name}`}
|
||||
{auth.authenticated ? (
|
||||
`Authenticated with ${auth.display_name}`
|
||||
) : (
|
||||
<>
|
||||
Login with {auth.display_name}
|
||||
{!auth.optional && (
|
||||
<Pill type={error ? "error" : "info"} css={{ marginLeft: 12 }}>
|
||||
Required
|
||||
</Pill>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</LoadingButton>
|
||||
|
||||
{displayRetry && (
|
||||
|
Reference in New Issue
Block a user