Skip to content

Commit 730cc3b

Browse files
committed
Add projects update command
1 parent 2f60cd7 commit 730cc3b

2 files changed

Lines changed: 181 additions & 0 deletions

File tree

cmd/projects.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ type ProjectsService interface {
2222
ProjectListService
2323
New(ctx context.Context, body kernel.ProjectNewParams, opts ...option.RequestOption) (res *kernel.Project, err error)
2424
Get(ctx context.Context, id string, opts ...option.RequestOption) (res *kernel.Project, err error)
25+
Update(ctx context.Context, id string, body kernel.ProjectUpdateParams, opts ...option.RequestOption) (res *kernel.Project, err error)
2526
Delete(ctx context.Context, id string, opts ...option.RequestOption) (err error)
2627
}
2728

@@ -48,6 +49,12 @@ type ProjectsGetInput struct {
4849
Output string
4950
}
5051

52+
type ProjectsUpdateInput struct {
53+
Identifier string
54+
Name string
55+
Output string
56+
}
57+
5158
type ProjectsDeleteInput struct {
5259
Identifier string
5360
}
@@ -155,6 +162,40 @@ func (c ProjectsCmd) Get(ctx context.Context, in ProjectsGetInput) error {
155162
return nil
156163
}
157164

165+
func (c ProjectsCmd) Update(ctx context.Context, in ProjectsUpdateInput) error {
166+
if err := validateJSONOutput(in.Output); err != nil {
167+
return err
168+
}
169+
if in.Name == "" {
170+
return util.RequiredFlag("--name", "<name>")
171+
}
172+
173+
projectID, err := resolveProjectArg(ctx, c.projects, in.Identifier)
174+
if err != nil {
175+
return err
176+
}
177+
178+
project, err := c.projects.Update(ctx, projectID, kernel.ProjectUpdateParams{
179+
UpdateProjectRequest: kernel.UpdateProjectRequestParam{
180+
Name: kernel.String(in.Name),
181+
},
182+
})
183+
if err != nil {
184+
return util.CleanedUpSdkError{Err: err}
185+
}
186+
187+
if in.Output == "json" {
188+
if project == nil {
189+
fmt.Println("null")
190+
return nil
191+
}
192+
return util.PrintPrettyJSON(project)
193+
}
194+
195+
pterm.Success.Printf("Updated project: %s (ID: %s)\n", project.Name, project.ID)
196+
return nil
197+
}
198+
158199
func (c ProjectsCmd) Delete(ctx context.Context, in ProjectsDeleteInput) error {
159200
projectID, err := resolveProjectArg(ctx, c.projects, in.Identifier)
160201
if err != nil {
@@ -297,6 +338,17 @@ func runProjectsGet(cmd *cobra.Command, args []string) error {
297338
return c.Get(cmd.Context(), ProjectsGetInput{Identifier: args[0], Output: output})
298339
}
299340

341+
func runProjectsUpdate(cmd *cobra.Command, args []string) error {
342+
c := getProjectsHandler(cmd)
343+
name, _ := cmd.Flags().GetString("name")
344+
output, _ := cmd.Flags().GetString("output")
345+
return c.Update(cmd.Context(), ProjectsUpdateInput{
346+
Identifier: args[0],
347+
Name: name,
348+
Output: output,
349+
})
350+
}
351+
300352
func runProjectsDelete(cmd *cobra.Command, args []string) error {
301353
c := getProjectsHandler(cmd)
302354
return c.Delete(cmd.Context(), ProjectsDeleteInput{Identifier: args[0]})
@@ -371,6 +423,13 @@ var projectsGetCmd = &cobra.Command{
371423
RunE: runProjectsGet,
372424
}
373425

426+
var projectsUpdateCmd = &cobra.Command{
427+
Use: "update <id-or-name>",
428+
Short: "Update a project",
429+
Args: cobra.ExactArgs(1),
430+
RunE: runProjectsUpdate,
431+
}
432+
374433
var projectsDeleteCmd = &cobra.Command{
375434
Use: "delete <id-or-name>",
376435
Short: "Delete a project",
@@ -419,6 +478,9 @@ var projectsSetLimitsCompatCmd = &cobra.Command{
419478
func init() {
420479
addJSONOutputFlag(projectsListCmd)
421480
addJSONOutputFlag(projectsGetCmd)
481+
addJSONOutputFlag(projectsUpdateCmd)
482+
projectsUpdateCmd.Flags().String("name", "", "New project name (required)")
483+
_ = projectsUpdateCmd.MarkFlagRequired("name")
422484
addJSONOutputFlag(projectsLimitsGetCmd)
423485
addProjectsLimitsSetFlags(projectsLimitsSetCmd)
424486
addJSONOutputFlag(projectsGetLimitsCompatCmd)
@@ -430,6 +492,7 @@ func init() {
430492
projectsCmd.AddCommand(projectsListCmd)
431493
projectsCmd.AddCommand(projectsCreateCmd)
432494
projectsCmd.AddCommand(projectsGetCmd)
495+
projectsCmd.AddCommand(projectsUpdateCmd)
433496
projectsCmd.AddCommand(projectsDeleteCmd)
434497
projectsCmd.AddCommand(projectsLimitsCmd)
435498
projectsCmd.AddCommand(projectsGetLimitsCompatCmd)

cmd/projects_test.go

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ type FakeProjectsService struct {
2121
ListFunc func(ctx context.Context, query kernel.ProjectListParams, opts ...option.RequestOption) (*pagination.OffsetPagination[kernel.Project], error)
2222
NewFunc func(ctx context.Context, body kernel.ProjectNewParams, opts ...option.RequestOption) (*kernel.Project, error)
2323
GetFunc func(ctx context.Context, id string, opts ...option.RequestOption) (*kernel.Project, error)
24+
UpdateFunc func(ctx context.Context, id string, body kernel.ProjectUpdateParams, opts ...option.RequestOption) (*kernel.Project, error)
2425
DeleteFunc func(ctx context.Context, id string, opts ...option.RequestOption) error
2526
}
2627

@@ -45,6 +46,13 @@ func (f *FakeProjectsService) Get(ctx context.Context, id string, opts ...option
4546
return &kernel.Project{ID: id, Name: "default"}, nil
4647
}
4748

49+
func (f *FakeProjectsService) Update(ctx context.Context, id string, body kernel.ProjectUpdateParams, opts ...option.RequestOption) (*kernel.Project, error) {
50+
if f.UpdateFunc != nil {
51+
return f.UpdateFunc(ctx, id, body, opts...)
52+
}
53+
return &kernel.Project{ID: id, Name: body.UpdateProjectRequest.Name.Value}, nil
54+
}
55+
4856
func (f *FakeProjectsService) Delete(ctx context.Context, id string, opts ...option.RequestOption) error {
4957
if f.DeleteFunc != nil {
5058
return f.DeleteFunc(ctx, id, opts...)
@@ -146,6 +154,116 @@ func TestProjectsGet_InvalidOutput(t *testing.T) {
146154
assert.Contains(t, err.Error(), "unsupported --output value")
147155
}
148156

157+
func TestProjectsUpdate_Success(t *testing.T) {
158+
buf := capturePtermOutput(t)
159+
c := ProjectsCmd{
160+
projects: &FakeProjectsService{
161+
UpdateFunc: func(ctx context.Context, id string, body kernel.ProjectUpdateParams, opts ...option.RequestOption) (*kernel.Project, error) {
162+
assert.Equal(t, "a12345678901234567890123", id)
163+
assert.True(t, body.UpdateProjectRequest.Name.Valid())
164+
assert.Equal(t, "Renamed", body.UpdateProjectRequest.Name.Value)
165+
return &kernel.Project{ID: id, Name: body.UpdateProjectRequest.Name.Value}, nil
166+
},
167+
},
168+
}
169+
170+
err := c.Update(context.Background(), ProjectsUpdateInput{
171+
Identifier: "a12345678901234567890123",
172+
Name: "Renamed",
173+
})
174+
175+
require.NoError(t, err)
176+
out := buf.String()
177+
assert.Contains(t, out, "Updated project: Renamed")
178+
assert.Contains(t, out, "a12345678901234567890123")
179+
}
180+
181+
func TestProjectsUpdate_JSONOutput(t *testing.T) {
182+
project := mustProject(t, `{"id":"a12345678901234567890123","name":"Renamed","status":"active","created_at":"2026-05-29T12:00:00Z","updated_at":"2026-05-29T12:30:00Z"}`)
183+
c := ProjectsCmd{
184+
projects: &FakeProjectsService{
185+
UpdateFunc: func(ctx context.Context, id string, body kernel.ProjectUpdateParams, opts ...option.RequestOption) (*kernel.Project, error) {
186+
assert.Equal(t, "a12345678901234567890123", id)
187+
assert.True(t, body.UpdateProjectRequest.Name.Valid())
188+
assert.Equal(t, "Renamed", body.UpdateProjectRequest.Name.Value)
189+
return &project, nil
190+
},
191+
},
192+
}
193+
194+
var err error
195+
out := captureStdout(t, func() {
196+
err = c.Update(context.Background(), ProjectsUpdateInput{
197+
Identifier: "a12345678901234567890123",
198+
Name: "Renamed",
199+
Output: "json",
200+
})
201+
})
202+
203+
require.NoError(t, err)
204+
assert.JSONEq(t, `{"id":"a12345678901234567890123","name":"Renamed","status":"active","created_at":"2026-05-29T12:00:00Z","updated_at":"2026-05-29T12:30:00Z"}`, out)
205+
}
206+
207+
func TestProjectsUpdate_ResolvesName(t *testing.T) {
208+
c := ProjectsCmd{
209+
projects: &FakeProjectsService{
210+
ListFunc: func(ctx context.Context, query kernel.ProjectListParams, opts ...option.RequestOption) (*pagination.OffsetPagination[kernel.Project], error) {
211+
return &pagination.OffsetPagination[kernel.Project]{
212+
Items: []kernel.Project{{ID: "a12345678901234567890123", Name: "Default"}},
213+
}, nil
214+
},
215+
UpdateFunc: func(ctx context.Context, id string, body kernel.ProjectUpdateParams, opts ...option.RequestOption) (*kernel.Project, error) {
216+
assert.Equal(t, "a12345678901234567890123", id)
217+
return &kernel.Project{ID: id, Name: body.UpdateProjectRequest.Name.Value}, nil
218+
},
219+
},
220+
}
221+
222+
err := c.Update(context.Background(), ProjectsUpdateInput{
223+
Identifier: "default",
224+
Name: "Renamed",
225+
})
226+
227+
require.NoError(t, err)
228+
}
229+
230+
func TestProjectsUpdate_RequiresName(t *testing.T) {
231+
c := ProjectsCmd{
232+
projects: &FakeProjectsService{
233+
UpdateFunc: func(ctx context.Context, id string, body kernel.ProjectUpdateParams, opts ...option.RequestOption) (*kernel.Project, error) {
234+
t.Fatal("Update should not be called")
235+
return nil, nil
236+
},
237+
},
238+
}
239+
240+
err := c.Update(context.Background(), ProjectsUpdateInput{Identifier: "a12345678901234567890123"})
241+
242+
require.Error(t, err)
243+
assert.Contains(t, err.Error(), "--name is required")
244+
assert.Contains(t, err.Error(), "add --name <name>")
245+
}
246+
247+
func TestProjectsUpdate_InvalidOutput(t *testing.T) {
248+
c := ProjectsCmd{
249+
projects: &FakeProjectsService{
250+
UpdateFunc: func(ctx context.Context, id string, body kernel.ProjectUpdateParams, opts ...option.RequestOption) (*kernel.Project, error) {
251+
t.Fatal("Update should not be called")
252+
return nil, nil
253+
},
254+
},
255+
}
256+
257+
err := c.Update(context.Background(), ProjectsUpdateInput{
258+
Identifier: "a12345678901234567890123",
259+
Name: "Renamed",
260+
Output: "yaml",
261+
})
262+
263+
require.Error(t, err)
264+
assert.Contains(t, err.Error(), "unsupported --output value")
265+
}
266+
149267
func TestProjectsLimitsGet_DefaultOutput(t *testing.T) {
150268
buf := capturePtermOutput(t)
151269
limits := &kernel.ProjectLimits{

0 commit comments

Comments
 (0)