Skip to content

Commit 3e3e2cc

Browse files
committed
Add activity list command
1 parent f48b9f9 commit 3e3e2cc

7 files changed

Lines changed: 391 additions & 0 deletions

File tree

SURFACE.txt

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
ARG fizzy account help 00 [command]
2+
ARG fizzy activity help 00 [command]
23
ARG fizzy auth help 00 [command]
34
ARG fizzy board help 00 [command]
45
ARG fizzy card attachments download 00 [ATTACHMENT_INDEX]
@@ -38,6 +39,10 @@ CMD fizzy account join-code-update
3839
CMD fizzy account settings-update
3940
CMD fizzy account show
4041
CMD fizzy account view
42+
CMD fizzy activity
43+
CMD fizzy activity help
44+
CMD fizzy activity list
45+
CMD fizzy activity ls
4146
CMD fizzy auth
4247
CMD fizzy auth help
4348
CMD fizzy auth list
@@ -391,6 +396,70 @@ FLAG fizzy account view --quiet type=bool
391396
FLAG fizzy account view --styled type=bool
392397
FLAG fizzy account view --token type=string
393398
FLAG fizzy account view --verbose type=bool
399+
FLAG fizzy activity --agent type=bool
400+
FLAG fizzy activity --api-url type=string
401+
FLAG fizzy activity --count type=bool
402+
FLAG fizzy activity --help type=bool
403+
FLAG fizzy activity --ids-only type=bool
404+
FLAG fizzy activity --jq type=string
405+
FLAG fizzy activity --json type=bool
406+
FLAG fizzy activity --limit type=int
407+
FLAG fizzy activity --markdown type=bool
408+
FLAG fizzy activity --profile type=string
409+
FLAG fizzy activity --quiet type=bool
410+
FLAG fizzy activity --styled type=bool
411+
FLAG fizzy activity --token type=string
412+
FLAG fizzy activity --verbose type=bool
413+
FLAG fizzy activity help --agent type=bool
414+
FLAG fizzy activity help --api-url type=string
415+
FLAG fizzy activity help --count type=bool
416+
FLAG fizzy activity help --help type=bool
417+
FLAG fizzy activity help --ids-only type=bool
418+
FLAG fizzy activity help --jq type=string
419+
FLAG fizzy activity help --json type=bool
420+
FLAG fizzy activity help --limit type=int
421+
FLAG fizzy activity help --markdown type=bool
422+
FLAG fizzy activity help --profile type=string
423+
FLAG fizzy activity help --quiet type=bool
424+
FLAG fizzy activity help --styled type=bool
425+
FLAG fizzy activity help --token type=string
426+
FLAG fizzy activity help --verbose type=bool
427+
FLAG fizzy activity list --agent type=bool
428+
FLAG fizzy activity list --all type=bool
429+
FLAG fizzy activity list --api-url type=string
430+
FLAG fizzy activity list --board type=string
431+
FLAG fizzy activity list --count type=bool
432+
FLAG fizzy activity list --creator type=string
433+
FLAG fizzy activity list --help type=bool
434+
FLAG fizzy activity list --ids-only type=bool
435+
FLAG fizzy activity list --jq type=string
436+
FLAG fizzy activity list --json type=bool
437+
FLAG fizzy activity list --limit type=int
438+
FLAG fizzy activity list --markdown type=bool
439+
FLAG fizzy activity list --page type=int
440+
FLAG fizzy activity list --profile type=string
441+
FLAG fizzy activity list --quiet type=bool
442+
FLAG fizzy activity list --styled type=bool
443+
FLAG fizzy activity list --token type=string
444+
FLAG fizzy activity list --verbose type=bool
445+
FLAG fizzy activity ls --agent type=bool
446+
FLAG fizzy activity ls --all type=bool
447+
FLAG fizzy activity ls --api-url type=string
448+
FLAG fizzy activity ls --board type=string
449+
FLAG fizzy activity ls --count type=bool
450+
FLAG fizzy activity ls --creator type=string
451+
FLAG fizzy activity ls --help type=bool
452+
FLAG fizzy activity ls --ids-only type=bool
453+
FLAG fizzy activity ls --jq type=string
454+
FLAG fizzy activity ls --json type=bool
455+
FLAG fizzy activity ls --limit type=int
456+
FLAG fizzy activity ls --markdown type=bool
457+
FLAG fizzy activity ls --page type=int
458+
FLAG fizzy activity ls --profile type=string
459+
FLAG fizzy activity ls --quiet type=bool
460+
FLAG fizzy activity ls --styled type=bool
461+
FLAG fizzy activity ls --token type=string
462+
FLAG fizzy activity ls --verbose type=bool
394463
FLAG fizzy auth --agent type=bool
395464
FLAG fizzy auth --api-url type=string
396465
FLAG fizzy auth --count type=bool
@@ -3130,6 +3199,10 @@ SUB fizzy account join-code-update
31303199
SUB fizzy account settings-update
31313200
SUB fizzy account show
31323201
SUB fizzy account view
3202+
SUB fizzy activity
3203+
SUB fizzy activity help
3204+
SUB fizzy activity list
3205+
SUB fizzy activity ls
31333206
SUB fizzy auth
31343207
SUB fizzy auth help
31353208
SUB fizzy auth list

e2e/cli_tests/activity_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package clitests
2+
3+
import (
4+
"strconv"
5+
"testing"
6+
"time"
7+
8+
"github.com/basecamp/fizzy-cli/e2e/harness"
9+
)
10+
11+
func TestActivityList(t *testing.T) {
12+
h := newHarness(t)
13+
boardID := createBoard(t, h)
14+
cardNum := createCard(t, h, boardID)
15+
creatorID := currentUserID(t, h)
16+
17+
var result *harness.Result
18+
for attempt := 0; attempt < 10; attempt++ {
19+
r := h.Run("activity", "list", "--board", boardID)
20+
if r.ExitCode == harness.ExitSuccess && len(r.GetDataArray()) > 0 {
21+
result = r
22+
break
23+
}
24+
time.Sleep(200 * time.Millisecond)
25+
}
26+
if result == nil {
27+
t.Fatal("expected at least one activity for throwaway board")
28+
}
29+
30+
assertOK(t, result)
31+
if len(result.GetDataArray()) == 0 {
32+
t.Fatal("expected activity list to return at least one item")
33+
}
34+
35+
foundCard := false
36+
for _, item := range result.GetDataArray() {
37+
m := asMap(item)
38+
if m == nil {
39+
continue
40+
}
41+
if eventable := asMap(m["eventable"]); eventable != nil {
42+
if got := mapValueString(eventable, "number"); got == strconv.Itoa(cardNum) {
43+
foundCard = true
44+
break
45+
}
46+
}
47+
}
48+
if !foundCard {
49+
t.Logf("activity list did not expose created card number %d; continuing because board activity was non-empty", cardNum)
50+
}
51+
52+
creatorResult := h.Run("activity", "list", "--board", boardID, "--creator", creatorID)
53+
assertOK(t, creatorResult)
54+
if creatorResult.GetDataArray() == nil {
55+
t.Fatal("expected activity creator-filter response array")
56+
}
57+
}

e2e/cli_tests/output_contract_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ func TestOutputContractListCommands(t *testing.T) {
174174
name string
175175
args []string
176176
}{
177+
{"activity-list", []string{"activity", "list", "--board", fixture.BoardID}},
177178
{"board-list", []string{"board", "list"}},
178179
{"board-closed", []string{"board", "closed", "--board", fixture.BoardID}},
179180
{"board-postponed", []string{"board", "postponed", "--board", fixture.BoardID}},

internal/commands/activity.go

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package commands
2+
3+
import (
4+
"fmt"
5+
"strconv"
6+
"strings"
7+
8+
"github.com/spf13/cobra"
9+
)
10+
11+
var activityCmd = &cobra.Command{
12+
Use: "activity",
13+
Short: "Manage activities",
14+
Long: "Commands for listing Fizzy activities.",
15+
}
16+
17+
var activityListBoard string
18+
var activityListCreator string
19+
var activityListPage int
20+
var activityListAll bool
21+
22+
var activityListCmd = &cobra.Command{
23+
Use: "list",
24+
Short: "List activities",
25+
Long: "Lists activities with optional board and creator filters.",
26+
RunE: func(cmd *cobra.Command, args []string) error {
27+
if err := requireAuthAndAccount(); err != nil {
28+
return err
29+
}
30+
if err := checkLimitAll(activityListAll); err != nil {
31+
return err
32+
}
33+
34+
ac := getSDK()
35+
path := "/activities.json"
36+
37+
var params []string
38+
if activityListBoard != "" {
39+
params = append(params, "board_ids[]="+activityListBoard)
40+
}
41+
if activityListCreator != "" {
42+
params = append(params, "creator_ids[]="+activityListCreator)
43+
}
44+
if activityListPage > 0 {
45+
params = append(params, "page="+strconv.Itoa(activityListPage))
46+
}
47+
if len(params) > 0 {
48+
path += "?" + strings.Join(params, "&")
49+
}
50+
51+
var items any
52+
var linkNext string
53+
54+
if activityListAll {
55+
pages, err := ac.GetAll(cmd.Context(), path)
56+
if err != nil {
57+
return convertSDKError(err)
58+
}
59+
items = jsonAnySlice(pages)
60+
} else {
61+
data, resp, err := ac.Cards().ListActivities(cmd.Context(), path)
62+
if err != nil {
63+
return convertSDKError(err)
64+
}
65+
items = normalizeAny(data)
66+
linkNext = parseSDKLinkNext(resp)
67+
}
68+
69+
count := dataCount(items)
70+
summary := fmt.Sprintf("%d activities", count)
71+
if activityListAll {
72+
summary += " (all)"
73+
} else if activityListPage > 0 {
74+
summary += fmt.Sprintf(" (page %d)", activityListPage)
75+
}
76+
77+
breadcrumbs := []Breadcrumb{
78+
breadcrumb("cards", "fizzy card show <number>", "View related card"),
79+
breadcrumb("board", "fizzy board show <id>", "View related board"),
80+
}
81+
if activityListBoard != "" {
82+
breadcrumbs = append(breadcrumbs, breadcrumb("board", fmt.Sprintf("fizzy board show %s", activityListBoard), "View board"))
83+
}
84+
85+
hasNext := linkNext != ""
86+
if hasNext {
87+
nextPage := activityListPage + 1
88+
if activityListPage == 0 {
89+
nextPage = 2
90+
}
91+
breadcrumbs = append(breadcrumbs, breadcrumb("next", fmt.Sprintf("fizzy activity list --page %d", nextPage), "Next page"))
92+
}
93+
94+
printListPaginated(items, activityColumns, hasNext, linkNext, activityListAll, summary, breadcrumbs)
95+
return nil
96+
},
97+
}
98+
99+
func init() {
100+
rootCmd.AddCommand(activityCmd)
101+
102+
activityListCmd.Flags().StringVar(&activityListBoard, "board", "", "Filter by board ID")
103+
activityListCmd.Flags().StringVar(&activityListCreator, "creator", "", "Filter by creator user ID")
104+
activityListCmd.Flags().IntVar(&activityListPage, "page", 0, "Page number")
105+
activityListCmd.Flags().BoolVar(&activityListAll, "all", false, "Fetch all pages")
106+
activityCmd.AddCommand(activityListCmd)
107+
}

0 commit comments

Comments
 (0)