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

Commitae7afd1

Browse files
authored
feat: split cli roles edit command into create and update commands (#17121)
Closes#14239
1 parent53af7e1 commitae7afd1

9 files changed

+361
-77
lines changed

‎cli/organizationroles.go‎

Lines changed: 163 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ func (r *RootCmd) organizationRoles(orgContext *OrganizationContext) *serpent.Co
2626
},
2727
Children: []*serpent.Command{
2828
r.showOrganizationRoles(orgContext),
29-
r.editOrganizationRole(orgContext),
29+
r.updateOrganizationRole(orgContext),
30+
r.createOrganizationRole(orgContext),
3031
},
3132
}
3233
returncmd
@@ -99,7 +100,7 @@ func (r *RootCmd) showOrganizationRoles(orgContext *OrganizationContext) *serpen
99100
returncmd
100101
}
101102

102-
func (r*RootCmd)editOrganizationRole(orgContext*OrganizationContext)*serpent.Command {
103+
func (r*RootCmd)createOrganizationRole(orgContext*OrganizationContext)*serpent.Command {
103104
formatter:=cliui.NewOutputFormatter(
104105
cliui.ChangeFormatterData(
105106
cliui.TableFormat([]roleTableRow{}, []string{"name","display name","site permissions","organization permissions","user permissions"}),
@@ -118,12 +119,12 @@ func (r *RootCmd) editOrganizationRole(orgContext *OrganizationContext) *serpent
118119

119120
client:=new(codersdk.Client)
120121
cmd:=&serpent.Command{
121-
Use:"edit <role_name>",
122-
Short:"Edit an organization custom role",
122+
Use:"create <role_name>",
123+
Short:"Create a new organization custom role",
123124
Long:FormatExamples(
124125
Example{
125126
Description:"Run with an input.json file",
126-
Command:"coder rolesedit --stdin < role.json",
127+
Command:"coderorganization -O <organization_name>rolescreate --stidin < role.json",
127128
},
128129
),
129130
Options: []serpent.Option{
@@ -152,10 +153,13 @@ func (r *RootCmd) editOrganizationRole(orgContext *OrganizationContext) *serpent
152153
returnerr
153154
}
154155

155-
createNewRole:=true
156+
existingRoles,err:=client.ListOrganizationRoles(ctx,org.ID)
157+
iferr!=nil {
158+
returnxerrors.Errorf("listing existing roles: %w",err)
159+
}
160+
156161
varcustomRole codersdk.Role
157162
ifjsonInput {
158-
// JSON Upload mode
159163
bytes,err:=io.ReadAll(inv.Stdin)
160164
iferr!=nil {
161165
returnxerrors.Errorf("reading stdin: %w",err)
@@ -175,29 +179,148 @@ func (r *RootCmd) editOrganizationRole(orgContext *OrganizationContext) *serpent
175179
returnxerrors.Errorf("json input does not appear to be a valid role")
176180
}
177181

178-
existingRoles,err:=client.ListOrganizationRoles(ctx,org.ID)
182+
ifrole:=existingRole(customRole.Name,existingRoles);role!=nil {
183+
returnxerrors.Errorf("The role %s already exists. If you'd like to edit this role use the update command instead",customRole.Name)
184+
}
185+
}else {
186+
iflen(inv.Args)==0 {
187+
returnxerrors.Errorf("missing role name argument, usage:\"coder organizations roles create <role_name>\"")
188+
}
189+
190+
ifrole:=existingRole(inv.Args[0],existingRoles);role!=nil {
191+
returnxerrors.Errorf("The role %s already exists. If you'd like to edit this role use the update command instead",inv.Args[0])
192+
}
193+
194+
interactiveRole,err:=interactiveOrgRoleEdit(inv,org.ID,nil)
195+
iferr!=nil {
196+
returnxerrors.Errorf("editing role: %w",err)
197+
}
198+
199+
customRole=*interactiveRole
200+
}
201+
202+
varupdated codersdk.Role
203+
ifdryRun {
204+
// Do not actually post
205+
updated=customRole
206+
}else {
207+
updated,err=client.CreateOrganizationRole(ctx,customRole)
208+
iferr!=nil {
209+
returnxerrors.Errorf("patch role: %w",err)
210+
}
211+
}
212+
213+
output,err:=formatter.Format(ctx,updated)
214+
iferr!=nil {
215+
returnxerrors.Errorf("formatting: %w",err)
216+
}
217+
218+
_,err=fmt.Fprintln(inv.Stdout,output)
219+
returnerr
220+
},
221+
}
222+
223+
returncmd
224+
}
225+
226+
func (r*RootCmd)updateOrganizationRole(orgContext*OrganizationContext)*serpent.Command {
227+
formatter:=cliui.NewOutputFormatter(
228+
cliui.ChangeFormatterData(
229+
cliui.TableFormat([]roleTableRow{}, []string{"name","display name","site permissions","organization permissions","user permissions"}),
230+
func(dataany) (any,error) {
231+
typed,_:=data.(codersdk.Role)
232+
return []roleTableRow{roleToTableView(typed)},nil
233+
},
234+
),
235+
cliui.JSONFormat(),
236+
)
237+
238+
var (
239+
dryRunbool
240+
jsonInputbool
241+
)
242+
243+
client:=new(codersdk.Client)
244+
cmd:=&serpent.Command{
245+
Use:"update <role_name>",
246+
Short:"Update an organization custom role",
247+
Long:FormatExamples(
248+
Example{
249+
Description:"Run with an input.json file",
250+
Command:"coder roles update --stdin < role.json",
251+
},
252+
),
253+
Options: []serpent.Option{
254+
cliui.SkipPromptOption(),
255+
{
256+
Name:"dry-run",
257+
Description:"Does all the work, but does not submit the final updated role.",
258+
Flag:"dry-run",
259+
Value:serpent.BoolOf(&dryRun),
260+
},
261+
{
262+
Name:"stdin",
263+
Description:"Reads stdin for the json role definition to upload.",
264+
Flag:"stdin",
265+
Value:serpent.BoolOf(&jsonInput),
266+
},
267+
},
268+
Middleware:serpent.Chain(
269+
serpent.RequireRangeArgs(0,1),
270+
r.InitClient(client),
271+
),
272+
Handler:func(inv*serpent.Invocation)error {
273+
ctx:=inv.Context()
274+
org,err:=orgContext.Selected(inv,client)
275+
iferr!=nil {
276+
returnerr
277+
}
278+
279+
existingRoles,err:=client.ListOrganizationRoles(ctx,org.ID)
280+
iferr!=nil {
281+
returnxerrors.Errorf("listing existing roles: %w",err)
282+
}
283+
284+
varcustomRole codersdk.Role
285+
ifjsonInput {
286+
bytes,err:=io.ReadAll(inv.Stdin)
287+
iferr!=nil {
288+
returnxerrors.Errorf("reading stdin: %w",err)
289+
}
290+
291+
err=json.Unmarshal(bytes,&customRole)
179292
iferr!=nil {
180-
returnxerrors.Errorf("listing existing roles: %w",err)
293+
returnxerrors.Errorf("parsing stdin json: %w",err)
181294
}
182-
for_,existingRole:=rangeexistingRoles {
183-
ifstrings.EqualFold(customRole.Name,existingRole.Name) {
184-
// Editing an existing role
185-
createNewRole=false
186-
break
295+
296+
ifcustomRole.Name=="" {
297+
arr:=make([]json.RawMessage,0)
298+
err=json.Unmarshal(bytes,&arr)
299+
iferr==nil&&len(arr)>0 {
300+
returnxerrors.Errorf("only 1 role can be sent at a time")
187301
}
302+
returnxerrors.Errorf("json input does not appear to be a valid role")
303+
}
304+
305+
ifrole:=existingRole(customRole.Name,existingRoles);role==nil {
306+
returnxerrors.Errorf("The role %s does not exist. If you'd like to create this role use the create command instead",customRole.Name)
188307
}
189308
}else {
190309
iflen(inv.Args)==0 {
191310
returnxerrors.Errorf("missing role name argument, usage:\"coder organizations roles edit <role_name>\"")
192311
}
193312

194-
interactiveRole,newRole,err:=interactiveOrgRoleEdit(inv,org.ID,client)
313+
role:=existingRole(inv.Args[0],existingRoles)
314+
ifrole==nil {
315+
returnxerrors.Errorf("The role %s does not exist. If you'd like to create this role use the create command instead",inv.Args[0])
316+
}
317+
318+
interactiveRole,err:=interactiveOrgRoleEdit(inv,org.ID,&role.Role)
195319
iferr!=nil {
196320
returnxerrors.Errorf("editing role: %w",err)
197321
}
198322

199323
customRole=*interactiveRole
200-
createNewRole=newRole
201324

202325
preview:=fmt.Sprintf("permissions: %d site, %d org, %d user",
203326
len(customRole.SitePermissions),len(customRole.OrganizationPermissions),len(customRole.UserPermissions))
@@ -216,12 +339,7 @@ func (r *RootCmd) editOrganizationRole(orgContext *OrganizationContext) *serpent
216339
// Do not actually post
217340
updated=customRole
218341
}else {
219-
switchcreateNewRole {
220-
casetrue:
221-
updated,err=client.CreateOrganizationRole(ctx,customRole)
222-
default:
223-
updated,err=client.UpdateOrganizationRole(ctx,customRole)
224-
}
342+
updated,err=client.UpdateOrganizationRole(ctx,customRole)
225343
iferr!=nil {
226344
returnxerrors.Errorf("patch role: %w",err)
227345
}
@@ -241,50 +359,27 @@ func (r *RootCmd) editOrganizationRole(orgContext *OrganizationContext) *serpent
241359
returncmd
242360
}
243361

244-
funcinteractiveOrgRoleEdit(inv*serpent.Invocation,orgID uuid.UUID,client*codersdk.Client) (*codersdk.Role,bool,error) {
245-
newRole:=false
246-
ctx:=inv.Context()
247-
roles,err:=client.ListOrganizationRoles(ctx,orgID)
248-
iferr!=nil {
249-
returnnil,newRole,xerrors.Errorf("listing roles: %w",err)
250-
}
251-
252-
// Make sure the role actually exists first
253-
varoriginalRole codersdk.AssignableRoles
254-
for_,r:=rangeroles {
255-
ifstrings.EqualFold(inv.Args[0],r.Name) {
256-
originalRole=r
257-
break
258-
}
259-
}
260-
261-
iforiginalRole.Name=="" {
262-
_,err=cliui.Prompt(inv, cliui.PromptOptions{
263-
Text:"No organization role exists with that name, do you want to create one?",
264-
Default:"yes",
265-
IsConfirm:true,
266-
})
267-
iferr!=nil {
268-
returnnil,newRole,xerrors.Errorf("abort: %w",err)
269-
}
270-
271-
originalRole.Role= codersdk.Role{
362+
funcinteractiveOrgRoleEdit(inv*serpent.Invocation,orgID uuid.UUID,updateRole*codersdk.Role) (*codersdk.Role,error) {
363+
varoriginalRole codersdk.Role
364+
ifupdateRole==nil {
365+
originalRole= codersdk.Role{
272366
Name:inv.Args[0],
273367
OrganizationID:orgID.String(),
274368
}
275-
newRole=true
369+
}else {
370+
originalRole=*updateRole
276371
}
277372

278373
// Some checks since interactive mode is limited in what it currently sees
279374
iflen(originalRole.SitePermissions)>0 {
280-
returnnil,newRole,xerrors.Errorf("unable to edit role in interactive mode, it contains site wide permissions")
375+
returnnil,xerrors.Errorf("unable to edit role in interactive mode, it contains site wide permissions")
281376
}
282377

283378
iflen(originalRole.UserPermissions)>0 {
284-
returnnil,newRole,xerrors.Errorf("unable to edit role in interactive mode, it contains user permissions")
379+
returnnil,xerrors.Errorf("unable to edit role in interactive mode, it contains user permissions")
285380
}
286381

287-
role:=&originalRole.Role
382+
role:=&originalRole
288383
allowedResources:= []codersdk.RBACResource{
289384
codersdk.ResourceTemplate,
290385
codersdk.ResourceWorkspace,
@@ -303,13 +398,13 @@ customRoleLoop:
303398
Options:append(permissionPreviews(role,allowedResources),done,abort),
304399
})
305400
iferr!=nil {
306-
returnrole,newRole,xerrors.Errorf("selecting resource: %w",err)
401+
returnrole,xerrors.Errorf("selecting resource: %w",err)
307402
}
308403
switchselected {
309404
casedone:
310405
break customRoleLoop
311406
caseabort:
312-
returnrole,newRole,xerrors.Errorf("edit role %q aborted",role.Name)
407+
returnrole,xerrors.Errorf("edit role %q aborted",role.Name)
313408
default:
314409
strs:=strings.Split(selected,"::")
315410
resource:=strings.TrimSpace(strs[0])
@@ -320,7 +415,7 @@ customRoleLoop:
320415
Defaults:defaultActions(role,resource),
321416
})
322417
iferr!=nil {
323-
returnrole,newRole,xerrors.Errorf("selecting actions for resource %q: %w",resource,err)
418+
returnrole,xerrors.Errorf("selecting actions for resource %q: %w",resource,err)
324419
}
325420
applyOrgResourceActions(role,resource,actions)
326421
// back to resources!
@@ -329,7 +424,7 @@ customRoleLoop:
329424
// This println is required because the prompt ends us on the same line as some text.
330425
_,_=fmt.Println()
331426

332-
returnrole,newRole,nil
427+
returnrole,nil
333428
}
334429

335430
funcapplyOrgResourceActions(role*codersdk.Role,resourcestring,actions []string) {
@@ -405,6 +500,16 @@ func roleToTableView(role codersdk.Role) roleTableRow {
405500
}
406501
}
407502

503+
funcexistingRole(newRoleNamestring,existingRoles []codersdk.AssignableRoles)*codersdk.AssignableRoles {
504+
for_,existingRole:=rangeexistingRoles {
505+
ifstrings.EqualFold(newRoleName,existingRole.Name) {
506+
return&existingRole
507+
}
508+
}
509+
510+
returnnil
511+
}
512+
408513
typeroleTableRowstruct {
409514
Namestring`table:"name,default_sort"`
410515
DisplayNamestring`table:"display name"`

‎cli/testdata/coder_organizations_roles_--help.golden‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ USAGE:
88
Aliases: role
99

1010
SUBCOMMANDS:
11-
edit Edit an organization custom role
12-
show Show role(s)
11+
create Create a new organization custom role
12+
show Show role(s)
13+
update Update an organization custom role
1314

1415
———
1516
Run `coder --help` for a list of global options.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
coder v0.0.0-devel
2+
3+
USAGE:
4+
coder organizations roles create [flags] <role_name>
5+
6+
Create a new organization custom role
7+
8+
- Run with an input.json file:
9+
10+
$ coder organization -O <organization_name> roles create --stidin <
11+
role.json
12+
13+
OPTIONS:
14+
--dry-run bool
15+
Does all the work, but does not submit the final updated role.
16+
17+
--stdin bool
18+
Reads stdin for the json role definition to upload.
19+
20+
-y, --yes bool
21+
Bypass prompts.
22+
23+
———
24+
Run `coder --help` for a list of global options.

‎cli/testdata/coder_organizations_roles_edit_--help.golden‎renamed to ‎cli/testdata/coder_organizations_roles_update_--help.golden‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
coder v0.0.0-devel
22

33
USAGE:
4-
coder organizations rolesedit [flags] <role_name>
4+
coder organizations rolesupdate [flags] <role_name>
55

6-
Edit an organization custom role
6+
Update an organization custom role
77

88
- Run with an input.json file:
99

10-
$ coder rolesedit --stdin < role.json
10+
$ coder rolesupdate --stdin < role.json
1111

1212
OPTIONS:
1313
-c, --column [name|display name|organization id|site permissions|organization permissions|user permissions] (default: name,display name,site permissions,organization permissions,user permissions)

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp