Skip to content

Commit 7ff4c5c

Browse files
committed
Add projects update command
1 parent 2f60cd7 commit 7ff4c5c

2 files changed

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

0 commit comments

Comments
 (0)