Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit774fc2a

Browse files
presleypkylecarbs
authored andcommitted
feat: UI for canceling workspace builds (#1735)
* Start hooking up cancel* Update xservice* Render cancelChanges behavior of other buttons too* Make outdated workspace story show max buttons* Remove retry code* Remove loading button state* Fix type, extend tests* Update story
1 parent90ea37c commit774fc2a

File tree

13 files changed

+106
-103
lines changed

13 files changed

+106
-103
lines changed

‎site/src/api/api.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
importaxios,{AxiosRequestHeaders}from"axios"
2+
import*asTypesfrom"./types"
23
import{WorkspaceBuildTransition}from"./types"
34
import*asTypesGenfrom"./typesGenerated"
45

@@ -161,6 +162,11 @@ export const startWorkspace = postWorkspaceBuild("start")
161162
exportconststopWorkspace=postWorkspaceBuild("stop")
162163
exportconstdeleteWorkspace=postWorkspaceBuild("delete")
163164

165+
exportconstcancelWorkspaceBuild=async(workspaceBuildId:TypesGen.WorkspaceBuild["id"]):Promise<Types.Message>=>{
166+
constresponse=awaitaxios.patch(`/api/v2/workspacebuilds/${workspaceBuildId}/cancel`)
167+
returnresponse.data
168+
}
169+
164170
exportconstcreateUser=async(user:TypesGen.CreateUserRequest):Promise<TypesGen.User>=>{
165171
constresponse=awaitaxios.post<TypesGen.User>("/api/v2/users",user)
166172
returnresponse.data

‎site/src/api/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,5 @@ export interface ReconnectingPTYRequest {
1212
}
1313

1414
exporttypeWorkspaceBuildTransition="start"|"stop"|"delete"
15+
16+
exporttypeMessage={message:string}

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ Started.args = {
3131
workspace:MockWorkspace,
3232
handleStart:action("start"),
3333
handleStop:action("stop"),
34-
handleRetry:action("retry"),
3534
resources:[MockWorkspaceResource,MockWorkspaceResource2],
3635
builds:[MockWorkspaceBuild],
3736
}

‎site/src/components/Workspace/Workspace.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ import { WorkspaceStats } from "../WorkspaceStats/WorkspaceStats"
1414
exportinterfaceWorkspaceProps{
1515
handleStart:()=>void
1616
handleStop:()=>void
17-
handleRetry:()=>void
1817
handleUpdate:()=>void
18+
handleCancel:()=>void
1919
workspace:TypesGen.Workspace
2020
resources?:TypesGen.WorkspaceResource[]
2121
getResourcesError?:Error
@@ -28,8 +28,8 @@ export interface WorkspaceProps {
2828
exportconstWorkspace:React.FC<WorkspaceProps>=({
2929
handleStart,
3030
handleStop,
31-
handleRetry,
3231
handleUpdate,
32+
handleCancel,
3333
workspace,
3434
resources,
3535
getResourcesError,
@@ -55,8 +55,8 @@ export const Workspace: React.FC<WorkspaceProps> = ({
5555
workspace={workspace}
5656
handleStart={handleStart}
5757
handleStop={handleStop}
58-
handleRetry={handleRetry}
5958
handleUpdate={handleUpdate}
59+
handleCancel={handleCancel}
6060
/>
6161
</div>
6262
</div>

‎site/src/components/WorkspaceActionButton/WorkspaceActionButton.stories.tsx

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,4 @@ export const Example = Template.bind({})
1414
Example.args={
1515
icon:<PlayArrowRoundedIcon/>,
1616
label:"Start workspace",
17-
loadingLabel:"Starting workspace",
18-
isLoading:false,
19-
}
20-
21-
exportconstLoading=Template.bind({})
22-
Loading.args={
23-
icon:<PlayArrowRoundedIcon/>,
24-
label:"Start workspace",
25-
loadingLabel:"Starting workspace",
26-
isLoading:true,
2717
}
Lines changed: 3 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,17 @@
11
importButtonfrom"@material-ui/core/Button"
2-
importCircularProgressfrom"@material-ui/core/CircularProgress"
3-
import{makeStyles}from"@material-ui/core/styles"
42
importReactfrom"react"
53

64
exportinterfaceWorkspaceActionButtonProps{
75
label:string
8-
loadingLabel:string
9-
isLoading:boolean
106
icon:JSX.Element
117
onClick:()=>void
128
className?:string
139
}
1410

15-
exportconstWorkspaceActionButton:React.FC<WorkspaceActionButtonProps>=({
16-
label,
17-
loadingLabel,
18-
isLoading,
19-
icon,
20-
onClick,
21-
className,
22-
})=>{
23-
conststyles=useStyles()
24-
11+
exportconstWorkspaceActionButton:React.FC<WorkspaceActionButtonProps>=({ label, icon, onClick, className})=>{
2512
return(
26-
<Button
27-
className={className}
28-
startIcon={isLoading ?<CircularProgresssize={12}className={styles.spinner}/> :icon}
29-
onClick={onClick}
30-
disabled={isLoading}
31-
>
32-
{isLoading ?loadingLabel :label}
13+
<ButtonclassName={className}startIcon={icon}onClick={onClick}>
14+
{label}
3315
</Button>
3416
)
3517
}
36-
37-
constuseStyles=makeStyles((theme)=>({
38-
spinner:{
39-
color:theme.palette.text.disabled,
40-
marginRight:theme.spacing(1),
41-
},
42-
}))
Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
importButtonfrom"@material-ui/core/Button"
22
importLinkfrom"@material-ui/core/Link"
33
import{makeStyles}from"@material-ui/core/styles"
4+
importCancelIconfrom"@material-ui/icons/Cancel"
45
importCloudDownloadIconfrom"@material-ui/icons/CloudDownload"
56
importPlayArrowRoundedIconfrom"@material-ui/icons/PlayArrowRounded"
6-
importReplayIconfrom"@material-ui/icons/Replay"
77
importStopIconfrom"@material-ui/icons/Stop"
88
importReactfrom"react"
99
import{LinkasRouterLink}from"react-router-dom"
@@ -17,7 +17,7 @@ export const Language = {
1717
stopping:"Stopping workspace",
1818
start:"Start workspace",
1919
starting:"Starting workspace",
20-
retry:"Retry",
20+
cancel:"Cancel action",
2121
update:"Update workspace",
2222
}
2323

@@ -28,20 +28,32 @@ export const Language = {
2828
constcanAcceptJobs=(workspaceStatus:WorkspaceStatus)=>
2929
["started","stopped","deleted","error","canceled"].includes(workspaceStatus)
3030

31+
/**
32+
* Jobs that are in progress (queued or pending) can be canceled.
33+
*@param workspaceStatus WorkspaceStatus
34+
*@returns boolean
35+
*/
36+
constcanCancelJobs=(workspaceStatus:WorkspaceStatus)=>
37+
["starting","stopping","deleting"].includes(workspaceStatus)
38+
39+
constcanStart=(workspaceStatus:WorkspaceStatus)=>["stopped","canceled","error"].includes(workspaceStatus)
40+
41+
constcanStop=(workspaceStatus:WorkspaceStatus)=>["started","canceled","error"].includes(workspaceStatus)
42+
3143
exportinterfaceWorkspaceActionsProps{
3244
workspace:Workspace
3345
handleStart:()=>void
3446
handleStop:()=>void
35-
handleRetry:()=>void
3647
handleUpdate:()=>void
48+
handleCancel:()=>void
3749
}
3850

3951
exportconstWorkspaceActions:React.FC<WorkspaceActionsProps>=({
4052
workspace,
4153
handleStart,
4254
handleStop,
43-
handleRetry,
4455
handleUpdate,
56+
handleCancel,
4557
})=>{
4658
conststyles=useStyles()
4759
constworkspaceStatus=getWorkspaceStatus(workspace.latest_build)
@@ -51,31 +63,30 @@ export const WorkspaceActions: React.FC<WorkspaceActionsProps> = ({
5163
<Linkunderline="none"component={RouterLink}to="edit">
5264
<Buttonvariant="outlined">Settings</Button>
5365
</Link>
54-
{(workspaceStatus==="started"||workspaceStatus==="stopping")&&(
66+
{canStart(workspaceStatus)&&(
67+
<WorkspaceActionButton
68+
className={styles.actionButton}
69+
icon={<PlayArrowRoundedIcon/>}
70+
onClick={handleStart}
71+
label={Language.start}
72+
/>
73+
)}
74+
{canStop(workspaceStatus)&&(
5575
<WorkspaceActionButton
5676
className={styles.actionButton}
5777
icon={<StopIcon/>}
5878
onClick={handleStop}
5979
label={Language.stop}
60-
loadingLabel={Language.stopping}
61-
isLoading={workspaceStatus==="stopping"}
6280
/>
6381
)}
64-
{(workspaceStatus==="stopped"||workspaceStatus==="starting")&&(
82+
{canCancelJobs(workspaceStatus)&&(
6583
<WorkspaceActionButton
6684
className={styles.actionButton}
67-
icon={<PlayArrowRoundedIcon/>}
68-
onClick={handleStart}
69-
label={Language.start}
70-
loadingLabel={Language.starting}
71-
isLoading={workspaceStatus==="starting"}
85+
icon={<CancelIcon/>}
86+
onClick={handleCancel}
87+
label={Language.cancel}
7288
/>
7389
)}
74-
{workspaceStatus==="error"&&(
75-
<ButtonclassName={styles.actionButton}startIcon={<ReplayIcon/>}onClick={handleRetry}>
76-
{Language.retry}
77-
</Button>
78-
)}
7990
{workspace.outdated&&canAcceptJobs(workspaceStatus)&&(
8091
<ButtonclassName={styles.actionButton}startIcon={<CloudDownloadIcon/>}onClick={handleUpdate}>
8192
{Language.update}
@@ -89,6 +100,6 @@ const useStyles = makeStyles((theme) => ({
89100
actionButton:{
90101
// Set fixed width for the action buttons so they will not change the size
91102
// during the transitions
92-
width:theme.spacing(30),
103+
width:theme.spacing(27),
93104
},
94105
}))

‎site/src/pages/WorkspacePage/WorkspacePage.test.tsx

Lines changed: 10 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { Workspace } from "../../api/typesGenerated"
66
import{Language}from"../../components/WorkspaceActions/WorkspaceActions"
77
import{
88
MockBuilds,
9+
MockCanceledWorkspace,
910
MockCancelingWorkspace,
1011
MockDeletedWorkspace,
1112
MockDeletingWorkspace,
@@ -86,45 +87,16 @@ describe("Workspace Page", () => {
8687
.mockImplementation(()=>Promise.resolve(MockWorkspaceBuild))
8788
awaittestButton(Language.start,startWorkspaceMock)
8889
})
89-
it("requests a start job when the user presses Retry after trying to start",async()=>{
90-
// Use a workspace that failed during start
90+
it("requests cancellation when the user presses Cancel",async()=>{
9191
server.use(
9292
rest.get(`/api/v2/workspaces/${MockWorkspace.id}`,(req,res,ctx)=>{
93-
returnres(
94-
ctx.status(200),
95-
ctx.json({
96-
...MockFailedWorkspace,
97-
latest_build:{
98-
...MockFailedWorkspace.latest_build,
99-
transition:"start",
100-
},
101-
}),
102-
)
93+
returnres(ctx.status(200),ctx.json(MockStartingWorkspace))
10394
}),
10495
)
105-
conststartWorkSpaceMock=jest.spyOn(api,"startWorkspace").mockResolvedValueOnce(MockWorkspaceBuild)
106-
awaittestButton(Language.retry,startWorkSpaceMock)
107-
})
108-
it("requests a stop job when the user presses Retry after trying to stop",async()=>{
109-
// Use a workspace that failed during stop
110-
server.use(
111-
rest.get(`/api/v2/workspaces/${MockWorkspace.id}`,(req,res,ctx)=>{
112-
returnres(
113-
ctx.status(200),
114-
ctx.json({
115-
...MockFailedWorkspace,
116-
latest_build:{
117-
...MockFailedWorkspace.latest_build,
118-
transition:"stop",
119-
},
120-
}),
121-
)
122-
}),
123-
)
124-
conststopWorkspaceMock=jest
125-
.spyOn(api,"stopWorkspace")
126-
.mockImplementation(()=>Promise.resolve(MockWorkspaceBuild))
127-
awaittestButton(Language.retry,stopWorkspaceMock)
96+
constcancelWorkspaceMock=jest
97+
.spyOn(api,"cancelWorkspaceBuild")
98+
.mockImplementation(()=>Promise.resolve({message:"job canceled"}))
99+
awaittestButton(Language.cancel,cancelWorkspaceMock)
128100
})
129101
it("requests a template when the user presses Update",async()=>{
130102
constgetTemplateMock=jest.spyOn(api,"getTemplate").mockResolvedValueOnce(MockTemplate)
@@ -153,6 +125,9 @@ describe("Workspace Page", () => {
153125
it("shows the Canceling status when the workspace is canceling",async()=>{
154126
awaittestStatus(MockCancelingWorkspace,DisplayStatusLanguage.canceling)
155127
})
128+
it("shows the Canceled status when the workspace is canceling",async()=>{
129+
awaittestStatus(MockCanceledWorkspace,DisplayStatusLanguage.canceled)
130+
})
156131
it("shows the Deleting status when the workspace is deleting",async()=>{
157132
awaittestStatus(MockDeletingWorkspace,DisplayStatusLanguage.deleting)
158133
})

‎site/src/pages/WorkspacePage/WorkspacePage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ export const WorkspacePage: React.FC = () => {
3636
workspace={workspace}
3737
handleStart={()=>workspaceSend("START")}
3838
handleStop={()=>workspaceSend("STOP")}
39-
handleRetry={()=>workspaceSend("RETRY")}
4039
handleUpdate={()=>workspaceSend("UPDATE")}
40+
handleCancel={()=>workspaceSend("CANCEL")}
4141
resources={resources}
4242
getResourcesError={getResourcesErrorinstanceofError ?getResourcesError :undefined}
4343
builds={builds}

‎site/src/testHelpers/entities.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ export const MockDeletingWorkspace: TypesGen.Workspace = {
182182
}
183183
exportconstMockDeletedWorkspace:TypesGen.Workspace={ ...MockWorkspace,latest_build:MockWorkspaceBuildDelete}
184184

185-
exportconstMockOutdatedWorkspace:TypesGen.Workspace={ ...MockWorkspace,outdated:true}
185+
exportconstMockOutdatedWorkspace:TypesGen.Workspace={ ...MockFailedWorkspace,outdated:true}
186186

187187
exportconstMockWorkspaceAgent:TypesGen.WorkspaceAgent={
188188
architecture:"amd64",
@@ -506,3 +506,7 @@ export const MockWorkspaceBuildLogs: TypesGen.ProvisionerJobLog[] = [
506506
output:"",
507507
},
508508
]
509+
510+
exportconstMockCancellationMessage={
511+
message:"Job successfully canceled",
512+
}

‎site/src/testHelpers/handlers.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,4 +130,7 @@ export const handlers = [
130130
rest.get("/api/v2/workspacebuilds/:workspaceBuildId/logs",(req,res,ctx)=>{
131131
returnres(ctx.status(200),ctx.json(M.MockWorkspaceBuildLogs))
132132
}),
133+
rest.patch("/api/v2/workspacebuilds/:workspaceBuildId/cancel",(req,res,ctx)=>{
134+
returnres(ctx.status(200),ctx.json(M.MockCancellationMessage))
135+
}),
133136
]

‎site/src/util/workspace.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ export const DisplayStatusLanguage = {
5858
stopped:"Stopped",
5959
deleting:"Deleting",
6060
deleted:"Deleted",
61-
canceling:"Canceling",
62-
canceled:"Canceled",
61+
canceling:"Canceling action",
62+
canceled:"Canceled action",
6363
failed:"Failed",
6464
queued:"Queued",
6565
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp