Skip to content

Commit f6929b3

Browse files
authored
Merge pull request #167 from basecamp/bump-fizzy-sdk-0.2.2
Bump fizzy SDK to v0.2.2
2 parents ebf77b2 + b2155eb commit f6929b3

12 files changed

Lines changed: 211 additions & 10 deletions

File tree

SURFACE.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ CMD fizzy help
144144
CMD fizzy identity
145145
CMD fizzy identity help
146146
CMD fizzy identity show
147+
CMD fizzy identity timezone-update
147148
CMD fizzy identity view
148149
CMD fizzy migrate
149150
CMD fizzy migrate board
@@ -1979,6 +1980,21 @@ FLAG fizzy identity show --quiet type=bool
19791980
FLAG fizzy identity show --styled type=bool
19801981
FLAG fizzy identity show --token type=string
19811982
FLAG fizzy identity show --verbose type=bool
1983+
FLAG fizzy identity timezone-update --agent type=bool
1984+
FLAG fizzy identity timezone-update --api-url type=string
1985+
FLAG fizzy identity timezone-update --count type=bool
1986+
FLAG fizzy identity timezone-update --help type=bool
1987+
FLAG fizzy identity timezone-update --ids-only type=bool
1988+
FLAG fizzy identity timezone-update --jq type=string
1989+
FLAG fizzy identity timezone-update --json type=bool
1990+
FLAG fizzy identity timezone-update --limit type=int
1991+
FLAG fizzy identity timezone-update --markdown type=bool
1992+
FLAG fizzy identity timezone-update --profile type=string
1993+
FLAG fizzy identity timezone-update --quiet type=bool
1994+
FLAG fizzy identity timezone-update --styled type=bool
1995+
FLAG fizzy identity timezone-update --timezone type=string
1996+
FLAG fizzy identity timezone-update --token type=string
1997+
FLAG fizzy identity timezone-update --verbose type=bool
19821998
FLAG fizzy identity view --agent type=bool
19831999
FLAG fizzy identity view --api-url type=string
19842000
FLAG fizzy identity view --count type=bool
@@ -3435,6 +3451,7 @@ SUB fizzy help
34353451
SUB fizzy identity
34363452
SUB fizzy identity help
34373453
SUB fizzy identity show
3454+
SUB fizzy identity timezone-update
34383455
SUB fizzy identity view
34393456
SUB fizzy migrate
34403457
SUB fizzy migrate board

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ go 1.26
44

55
require (
66
github.com/basecamp/cli v0.2.1
7-
github.com/basecamp/fizzy-sdk/go v0.2.1
7+
github.com/basecamp/fizzy-sdk/go v0.2.2
88
github.com/charmbracelet/huh v1.0.0
99
github.com/charmbracelet/lipgloss v1.1.0
1010
github.com/charmbracelet/x/term v0.2.2

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ github.com/aymanbagabas/go-udiff v0.3.1 h1:LV+qyBQ2pqe0u42ZsUEtPiCaUoqgA9gYRDs3v
88
github.com/aymanbagabas/go-udiff v0.3.1/go.mod h1:G0fsKmG+P6ylD0r6N/KgQD/nWzgfnl8ZBcNLgcbrw8E=
99
github.com/basecamp/cli v0.2.1 h1:8GyehPVtsTXla0oOPu4QgXRjwwzJ99prlByvyi+0HRQ=
1010
github.com/basecamp/cli v0.2.1/go.mod h1:p8tt/DatJ2LAzWO6N6tNfV8x3gF5T3IxDTo+U8FfWPo=
11-
github.com/basecamp/fizzy-sdk/go v0.2.1 h1:4xRRBQWP0V5rMkppAramn9bSDILl+zY0RD7ax8IDjKQ=
12-
github.com/basecamp/fizzy-sdk/go v0.2.1/go.mod h1:XvOTc+2/6NaECvb2mVhIMq2pNsl9P2wNqwvybIUtQ2g=
11+
github.com/basecamp/fizzy-sdk/go v0.2.2 h1:o2unUWdeJlUlm4L/0Y+/VhLiAjdR9tMNulSyTwFlg+c=
12+
github.com/basecamp/fizzy-sdk/go v0.2.2/go.mod h1:XvOTc+2/6NaECvb2mVhIMq2pNsl9P2wNqwvybIUtQ2g=
1313
github.com/catppuccin/go v0.3.0 h1:d+0/YicIq+hSTo5oPuRi5kOpqkVA5tAsU6dNhvRu+aY=
1414
github.com/catppuccin/go v0.3.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc=
1515
github.com/charmbracelet/bubbles v0.21.1-0.20250623103423-23b8fd6302d7 h1:JFgG/xnwFfbezlUnFMJy0nusZvytYysV4SCS2cYbvws=

internal/commands/board_test.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,7 @@ func TestBoardUpdate(t *testing.T) {
385385
},
386386
}
387387

388-
SetTestModeWithSDK(mock)
388+
result := SetTestModeWithSDK(mock)
389389
SetTestConfig("token", "account", "https://api.example.com")
390390
defer resetTest()
391391

@@ -404,6 +404,13 @@ func TestBoardUpdate(t *testing.T) {
404404
if mock.PatchCalls[0].Path != "/boards/123" {
405405
t.Errorf("expected path '/boards/123', got '%s'", mock.PatchCalls[0].Path)
406406
}
407+
data := responseDataMap(t, result)
408+
if got := data["name"]; got != "Updated Name" {
409+
t.Errorf("expected update response body name, got %#v", got)
410+
}
411+
if got := data["id"]; got != "123" {
412+
t.Errorf("expected update response body id, got %#v", got)
413+
}
407414
})
408415

409416
t.Run("handles API error", func(t *testing.T) {

internal/commands/card_test.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1274,7 +1274,7 @@ func TestCardMove(t *testing.T) {
12741274
},
12751275
}
12761276

1277-
SetTestModeWithSDK(mock)
1277+
result := SetTestModeWithSDK(mock)
12781278
SetTestConfig("token", "account", "https://api.example.com")
12791279
defer resetTest()
12801280

@@ -1294,6 +1294,9 @@ func TestCardMove(t *testing.T) {
12941294
if body["board_id"] != "board-456" {
12951295
t.Errorf("expected board_id 'board-456', got '%v'", body["board_id"])
12961296
}
1297+
if got := responseDataMap(t, result)["title"]; got != "Test Card" {
1298+
t.Errorf("expected move response body title, got %#v", got)
1299+
}
12971300
})
12981301

12991302
t.Run("requires --to flag", func(t *testing.T) {

internal/commands/column_test.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ func TestColumnUpdate(t *testing.T) {
264264
},
265265
}
266266

267-
SetTestModeWithSDK(mock)
267+
result := SetTestModeWithSDK(mock)
268268
SetTestConfig("token", "account", "https://api.example.com")
269269
defer resetTest()
270270

@@ -282,6 +282,13 @@ func TestColumnUpdate(t *testing.T) {
282282
if mock.PatchCalls[0].Path != "/boards/123/columns/col-1" {
283283
t.Errorf("expected path '/boards/123/columns/col-1', got '%s'", mock.PatchCalls[0].Path)
284284
}
285+
data := responseDataMap(t, result)
286+
if got := data["name"]; got != "Updated Column" {
287+
t.Errorf("expected update response body name, got %#v", got)
288+
}
289+
if got := data["id"]; got != "col-1" {
290+
t.Errorf("expected update response body id, got %#v", got)
291+
}
285292
})
286293

287294
t.Run("requires board flag", func(t *testing.T) {

internal/commands/comment_test.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ func TestCommentUpdate(t *testing.T) {
323323
},
324324
}
325325

326-
SetTestModeWithSDK(mock)
326+
result := SetTestModeWithSDK(mock)
327327
SetTestConfig("token", "account", "https://api.example.com")
328328
defer resetTest()
329329

@@ -341,6 +341,13 @@ func TestCommentUpdate(t *testing.T) {
341341
if mock.PatchCalls[0].Path != "/cards/42/comments/comment-1" {
342342
t.Errorf("expected path '/cards/42/comments/comment-1', got '%s'", mock.PatchCalls[0].Path)
343343
}
344+
body, ok := responseDataMap(t, result)["body"].(map[string]any)
345+
if !ok {
346+
t.Fatalf("expected update response body map, got %#v", responseDataMap(t, result)["body"])
347+
}
348+
if got := body["plain_text"]; got != "Updated comment" {
349+
t.Errorf("expected update response body plain_text, got %#v", got)
350+
}
344351
})
345352

346353
t.Run("uploads and appends inline attachments", func(t *testing.T) {

internal/commands/identity.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package commands
22

33
import (
4+
"github.com/basecamp/fizzy-sdk/go/pkg/generated"
45
"github.com/spf13/cobra"
56
)
67

@@ -37,7 +38,47 @@ var identityShowCmd = &cobra.Command{
3738
},
3839
}
3940

41+
var identityTimezoneUpdateTimezone string
42+
43+
var identityTimezoneUpdateCmd = &cobra.Command{
44+
Use: "timezone-update",
45+
Short: "Update your timezone",
46+
Long: "Updates your timezone for the current account.",
47+
RunE: func(cmd *cobra.Command, args []string) error {
48+
if err := requireAuthAndAccount(); err != nil {
49+
return err
50+
}
51+
52+
if identityTimezoneUpdateTimezone == "" {
53+
return newRequiredFlagError("timezone")
54+
}
55+
56+
resp, err := getSDKClient().Identity().UpdateMyTimezone(cmd.Context(), cfg.Account, &generated.UpdateMyTimezoneRequest{
57+
TimezoneName: identityTimezoneUpdateTimezone,
58+
})
59+
if err != nil {
60+
return convertSDKError(err)
61+
}
62+
63+
data := any(map[string]any{"timezone_name": identityTimezoneUpdateTimezone})
64+
if resp != nil && len(resp.Data) > 0 {
65+
if normalized := normalizeAny(resp.Data); normalized != nil {
66+
data = normalized
67+
}
68+
}
69+
70+
breadcrumbs := []Breadcrumb{
71+
breadcrumb("show", "fizzy identity show", "View identity"),
72+
}
73+
74+
printMutation(data, "Timezone updated", breadcrumbs)
75+
return nil
76+
},
77+
}
78+
4079
func init() {
4180
rootCmd.AddCommand(identityCmd)
4281
identityCmd.AddCommand(identityShowCmd)
82+
identityTimezoneUpdateCmd.Flags().StringVar(&identityTimezoneUpdateTimezone, "timezone", "", "Timezone name, for example America/New_York (required)")
83+
identityCmd.AddCommand(identityTimezoneUpdateCmd)
4384
}

internal/commands/identity_test.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,98 @@ import (
77
"github.com/basecamp/fizzy-cli/internal/errors"
88
)
99

10+
func TestIdentityTimezoneUpdate(t *testing.T) {
11+
t.Run("updates timezone", func(t *testing.T) {
12+
mock := NewMockClient()
13+
mock.PatchResponse = &client.APIResponse{
14+
StatusCode: 200,
15+
Data: map[string]any{
16+
"timezone_name": "America/New_York",
17+
"updated_at": "2026-06-03T21:15:00Z",
18+
},
19+
}
20+
21+
result := SetTestModeWithSDK(mock)
22+
SetTestConfig("token", "account", "https://api.example.com")
23+
identityTimezoneUpdateTimezone = "America/New_York"
24+
defer func() {
25+
identityTimezoneUpdateTimezone = ""
26+
resetTest()
27+
}()
28+
29+
err := identityTimezoneUpdateCmd.RunE(identityTimezoneUpdateCmd, []string{})
30+
assertExitCode(t, err, 0)
31+
32+
if err != nil {
33+
t.Fatalf("unexpected error: %v", err)
34+
}
35+
if len(mock.PatchCalls) != 1 {
36+
t.Fatalf("expected 1 patch call, got %d", len(mock.PatchCalls))
37+
}
38+
if mock.PatchCalls[0].Path != "/my/timezone.json" {
39+
t.Errorf("expected path '/my/timezone.json', got %q", mock.PatchCalls[0].Path)
40+
}
41+
body, ok := mock.PatchCalls[0].Body.(map[string]any)
42+
if !ok {
43+
t.Fatalf("expected map body, got %#v", mock.PatchCalls[0].Body)
44+
}
45+
if body["timezone_name"] != "America/New_York" {
46+
t.Errorf("expected timezone_name body, got %#v", body)
47+
}
48+
if result.Response.Summary != "Timezone updated" {
49+
t.Errorf("expected timezone summary, got %q", result.Response.Summary)
50+
}
51+
if got := responseDataMap(t, result)["updated_at"]; got != "2026-06-03T21:15:00Z" {
52+
t.Errorf("expected timezone response body updated_at, got %#v", got)
53+
}
54+
})
55+
56+
t.Run("falls back to requested timezone for empty response", func(t *testing.T) {
57+
mock := NewMockClient()
58+
mock.PatchResponse = &client.APIResponse{StatusCode: 204, Data: nil}
59+
60+
result := SetTestModeWithSDK(mock)
61+
SetTestConfig("token", "account", "https://api.example.com")
62+
identityTimezoneUpdateTimezone = "America/New_York"
63+
defer func() {
64+
identityTimezoneUpdateTimezone = ""
65+
resetTest()
66+
}()
67+
68+
err := identityTimezoneUpdateCmd.RunE(identityTimezoneUpdateCmd, []string{})
69+
assertExitCode(t, err, 0)
70+
71+
if got := responseDataMap(t, result)["timezone_name"]; got != "America/New_York" {
72+
t.Errorf("expected fallback timezone_name, got %#v", got)
73+
}
74+
})
75+
76+
t.Run("requires timezone", func(t *testing.T) {
77+
mock := NewMockClient()
78+
SetTestModeWithSDK(mock)
79+
SetTestConfig("token", "account", "https://api.example.com")
80+
identityTimezoneUpdateTimezone = ""
81+
defer resetTest()
82+
83+
err := identityTimezoneUpdateCmd.RunE(identityTimezoneUpdateCmd, []string{})
84+
assertExitCode(t, err, errors.ExitInvalidArgs)
85+
})
86+
87+
t.Run("requires account", func(t *testing.T) {
88+
mock := NewMockClient()
89+
SetTestModeWithSDK(mock)
90+
SetTestConfig("token", "", "https://api.example.com")
91+
identityTimezoneUpdateTimezone = "America/New_York"
92+
defer func() {
93+
identityTimezoneUpdateTimezone = ""
94+
resetTest()
95+
}()
96+
97+
err := identityTimezoneUpdateCmd.RunE(identityTimezoneUpdateCmd, []string{})
98+
assertExitCode(t, err, errors.ExitInvalidArgs)
99+
})
100+
}
101+
10102
func TestIdentityShow(t *testing.T) {
11103
t.Run("shows identity", func(t *testing.T) {
12104
mock := NewMockClient()
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package commands
2+
3+
import "testing"
4+
5+
func responseDataMap(t *testing.T, result *CommandResult) map[string]any {
6+
t.Helper()
7+
if result == nil || result.Response == nil {
8+
t.Fatal("expected command response")
9+
}
10+
data, ok := result.Response.Data.(map[string]any)
11+
if !ok {
12+
t.Fatalf("expected response data map, got %#v", result.Response.Data)
13+
}
14+
return data
15+
}

0 commit comments

Comments
 (0)