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

Commit6bf7e5a

Browse files
feat(site): support match option for auto create workspace flow (#13836)
1 parent8c33b02 commit6bf7e5a

File tree

9 files changed

+163
-42
lines changed

9 files changed

+163
-42
lines changed

‎site/e2e/parameters.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { RichParameter } from "./provisionerGenerated";
22

33
// Rich parameters
44

5-
constemptyParameter:RichParameter={
5+
exportconstemptyParameter:RichParameter={
66
name:"",
77
description:"",
88
type:"",
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import{test,expect}from"@playwright/test";
2+
import{username}from"../../constants";
3+
import{
4+
createTemplate,
5+
createWorkspace,
6+
echoResponsesWithParameters,
7+
}from"../../helpers";
8+
import{emptyParameter}from"../../parameters";
9+
importtype{RichParameter}from"../../provisionerGenerated";
10+
11+
test("create workspace in auto mode",async({ page})=>{
12+
constrichParameters:RichParameter[]=[
13+
{ ...emptyParameter,name:"repo",type:"string"},
14+
];
15+
consttemplate=awaitcreateTemplate(
16+
page,
17+
echoResponsesWithParameters(richParameters),
18+
);
19+
constname="test-workspace";
20+
awaitpage.goto(
21+
`/templates/${template}/workspace?mode=auto&param.repo=example&name=${name}`,
22+
{
23+
waitUntil:"domcontentloaded",
24+
},
25+
);
26+
awaitexpect(page).toHaveTitle(`${username}/${name} - Coder`);
27+
});
28+
29+
test("use an existing workspace that matches the `match` parameter instead of creating a new one",async({
30+
page,
31+
})=>{
32+
constrichParameters:RichParameter[]=[
33+
{ ...emptyParameter,name:"repo",type:"string"},
34+
];
35+
consttemplate=awaitcreateTemplate(
36+
page,
37+
echoResponsesWithParameters(richParameters),
38+
);
39+
constprevWorkspace=awaitcreateWorkspace(page,template);
40+
awaitpage.goto(
41+
`/templates/${template}/workspace?mode=auto&param.repo=example&name=new-name&match=name:${prevWorkspace}`,
42+
{
43+
waitUntil:"domcontentloaded",
44+
},
45+
);
46+
awaitexpect(page).toHaveTitle(`${username}/${prevWorkspace} - Coder`);
47+
});
48+
49+
test("show error if `match` parameter is invalid",async({ page})=>{
50+
constrichParameters:RichParameter[]=[
51+
{ ...emptyParameter,name:"repo",type:"string"},
52+
];
53+
consttemplate=awaitcreateTemplate(
54+
page,
55+
echoResponsesWithParameters(richParameters),
56+
);
57+
constprevWorkspace=awaitcreateWorkspace(page,template);
58+
awaitpage.goto(
59+
`/templates/${template}/workspace?mode=auto&param.repo=example&name=new-name&match=not-valid-query:${prevWorkspace}`,
60+
{
61+
waitUntil:"domcontentloaded",
62+
},
63+
);
64+
awaitexpect(page.getByText("Invalid match value")).toBeVisible();
65+
});

‎site/e2e/tests/createWorkspace.spec.tsrenamed to‎site/e2e/tests/workspaces/createWorkspace.spec.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import {
77
openTerminalWindow,
88
requireTerraformProvisioner,
99
verifyParameters,
10-
}from"../helpers";
11-
import{beforeCoderTest}from"../hooks";
10+
}from"../../helpers";
11+
import{beforeCoderTest}from"../../hooks";
1212
import{
1313
secondParameter,
1414
fourthParameter,
@@ -18,8 +18,8 @@ import {
1818
seventhParameter,
1919
sixthParameter,
2020
randParamName,
21-
}from"../parameters";
22-
importtype{RichParameter}from"../provisionerGenerated";
21+
}from"../../parameters";
22+
importtype{RichParameter}from"../../provisionerGenerated";
2323

2424
test.beforeEach(({ page})=>beforeCoderTest(page));
2525

‎site/e2e/tests/restartWorkspace.spec.tsrenamed to‎site/e2e/tests/workspaces/restartWorkspace.spec.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ import {
55
createWorkspace,
66
echoResponsesWithParameters,
77
verifyParameters,
8-
}from"../helpers";
9-
import{beforeCoderTest}from"../hooks";
10-
import{firstBuildOption,secondBuildOption}from"../parameters";
11-
importtype{RichParameter}from"../provisionerGenerated";
8+
}from"../../helpers";
9+
import{beforeCoderTest}from"../../hooks";
10+
import{firstBuildOption,secondBuildOption}from"../../parameters";
11+
importtype{RichParameter}from"../../provisionerGenerated";
1212

1313
test.beforeEach(({ page})=>beforeCoderTest(page));
1414

‎site/e2e/tests/startWorkspace.spec.tsrenamed to‎site/e2e/tests/workspaces/startWorkspace.spec.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ import {
66
echoResponsesWithParameters,
77
stopWorkspace,
88
verifyParameters,
9-
}from"../helpers";
10-
import{beforeCoderTest}from"../hooks";
11-
import{firstBuildOption,secondBuildOption}from"../parameters";
12-
importtype{RichParameter}from"../provisionerGenerated";
9+
}from"../../helpers";
10+
import{beforeCoderTest}from"../../hooks";
11+
import{firstBuildOption,secondBuildOption}from"../../parameters";
12+
importtype{RichParameter}from"../../provisionerGenerated";
1313

1414
test.beforeEach(({ page})=>beforeCoderTest(page));
1515

‎site/e2e/tests/updateWorkspace.spec.tsrenamed to‎site/e2e/tests/workspaces/updateWorkspace.spec.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,16 @@ import {
77
updateWorkspace,
88
updateWorkspaceParameters,
99
verifyParameters,
10-
}from"../helpers";
11-
import{beforeCoderTest}from"../hooks";
10+
}from"../../helpers";
11+
import{beforeCoderTest}from"../../hooks";
1212
import{
1313
fifthParameter,
1414
firstParameter,
1515
secondParameter,
1616
sixthParameter,
1717
secondBuildOption,
18-
}from"../parameters";
19-
importtype{RichParameter}from"../provisionerGenerated";
18+
}from"../../parameters";
19+
importtype{RichParameter}from"../../provisionerGenerated";
2020

2121
test.beforeEach(({ page})=>beforeCoderTest(page));
2222

‎site/src/api/errors.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@ export const getValidationErrorMessage = (error: unknown): string => {
111111
};
112112

113113
exportconstgetErrorDetail=(error:unknown):string|undefined=>{
114+
if(errorinstanceofDetailedError){
115+
returnerror.detail;
116+
}
117+
114118
if(errorinstanceofError){
115119
return"Please check the developer console for more details.";
116120
}
@@ -125,3 +129,12 @@ export const getErrorDetail = (error: unknown): string | undefined => {
125129

126130
returnundefined;
127131
};
132+
133+
exportclassDetailedErrorextendsError{
134+
constructor(
135+
message:string,
136+
publicdetail?:string,
137+
){
138+
super(message);
139+
}
140+
}

‎site/src/api/queries/workspaces.ts

Lines changed: 56 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type {
55
UseMutationOptions,
66
}from"react-query";
77
import{typeDeleteWorkspaceOptions,API}from"api/api";
8+
import{DetailedError,isApiValidationError}from"api/errors";
89
importtype{
910
CreateWorkspaceRequest,
1011
ProvisionerLogLevel,
@@ -36,14 +37,6 @@ export const workspaceByOwnerAndName = (owner: string, name: string) => {
3637
};
3738
};
3839

39-
typeAutoCreateWorkspaceOptions={
40-
templateName:string;
41-
versionId?:string;
42-
organizationId:string;
43-
defaultBuildParameters?:WorkspaceBuildParameter[];
44-
defaultName:string;
45-
};
46-
4740
typeCreateWorkspaceMutationVariables=CreateWorkspaceRequest&{
4841
userId:string;
4942
organizationId:string;
@@ -61,19 +54,45 @@ export const createWorkspace = (queryClient: QueryClient) => {
6154
};
6255
};
6356

57+
typeAutoCreateWorkspaceOptions={
58+
organizationId:string;
59+
templateName:string;
60+
workspaceName:string;
61+
/**
62+
* If provided, the auto-create workspace feature will attempt to find a
63+
* matching workspace. If found, it will return the existing workspace instead
64+
* of creating a new one. Its value supports [advanced filtering queries for
65+
* workspaces](https://coder.com/docs/workspaces#workspace-filtering). If
66+
* multiple values are returned, the first one will be returned.
67+
*/
68+
match:string|null;
69+
templateVersionId?:string;
70+
buildParameters?:WorkspaceBuildParameter[];
71+
};
72+
6473
exportconstautoCreateWorkspace=(queryClient:QueryClient)=>{
6574
return{
6675
mutationFn:async({
67-
templateName,
68-
versionId,
6976
organizationId,
70-
defaultBuildParameters,
71-
defaultName,
77+
templateName,
78+
workspaceName,
79+
templateVersionId,
80+
buildParameters,
81+
match,
7282
}:AutoCreateWorkspaceOptions)=>{
83+
if(match){
84+
constmatchWorkspace=awaitfindMatchWorkspace(
85+
`owner:me template:${templateName}${match}`,
86+
);
87+
if(matchWorkspace){
88+
returnmatchWorkspace;
89+
}
90+
}
91+
7392
lettemplateVersionParameters;
7493

75-
if(versionId){
76-
templateVersionParameters={template_version_id:versionId};
94+
if(templateVersionId){
95+
templateVersionParameters={template_version_id:templateVersionId};
7796
}else{
7897
consttemplate=awaitAPI.getTemplateByName(
7998
organizationId,
@@ -84,8 +103,8 @@ export const autoCreateWorkspace = (queryClient: QueryClient) => {
84103

85104
returnAPI.createWorkspace(organizationId,"me",{
86105
...templateVersionParameters,
87-
name:defaultName,
88-
rich_parameter_values:defaultBuildParameters,
106+
name:workspaceName,
107+
rich_parameter_values:buildParameters,
89108
});
90109
},
91110
onSuccess:async()=>{
@@ -94,6 +113,27 @@ export const autoCreateWorkspace = (queryClient: QueryClient) => {
94113
};
95114
};
96115

116+
asyncfunctionfindMatchWorkspace(q:string):Promise<Workspace|undefined>{
117+
try{
118+
const{ workspaces}=awaitAPI.getWorkspaces({ q,limit:1});
119+
constmatchWorkspace=workspaces.at(0);
120+
if(matchWorkspace){
121+
returnmatchWorkspace;
122+
}
123+
}catch(err){
124+
if(isApiValidationError(err)){
125+
constfirstValidationErrorDetail=
126+
err.response.data.validations?.[0].detail;
127+
thrownewDetailedError(
128+
"Invalid match value",
129+
firstValidationErrorDetail,
130+
);
131+
}
132+
133+
throwerr;
134+
}
135+
}
136+
97137
exportfunctionworkspacesKey(config:WorkspacesRequest={}){
98138
const{ q, limit}=config;
99139
return["workspaces",{ q, limit}]asconst;

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

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import type {
1616
UserParameter,
1717
Workspace,
1818
}from"api/typesGenerated";
19-
import{ErrorAlert}from"components/Alert/ErrorAlert";
2019
import{Loader}from"components/Loader/Loader";
2120
import{useAuthenticated}from"contexts/auth/RequireAuth";
2221
import{useEffectEvent}from"hooks/hookPolyfills";
@@ -37,7 +36,7 @@ const CreateWorkspacePage: FC = () => {
3736
const{template:templateName}=useParams()as{template:string};
3837
const{user:me}=useAuthenticated();
3938
constnavigate=useNavigate();
40-
const[searchParams,setSearchParams]=useSearchParams();
39+
const[searchParams]=useSearchParams();
4140
const{ experiments, organizationId}=useDashboard();
4241

4342
constcustomVersionId=searchParams.get("version")??undefined;
@@ -118,15 +117,15 @@ const CreateWorkspacePage: FC = () => {
118117
constnewWorkspace=awaitautoCreateWorkspaceMutation.mutateAsync({
119118
templateName,
120119
organizationId,
121-
defaultBuildParameters:autofillParameters,
122-
defaultName:defaultName??generateWorkspaceName(),
123-
versionId:realizedVersionId,
120+
buildParameters:autofillParameters,
121+
workspaceName:defaultName??generateWorkspaceName(),
122+
templateVersionId:realizedVersionId,
123+
match:searchParams.get("match"),
124124
});
125125

126126
onCreateWorkspace(newWorkspace);
127127
}catch(err){
128-
searchParams.delete("mode");
129-
setSearchParams(searchParams);
128+
setMode("form");
130129
}
131130
});
132131

@@ -175,7 +174,6 @@ const CreateWorkspacePage: FC = () => {
175174
<Helmet>
176175
<title>{pageTitle(title)}</title>
177176
</Helmet>
178-
{loadFormDataError&&<ErrorAlerterror={loadFormDataError}/>}
179177
{isLoadingFormData||isLoadingExternalAuth||autoCreateReady ?(
180178
<Loader/>
181179
) :(
@@ -185,7 +183,12 @@ const CreateWorkspacePage: FC = () => {
185183
disabledParams={disabledParams}
186184
defaultOwner={me}
187185
autofillParameters={autofillParameters}
188-
error={createWorkspaceMutation.error||autoCreateError}
186+
error={
187+
createWorkspaceMutation.error||
188+
autoCreateError||
189+
loadFormDataError||
190+
autoCreateWorkspaceMutation.error
191+
}
189192
resetMutation={createWorkspaceMutation.reset}
190193
template={templateQuery.data!}
191194
versionId={realizedVersionId}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp