Skip to content

fix: handle workspace errors #3341

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 13 commits into from
Aug 5, 2022
5 changes: 3 additions & 2 deletions site/src/components/Resources/Resources.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import TableContainer from "@material-ui/core/TableContainer"
import TableHead from "@material-ui/core/TableHead"
import TableRow from "@material-ui/core/TableRow"
import useTheme from "@material-ui/styles/useTheme"
import { ErrorSummary } from "components/ErrorSummary/ErrorSummary"
import { FC } from "react"
import { Workspace, WorkspaceResource } from "../../api/typesGenerated"
import { AvatarData } from "../../components/AvatarData/AvatarData"
Expand All @@ -28,7 +29,7 @@ const Language = {

interface ResourcesProps {
resources?: WorkspaceResource[]
getResourcesError?: Error
getResourcesError?: Error | unknown
workspace: Workspace
canUpdateWorkspace: boolean
}
Expand All @@ -45,7 +46,7 @@ export const Resources: FC<ResourcesProps> = ({
return (
<div aria-label={Language.resources} className={styles.wrapper}>
{getResourcesError ? (
{ getResourcesError }
<ErrorSummary error={getResourcesError} />
) : (
<TableContainer className={styles.tableContainer}>
<Table>
Expand Down
38 changes: 37 additions & 1 deletion site/src/components/Workspace/Workspace.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { action } from "@storybook/addon-actions"
import { Story } from "@storybook/react"
import * as Mocks from "../../testHelpers/entities"
import { Workspace, WorkspaceProps } from "./Workspace"
import { Workspace, WorkspaceErrors, WorkspaceProps } from "./Workspace"

export default {
title: "components/Workspace",
Expand Down Expand Up @@ -31,6 +31,7 @@ Started.args = {
resources: [Mocks.MockWorkspaceResource, Mocks.MockWorkspaceResource2],
builds: [Mocks.MockWorkspaceBuild],
canUpdateWorkspace: true,
workspaceErrors: {},
}

export const WithoutUpdateAccess = Template.bind({})
Expand Down Expand Up @@ -71,6 +72,11 @@ Error.args = {
transition: "start",
},
},
workspaceErrors: {
[WorkspaceErrors.BUILD_ERROR]: Mocks.makeMockApiError({
message: "A workspace build is already active.",
}),
},
}

export const Deleting = Template.bind({})
Expand Down Expand Up @@ -102,3 +108,33 @@ Outdated.args = {
...Started.args,
workspace: Mocks.MockOutdatedWorkspace,
}

export const GetBuildsError = Template.bind({})
GetBuildsError.args = {
...Started.args,
workspaceErrors: {
[WorkspaceErrors.GET_BUILDS_ERROR]: Mocks.makeMockApiError({
message: "There is a problem fetching builds.",
}),
},
}

export const GetResourcesError = Template.bind({})
GetResourcesError.args = {
...Started.args,
workspaceErrors: {
[WorkspaceErrors.GET_RESOURCES_ERROR]: Mocks.makeMockApiError({
message: "There is a problem fetching workspace resources.",
}),
},
}

export const CancellationError = Template.bind({})
CancellationError.args = {
...Error.args,
workspaceErrors: {
[WorkspaceErrors.CANCELLATION_ERROR]: Mocks.makeMockApiError({
message: "Job could not be canceled.",
}),
},
}
28 changes: 24 additions & 4 deletions site/src/components/Workspace/Workspace.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { makeStyles } from "@material-ui/core/styles"
import { ErrorSummary } from "components/ErrorSummary/ErrorSummary"
import { WorkspaceStatusBadge } from "components/WorkspaceStatusBadge/WorkspaceStatusBadge"
import { FC } from "react"
import { useNavigate } from "react-router-dom"
Expand All @@ -15,6 +16,13 @@ import { WorkspaceScheduleButton } from "../WorkspaceScheduleButton/WorkspaceSch
import { WorkspaceSection } from "../WorkspaceSection/WorkspaceSection"
import { WorkspaceStats } from "../WorkspaceStats/WorkspaceStats"

export enum WorkspaceErrors {
GET_RESOURCES_ERROR = "getResourcesError",
GET_BUILDS_ERROR = "getBuildsError",
BUILD_ERROR = "buildError",
CANCELLATION_ERROR = "cancellationError",
}

export interface WorkspaceProps {
bannerProps: {
isLoading?: boolean
Expand All @@ -31,9 +39,9 @@ export interface WorkspaceProps {
handleCancel: () => void
workspace: TypesGen.Workspace
resources?: TypesGen.WorkspaceResource[]
getResourcesError?: Error
builds?: TypesGen.WorkspaceBuild[]
canUpdateWorkspace: boolean
workspaceErrors: Partial<Record<WorkspaceErrors, Error | unknown>>
}

/**
Expand All @@ -49,15 +57,23 @@ export const Workspace: FC<WorkspaceProps> = ({
handleCancel,
workspace,
resources,
getResourcesError,
builds,
canUpdateWorkspace,
workspaceErrors,
}) => {
const styles = useStyles()
const navigate = useNavigate()

return (
<Margins>
<Stack spacing={1}>
{workspaceErrors[WorkspaceErrors.BUILD_ERROR] && (
<ErrorSummary error={workspaceErrors[WorkspaceErrors.BUILD_ERROR]} dismissible />
)}
{workspaceErrors[WorkspaceErrors.CANCELLATION_ERROR] && (
<ErrorSummary error={workspaceErrors[WorkspaceErrors.CANCELLATION_ERROR]} dismissible />
)}
</Stack>
<PageHeader
actions={
<Stack direction="row" spacing={1} className={styles.actions}>
Expand Down Expand Up @@ -101,14 +117,18 @@ export const Workspace: FC<WorkspaceProps> = ({
{!!resources && !!resources.length && (
<Resources
resources={resources}
getResourcesError={getResourcesError}
getResourcesError={workspaceErrors[WorkspaceErrors.GET_RESOURCES_ERROR]}
workspace={workspace}
canUpdateWorkspace={canUpdateWorkspace}
/>
)}

<WorkspaceSection title="Logs" contentsProps={{ className: styles.timelineContents }}>
<BuildsTable builds={builds} className={styles.timelineTable} />
{workspaceErrors[WorkspaceErrors.GET_BUILDS_ERROR] ? (
<ErrorSummary error={workspaceErrors[WorkspaceErrors.GET_BUILDS_ERROR]} />
) : (
<BuildsTable builds={builds} className={styles.timelineTable} />
)}
</WorkspaceSection>
</Stack>
</Stack>
Expand Down
39 changes: 34 additions & 5 deletions site/src/pages/WorkspacePage/WorkspacePage.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { makeStyles } from "@material-ui/core/styles"
import { useMachine, useSelector } from "@xstate/react"
import dayjs from "dayjs"
import minMax from "dayjs/plugin/minMax"
Expand All @@ -7,7 +8,7 @@ import { useParams } from "react-router-dom"
import { DeleteWorkspaceDialog } from "../../components/DeleteWorkspaceDialog/DeleteWorkspaceDialog"
import { ErrorSummary } from "../../components/ErrorSummary/ErrorSummary"
import { FullScreenLoader } from "../../components/Loader/FullScreenLoader"
import { Workspace } from "../../components/Workspace/Workspace"
import { Workspace, WorkspaceErrors } from "../../components/Workspace/Workspace"
import { firstOrItem } from "../../util/array"
import { pageTitle } from "../../util/page"
import { getFaviconByStatus } from "../../util/workspace"
Expand All @@ -31,13 +32,25 @@ export const WorkspacePage: React.FC = () => {
userId: me?.id,
},
})
const { workspace, resources, getWorkspaceError, getResourcesError, builds, permissions } =
workspaceState.context
const {
workspace,
getWorkspaceError,
resources,
getResourcesError,
builds,
getBuildsError,
permissions,
checkPermissionsError,
buildError,
cancellationError,
} = workspaceState.context

const canUpdateWorkspace = !!permissions?.updateWorkspace

const [bannerState, bannerSend] = useMachine(workspaceScheduleBannerMachine)

const styles = useStyles()

/**
* Get workspace, template, and organization on mount and whenever workspaceId changes.
* workspaceSend should not change.
Expand All @@ -47,7 +60,12 @@ export const WorkspacePage: React.FC = () => {
}, [username, workspaceName, workspaceSend])

if (workspaceState.matches("error")) {
return <ErrorSummary error={getWorkspaceError} />
return (
<div className={styles.error}>
{getWorkspaceError && <ErrorSummary error={getWorkspaceError} />}
{checkPermissionsError && <ErrorSummary error={checkPermissionsError} />}
</div>
)
} else if (!workspace) {
return <FullScreenLoader />
} else {
Expand Down Expand Up @@ -100,9 +118,14 @@ export const WorkspacePage: React.FC = () => {
handleUpdate={() => workspaceSend("UPDATE")}
handleCancel={() => workspaceSend("CANCEL")}
resources={resources}
getResourcesError={getResourcesError instanceof Error ? getResourcesError : undefined}
builds={builds}
canUpdateWorkspace={canUpdateWorkspace}
workspaceErrors={{
[WorkspaceErrors.GET_RESOURCES_ERROR]: getResourcesError,
[WorkspaceErrors.GET_BUILDS_ERROR]: getBuildsError,
[WorkspaceErrors.BUILD_ERROR]: buildError,
[WorkspaceErrors.CANCELLATION_ERROR]: cancellationError,
}}
/>
<DeleteWorkspaceDialog
isOpen={workspaceState.matches({ ready: { build: "askingDelete" } })}
Expand All @@ -121,3 +144,9 @@ export const boundedDeadline = (newDeadline: dayjs.Dayjs, now: dayjs.Dayjs): day
const maxDeadline = now.add(24, "hours")
return dayjs.min(dayjs.max(minDeadline, newDeadline), maxDeadline)
}

const useStyles = makeStyles((theme) => ({
error: {
margin: theme.spacing(2),
},
}))
Loading