Skip to content

Commit 086cdcc

Browse files
committed
Fix activity discovery and board accesses pagination
1 parent 93f3041 commit 086cdcc

7 files changed

Lines changed: 93 additions & 7 deletions

File tree

internal/commands/board.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -439,10 +439,11 @@ var boardAccessesCmd = &cobra.Command{
439439
page = &pageVal
440440
}
441441

442-
data, _, err := getSDK().Boards().ListBoardAccesses(cmd.Context(), boardID, page)
442+
data, resp, err := getSDK().Boards().ListBoardAccesses(cmd.Context(), boardID, page)
443443
if err != nil {
444444
return convertSDKError(err)
445445
}
446+
linkNext := parseSDKLinkNext(resp)
446447

447448
summary := "Board accesses"
448449
if boardAccessesPage > 0 {
@@ -454,7 +455,16 @@ var boardAccessesCmd = &cobra.Command{
454455
breadcrumb("cards", fmt.Sprintf("fizzy card list --board %s", boardID), "List cards"),
455456
}
456457

457-
printDetail(normalizeAny(data), summary, breadcrumbs)
458+
hasNext := linkNext != ""
459+
if hasNext {
460+
nextPage := boardAccessesPage + 1
461+
if boardAccessesPage == 0 {
462+
nextPage = 2
463+
}
464+
breadcrumbs = append(breadcrumbs, breadcrumb("next", fmt.Sprintf("fizzy board accesses --board %s --page %d", boardID, nextPage), "Next page"))
465+
}
466+
467+
printDetailPaginated(normalizeAny(data), summary, breadcrumbs, hasNext, linkNext)
458468
return nil
459469
},
460470
}

internal/commands/board_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,45 @@ func TestBoardAccesses(t *testing.T) {
722722
}
723723
})
724724

725+
t.Run("includes next pagination context and breadcrumb", func(t *testing.T) {
726+
mock := NewMockClient()
727+
mock.GetResponse = &client.APIResponse{
728+
StatusCode: 200,
729+
Data: map[string]any{"board_id": "123", "all_access": false, "users": []any{}},
730+
LinkNext: "/boards/123/accesses.json?page=2",
731+
}
732+
733+
result := SetTestModeWithSDK(mock)
734+
SetTestConfig("token", "account", "https://api.example.com")
735+
defer resetTest()
736+
737+
boardAccessesBoard = "123"
738+
err := boardAccessesCmd.RunE(boardAccessesCmd, []string{})
739+
boardAccessesBoard = ""
740+
boardAccessesPage = 0
741+
742+
assertExitCode(t, err, 0)
743+
744+
var nextCmd string
745+
for _, bc := range result.Response.Breadcrumbs {
746+
if bc.Action == "next" {
747+
nextCmd = bc.Cmd
748+
break
749+
}
750+
}
751+
if nextCmd != "fizzy board accesses --board 123 --page 2" {
752+
t.Fatalf("expected next breadcrumb, got %q", nextCmd)
753+
}
754+
755+
pagination, ok := result.Response.Context["pagination"].(map[string]any)
756+
if !ok {
757+
t.Fatalf("expected pagination context, got %#v", result.Response.Context)
758+
}
759+
if pagination["has_next"] != true || pagination["next_url"] != "/boards/123/accesses.json?page=2" {
760+
t.Fatalf("unexpected pagination context: %#v", pagination)
761+
}
762+
})
763+
725764
t.Run("requires board", func(t *testing.T) {
726765
mock := NewMockClient()
727766
SetTestModeWithSDK(mock)

internal/commands/commands.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ var commandCatalogTitles = map[string]string{
3131
}
3232

3333
var commandCatalogGroups = map[string][]string{
34-
"core": {"board", "card", "column", "comment", "search", "step"},
34+
"core": {"activity", "board", "card", "column", "comment", "search", "step"},
3535
"collaboration": {"notification", "pin", "reaction", "tag", "user"},
3636
"admin": {"auth", "account", "identity", "webhook", "upload", "migrate"},
3737
"utilities": {"setup", "signup", "completion", "doctor", "config", "skill", "commands", "version"},

internal/commands/commands_test.go

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ func TestCommandsStyledOutputRendersHumanCatalog(t *testing.T) {
2121
if !strings.Contains(raw, "CORE COMMANDS") {
2222
t.Fatalf("expected styled catalog heading, got:\n%s", raw)
2323
}
24-
if !strings.Contains(raw, "auth") || !strings.Contains(raw, "board") {
24+
if !strings.Contains(raw, "auth") || !strings.Contains(raw, "activity") || !strings.Contains(raw, "board") {
2525
t.Fatalf("expected styled catalog to include commands, got:\n%s", raw)
2626
}
2727
if strings.Contains(raw, "list, show") {
@@ -51,6 +51,25 @@ func TestCommandsFilterRendersMatchingHumanCatalog(t *testing.T) {
5151
}
5252
}
5353

54+
func TestCommandsFilterFindsActivity(t *testing.T) {
55+
mock := NewMockClient()
56+
SetTestModeWithSDK(mock)
57+
SetTestFormat(output.FormatStyled)
58+
defer resetTest()
59+
60+
if err := commandsCmd.RunE(commandsCmd, []string{"activity"}); err != nil {
61+
t.Fatalf("unexpected error: %v", err)
62+
}
63+
64+
raw := TestOutput()
65+
if !strings.Contains(raw, "activity") || !strings.Contains(raw, "list") {
66+
t.Fatalf("expected filtered catalog to include activity list, got:\n%s", raw)
67+
}
68+
if strings.Contains(raw, "No commands match") {
69+
t.Fatalf("expected activity to be discoverable, got:\n%s", raw)
70+
}
71+
}
72+
5473
func TestCommandsJSONOutputReturnsStructuredCatalog(t *testing.T) {
5574
mock := NewMockClient()
5675
result := SetTestModeWithSDK(mock)

internal/commands/help.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@ var rootCommandGroupTitles = map[string]string{
379379
}
380380

381381
var rootCommandGroups = map[string][]string{
382-
"core": {"auth", "board", "card", "search"},
382+
"core": {"auth", "activity", "board", "card", "search"},
383383
"collaboration": {"comment", "notification"},
384384
"getting-started": {"setup", "signup"},
385385
"discover": {"doctor", "config", "commands", "version"},
@@ -389,6 +389,8 @@ var commandExamples = map[string]string{
389389
"fizzy auth": "$ fizzy auth status\n$ fizzy auth login TOKEN --profile acme",
390390
"fizzy auth status": "$ fizzy auth status",
391391
"fizzy auth list": "$ fizzy auth list\n$ fizzy auth switch acme",
392+
"fizzy activity": "$ fizzy activity list\n$ fizzy activity list --board <id>",
393+
"fizzy activity list": "$ fizzy activity list --board <id>\n$ fizzy activity list --creator <user_id>",
392394
"fizzy board": "$ fizzy board list\n$ fizzy board show <id>",
393395
"fizzy board list": "$ fizzy board list\n$ fizzy board list --page 2",
394396
"fizzy board show": "$ fizzy board show <id>",

internal/commands/help_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ func TestRenderRootHelp(t *testing.T) {
1515
renderHelp(rootCmd, &buf)
1616
out := buf.String()
1717

18-
for _, want := range []string{"CORE COMMANDS", "GETTING STARTED", "DISCOVER", "FLAGS", "--profile", "LEARN MORE", "Use `fizzy commands` to see the full command catalog.", "implies --json"} {
18+
for _, want := range []string{"CORE COMMANDS", "activity", "GETTING STARTED", "DISCOVER", "FLAGS", "--profile", "LEARN MORE", "Use `fizzy commands` to see the full command catalog.", "implies --json"} {
1919
if !strings.Contains(out, want) {
2020
t.Fatalf("expected root help to contain %q, got:\n%s", want, out)
2121
}

internal/commands/root.go

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -808,6 +808,11 @@ func printListPaginated(data any, cols render.Columns, hasNext bool, nextURL str
808808

809809
// printDetail renders a single object with format-aware dispatch.
810810
func printDetail(data any, summary string, breadcrumbs []Breadcrumb) {
811+
printDetailPaginated(data, summary, breadcrumbs, false, "")
812+
}
813+
814+
// printDetailPaginated renders a single object and includes pagination context when present.
815+
func printDetailPaginated(data any, summary string, breadcrumbs []Breadcrumb, hasNext bool, nextURL string) {
811816
switch out.EffectiveFormat() {
812817
case output.FormatStyled:
813818
body := render.StyledDetail(toMap(data), summary)
@@ -818,7 +823,18 @@ func printDetail(data any, summary string, breadcrumbs []Breadcrumb) {
818823
writeOutputString(appendHumanSections(body, "", "", breadcrumbs, true))
819824
captureResponse()
820825
default:
821-
printSuccessWithBreadcrumbs(data, summary, breadcrumbs)
826+
opts := []output.ResponseOption{output.WithBreadcrumbs(breadcrumbs...)}
827+
if summary != "" {
828+
opts = append(opts, output.WithSummary(summary))
829+
}
830+
if hasNext || nextURL != "" {
831+
opts = append(opts, output.WithContext("pagination", map[string]any{
832+
"has_next": hasNext,
833+
"next_url": nextURL,
834+
}))
835+
}
836+
recordOutputError(out.OK(data, opts...))
837+
captureResponse()
822838
}
823839
}
824840

0 commit comments

Comments
 (0)