fizzy-cli 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +42 -0
- package/cmd/board_create.go +15 -16
- package/cmd/board_create_test.go +0 -17
- package/cmd/card_create.go +24 -25
- package/cmd/card_create_test.go +20 -40
- package/cmd/card_update.go +30 -21
- package/cmd/card_update_test.go +28 -24
- package/cmd/column_create.go +11 -12
- package/cmd/column_create_test.go +3 -21
- package/cmd/comment.go +14 -0
- package/cmd/comment_create.go +51 -0
- package/cmd/comment_create_test.go +129 -0
- package/cmd/comment_delete.go +46 -0
- package/cmd/comment_delete_test.go +92 -0
- package/cmd/comment_list.go +51 -0
- package/cmd/comment_list_test.go +132 -0
- package/cmd/comment_show.go +46 -0
- package/cmd/comment_show_test.go +104 -0
- package/cmd/comment_update.go +51 -0
- package/cmd/comment_update_test.go +130 -0
- package/cmd/reaction.go +13 -0
- package/cmd/reaction_create.go +46 -0
- package/cmd/reaction_create_test.go +113 -0
- package/cmd/reaction_delete.go +46 -0
- package/cmd/reaction_delete_test.go +92 -0
- package/cmd/reaction_list.go +51 -0
- package/cmd/reaction_list_test.go +125 -0
- package/cmd/step.go +14 -0
- package/cmd/step_create.go +53 -0
- package/cmd/step_create_test.go +171 -0
- package/cmd/step_delete.go +46 -0
- package/cmd/step_delete_test.go +92 -0
- package/cmd/step_update.go +66 -0
- package/cmd/step_update_test.go +190 -0
- package/internal/api/boards.go +59 -0
- package/internal/api/cards.go +288 -0
- package/internal/api/client.go +5 -644
- package/internal/api/columns.go +50 -0
- package/internal/api/comments.go +99 -0
- package/internal/api/identity.go +24 -0
- package/internal/api/notifications.go +89 -0
- package/internal/api/reactions.go +61 -0
- package/internal/api/steps.go +93 -0
- package/internal/api/tags.go +24 -0
- package/internal/api/types.go +178 -0
- package/internal/ui/comment_list.go +25 -0
- package/internal/ui/reaction_list.go +14 -0
- package/package.json +1 -1
- package/IMPLEMENTATION_PLAN.md +0 -338
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,47 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.0 - 2026-01-25
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
#### Reaction Management
|
|
8
|
+
|
|
9
|
+
- `fizzy reaction list <card_number> <comment_id>` - List all reactions on a comment
|
|
10
|
+
- `fizzy reaction add <card_number> <comment_id> <emoji>` - Add a reaction to a comment
|
|
11
|
+
- `fizzy reaction remove <card_number> <comment_id> <reaction_id>` - Remove a reaction from a comment
|
|
12
|
+
|
|
13
|
+
#### Step Management
|
|
14
|
+
|
|
15
|
+
- `fizzy step create <card_number> --content <text> [--completed]` - Create a new step on a card
|
|
16
|
+
- `fizzy step update <card_number> <step_id> [--content <text>] [--completed]` - Update a step
|
|
17
|
+
- `fizzy step delete <card_number> <step_id>` - Delete a step from a card
|
|
18
|
+
|
|
19
|
+
#### Comment Management
|
|
20
|
+
|
|
21
|
+
- `fizzy comment delete <card_number> <comment_id>` - Delete a comment from a card
|
|
22
|
+
- `fizzy comment update <card_number> <comment_id> --body <text>` - Update a comment on a card
|
|
23
|
+
|
|
24
|
+
### Improvements
|
|
25
|
+
|
|
26
|
+
- Refactored API client logic into separate files for better organization
|
|
27
|
+
- Enhanced card update command to use `Flags().Changed()` for better flag handling
|
|
28
|
+
- Comprehensive test coverage for all new commands (168 tests total)
|
|
29
|
+
|
|
30
|
+
## 0.5.0 - 2026-01-25
|
|
31
|
+
|
|
32
|
+
### Features
|
|
33
|
+
|
|
34
|
+
#### Comment Management
|
|
35
|
+
|
|
36
|
+
- `fizzy comment list <card_number>` - List all comments on a card
|
|
37
|
+
- `fizzy comment show <card_number> <comment_id>` - Display a specific comment
|
|
38
|
+
- `fizzy comment add <card_number> <body>` - Create a new comment on a card
|
|
39
|
+
|
|
40
|
+
### Fixes
|
|
41
|
+
|
|
42
|
+
- Fixed error handling in notification read-all command
|
|
43
|
+
- Removed global flag usage in some commands
|
|
44
|
+
|
|
3
45
|
## 0.4.0 - 2026-01-19
|
|
4
46
|
|
|
5
47
|
### Features
|
package/cmd/board_create.go
CHANGED
|
@@ -9,13 +9,6 @@ import (
|
|
|
9
9
|
"github.com/spf13/cobra"
|
|
10
10
|
)
|
|
11
11
|
|
|
12
|
-
var (
|
|
13
|
-
boardName string
|
|
14
|
-
boardAllAccess bool
|
|
15
|
-
boardAutoPostponePeriod int
|
|
16
|
-
boardPublicDescription string
|
|
17
|
-
)
|
|
18
|
-
|
|
19
12
|
var boardCreateCmd = &cobra.Command{
|
|
20
13
|
Use: "create",
|
|
21
14
|
Short: "Create a new board",
|
|
@@ -33,11 +26,17 @@ func handleCreateBoard(cmd *cobra.Command) error {
|
|
|
33
26
|
return fmt.Errorf("API client not available")
|
|
34
27
|
}
|
|
35
28
|
|
|
29
|
+
// Read flag values directly from command
|
|
30
|
+
name, _ := cmd.Flags().GetString("name")
|
|
31
|
+
allAccess, _ := cmd.Flags().GetBool("all-access")
|
|
32
|
+
autoPostponePeriod, _ := cmd.Flags().GetInt("auto-postpone-period")
|
|
33
|
+
publicDescription, _ := cmd.Flags().GetString("description")
|
|
34
|
+
|
|
36
35
|
payload := api.CreateBoardPayload{
|
|
37
|
-
Name:
|
|
38
|
-
AllAccess:
|
|
39
|
-
AutoPostponePeriod:
|
|
40
|
-
PublicDescription:
|
|
36
|
+
Name: name,
|
|
37
|
+
AllAccess: allAccess,
|
|
38
|
+
AutoPostponePeriod: autoPostponePeriod,
|
|
39
|
+
PublicDescription: publicDescription,
|
|
41
40
|
}
|
|
42
41
|
|
|
43
42
|
_, err := a.Client.PostBoards(context.Background(), payload)
|
|
@@ -45,16 +44,16 @@ func handleCreateBoard(cmd *cobra.Command) error {
|
|
|
45
44
|
return fmt.Errorf("creating board: %w", err)
|
|
46
45
|
}
|
|
47
46
|
|
|
48
|
-
fmt.Printf("✓ Board '%s' created successfully\n",
|
|
47
|
+
fmt.Printf("✓ Board '%s' created successfully\n", name)
|
|
49
48
|
return nil
|
|
50
49
|
}
|
|
51
50
|
|
|
52
51
|
func init() {
|
|
53
|
-
boardCreateCmd.Flags().
|
|
52
|
+
boardCreateCmd.Flags().StringP("name", "n", "", "Board name (required)")
|
|
54
53
|
boardCreateCmd.MarkFlagRequired("name")
|
|
55
|
-
boardCreateCmd.Flags().
|
|
56
|
-
boardCreateCmd.Flags().
|
|
57
|
-
boardCreateCmd.Flags().
|
|
54
|
+
boardCreateCmd.Flags().Bool("all-access", false, "Allow all access to the board")
|
|
55
|
+
boardCreateCmd.Flags().Int("auto-postpone-period", 0, "Auto postpone period in days")
|
|
56
|
+
boardCreateCmd.Flags().String("description", "", "Public description of the board")
|
|
58
57
|
|
|
59
58
|
boardCmd.AddCommand(boardCreateCmd)
|
|
60
59
|
}
|
package/cmd/board_create_test.go
CHANGED
|
@@ -54,11 +54,6 @@ func TestBoardCreateCommandSuccess(t *testing.T) {
|
|
|
54
54
|
cmd.SetContext(testApp.ToContext(context.Background()))
|
|
55
55
|
cmd.ParseFlags([]string{"--name", "Test Board"})
|
|
56
56
|
|
|
57
|
-
boardName = "Test Board"
|
|
58
|
-
boardAllAccess = false
|
|
59
|
-
boardAutoPostponePeriod = 0
|
|
60
|
-
boardPublicDescription = ""
|
|
61
|
-
|
|
62
57
|
if err := handleCreateBoard(cmd); err != nil {
|
|
63
58
|
t.Fatalf("handleCreateBoard failed: %v", err)
|
|
64
59
|
}
|
|
@@ -101,11 +96,6 @@ func TestBoardCreateCommandWithAllFlags(t *testing.T) {
|
|
|
101
96
|
"--description", "Team project",
|
|
102
97
|
})
|
|
103
98
|
|
|
104
|
-
boardName = "Test Board"
|
|
105
|
-
boardAllAccess = true
|
|
106
|
-
boardAutoPostponePeriod = 7
|
|
107
|
-
boardPublicDescription = "Team project"
|
|
108
|
-
|
|
109
99
|
if err := handleCreateBoard(cmd); err != nil {
|
|
110
100
|
t.Fatalf("handleCreateBoard failed: %v", err)
|
|
111
101
|
}
|
|
@@ -125,11 +115,6 @@ func TestBoardCreateCommandAPIError(t *testing.T) {
|
|
|
125
115
|
cmd.SetContext(testApp.ToContext(context.Background()))
|
|
126
116
|
cmd.ParseFlags([]string{"--name", "Test Board"})
|
|
127
117
|
|
|
128
|
-
boardName = "Test Board"
|
|
129
|
-
boardAllAccess = false
|
|
130
|
-
boardAutoPostponePeriod = 0
|
|
131
|
-
boardPublicDescription = ""
|
|
132
|
-
|
|
133
118
|
err := handleCreateBoard(cmd)
|
|
134
119
|
if err == nil {
|
|
135
120
|
t.Errorf("expected error for API failure")
|
|
@@ -146,8 +131,6 @@ func TestBoardCreateCommandNoClient(t *testing.T) {
|
|
|
146
131
|
cmd.SetContext(testApp.ToContext(context.Background()))
|
|
147
132
|
cmd.ParseFlags([]string{"--name", "Test Board"})
|
|
148
133
|
|
|
149
|
-
boardName = "Test Board"
|
|
150
|
-
|
|
151
134
|
err := handleCreateBoard(cmd)
|
|
152
135
|
if err == nil {
|
|
153
136
|
t.Errorf("expected error when client not available")
|
package/cmd/card_create.go
CHANGED
|
@@ -9,16 +9,6 @@ import (
|
|
|
9
9
|
"github.com/spf13/cobra"
|
|
10
10
|
)
|
|
11
11
|
|
|
12
|
-
var (
|
|
13
|
-
cardTitle string
|
|
14
|
-
cardDescription string
|
|
15
|
-
cardStatus string
|
|
16
|
-
cardImageURL string
|
|
17
|
-
cardTagIDs []string
|
|
18
|
-
cardCreatedAt string
|
|
19
|
-
cardLastActiveAt string
|
|
20
|
-
)
|
|
21
|
-
|
|
22
12
|
var cardCreateCmd = &cobra.Command{
|
|
23
13
|
Use: "create",
|
|
24
14
|
Short: "Create a new card",
|
|
@@ -40,14 +30,23 @@ func handleCreateCard(cmd *cobra.Command) error {
|
|
|
40
30
|
return fmt.Errorf("no board selected")
|
|
41
31
|
}
|
|
42
32
|
|
|
33
|
+
// Read flag values directly from command
|
|
34
|
+
title, _ := cmd.Flags().GetString("title")
|
|
35
|
+
description, _ := cmd.Flags().GetString("description")
|
|
36
|
+
status, _ := cmd.Flags().GetString("status")
|
|
37
|
+
imageURL, _ := cmd.Flags().GetString("image-url")
|
|
38
|
+
tagIDs, _ := cmd.Flags().GetStringSlice("tag-id")
|
|
39
|
+
createdAt, _ := cmd.Flags().GetString("created-at")
|
|
40
|
+
lastActiveAt, _ := cmd.Flags().GetString("last-active-at")
|
|
41
|
+
|
|
43
42
|
payload := api.CreateCardPayload{
|
|
44
|
-
Title:
|
|
45
|
-
Description:
|
|
46
|
-
Status:
|
|
47
|
-
ImageURL:
|
|
48
|
-
TagIDS:
|
|
49
|
-
CreatedAt:
|
|
50
|
-
LastActiveAt:
|
|
43
|
+
Title: title,
|
|
44
|
+
Description: description,
|
|
45
|
+
Status: status,
|
|
46
|
+
ImageURL: imageURL,
|
|
47
|
+
TagIDS: tagIDs,
|
|
48
|
+
CreatedAt: createdAt,
|
|
49
|
+
LastActiveAt: lastActiveAt,
|
|
51
50
|
}
|
|
52
51
|
|
|
53
52
|
_, err := a.Client.PostCards(context.Background(), payload)
|
|
@@ -55,19 +54,19 @@ func handleCreateCard(cmd *cobra.Command) error {
|
|
|
55
54
|
return fmt.Errorf("creating card: %w", err)
|
|
56
55
|
}
|
|
57
56
|
|
|
58
|
-
fmt.Printf("✓ Card '%s' created successfully\n",
|
|
57
|
+
fmt.Printf("✓ Card '%s' created successfully\n", title)
|
|
59
58
|
return nil
|
|
60
59
|
}
|
|
61
60
|
|
|
62
61
|
func init() {
|
|
63
|
-
cardCreateCmd.Flags().
|
|
62
|
+
cardCreateCmd.Flags().StringP("title", "t", "", "Card title (required)")
|
|
64
63
|
cardCreateCmd.MarkFlagRequired("title")
|
|
65
|
-
cardCreateCmd.Flags().
|
|
66
|
-
cardCreateCmd.Flags().
|
|
67
|
-
cardCreateCmd.Flags().
|
|
68
|
-
cardCreateCmd.Flags().
|
|
69
|
-
cardCreateCmd.Flags().
|
|
70
|
-
cardCreateCmd.Flags().
|
|
64
|
+
cardCreateCmd.Flags().StringP("description", "d", "", "Card description")
|
|
65
|
+
cardCreateCmd.Flags().String("status", "", "Card status")
|
|
66
|
+
cardCreateCmd.Flags().String("image-url", "", "Card image URL")
|
|
67
|
+
cardCreateCmd.Flags().StringSlice("tag-id", []string{}, "Tag ID (can be used multiple times)")
|
|
68
|
+
cardCreateCmd.Flags().String("created-at", "", "Creation timestamp")
|
|
69
|
+
cardCreateCmd.Flags().String("last-active-at", "", "Last active timestamp")
|
|
71
70
|
|
|
72
71
|
cardCmd.AddCommand(cardCreateCmd)
|
|
73
72
|
}
|
package/cmd/card_create_test.go
CHANGED
|
@@ -58,14 +58,10 @@ func TestCardCreateCommandSuccess(t *testing.T) {
|
|
|
58
58
|
|
|
59
59
|
cmd := cardCreateCmd
|
|
60
60
|
cmd.SetContext(testApp.ToContext(context.Background()))
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
cardImageURL = ""
|
|
66
|
-
cardTagIDs = []string{}
|
|
67
|
-
cardCreatedAt = ""
|
|
68
|
-
cardLastActiveAt = ""
|
|
61
|
+
cmd.ParseFlags([]string{
|
|
62
|
+
"--title", "Implement feature",
|
|
63
|
+
"--description", "A detailed description",
|
|
64
|
+
})
|
|
69
65
|
|
|
70
66
|
if err := handleCreateCard(cmd); err != nil {
|
|
71
67
|
t.Fatalf("handleCreateCard failed: %v", err)
|
|
@@ -108,14 +104,13 @@ func TestCardCreateCommandWithAllFields(t *testing.T) {
|
|
|
108
104
|
|
|
109
105
|
cmd := cardCreateCmd
|
|
110
106
|
cmd.SetContext(testApp.ToContext(context.Background()))
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
cardLastActiveAt = ""
|
|
107
|
+
cmd.ParseFlags([]string{
|
|
108
|
+
"--title", "Fix bug",
|
|
109
|
+
"--status", "in_progress",
|
|
110
|
+
"--image-url", "https://example.com/image.jpg",
|
|
111
|
+
"--tag-id", "tag-1",
|
|
112
|
+
"--tag-id", "tag-2",
|
|
113
|
+
})
|
|
119
114
|
|
|
120
115
|
if err := handleCreateCard(cmd); err != nil {
|
|
121
116
|
t.Fatalf("handleCreateCard failed: %v", err)
|
|
@@ -131,14 +126,9 @@ func TestCardCreateCommandNoBoard(t *testing.T) {
|
|
|
131
126
|
|
|
132
127
|
cmd := cardCreateCmd
|
|
133
128
|
cmd.SetContext(testApp.ToContext(context.Background()))
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
cardStatus = ""
|
|
138
|
-
cardImageURL = ""
|
|
139
|
-
cardTagIDs = []string{}
|
|
140
|
-
cardCreatedAt = ""
|
|
141
|
-
cardLastActiveAt = ""
|
|
129
|
+
cmd.ParseFlags([]string{
|
|
130
|
+
"--title", "Test",
|
|
131
|
+
})
|
|
142
132
|
|
|
143
133
|
err := handleCreateCard(cmd)
|
|
144
134
|
if err == nil {
|
|
@@ -154,14 +144,9 @@ func TestCardCreateCommandNoClient(t *testing.T) {
|
|
|
154
144
|
|
|
155
145
|
cmd := cardCreateCmd
|
|
156
146
|
cmd.SetContext(testApp.ToContext(context.Background()))
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
cardStatus = ""
|
|
161
|
-
cardImageURL = ""
|
|
162
|
-
cardTagIDs = []string{}
|
|
163
|
-
cardCreatedAt = ""
|
|
164
|
-
cardLastActiveAt = ""
|
|
147
|
+
cmd.ParseFlags([]string{
|
|
148
|
+
"--title", "Test",
|
|
149
|
+
})
|
|
165
150
|
|
|
166
151
|
err := handleCreateCard(cmd)
|
|
167
152
|
if err == nil {
|
|
@@ -187,14 +172,9 @@ func TestCardCreateCommandAPIError(t *testing.T) {
|
|
|
187
172
|
|
|
188
173
|
cmd := cardCreateCmd
|
|
189
174
|
cmd.SetContext(testApp.ToContext(context.Background()))
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
cardStatus = ""
|
|
194
|
-
cardImageURL = ""
|
|
195
|
-
cardTagIDs = []string{}
|
|
196
|
-
cardCreatedAt = ""
|
|
197
|
-
cardLastActiveAt = ""
|
|
175
|
+
cmd.ParseFlags([]string{
|
|
176
|
+
"--title", "Test",
|
|
177
|
+
})
|
|
198
178
|
|
|
199
179
|
err := handleCreateCard(cmd)
|
|
200
180
|
if err == nil {
|
package/cmd/card_update.go
CHANGED
|
@@ -10,14 +10,6 @@ import (
|
|
|
10
10
|
"github.com/spf13/cobra"
|
|
11
11
|
)
|
|
12
12
|
|
|
13
|
-
var (
|
|
14
|
-
updateTitle string
|
|
15
|
-
updateDescription string
|
|
16
|
-
updateStatus string
|
|
17
|
-
updateTagIDs []string
|
|
18
|
-
updateLastActiveAt string
|
|
19
|
-
)
|
|
20
|
-
|
|
21
13
|
var cardUpdateCmd = &cobra.Command{
|
|
22
14
|
Use: "update <card_number>",
|
|
23
15
|
Short: "Update a card",
|
|
@@ -41,16 +33,33 @@ func handleUpdateCard(cmd *cobra.Command, cardNumber string) error {
|
|
|
41
33
|
return fmt.Errorf("API client not available")
|
|
42
34
|
}
|
|
43
35
|
|
|
44
|
-
|
|
45
|
-
|
|
36
|
+
// Build payload only with flags that were explicitly set
|
|
37
|
+
var payload api.UpdateCardPayload
|
|
38
|
+
hasChanges := false
|
|
39
|
+
|
|
40
|
+
if cmd.Flags().Changed("title") {
|
|
41
|
+
payload.Title, _ = cmd.Flags().GetString("title")
|
|
42
|
+
hasChanges = true
|
|
43
|
+
}
|
|
44
|
+
if cmd.Flags().Changed("description") {
|
|
45
|
+
payload.Description, _ = cmd.Flags().GetString("description")
|
|
46
|
+
hasChanges = true
|
|
47
|
+
}
|
|
48
|
+
if cmd.Flags().Changed("status") {
|
|
49
|
+
payload.Status, _ = cmd.Flags().GetString("status")
|
|
50
|
+
hasChanges = true
|
|
51
|
+
}
|
|
52
|
+
if cmd.Flags().Changed("tag-id") {
|
|
53
|
+
payload.TagIDS, _ = cmd.Flags().GetStringSlice("tag-id")
|
|
54
|
+
hasChanges = true
|
|
55
|
+
}
|
|
56
|
+
if cmd.Flags().Changed("last-active-at") {
|
|
57
|
+
payload.LastActiveAt, _ = cmd.Flags().GetString("last-active-at")
|
|
58
|
+
hasChanges = true
|
|
46
59
|
}
|
|
47
60
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
Description: updateDescription,
|
|
51
|
-
Status: updateStatus,
|
|
52
|
-
TagIDS: updateTagIDs,
|
|
53
|
-
LastActiveAt: updateLastActiveAt,
|
|
61
|
+
if !hasChanges {
|
|
62
|
+
return fmt.Errorf("must provide at least one flag to update (--title, --description, --status, --tag-id, or --last-active-at)")
|
|
54
63
|
}
|
|
55
64
|
|
|
56
65
|
card, err := a.Client.PutCard(context.Background(), cardNum, payload)
|
|
@@ -63,11 +72,11 @@ func handleUpdateCard(cmd *cobra.Command, cardNumber string) error {
|
|
|
63
72
|
}
|
|
64
73
|
|
|
65
74
|
func init() {
|
|
66
|
-
cardUpdateCmd.Flags().
|
|
67
|
-
cardUpdateCmd.Flags().
|
|
68
|
-
cardUpdateCmd.Flags().
|
|
69
|
-
cardUpdateCmd.Flags().
|
|
70
|
-
cardUpdateCmd.Flags().
|
|
75
|
+
cardUpdateCmd.Flags().StringP("title", "t", "", "Card title")
|
|
76
|
+
cardUpdateCmd.Flags().StringP("description", "d", "", "Card description")
|
|
77
|
+
cardUpdateCmd.Flags().String("status", "", "Card status")
|
|
78
|
+
cardUpdateCmd.Flags().StringSlice("tag-id", []string{}, "Tag ID (can be used multiple times)")
|
|
79
|
+
cardUpdateCmd.Flags().String("last-active-at", "", "Last active timestamp")
|
|
71
80
|
|
|
72
81
|
cardCmd.AddCommand(cardUpdateCmd)
|
|
73
82
|
}
|
package/cmd/card_update_test.go
CHANGED
|
@@ -10,6 +10,7 @@ import (
|
|
|
10
10
|
"github.com/rogeriopvl/fizzy/internal/api"
|
|
11
11
|
"github.com/rogeriopvl/fizzy/internal/app"
|
|
12
12
|
"github.com/rogeriopvl/fizzy/internal/testutil"
|
|
13
|
+
"github.com/spf13/cobra"
|
|
13
14
|
)
|
|
14
15
|
|
|
15
16
|
func TestCardUpdateCommand(t *testing.T) {
|
|
@@ -48,11 +49,12 @@ func TestCardUpdateCommand(t *testing.T) {
|
|
|
48
49
|
|
|
49
50
|
cmd := cardUpdateCmd
|
|
50
51
|
cmd.SetContext(testApp.ToContext(context.Background()))
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
cmd.ParseFlags([]string{
|
|
53
|
+
"--title", "Updated card title",
|
|
54
|
+
"--description", "Updated description",
|
|
55
|
+
"--status", "published",
|
|
56
|
+
"--tag-id", "updated-tag",
|
|
57
|
+
})
|
|
56
58
|
|
|
57
59
|
if err := handleUpdateCard(cmd, "1"); err != nil {
|
|
58
60
|
t.Fatalf("handleUpdateCard failed: %v", err)
|
|
@@ -71,8 +73,9 @@ func TestCardUpdateCommandAPIError(t *testing.T) {
|
|
|
71
73
|
|
|
72
74
|
cmd := cardUpdateCmd
|
|
73
75
|
cmd.SetContext(testApp.ToContext(context.Background()))
|
|
74
|
-
|
|
75
|
-
|
|
76
|
+
cmd.ParseFlags([]string{
|
|
77
|
+
"--title", "Updated card",
|
|
78
|
+
})
|
|
76
79
|
|
|
77
80
|
err := handleUpdateCard(cmd, "999")
|
|
78
81
|
if err == nil {
|
|
@@ -88,8 +91,9 @@ func TestCardUpdateCommandNoClient(t *testing.T) {
|
|
|
88
91
|
|
|
89
92
|
cmd := cardUpdateCmd
|
|
90
93
|
cmd.SetContext(testApp.ToContext(context.Background()))
|
|
91
|
-
|
|
92
|
-
|
|
94
|
+
cmd.ParseFlags([]string{
|
|
95
|
+
"--title", "Updated card",
|
|
96
|
+
})
|
|
93
97
|
|
|
94
98
|
err := handleUpdateCard(cmd, "1")
|
|
95
99
|
if err == nil {
|
|
@@ -105,8 +109,9 @@ func TestCardUpdateCommandInvalidCardNumber(t *testing.T) {
|
|
|
105
109
|
|
|
106
110
|
cmd := cardUpdateCmd
|
|
107
111
|
cmd.SetContext(testApp.ToContext(context.Background()))
|
|
108
|
-
|
|
109
|
-
|
|
112
|
+
cmd.ParseFlags([]string{
|
|
113
|
+
"--title", "Updated card",
|
|
114
|
+
})
|
|
110
115
|
|
|
111
116
|
err := handleUpdateCard(cmd, "not-a-number")
|
|
112
117
|
if err == nil {
|
|
@@ -118,22 +123,21 @@ func TestCardUpdateCommandInvalidCardNumber(t *testing.T) {
|
|
|
118
123
|
}
|
|
119
124
|
|
|
120
125
|
func TestCardUpdateCommandNoFlags(t *testing.T) {
|
|
121
|
-
|
|
122
|
-
w.WriteHeader(http.StatusOK)
|
|
123
|
-
}))
|
|
124
|
-
defer server.Close()
|
|
125
|
-
|
|
126
|
-
client := testutil.NewTestClient(server.URL, "", "", "test-token")
|
|
126
|
+
client := testutil.NewTestClient("http://localhost", "", "", "test-token")
|
|
127
127
|
testApp := &app.App{Client: client}
|
|
128
128
|
|
|
129
|
-
|
|
130
|
-
cmd.
|
|
129
|
+
// Create a fresh command to avoid flag pollution from other tests
|
|
130
|
+
cmd := &cobra.Command{
|
|
131
|
+
Use: "update <card_number>",
|
|
132
|
+
Args: cobra.ExactArgs(1),
|
|
133
|
+
}
|
|
134
|
+
cmd.Flags().String("title", "", "Card title")
|
|
135
|
+
cmd.Flags().String("description", "", "Card description")
|
|
136
|
+
cmd.Flags().String("status", "", "Card status")
|
|
137
|
+
cmd.Flags().StringSlice("tag-id", []string{}, "Tag ID")
|
|
138
|
+
cmd.Flags().String("last-active-at", "", "Last active timestamp")
|
|
131
139
|
|
|
132
|
-
|
|
133
|
-
updateDescription = ""
|
|
134
|
-
updateStatus = ""
|
|
135
|
-
updateTagIDs = []string{}
|
|
136
|
-
updateLastActiveAt = ""
|
|
140
|
+
cmd.SetContext(testApp.ToContext(context.Background()))
|
|
137
141
|
|
|
138
142
|
err := handleUpdateCard(cmd, "1")
|
|
139
143
|
if err == nil {
|
package/cmd/column_create.go
CHANGED
|
@@ -11,11 +11,6 @@ import (
|
|
|
11
11
|
"github.com/spf13/cobra"
|
|
12
12
|
)
|
|
13
13
|
|
|
14
|
-
var (
|
|
15
|
-
columnName string
|
|
16
|
-
columnColor string
|
|
17
|
-
)
|
|
18
|
-
|
|
19
14
|
func buildColorAliases() map[string]api.Color {
|
|
20
15
|
aliases := make(map[string]api.Color)
|
|
21
16
|
for _, colorDef := range colors.All {
|
|
@@ -49,15 +44,19 @@ func handleCreateColumn(cmd *cobra.Command) error {
|
|
|
49
44
|
return fmt.Errorf("API client not available")
|
|
50
45
|
}
|
|
51
46
|
|
|
47
|
+
// Read flag values directly from command
|
|
48
|
+
name, _ := cmd.Flags().GetString("name")
|
|
49
|
+
colorStr, _ := cmd.Flags().GetString("color")
|
|
50
|
+
|
|
52
51
|
payload := api.CreateColumnPayload{
|
|
53
|
-
Name:
|
|
52
|
+
Name: name,
|
|
54
53
|
}
|
|
55
54
|
|
|
56
|
-
if
|
|
55
|
+
if colorStr != "" {
|
|
57
56
|
colorAliases := buildColorAliases()
|
|
58
|
-
color, ok := colorAliases[
|
|
57
|
+
color, ok := colorAliases[colorStr]
|
|
59
58
|
if !ok {
|
|
60
|
-
return fmt.Errorf("invalid color '%s'. Available colors: %s",
|
|
59
|
+
return fmt.Errorf("invalid color '%s'. Available colors: %s", colorStr, getAvailableColors())
|
|
61
60
|
}
|
|
62
61
|
payload.Color = &color
|
|
63
62
|
}
|
|
@@ -67,14 +66,14 @@ func handleCreateColumn(cmd *cobra.Command) error {
|
|
|
67
66
|
return fmt.Errorf("creating column: %w", err)
|
|
68
67
|
}
|
|
69
68
|
|
|
70
|
-
fmt.Printf("✓ Column '%s' created successfully\n",
|
|
69
|
+
fmt.Printf("✓ Column '%s' created successfully\n", name)
|
|
71
70
|
return nil
|
|
72
71
|
}
|
|
73
72
|
|
|
74
73
|
func init() {
|
|
75
|
-
columnCreateCmd.Flags().
|
|
74
|
+
columnCreateCmd.Flags().StringP("name", "n", "", "Column name (required)")
|
|
76
75
|
columnCreateCmd.MarkFlagRequired("name")
|
|
77
|
-
columnCreateCmd.Flags().
|
|
76
|
+
columnCreateCmd.Flags().String("color", "", fmt.Sprintf("Column color (optional). Available: %s", getAvailableColors()))
|
|
78
77
|
|
|
79
78
|
columnCmd.AddCommand(columnCreateCmd)
|
|
80
79
|
}
|
|
@@ -57,9 +57,6 @@ func TestColumnCreateCommandSuccess(t *testing.T) {
|
|
|
57
57
|
cmd.SetContext(testApp.ToContext(context.Background()))
|
|
58
58
|
cmd.ParseFlags([]string{"--name", "Todo"})
|
|
59
59
|
|
|
60
|
-
columnName = "Todo"
|
|
61
|
-
columnColor = ""
|
|
62
|
-
|
|
63
60
|
if err := handleCreateColumn(cmd); err != nil {
|
|
64
61
|
t.Fatalf("handleCreateColumn failed: %v", err)
|
|
65
62
|
}
|
|
@@ -97,9 +94,6 @@ func TestColumnCreateCommandWithColor(t *testing.T) {
|
|
|
97
94
|
cmd.SetContext(testApp.ToContext(context.Background()))
|
|
98
95
|
cmd.ParseFlags([]string{"--name", "In Progress", "--color", "lime"})
|
|
99
96
|
|
|
100
|
-
columnName = "In Progress"
|
|
101
|
-
columnColor = "lime"
|
|
102
|
-
|
|
103
97
|
if err := handleCreateColumn(cmd); err != nil {
|
|
104
98
|
t.Fatalf("handleCreateColumn failed: %v", err)
|
|
105
99
|
}
|
|
@@ -113,9 +107,6 @@ func TestColumnCreateCommandInvalidColor(t *testing.T) {
|
|
|
113
107
|
cmd.SetContext(testApp.ToContext(context.Background()))
|
|
114
108
|
cmd.ParseFlags([]string{"--name", "Todo", "--color", "invalid"})
|
|
115
109
|
|
|
116
|
-
columnName = "Todo"
|
|
117
|
-
columnColor = "invalid"
|
|
118
|
-
|
|
119
110
|
err := handleCreateColumn(cmd)
|
|
120
111
|
if err == nil {
|
|
121
112
|
t.Errorf("expected error for invalid color")
|
|
@@ -136,10 +127,7 @@ func TestColumnCreateCommandNoBoard(t *testing.T) {
|
|
|
136
127
|
|
|
137
128
|
cmd := columnCreateCmd
|
|
138
129
|
cmd.SetContext(testApp.ToContext(context.Background()))
|
|
139
|
-
cmd.ParseFlags([]string{"--name", "Todo"})
|
|
140
|
-
|
|
141
|
-
columnName = "Todo"
|
|
142
|
-
columnColor = ""
|
|
130
|
+
cmd.ParseFlags([]string{"--name", "Todo", "--color", ""})
|
|
143
131
|
|
|
144
132
|
err := handleCreateColumn(cmd)
|
|
145
133
|
if err == nil {
|
|
@@ -155,10 +143,7 @@ func TestColumnCreateCommandNoClient(t *testing.T) {
|
|
|
155
143
|
|
|
156
144
|
cmd := columnCreateCmd
|
|
157
145
|
cmd.SetContext(testApp.ToContext(context.Background()))
|
|
158
|
-
cmd.ParseFlags([]string{"--name", "Todo"})
|
|
159
|
-
|
|
160
|
-
columnName = "Todo"
|
|
161
|
-
columnColor = ""
|
|
146
|
+
cmd.ParseFlags([]string{"--name", "Todo", "--color", ""})
|
|
162
147
|
|
|
163
148
|
err := handleCreateColumn(cmd)
|
|
164
149
|
if err == nil {
|
|
@@ -181,10 +166,7 @@ func TestColumnCreateCommandAPIError(t *testing.T) {
|
|
|
181
166
|
|
|
182
167
|
cmd := columnCreateCmd
|
|
183
168
|
cmd.SetContext(testApp.ToContext(context.Background()))
|
|
184
|
-
cmd.ParseFlags([]string{"--name", "Todo"})
|
|
185
|
-
|
|
186
|
-
columnName = "Todo"
|
|
187
|
-
columnColor = ""
|
|
169
|
+
cmd.ParseFlags([]string{"--name", "Todo", "--color", ""})
|
|
188
170
|
|
|
189
171
|
err := handleCreateColumn(cmd)
|
|
190
172
|
if err == nil {
|
package/cmd/comment.go
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Package cmd
|
|
2
|
+
package cmd
|
|
3
|
+
|
|
4
|
+
import "github.com/spf13/cobra"
|
|
5
|
+
|
|
6
|
+
var commentCmd = &cobra.Command{
|
|
7
|
+
Use: "comment",
|
|
8
|
+
Short: "Manage card comments",
|
|
9
|
+
Long: `Manage comments on cards in Fizzy`,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
func init() {
|
|
13
|
+
rootCmd.AddCommand(commentCmd)
|
|
14
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
package cmd
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"context"
|
|
5
|
+
"fmt"
|
|
6
|
+
"strconv"
|
|
7
|
+
|
|
8
|
+
"github.com/rogeriopvl/fizzy/internal/app"
|
|
9
|
+
"github.com/spf13/cobra"
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
var commentCreateCmd = &cobra.Command{
|
|
13
|
+
Use: "create <card_number>",
|
|
14
|
+
Short: "Create a new comment",
|
|
15
|
+
Long: `Create a new comment on a card`,
|
|
16
|
+
Args: cobra.ExactArgs(1),
|
|
17
|
+
Run: func(cmd *cobra.Command, args []string) {
|
|
18
|
+
if err := handleCreateComment(cmd, args[0]); err != nil {
|
|
19
|
+
fmt.Fprintf(cmd.OutOrStderr(), "Error: %v\n", err)
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
func handleCreateComment(cmd *cobra.Command, cardNumber string) error {
|
|
25
|
+
cardNum, err := strconv.Atoi(cardNumber)
|
|
26
|
+
if err != nil {
|
|
27
|
+
return fmt.Errorf("invalid card number: %w", err)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
a := app.FromContext(cmd.Context())
|
|
31
|
+
if a == nil || a.Client == nil {
|
|
32
|
+
return fmt.Errorf("API client not available")
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
body, _ := cmd.Flags().GetString("body")
|
|
36
|
+
|
|
37
|
+
comment, err := a.Client.PostCardComment(context.Background(), cardNum, body)
|
|
38
|
+
if err != nil {
|
|
39
|
+
return fmt.Errorf("creating comment: %w", err)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
fmt.Printf("✓ Comment created successfully (id: %s)\n", comment.ID)
|
|
43
|
+
return nil
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
func init() {
|
|
47
|
+
commentCreateCmd.Flags().StringP("body", "b", "", "Comment body (required)")
|
|
48
|
+
commentCreateCmd.MarkFlagRequired("body")
|
|
49
|
+
|
|
50
|
+
commentCmd.AddCommand(commentCreateCmd)
|
|
51
|
+
}
|