Skip to content

Commit e7bc013

Browse files
authored
fix: handle workspace errors (#3341)
1 parent 01fe5e6 commit e7bc013

File tree

5 files changed

+157
-69
lines changed

5 files changed

+157
-69
lines changed

site/src/components/Resources/Resources.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import TableContainer from "@material-ui/core/TableContainer"
66
import TableHead from "@material-ui/core/TableHead"
77
import TableRow from "@material-ui/core/TableRow"
88
import useTheme from "@material-ui/styles/useTheme"
9+
import { ErrorSummary } from "components/ErrorSummary/ErrorSummary"
910
import { FC } from "react"
1011
import { Workspace, WorkspaceResource } from "../../api/typesGenerated"
1112
import { AvatarData } from "../../components/AvatarData/AvatarData"
@@ -28,7 +29,7 @@ const Language = {
2829

2930
interface ResourcesProps {
3031
resources?: WorkspaceResource[]
31-
getResourcesError?: Error
32+
getResourcesError?: Error | unknown
3233
workspace: Workspace
3334
canUpdateWorkspace: boolean
3435
}
@@ -45,7 +46,7 @@ export const Resources: FC<ResourcesProps> = ({
4546
return (
4647
<div aria-label={Language.resources} className={styles.wrapper}>
4748
{getResourcesError ? (
48-
{ getResourcesError }
49+
<ErrorSummary error={getResourcesError} />
4950
) : (
5051
<TableContainer className={styles.tableContainer}>
5152
<Table>

site/src/components/Workspace/Workspace.stories.tsx

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { action } from "@storybook/addon-actions"
22
import { Story } from "@storybook/react"
33
import * as Mocks from "../../testHelpers/entities"
4-
import { Workspace, WorkspaceProps } from "./Workspace"
4+
import { Workspace, WorkspaceErrors, WorkspaceProps } from "./Workspace"
55

66
export default {
77
title: "components/Workspace",
@@ -31,6 +31,7 @@ Started.args = {
3131
resources: [Mocks.MockWorkspaceResource, Mocks.MockWorkspaceResource2],
3232
builds: [Mocks.MockWorkspaceBuild],
3333
canUpdateWorkspace: true,
34+
workspaceErrors: {},
3435
}
3536

3637
export const WithoutUpdateAccess = Template.bind({})
@@ -71,6 +72,11 @@ Error.args = {
7172
transition: "start",
7273
},
7374
},
75+
workspaceErrors: {
76+
[WorkspaceErrors.BUILD_ERROR]: Mocks.makeMockApiError({
77+
message: "A workspace build is already active.",
78+
}),
79+
},
7480
}
7581

7682
export const Deleting = Template.bind({})
@@ -102,3 +108,33 @@ Outdated.args = {
102108
...Started.args,
103109
workspace: Mocks.MockOutdatedWorkspace,
104110
}
111+
112+
export const GetBuildsError = Template.bind({})
113+
GetBuildsError.args = {
114+
...Started.args,
115+
workspaceErrors: {
116+
[WorkspaceErrors.GET_BUILDS_ERROR]: Mocks.makeMockApiError({
117+
message: "There is a problem fetching builds.",
118+
}),
119+
},
120+
}
121+
122+
export const GetResourcesError = Template.bind({})
123+
GetResourcesError.args = {
124+
...Started.args,
125+
workspaceErrors: {
126+
[WorkspaceErrors.GET_RESOURCES_ERROR]: Mocks.makeMockApiError({
127+
message: "There is a problem fetching workspace resources.",
128+
}),
129+
},
130+
}
131+
132+
export const CancellationError = Template.bind({})
133+
CancellationError.args = {
134+
...Error.args,
135+
workspaceErrors: {
136+
[WorkspaceErrors.CANCELLATION_ERROR]: Mocks.makeMockApiError({
137+
message: "Job could not be canceled.",
138+
}),
139+
},
140+
}

site/src/components/Workspace/Workspace.tsx

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { makeStyles } from "@material-ui/core/styles"
2+
import { ErrorSummary } from "components/ErrorSummary/ErrorSummary"
23
import { WorkspaceStatusBadge } from "components/WorkspaceStatusBadge/WorkspaceStatusBadge"
34
import { FC } from "react"
45
import { useNavigate } from "react-router-dom"
@@ -15,6 +16,13 @@ import { WorkspaceScheduleButton } from "../WorkspaceScheduleButton/WorkspaceSch
1516
import { WorkspaceSection } from "../WorkspaceSection/WorkspaceSection"
1617
import { WorkspaceStats } from "../WorkspaceStats/WorkspaceStats"
1718

19+
export enum WorkspaceErrors {
20+
GET_RESOURCES_ERROR = "getResourcesError",
21+
GET_BUILDS_ERROR = "getBuildsError",
22+
BUILD_ERROR = "buildError",
23+
CANCELLATION_ERROR = "cancellationError",
24+
}
25+
1826
export interface WorkspaceProps {
1927
bannerProps: {
2028
isLoading?: boolean
@@ -31,9 +39,9 @@ export interface WorkspaceProps {
3139
handleCancel: () => void
3240
workspace: TypesGen.Workspace
3341
resources?: TypesGen.WorkspaceResource[]
34-
getResourcesError?: Error
3542
builds?: TypesGen.WorkspaceBuild[]
3643
canUpdateWorkspace: boolean
44+
workspaceErrors: Partial<Record<WorkspaceErrors, Error | unknown>>
3745
}
3846

3947
/**
@@ -49,15 +57,23 @@ export const Workspace: FC<WorkspaceProps> = ({
4957
handleCancel,
5058
workspace,
5159
resources,
52-
getResourcesError,
5360
builds,
5461
canUpdateWorkspace,
62+
workspaceErrors,
5563
}) => {
5664
const styles = useStyles()
5765
const navigate = useNavigate()
5866

5967
return (
6068
<Margins>
69+
<Stack spacing={1}>
70+
{workspaceErrors[WorkspaceErrors.BUILD_ERROR] && (
71+
<ErrorSummary error={workspaceErrors[WorkspaceErrors.BUILD_ERROR]} dismissible />
72+
)}
73+
{workspaceErrors[WorkspaceErrors.CANCELLATION_ERROR] && (
74+
<ErrorSummary error={workspaceErrors[WorkspaceErrors.CANCELLATION_ERROR]} dismissible />
75+
)}
76+
</Stack>
6177
<PageHeader
6278
actions={
6379
<Stack direction="row" spacing={1} className={styles.actions}>
@@ -101,14 +117,18 @@ export const Workspace: FC<WorkspaceProps> = ({
101117
{!!resources && !!resources.length && (
102118
<Resources
103119
resources={resources}
104-
getResourcesError={getResourcesError}
120+
getResourcesError={workspaceErrors[WorkspaceErrors.GET_RESOURCES_ERROR]}
105121
workspace={workspace}
106122
canUpdateWorkspace={canUpdateWorkspace}
107123
/>
108124
)}
109125

110126
<WorkspaceSection title="Logs" contentsProps={{ className: styles.timelineContents }}>
111-
<BuildsTable builds={builds} className={styles.timelineTable} />
127+
{workspaceErrors[WorkspaceErrors.GET_BUILDS_ERROR] ? (
128+
<ErrorSummary error={workspaceErrors[WorkspaceErrors.GET_BUILDS_ERROR]} />
129+
) : (
130+
<BuildsTable builds={builds} className={styles.timelineTable} />
131+
)}
112132
</WorkspaceSection>
113133
</Stack>
114134
</Stack>

site/src/pages/WorkspacePage/WorkspacePage.tsx

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { makeStyles } from "@material-ui/core/styles"
12
import { useMachine, useSelector } from "@xstate/react"
23
import dayjs from "dayjs"
34
import minMax from "dayjs/plugin/minMax"
@@ -7,7 +8,7 @@ import { useParams } from "react-router-dom"
78
import { DeleteWorkspaceDialog } from "../../components/DeleteWorkspaceDialog/DeleteWorkspaceDialog"
89
import { ErrorSummary } from "../../components/ErrorSummary/ErrorSummary"
910
import { FullScreenLoader } from "../../components/Loader/FullScreenLoader"
10-
import { Workspace } from "../../components/Workspace/Workspace"
11+
import { Workspace, WorkspaceErrors } from "../../components/Workspace/Workspace"
1112
import { firstOrItem } from "../../util/array"
1213
import { pageTitle } from "../../util/page"
1314
import { getFaviconByStatus } from "../../util/workspace"
@@ -31,13 +32,25 @@ export const WorkspacePage: React.FC = () => {
3132
userId: me?.id,
3233
},
3334
})
34-
const { workspace, resources, getWorkspaceError, getResourcesError, builds, permissions } =
35-
workspaceState.context
35+
const {
36+
workspace,
37+
getWorkspaceError,
38+
resources,
39+
getResourcesError,
40+
builds,
41+
getBuildsError,
42+
permissions,
43+
checkPermissionsError,
44+
buildError,
45+
cancellationError,
46+
} = workspaceState.context
3647

3748
const canUpdateWorkspace = !!permissions?.updateWorkspace
3849

3950
const [bannerState, bannerSend] = useMachine(workspaceScheduleBannerMachine)
4051

52+
const styles = useStyles()
53+
4154
/**
4255
* Get workspace, template, and organization on mount and whenever workspaceId changes.
4356
* workspaceSend should not change.
@@ -47,7 +60,12 @@ export const WorkspacePage: React.FC = () => {
4760
}, [username, workspaceName, workspaceSend])
4861

4962
if (workspaceState.matches("error")) {
50-
return <ErrorSummary error={getWorkspaceError} />
63+
return (
64+
<div className={styles.error}>
65+
{getWorkspaceError && <ErrorSummary error={getWorkspaceError} />}
66+
{checkPermissionsError && <ErrorSummary error={checkPermissionsError} />}
67+
</div>
68+
)
5169
} else if (!workspace) {
5270
return <FullScreenLoader />
5371
} else {
@@ -100,9 +118,14 @@ export const WorkspacePage: React.FC = () => {
100118
handleUpdate={() => workspaceSend("UPDATE")}
101119
handleCancel={() => workspaceSend("CANCEL")}
102120
resources={resources}
103-
getResourcesError={getResourcesError instanceof Error ? getResourcesError : undefined}
104121
builds={builds}
105122
canUpdateWorkspace={canUpdateWorkspace}
123+
workspaceErrors={{
124+
[WorkspaceErrors.GET_RESOURCES_ERROR]: getResourcesError,
125+
[WorkspaceErrors.GET_BUILDS_ERROR]: getBuildsError,
126+
[WorkspaceErrors.BUILD_ERROR]: buildError,
127+
[WorkspaceErrors.CANCELLATION_ERROR]: cancellationError,
128+
}}
106129
/>
107130
<DeleteWorkspaceDialog
108131
isOpen={workspaceState.matches({ ready: { build: "askingDelete" } })}
@@ -121,3 +144,9 @@ export const boundedDeadline = (newDeadline: dayjs.Dayjs, now: dayjs.Dayjs): day
121144
const maxDeadline = now.add(24, "hours")
122145
return dayjs.min(dayjs.max(minDeadline, newDeadline), maxDeadline)
123146
}
147+
148+
const useStyles = makeStyles((theme) => ({
149+
error: {
150+
margin: theme.spacing(2),
151+
},
152+
}))

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy