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

Commit8bcf23e

Browse files
authored
fix: handle create workspace errors (#3346)
1 parent83c63d4 commit8bcf23e

File tree

5 files changed

+143
-13
lines changed

5 files changed

+143
-13
lines changed

‎coderd/workspaces.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req
329329
Message:fmt.Sprintf("Workspace %q already exists in the %q template.",createWorkspace.Name,template.Name),
330330
Validations: []codersdk.ValidationError{{
331331
Field:"name",
332-
Detail:"this value is already in use and should be unique",
332+
Detail:"This value is already in use and should be unique.",
333333
}},
334334
})
335335
return

‎site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { useNavigate, useParams } from "react-router-dom"
55
import{useOrganizationId}from"../../hooks/useOrganizationId"
66
import{pageTitle}from"../../util/page"
77
import{createWorkspaceMachine}from"../../xServices/createWorkspace/createWorkspaceXService"
8-
import{CreateWorkspacePageView}from"./CreateWorkspacePageView"
8+
import{CreateWorkspaceErrors,CreateWorkspacePageView}from"./CreateWorkspacePageView"
99

1010
constCreateWorkspacePage:FC=()=>{
1111
constorganizationId=useOrganizationId()
@@ -21,6 +21,15 @@ const CreateWorkspacePage: FC = () => {
2121
},
2222
})
2323

24+
const{
25+
templates,
26+
templateSchema,
27+
selectedTemplate,
28+
getTemplateSchemaError,
29+
getTemplatesError,
30+
createWorkspaceError,
31+
}=createWorkspaceState.context
32+
2433
return(
2534
<>
2635
<Helmet>
@@ -30,10 +39,16 @@ const CreateWorkspacePage: FC = () => {
3039
loadingTemplates={createWorkspaceState.matches("gettingTemplates")}
3140
loadingTemplateSchema={createWorkspaceState.matches("gettingTemplateSchema")}
3241
creatingWorkspace={createWorkspaceState.matches("creatingWorkspace")}
33-
templateName={createWorkspaceState.context.templateName}
34-
templates={createWorkspaceState.context.templates}
35-
selectedTemplate={createWorkspaceState.context.selectedTemplate}
36-
templateSchema={createWorkspaceState.context.templateSchema}
42+
hasTemplateErrors={createWorkspaceState.matches("error")}
43+
templateName={templateName}
44+
templates={templates}
45+
selectedTemplate={selectedTemplate}
46+
templateSchema={templateSchema}
47+
createWorkspaceErrors={{
48+
[CreateWorkspaceErrors.GET_TEMPLATES_ERROR]:getTemplatesError,
49+
[CreateWorkspaceErrors.GET_TEMPLATE_SCHEMA_ERROR]:getTemplateSchemaError,
50+
[CreateWorkspaceErrors.CREATE_WORKSPACE_ERROR]:createWorkspaceError,
51+
}}
3752
onCancel={()=>{
3853
navigate("/templates")
3954
}}

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

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import{ComponentMeta,Story}from"@storybook/react"
22
import{ParameterSchema}from"../../api/typesGenerated"
3-
import{MockTemplate}from"../../testHelpers/entities"
4-
import{CreateWorkspacePageView,CreateWorkspacePageViewProps}from"./CreateWorkspacePageView"
3+
import{makeMockApiError,MockTemplate}from"../../testHelpers/entities"
4+
import{
5+
CreateWorkspaceErrors,
6+
CreateWorkspacePageView,
7+
CreateWorkspacePageViewProps,
8+
}from"./CreateWorkspacePageView"
59

610
constcreateParameterSchema=(partial:Partial<ParameterSchema>):ParameterSchema=>{
711
return{
@@ -40,6 +44,7 @@ NoParameters.args = {
4044
templates:[MockTemplate],
4145
selectedTemplate:MockTemplate,
4246
templateSchema:[],
47+
createWorkspaceErrors:{},
4348
}
4449

4550
exportconstParameters=Template.bind({})
@@ -60,4 +65,48 @@ Parameters.args = {
6065
validation_contains:["Small","Medium","Big"],
6166
}),
6267
],
68+
createWorkspaceErrors:{},
69+
}
70+
71+
exportconstGetTemplatesError=Template.bind({})
72+
GetTemplatesError.args={
73+
...Parameters.args,
74+
createWorkspaceErrors:{
75+
[CreateWorkspaceErrors.GET_TEMPLATES_ERROR]:makeMockApiError({
76+
message:"Failed to fetch templates.",
77+
detail:"You do not have permission to access this resource.",
78+
}),
79+
},
80+
hasTemplateErrors:true,
81+
}
82+
83+
exportconstGetTemplateSchemaError=Template.bind({})
84+
GetTemplateSchemaError.args={
85+
...Parameters.args,
86+
createWorkspaceErrors:{
87+
[CreateWorkspaceErrors.GET_TEMPLATE_SCHEMA_ERROR]:makeMockApiError({
88+
message:'Failed to fetch template schema for "docker-amd64".',
89+
detail:"You do not have permission to access this resource.",
90+
}),
91+
},
92+
hasTemplateErrors:true,
93+
}
94+
95+
exportconstCreateWorkspaceError=Template.bind({})
96+
CreateWorkspaceError.args={
97+
...Parameters.args,
98+
createWorkspaceErrors:{
99+
[CreateWorkspaceErrors.CREATE_WORKSPACE_ERROR]:makeMockApiError({
100+
message:'Workspace "test" already exists in the "docker-amd64" template.',
101+
validations:[
102+
{
103+
field:"name",
104+
detail:"This value is already in use and should be unique.",
105+
},
106+
],
107+
}),
108+
},
109+
initialTouched:{
110+
name:true,
111+
},
63112
}

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

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import{makeStyles}from"@material-ui/core/styles"
22
importTextFieldfrom"@material-ui/core/TextField"
3-
import{FormikContextType,useFormik}from"formik"
3+
import{ErrorSummary}from"components/ErrorSummary/ErrorSummary"
4+
import{FormikContextType,FormikTouched,useFormik}from"formik"
45
import{FC,useState}from"react"
56
import*asYupfrom"yup"
67
import*asTypesGenfrom"../../api/typesGenerated"
@@ -9,23 +10,33 @@ import { FullPageForm } from "../../components/FullPageForm/FullPageForm"
910
import{Loader}from"../../components/Loader/Loader"
1011
import{ParameterInput}from"../../components/ParameterInput/ParameterInput"
1112
import{Stack}from"../../components/Stack/Stack"
12-
import{getFormHelpers,nameValidator,onChangeTrimmed}from"../../util/formUtils"
13+
import{getFormHelpersWithError,nameValidator,onChangeTrimmed}from"../../util/formUtils"
1314

1415
exportconstLanguage={
1516
templateLabel:"Template",
1617
nameLabel:"Name",
1718
}
1819

20+
exportenumCreateWorkspaceErrors{
21+
GET_TEMPLATES_ERROR="getTemplatesError",
22+
GET_TEMPLATE_SCHEMA_ERROR="getTemplateSchemaError",
23+
CREATE_WORKSPACE_ERROR="createWorkspaceError",
24+
}
25+
1926
exportinterfaceCreateWorkspacePageViewProps{
2027
loadingTemplates:boolean
2128
loadingTemplateSchema:boolean
2229
creatingWorkspace:boolean
30+
hasTemplateErrors:boolean
2331
templateName:string
2432
templates?:TypesGen.Template[]
2533
selectedTemplate?:TypesGen.Template
2634
templateSchema?:TypesGen.ParameterSchema[]
35+
createWorkspaceErrors:Partial<Record<CreateWorkspaceErrors,Error|unknown>>
2736
onCancel:()=>void
2837
onSubmit:(req:TypesGen.CreateWorkspaceRequest)=>void
38+
// initialTouched is only used for testing the error state of the form.
39+
initialTouched?:FormikTouched<TypesGen.CreateWorkspaceRequest>
2940
}
3041

3142
exportconstvalidationSchema=Yup.object({
@@ -44,6 +55,7 @@ export const CreateWorkspacePageView: FC<CreateWorkspacePageViewProps> = (props)
4455
},
4556
enableReinitialize:true,
4657
validationSchema,
58+
initialTouched:props.initialTouched,
4759
onSubmit:(request)=>{
4860
if(!props.templateSchema){
4961
thrownewError("No template schema loaded")
@@ -62,18 +74,45 @@ export const CreateWorkspacePageView: FC<CreateWorkspacePageViewProps> = (props)
6274
source_value:value,
6375
})
6476
})
65-
returnprops.onSubmit({
77+
props.onSubmit({
6678
...request,
6779
parameter_values:createRequests,
6880
})
81+
form.setSubmitting(false)
6982
},
7083
})
71-
constgetFieldHelpers=getFormHelpers<TypesGen.CreateWorkspaceRequest>(form)
84+
85+
constgetFieldHelpers=getFormHelpersWithError<TypesGen.CreateWorkspaceRequest>(
86+
form,
87+
props.createWorkspaceErrors[CreateWorkspaceErrors.CREATE_WORKSPACE_ERROR],
88+
)
89+
90+
if(props.hasTemplateErrors){
91+
return(
92+
<Stack>
93+
{props.createWorkspaceErrors[CreateWorkspaceErrors.GET_TEMPLATES_ERROR]&&(
94+
<ErrorSummary
95+
error={props.createWorkspaceErrors[CreateWorkspaceErrors.GET_TEMPLATES_ERROR]}
96+
/>
97+
)}
98+
{props.createWorkspaceErrors[CreateWorkspaceErrors.GET_TEMPLATE_SCHEMA_ERROR]&&(
99+
<ErrorSummary
100+
error={props.createWorkspaceErrors[CreateWorkspaceErrors.GET_TEMPLATE_SCHEMA_ERROR]}
101+
/>
102+
)}
103+
</Stack>
104+
)
105+
}
72106

73107
return(
74108
<FullPageFormtitle="Create workspace"onCancel={props.onCancel}>
75109
<formonSubmit={form.handleSubmit}>
76110
<Stack>
111+
{props.createWorkspaceErrors[CreateWorkspaceErrors.CREATE_WORKSPACE_ERROR]&&(
112+
<ErrorSummary
113+
error={props.createWorkspaceErrors[CreateWorkspaceErrors.CREATE_WORKSPACE_ERROR]}
114+
/>
115+
)}
77116
<TextField
78117
disabled
79118
fullWidth

‎site/src/xServices/createWorkspace/createWorkspaceXService.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ type CreateWorkspaceContext = {
1515
templateSchema?:ParameterSchema[]
1616
createWorkspaceRequest?:CreateWorkspaceRequest
1717
createdWorkspace?:Workspace
18+
createWorkspaceError?:Error|unknown
19+
getTemplatesError?:Error|unknown
20+
getTemplateSchemaError?:Error|unknown
1821
}
1922

2023
typeCreateWorkspaceEvent={
@@ -44,6 +47,7 @@ export const createWorkspaceMachine = createMachine(
4447
tsTypes:{}asimport("./createWorkspaceXService.typegen").Typegen0,
4548
states:{
4649
gettingTemplates:{
50+
entry:"clearGetTemplatesError",
4751
invoke:{
4852
src:"getTemplates",
4953
onDone:[
@@ -57,18 +61,21 @@ export const createWorkspaceMachine = createMachine(
5761
},
5862
],
5963
onError:{
64+
actions:["assignGetTemplatesError"],
6065
target:"error",
6166
},
6267
},
6368
},
6469
gettingTemplateSchema:{
70+
entry:"clearGetTemplateSchemaError",
6571
invoke:{
6672
src:"getTemplateSchema",
6773
onDone:{
6874
actions:["assignTemplateSchema"],
6975
target:"fillingParams",
7076
},
7177
onError:{
78+
actions:["assignGetTemplateSchemaError"],
7279
target:"error",
7380
},
7481
},
@@ -82,14 +89,16 @@ export const createWorkspaceMachine = createMachine(
8289
},
8390
},
8491
creatingWorkspace:{
92+
entry:"clearCreateWorkspaceError",
8593
invoke:{
8694
src:"createWorkspace",
8795
onDone:{
8896
actions:["onCreateWorkspace"],
8997
target:"created",
9098
},
9199
onError:{
92-
target:"error",
100+
actions:["assignCreateWorkspaceError"],
101+
target:"fillingParams",
93102
},
94103
},
95104
},
@@ -142,6 +151,24 @@ export const createWorkspaceMachine = createMachine(
142151
assignCreateWorkspaceRequest:assign({
143152
createWorkspaceRequest:(_,event)=>event.request,
144153
}),
154+
assignCreateWorkspaceError:assign({
155+
createWorkspaceError:(_,event)=>event.data,
156+
}),
157+
clearCreateWorkspaceError:assign({
158+
createWorkspaceError:(_)=>undefined,
159+
}),
160+
assignGetTemplatesError:assign({
161+
getTemplatesError:(_,event)=>event.data,
162+
}),
163+
clearGetTemplatesError:assign({
164+
getTemplatesError:(_)=>undefined,
165+
}),
166+
assignGetTemplateSchemaError:assign({
167+
getTemplateSchemaError:(_,event)=>event.data,
168+
}),
169+
clearGetTemplateSchemaError:assign({
170+
getTemplateSchemaError:(_)=>undefined,
171+
}),
145172
},
146173
},
147174
)

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp