fizzy-cli 0.8.0 → 0.9.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/package.json +2 -1
- package/.env +0 -1
- package/.github/workflows/release.yml +0 -29
- package/.github/workflows/tests.yml +0 -24
- package/AGENTS.md +0 -33
- package/CHANGELOG.md +0 -194
- package/Makefile +0 -43
- package/bin/fizzy +0 -0
- package/cmd/account.go +0 -14
- package/cmd/account_list.go +0 -44
- package/cmd/account_list_test.go +0 -118
- package/cmd/board.go +0 -49
- package/cmd/board_create.go +0 -59
- package/cmd/board_create_test.go +0 -141
- package/cmd/board_delete.go +0 -40
- package/cmd/board_delete_test.go +0 -121
- package/cmd/board_list.go +0 -44
- package/cmd/board_list_test.go +0 -115
- package/cmd/board_show.go +0 -40
- package/cmd/board_show_test.go +0 -113
- package/cmd/board_test.go +0 -92
- package/cmd/board_update.go +0 -72
- package/cmd/board_update_test.go +0 -233
- package/cmd/card.go +0 -24
- package/cmd/card_assign.go +0 -55
- package/cmd/card_assign_test.go +0 -130
- package/cmd/card_close.go +0 -46
- package/cmd/card_close_test.go +0 -92
- package/cmd/card_create.go +0 -72
- package/cmd/card_create_test.go +0 -186
- package/cmd/card_delete.go +0 -46
- package/cmd/card_delete_test.go +0 -92
- package/cmd/card_golden.go +0 -46
- package/cmd/card_golden_test.go +0 -92
- package/cmd/card_list.go +0 -114
- package/cmd/card_list_test.go +0 -373
- package/cmd/card_not_now.go +0 -46
- package/cmd/card_not_now_test.go +0 -92
- package/cmd/card_reaction.go +0 -13
- package/cmd/card_reaction_create.go +0 -46
- package/cmd/card_reaction_create_test.go +0 -148
- package/cmd/card_reaction_delete.go +0 -46
- package/cmd/card_reaction_delete_test.go +0 -112
- package/cmd/card_reaction_list.go +0 -51
- package/cmd/card_reaction_list_test.go +0 -127
- package/cmd/card_reopen.go +0 -46
- package/cmd/card_reopen_test.go +0 -92
- package/cmd/card_show.go +0 -46
- package/cmd/card_show_test.go +0 -92
- package/cmd/card_tag.go +0 -51
- package/cmd/card_tag_test.go +0 -112
- package/cmd/card_triage.go +0 -46
- package/cmd/card_ungolden.go +0 -46
- package/cmd/card_ungolden_test.go +0 -92
- package/cmd/card_untriage.go +0 -46
- package/cmd/card_untriage_test.go +0 -92
- package/cmd/card_unwatch.go +0 -46
- package/cmd/card_unwatch_test.go +0 -92
- package/cmd/card_update.go +0 -82
- package/cmd/card_update_test.go +0 -149
- package/cmd/card_watch.go +0 -46
- package/cmd/card_watch_test.go +0 -92
- package/cmd/column.go +0 -14
- package/cmd/column_create.go +0 -79
- package/cmd/column_create_test.go +0 -178
- package/cmd/column_delete.go +0 -40
- package/cmd/column_delete_test.go +0 -121
- package/cmd/column_list.go +0 -44
- package/cmd/column_list_test.go +0 -138
- package/cmd/column_show.go +0 -40
- package/cmd/column_show_test.go +0 -111
- package/cmd/column_update.go +0 -67
- package/cmd/column_update_test.go +0 -198
- package/cmd/comment.go +0 -14
- package/cmd/comment_create.go +0 -51
- package/cmd/comment_create_test.go +0 -129
- package/cmd/comment_delete.go +0 -46
- package/cmd/comment_delete_test.go +0 -92
- package/cmd/comment_list.go +0 -51
- package/cmd/comment_list_test.go +0 -132
- package/cmd/comment_show.go +0 -46
- package/cmd/comment_show_test.go +0 -104
- package/cmd/comment_update.go +0 -51
- package/cmd/comment_update_test.go +0 -130
- package/cmd/login.go +0 -81
- package/cmd/login_test.go +0 -98
- package/cmd/notification.go +0 -14
- package/cmd/notification_list.go +0 -69
- package/cmd/notification_list_test.go +0 -288
- package/cmd/notification_read.go +0 -51
- package/cmd/notification_read_all.go +0 -38
- package/cmd/notification_read_all_test.go +0 -75
- package/cmd/notification_read_test.go +0 -138
- package/cmd/notification_unread.go +0 -44
- package/cmd/notification_unread_test.go +0 -99
- package/cmd/reaction.go +0 -13
- package/cmd/reaction_create.go +0 -46
- package/cmd/reaction_create_test.go +0 -113
- package/cmd/reaction_delete.go +0 -46
- package/cmd/reaction_delete_test.go +0 -92
- package/cmd/reaction_list.go +0 -51
- package/cmd/reaction_list_test.go +0 -125
- package/cmd/root.go +0 -38
- package/cmd/step.go +0 -14
- package/cmd/step_create.go +0 -53
- package/cmd/step_create_test.go +0 -171
- package/cmd/step_delete.go +0 -46
- package/cmd/step_delete_test.go +0 -92
- package/cmd/step_update.go +0 -66
- package/cmd/step_update_test.go +0 -190
- package/cmd/tag.go +0 -15
- package/cmd/tag_list.go +0 -47
- package/cmd/tag_list_test.go +0 -109
- package/cmd/use.go +0 -85
- package/cmd/use_test.go +0 -186
- package/cmd/user.go +0 -22
- package/cmd/user_deactivate.go +0 -40
- package/cmd/user_deactivate_test.go +0 -121
- package/cmd/user_list.go +0 -44
- package/cmd/user_list_test.go +0 -126
- package/cmd/user_show.go +0 -40
- package/cmd/user_show_test.go +0 -110
- package/cmd/user_update.go +0 -71
- package/cmd/user_update_test.go +0 -177
- package/go.mod +0 -31
- package/go.sum +0 -53
- package/internal/api/boards.go +0 -93
- package/internal/api/cards.go +0 -322
- package/internal/api/client.go +0 -99
- package/internal/api/columns.go +0 -113
- package/internal/api/comments.go +0 -108
- package/internal/api/identity.go +0 -24
- package/internal/api/notifications.go +0 -89
- package/internal/api/reactions.go +0 -130
- package/internal/api/steps.go +0 -101
- package/internal/api/tags.go +0 -24
- package/internal/api/types.go +0 -195
- package/internal/api/users.go +0 -75
- package/internal/app/app.go +0 -49
- package/internal/colors/colors.go +0 -32
- package/internal/config/config.go +0 -70
- package/internal/testutil/client.go +0 -26
- package/internal/ui/account_list.go +0 -14
- package/internal/ui/account_selector.go +0 -63
- package/internal/ui/board_list.go +0 -14
- package/internal/ui/board_show.go +0 -17
- package/internal/ui/card_list.go +0 -14
- package/internal/ui/card_show.go +0 -23
- package/internal/ui/column_list.go +0 -28
- package/internal/ui/column_show.go +0 -16
- package/internal/ui/comment_list.go +0 -25
- package/internal/ui/format.go +0 -27
- package/internal/ui/notification_list.go +0 -27
- package/internal/ui/reaction_list.go +0 -14
- package/internal/ui/user_list.go +0 -19
- package/internal/ui/user_show.go +0 -23
package/cmd/notification_list.go
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
package cmd
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"context"
|
|
5
|
-
"fmt"
|
|
6
|
-
|
|
7
|
-
"github.com/rogeriopvl/fizzy/internal/api"
|
|
8
|
-
"github.com/rogeriopvl/fizzy/internal/app"
|
|
9
|
-
"github.com/rogeriopvl/fizzy/internal/ui"
|
|
10
|
-
"github.com/spf13/cobra"
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
var notificationListCmd = &cobra.Command{
|
|
14
|
-
Use: "list",
|
|
15
|
-
Short: "List all notifications",
|
|
16
|
-
Long: `Retrieve and display all notifications from Fizzy`,
|
|
17
|
-
Run: func(cmd *cobra.Command, args []string) {
|
|
18
|
-
if err := handleListNotifications(cmd); err != nil {
|
|
19
|
-
fmt.Fprintf(cmd.OutOrStderr(), "Error: %v\n", err)
|
|
20
|
-
}
|
|
21
|
-
},
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
func handleListNotifications(cmd *cobra.Command) error {
|
|
25
|
-
a := app.FromContext(cmd.Context())
|
|
26
|
-
if a == nil || a.Client == nil {
|
|
27
|
-
return fmt.Errorf("API client not available")
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
notifications, err := a.Client.GetNotifications(context.Background())
|
|
31
|
-
if err != nil {
|
|
32
|
-
return fmt.Errorf("fetching notifications: %w", err)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
read, _ := cmd.Flags().GetBool("read")
|
|
36
|
-
unread, _ := cmd.Flags().GetBool("unread")
|
|
37
|
-
|
|
38
|
-
filtered := filterNotifications(notifications, read, unread)
|
|
39
|
-
|
|
40
|
-
if len(filtered) == 0 {
|
|
41
|
-
fmt.Println("No notifications found")
|
|
42
|
-
return nil
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return ui.DisplayNotifications(filtered)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
func filterNotifications(notifications []api.Notification, read bool, unread bool) []api.Notification {
|
|
49
|
-
if !read && !unread {
|
|
50
|
-
return notifications
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
var filtered []api.Notification
|
|
54
|
-
for _, notification := range notifications {
|
|
55
|
-
if read && notification.Read {
|
|
56
|
-
filtered = append(filtered, notification)
|
|
57
|
-
} else if unread && !notification.Read {
|
|
58
|
-
filtered = append(filtered, notification)
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return filtered
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
func init() {
|
|
66
|
-
notificationCmd.AddCommand(notificationListCmd)
|
|
67
|
-
notificationListCmd.Flags().BoolP("read", "r", false, "Show only read notifications")
|
|
68
|
-
notificationListCmd.Flags().BoolP("unread", "u", false, "Show only unread notifications")
|
|
69
|
-
}
|
|
@@ -1,288 +0,0 @@
|
|
|
1
|
-
package cmd
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"context"
|
|
5
|
-
"encoding/json"
|
|
6
|
-
"net/http"
|
|
7
|
-
"net/http/httptest"
|
|
8
|
-
"testing"
|
|
9
|
-
|
|
10
|
-
"github.com/rogeriopvl/fizzy/internal/api"
|
|
11
|
-
"github.com/rogeriopvl/fizzy/internal/app"
|
|
12
|
-
"github.com/rogeriopvl/fizzy/internal/config"
|
|
13
|
-
"github.com/rogeriopvl/fizzy/internal/testutil"
|
|
14
|
-
)
|
|
15
|
-
|
|
16
|
-
func TestNotificationListCommand(t *testing.T) {
|
|
17
|
-
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
18
|
-
if r.URL.Path != "/notifications" {
|
|
19
|
-
t.Errorf("expected /notifications, got %s", r.URL.Path)
|
|
20
|
-
}
|
|
21
|
-
if r.Method != http.MethodGet {
|
|
22
|
-
t.Errorf("expected GET, got %s", r.Method)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
auth := r.Header.Get("Authorization")
|
|
26
|
-
if auth != "Bearer test-token" {
|
|
27
|
-
t.Errorf("expected Bearer test-token, got %s", auth)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
w.Header().Set("Content-Type", "application/json")
|
|
31
|
-
response := []api.Notification{
|
|
32
|
-
{
|
|
33
|
-
ID: "notif-123",
|
|
34
|
-
Read: false,
|
|
35
|
-
ReadAt: "",
|
|
36
|
-
CreatedAt: "2025-01-01T00:00:00Z",
|
|
37
|
-
Title: "Plain text mentions",
|
|
38
|
-
Body: "Assigned to self",
|
|
39
|
-
Creator: api.User{
|
|
40
|
-
ID: "user-123",
|
|
41
|
-
Name: "David Heinemeier Hansson",
|
|
42
|
-
Email: "david@example.com",
|
|
43
|
-
Role: "owner",
|
|
44
|
-
Active: true,
|
|
45
|
-
CreatedAt: "2025-12-05T19:36:35.401Z",
|
|
46
|
-
URL: "http://fizzy.localhost:3006/897362094/users/03f5v9zjw7pz8717a4no1h8a7",
|
|
47
|
-
},
|
|
48
|
-
Card: api.CardReference{
|
|
49
|
-
ID: "card-123",
|
|
50
|
-
Title: "Plain text mentions",
|
|
51
|
-
Status: "published",
|
|
52
|
-
URL: "http://fizzy.localhost:3006/897362094/cards/3",
|
|
53
|
-
},
|
|
54
|
-
URL: "http://fizzy.localhost:3006/897362094/notifications/03f5va03bpuvkcjemcxl73ho2",
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
ID: "notif-456",
|
|
58
|
-
Read: true,
|
|
59
|
-
ReadAt: "2025-01-02T00:00:00Z",
|
|
60
|
-
CreatedAt: "2025-01-01T12:00:00Z",
|
|
61
|
-
Title: "Comment reply",
|
|
62
|
-
Body: "Someone replied to your comment",
|
|
63
|
-
Creator: api.User{
|
|
64
|
-
ID: "user-456",
|
|
65
|
-
Name: "Jason Fried",
|
|
66
|
-
Email: "jason@example.com",
|
|
67
|
-
Role: "member",
|
|
68
|
-
Active: true,
|
|
69
|
-
CreatedAt: "2025-12-05T19:36:35.419Z",
|
|
70
|
-
URL: "http://fizzy.localhost:3006/897362094/users/03f5v9zjysoy0fqs9yg0ei3hq",
|
|
71
|
-
},
|
|
72
|
-
Card: api.CardReference{
|
|
73
|
-
ID: "card-456",
|
|
74
|
-
Title: "Fix bug",
|
|
75
|
-
Status: "in_progress",
|
|
76
|
-
URL: "http://fizzy.localhost:3006/897362094/cards/4",
|
|
77
|
-
},
|
|
78
|
-
URL: "http://fizzy.localhost:3006/897362094/notifications/notif-456",
|
|
79
|
-
},
|
|
80
|
-
}
|
|
81
|
-
json.NewEncoder(w).Encode(response)
|
|
82
|
-
}))
|
|
83
|
-
defer server.Close()
|
|
84
|
-
|
|
85
|
-
client := testutil.NewTestClient(server.URL, "", "board-123", "test-token")
|
|
86
|
-
testApp := &app.App{
|
|
87
|
-
Client: client,
|
|
88
|
-
Config: &config.Config{SelectedBoard: "board-123"},
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
cmd := notificationListCmd
|
|
92
|
-
cmd.SetContext(testApp.ToContext(context.Background()))
|
|
93
|
-
|
|
94
|
-
if err := handleListNotifications(cmd); err != nil {
|
|
95
|
-
t.Fatalf("handleListNotifications failed: %v", err)
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
func TestNotificationListCommandNoNotifications(t *testing.T) {
|
|
100
|
-
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
101
|
-
w.Header().Set("Content-Type", "application/json")
|
|
102
|
-
json.NewEncoder(w).Encode([]api.Notification{})
|
|
103
|
-
}))
|
|
104
|
-
defer server.Close()
|
|
105
|
-
|
|
106
|
-
client := testutil.NewTestClient(server.URL, "", "board-123", "test-token")
|
|
107
|
-
testApp := &app.App{
|
|
108
|
-
Client: client,
|
|
109
|
-
Config: &config.Config{SelectedBoard: "board-123"},
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
cmd := notificationListCmd
|
|
113
|
-
cmd.SetContext(testApp.ToContext(context.Background()))
|
|
114
|
-
|
|
115
|
-
if err := handleListNotifications(cmd); err != nil {
|
|
116
|
-
t.Fatalf("handleListNotifications failed: %v", err)
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
func TestNotificationListCommandAPIError(t *testing.T) {
|
|
121
|
-
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
122
|
-
w.WriteHeader(http.StatusInternalServerError)
|
|
123
|
-
w.Write([]byte("Internal Server Error"))
|
|
124
|
-
}))
|
|
125
|
-
defer server.Close()
|
|
126
|
-
|
|
127
|
-
client := testutil.NewTestClient(server.URL, "", "board-123", "test-token")
|
|
128
|
-
testApp := &app.App{
|
|
129
|
-
Client: client,
|
|
130
|
-
Config: &config.Config{SelectedBoard: "board-123"},
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
cmd := notificationListCmd
|
|
134
|
-
cmd.SetContext(testApp.ToContext(context.Background()))
|
|
135
|
-
|
|
136
|
-
err := handleListNotifications(cmd)
|
|
137
|
-
if err == nil {
|
|
138
|
-
t.Errorf("expected error for API failure")
|
|
139
|
-
}
|
|
140
|
-
if err.Error() != "fetching notifications: unexpected status code 500: Internal Server Error" {
|
|
141
|
-
t.Errorf("expected API error, got %v", err)
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
func TestNotificationListCommandNoClient(t *testing.T) {
|
|
146
|
-
testApp := &app.App{}
|
|
147
|
-
|
|
148
|
-
cmd := notificationListCmd
|
|
149
|
-
cmd.SetContext(testApp.ToContext(context.Background()))
|
|
150
|
-
|
|
151
|
-
err := handleListNotifications(cmd)
|
|
152
|
-
if err == nil {
|
|
153
|
-
t.Errorf("expected error when client not available")
|
|
154
|
-
}
|
|
155
|
-
if err.Error() != "API client not available" {
|
|
156
|
-
t.Errorf("expected 'client not available' error, got %v", err)
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
func TestNotificationListCommandWithReadFilter(t *testing.T) {
|
|
161
|
-
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
162
|
-
w.Header().Set("Content-Type", "application/json")
|
|
163
|
-
response := []api.Notification{
|
|
164
|
-
{
|
|
165
|
-
ID: "notif-123",
|
|
166
|
-
Read: false,
|
|
167
|
-
CreatedAt: "2025-01-01T00:00:00Z",
|
|
168
|
-
Title: "Unread notification",
|
|
169
|
-
Body: "This should be filtered out",
|
|
170
|
-
Creator: api.User{
|
|
171
|
-
ID: "user-123",
|
|
172
|
-
Name: "David Heinemeier Hansson",
|
|
173
|
-
Email: "david@example.com",
|
|
174
|
-
Role: "owner",
|
|
175
|
-
Active: true,
|
|
176
|
-
CreatedAt: "2025-12-05T19:36:35.401Z",
|
|
177
|
-
},
|
|
178
|
-
Card: api.CardReference{
|
|
179
|
-
ID: "card-123",
|
|
180
|
-
Title: "Test card",
|
|
181
|
-
Status: "published",
|
|
182
|
-
},
|
|
183
|
-
},
|
|
184
|
-
{
|
|
185
|
-
ID: "notif-456",
|
|
186
|
-
Read: true,
|
|
187
|
-
ReadAt: "2025-01-02T00:00:00Z",
|
|
188
|
-
CreatedAt: "2025-01-01T12:00:00Z",
|
|
189
|
-
Title: "Read notification",
|
|
190
|
-
Body: "This should be shown",
|
|
191
|
-
Creator: api.User{
|
|
192
|
-
ID: "user-456",
|
|
193
|
-
Name: "Jason Fried",
|
|
194
|
-
Email: "jason@example.com",
|
|
195
|
-
Role: "member",
|
|
196
|
-
Active: true,
|
|
197
|
-
CreatedAt: "2025-12-05T19:36:35.419Z",
|
|
198
|
-
},
|
|
199
|
-
Card: api.CardReference{
|
|
200
|
-
ID: "card-456",
|
|
201
|
-
Title: "Another card",
|
|
202
|
-
Status: "in_progress",
|
|
203
|
-
},
|
|
204
|
-
},
|
|
205
|
-
}
|
|
206
|
-
json.NewEncoder(w).Encode(response)
|
|
207
|
-
}))
|
|
208
|
-
defer server.Close()
|
|
209
|
-
|
|
210
|
-
client := testutil.NewTestClient(server.URL, "", "board-123", "test-token")
|
|
211
|
-
testApp := &app.App{
|
|
212
|
-
Client: client,
|
|
213
|
-
Config: &config.Config{SelectedBoard: "board-123"},
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
cmd := notificationListCmd
|
|
217
|
-
cmd.SetContext(testApp.ToContext(context.Background()))
|
|
218
|
-
cmd.Flags().Set("read", "true")
|
|
219
|
-
|
|
220
|
-
if err := handleListNotifications(cmd); err != nil {
|
|
221
|
-
t.Fatalf("handleListNotifications failed: %v", err)
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
func TestNotificationListCommandWithUnreadFilter(t *testing.T) {
|
|
226
|
-
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
227
|
-
w.Header().Set("Content-Type", "application/json")
|
|
228
|
-
response := []api.Notification{
|
|
229
|
-
{
|
|
230
|
-
ID: "notif-123",
|
|
231
|
-
Read: false,
|
|
232
|
-
CreatedAt: "2025-01-01T00:00:00Z",
|
|
233
|
-
Title: "Unread notification",
|
|
234
|
-
Body: "This should be shown",
|
|
235
|
-
Creator: api.User{
|
|
236
|
-
ID: "user-123",
|
|
237
|
-
Name: "David Heinemeier Hansson",
|
|
238
|
-
Email: "david@example.com",
|
|
239
|
-
Role: "owner",
|
|
240
|
-
Active: true,
|
|
241
|
-
CreatedAt: "2025-12-05T19:36:35.401Z",
|
|
242
|
-
},
|
|
243
|
-
Card: api.CardReference{
|
|
244
|
-
ID: "card-123",
|
|
245
|
-
Title: "Test card",
|
|
246
|
-
Status: "published",
|
|
247
|
-
},
|
|
248
|
-
},
|
|
249
|
-
{
|
|
250
|
-
ID: "notif-456",
|
|
251
|
-
Read: true,
|
|
252
|
-
ReadAt: "2025-01-02T00:00:00Z",
|
|
253
|
-
CreatedAt: "2025-01-01T12:00:00Z",
|
|
254
|
-
Title: "Read notification",
|
|
255
|
-
Body: "This should be filtered out",
|
|
256
|
-
Creator: api.User{
|
|
257
|
-
ID: "user-456",
|
|
258
|
-
Name: "Jason Fried",
|
|
259
|
-
Email: "jason@example.com",
|
|
260
|
-
Role: "member",
|
|
261
|
-
Active: true,
|
|
262
|
-
CreatedAt: "2025-12-05T19:36:35.419Z",
|
|
263
|
-
},
|
|
264
|
-
Card: api.CardReference{
|
|
265
|
-
ID: "card-456",
|
|
266
|
-
Title: "Another card",
|
|
267
|
-
Status: "in_progress",
|
|
268
|
-
},
|
|
269
|
-
},
|
|
270
|
-
}
|
|
271
|
-
json.NewEncoder(w).Encode(response)
|
|
272
|
-
}))
|
|
273
|
-
defer server.Close()
|
|
274
|
-
|
|
275
|
-
client := testutil.NewTestClient(server.URL, "", "board-123", "test-token")
|
|
276
|
-
testApp := &app.App{
|
|
277
|
-
Client: client,
|
|
278
|
-
Config: &config.Config{SelectedBoard: "board-123"},
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
cmd := notificationListCmd
|
|
282
|
-
cmd.SetContext(testApp.ToContext(context.Background()))
|
|
283
|
-
cmd.Flags().Set("unread", "true")
|
|
284
|
-
|
|
285
|
-
if err := handleListNotifications(cmd); err != nil {
|
|
286
|
-
t.Fatalf("handleListNotifications failed: %v", err)
|
|
287
|
-
}
|
|
288
|
-
}
|
package/cmd/notification_read.go
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
package cmd
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"context"
|
|
5
|
-
"fmt"
|
|
6
|
-
"strings"
|
|
7
|
-
|
|
8
|
-
"github.com/rogeriopvl/fizzy/internal/app"
|
|
9
|
-
"github.com/rogeriopvl/fizzy/internal/ui"
|
|
10
|
-
"github.com/spf13/cobra"
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
var notificationReadCmd = &cobra.Command{
|
|
14
|
-
Use: "read <notification_id>",
|
|
15
|
-
Short: "Mark notification as read and display it",
|
|
16
|
-
Long: `Mark a notification as read and display its content`,
|
|
17
|
-
Args: cobra.ExactArgs(1),
|
|
18
|
-
Run: func(cmd *cobra.Command, args []string) {
|
|
19
|
-
if err := handleReadNotification(cmd, args[0]); err != nil {
|
|
20
|
-
fmt.Fprintf(cmd.OutOrStderr(), "Error: %v\n", err)
|
|
21
|
-
}
|
|
22
|
-
},
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
func handleReadNotification(cmd *cobra.Command, notificationID string) error {
|
|
26
|
-
a := app.FromContext(cmd.Context())
|
|
27
|
-
if a == nil || a.Client == nil {
|
|
28
|
-
return fmt.Errorf("API client not available")
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if _, err := a.Client.PostNotificationReading(context.Background(), notificationID); err != nil {
|
|
32
|
-
if strings.Contains(err.Error(), "404") {
|
|
33
|
-
return fmt.Errorf("notification not found")
|
|
34
|
-
}
|
|
35
|
-
return fmt.Errorf("marking notification as read: %w", err)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
notification, err := a.Client.GetNotification(context.Background(), notificationID)
|
|
39
|
-
if err != nil {
|
|
40
|
-
if strings.Contains(err.Error(), "404") {
|
|
41
|
-
return fmt.Errorf("notification not found")
|
|
42
|
-
}
|
|
43
|
-
return fmt.Errorf("fetching notification: %w", err)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return ui.DisplayNotification(notification)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
func init() {
|
|
50
|
-
notificationCmd.AddCommand(notificationReadCmd)
|
|
51
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
package cmd
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"context"
|
|
5
|
-
"fmt"
|
|
6
|
-
|
|
7
|
-
"github.com/rogeriopvl/fizzy/internal/app"
|
|
8
|
-
"github.com/spf13/cobra"
|
|
9
|
-
)
|
|
10
|
-
|
|
11
|
-
var notificationReadAllCmd = &cobra.Command{
|
|
12
|
-
Use: "read-all",
|
|
13
|
-
Short: "Mark all unread notifications as read",
|
|
14
|
-
Long: `Mark all unread notifications as read`,
|
|
15
|
-
Run: func(cmd *cobra.Command, args []string) {
|
|
16
|
-
if err := handleReadAllNotifications(cmd); err != nil {
|
|
17
|
-
fmt.Fprintf(cmd.OutOrStderr(), "Error: %v\n", err)
|
|
18
|
-
}
|
|
19
|
-
},
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
func handleReadAllNotifications(cmd *cobra.Command) error {
|
|
23
|
-
a := app.FromContext(cmd.Context())
|
|
24
|
-
if a == nil || a.Client == nil {
|
|
25
|
-
return fmt.Errorf("API client not available")
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if _, err := a.Client.PostBulkNotificationsReading(context.Background()); err != nil {
|
|
29
|
-
return fmt.Errorf("marking all notifications as read: %w", err)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
fmt.Println("✓ All notifications marked as read successfully")
|
|
33
|
-
return nil
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
func init() {
|
|
37
|
-
notificationCmd.AddCommand(notificationReadAllCmd)
|
|
38
|
-
}
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
package cmd
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"context"
|
|
5
|
-
"net/http"
|
|
6
|
-
"net/http/httptest"
|
|
7
|
-
"testing"
|
|
8
|
-
|
|
9
|
-
"github.com/rogeriopvl/fizzy/internal/app"
|
|
10
|
-
"github.com/rogeriopvl/fizzy/internal/config"
|
|
11
|
-
"github.com/rogeriopvl/fizzy/internal/testutil"
|
|
12
|
-
)
|
|
13
|
-
|
|
14
|
-
func TestNotificationReadAllCommand(t *testing.T) {
|
|
15
|
-
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
16
|
-
if r.URL.Path == "/notifications/bulk_reading" && r.Method == http.MethodPost {
|
|
17
|
-
w.WriteHeader(http.StatusNoContent)
|
|
18
|
-
return
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
t.Errorf("unexpected request: %s %s", r.Method, r.URL.Path)
|
|
22
|
-
w.WriteHeader(http.StatusNotFound)
|
|
23
|
-
}))
|
|
24
|
-
defer server.Close()
|
|
25
|
-
|
|
26
|
-
client := testutil.NewTestClient(server.URL, "", "board-123", "test-token")
|
|
27
|
-
testApp := &app.App{
|
|
28
|
-
Client: client,
|
|
29
|
-
Config: &config.Config{SelectedBoard: "board-123"},
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
cmd := notificationReadAllCmd
|
|
33
|
-
cmd.SetContext(testApp.ToContext(context.Background()))
|
|
34
|
-
|
|
35
|
-
if err := handleReadAllNotifications(cmd); err != nil {
|
|
36
|
-
t.Fatalf("handleReadAllNotifications failed: %v", err)
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
func TestNotificationReadAllCommandAPIError(t *testing.T) {
|
|
41
|
-
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
42
|
-
w.WriteHeader(http.StatusInternalServerError)
|
|
43
|
-
w.Write([]byte("Internal Server Error"))
|
|
44
|
-
}))
|
|
45
|
-
defer server.Close()
|
|
46
|
-
|
|
47
|
-
client := testutil.NewTestClient(server.URL, "", "board-123", "test-token")
|
|
48
|
-
testApp := &app.App{
|
|
49
|
-
Client: client,
|
|
50
|
-
Config: &config.Config{SelectedBoard: "board-123"},
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
cmd := notificationReadAllCmd
|
|
54
|
-
cmd.SetContext(testApp.ToContext(context.Background()))
|
|
55
|
-
|
|
56
|
-
err := handleReadAllNotifications(cmd)
|
|
57
|
-
if err == nil {
|
|
58
|
-
t.Errorf("expected error for API failure")
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
func TestNotificationReadAllCommandNoClient(t *testing.T) {
|
|
63
|
-
testApp := &app.App{}
|
|
64
|
-
|
|
65
|
-
cmd := notificationReadAllCmd
|
|
66
|
-
cmd.SetContext(testApp.ToContext(context.Background()))
|
|
67
|
-
|
|
68
|
-
err := handleReadAllNotifications(cmd)
|
|
69
|
-
if err == nil {
|
|
70
|
-
t.Errorf("expected error when client not available")
|
|
71
|
-
}
|
|
72
|
-
if err.Error() != "API client not available" {
|
|
73
|
-
t.Errorf("expected 'client not available' error, got %v", err)
|
|
74
|
-
}
|
|
75
|
-
}
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
package cmd
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"context"
|
|
5
|
-
"encoding/json"
|
|
6
|
-
"net/http"
|
|
7
|
-
"net/http/httptest"
|
|
8
|
-
"testing"
|
|
9
|
-
|
|
10
|
-
"github.com/rogeriopvl/fizzy/internal/api"
|
|
11
|
-
"github.com/rogeriopvl/fizzy/internal/app"
|
|
12
|
-
"github.com/rogeriopvl/fizzy/internal/config"
|
|
13
|
-
"github.com/rogeriopvl/fizzy/internal/testutil"
|
|
14
|
-
)
|
|
15
|
-
|
|
16
|
-
func TestNotificationReadCommand(t *testing.T) {
|
|
17
|
-
requestCount := 0
|
|
18
|
-
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
19
|
-
requestCount++
|
|
20
|
-
|
|
21
|
-
if r.URL.Path == "/notifications/notif-123/reading" && r.Method == http.MethodPost {
|
|
22
|
-
w.WriteHeader(http.StatusNoContent)
|
|
23
|
-
return
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
if r.URL.Path == "/notifications/notif-123" && r.Method == http.MethodGet {
|
|
27
|
-
w.Header().Set("Content-Type", "application/json")
|
|
28
|
-
response := api.Notification{
|
|
29
|
-
ID: "notif-123",
|
|
30
|
-
Read: true,
|
|
31
|
-
ReadAt: "2025-01-01T00:00:00Z",
|
|
32
|
-
CreatedAt: "2025-01-01T00:00:00Z",
|
|
33
|
-
Title: "Test Notification",
|
|
34
|
-
Body: "This is a test notification",
|
|
35
|
-
Creator: api.User{
|
|
36
|
-
ID: "user-123",
|
|
37
|
-
Name: "David Heinemeier Hansson",
|
|
38
|
-
Email: "david@example.com",
|
|
39
|
-
Role: "owner",
|
|
40
|
-
Active: true,
|
|
41
|
-
CreatedAt: "2025-12-05T19:36:35.401Z",
|
|
42
|
-
},
|
|
43
|
-
Card: api.CardReference{
|
|
44
|
-
ID: "card-123",
|
|
45
|
-
Title: "Test Card",
|
|
46
|
-
Status: "published",
|
|
47
|
-
URL: "http://fizzy.localhost:3006/897362094/cards/1",
|
|
48
|
-
},
|
|
49
|
-
URL: "http://fizzy.localhost:3006/897362094/notifications/notif-123",
|
|
50
|
-
}
|
|
51
|
-
json.NewEncoder(w).Encode(response)
|
|
52
|
-
return
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
t.Errorf("unexpected request: %s %s", r.Method, r.URL.Path)
|
|
56
|
-
w.WriteHeader(http.StatusNotFound)
|
|
57
|
-
}))
|
|
58
|
-
defer server.Close()
|
|
59
|
-
|
|
60
|
-
client := testutil.NewTestClient(server.URL, "", "board-123", "test-token")
|
|
61
|
-
testApp := &app.App{
|
|
62
|
-
Client: client,
|
|
63
|
-
Config: &config.Config{SelectedBoard: "board-123"},
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
cmd := notificationReadCmd
|
|
67
|
-
cmd.SetContext(testApp.ToContext(context.Background()))
|
|
68
|
-
|
|
69
|
-
if err := handleReadNotification(cmd, "notif-123"); err != nil {
|
|
70
|
-
t.Fatalf("handleReadNotification failed: %v", err)
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if requestCount != 2 {
|
|
74
|
-
t.Errorf("expected 2 requests (POST and GET), got %d", requestCount)
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
func TestNotificationReadCommandNotFound(t *testing.T) {
|
|
79
|
-
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
80
|
-
w.WriteHeader(http.StatusNotFound)
|
|
81
|
-
w.Write([]byte("Notification not found"))
|
|
82
|
-
}))
|
|
83
|
-
defer server.Close()
|
|
84
|
-
|
|
85
|
-
client := testutil.NewTestClient(server.URL, "", "board-123", "test-token")
|
|
86
|
-
testApp := &app.App{
|
|
87
|
-
Client: client,
|
|
88
|
-
Config: &config.Config{SelectedBoard: "board-123"},
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
cmd := notificationReadCmd
|
|
92
|
-
cmd.SetContext(testApp.ToContext(context.Background()))
|
|
93
|
-
|
|
94
|
-
err := handleReadNotification(cmd, "notif-invalid")
|
|
95
|
-
if err == nil {
|
|
96
|
-
t.Errorf("expected error for invalid notification")
|
|
97
|
-
}
|
|
98
|
-
if err.Error() != "notification not found" {
|
|
99
|
-
t.Errorf("expected 'notification not found' error, got %v", err)
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
func TestNotificationReadCommandAPIError(t *testing.T) {
|
|
104
|
-
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
105
|
-
w.WriteHeader(http.StatusInternalServerError)
|
|
106
|
-
w.Write([]byte("Internal Server Error"))
|
|
107
|
-
}))
|
|
108
|
-
defer server.Close()
|
|
109
|
-
|
|
110
|
-
client := testutil.NewTestClient(server.URL, "", "board-123", "test-token")
|
|
111
|
-
testApp := &app.App{
|
|
112
|
-
Client: client,
|
|
113
|
-
Config: &config.Config{SelectedBoard: "board-123"},
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
cmd := notificationReadCmd
|
|
117
|
-
cmd.SetContext(testApp.ToContext(context.Background()))
|
|
118
|
-
|
|
119
|
-
err := handleReadNotification(cmd, "notif-123")
|
|
120
|
-
if err == nil {
|
|
121
|
-
t.Errorf("expected error for API failure")
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
func TestNotificationReadCommandNoClient(t *testing.T) {
|
|
126
|
-
testApp := &app.App{}
|
|
127
|
-
|
|
128
|
-
cmd := notificationReadCmd
|
|
129
|
-
cmd.SetContext(testApp.ToContext(context.Background()))
|
|
130
|
-
|
|
131
|
-
err := handleReadNotification(cmd, "notif-123")
|
|
132
|
-
if err == nil {
|
|
133
|
-
t.Errorf("expected error when client not available")
|
|
134
|
-
}
|
|
135
|
-
if err.Error() != "API client not available" {
|
|
136
|
-
t.Errorf("expected 'client not available' error, got %v", err)
|
|
137
|
-
}
|
|
138
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
package cmd
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"context"
|
|
5
|
-
"fmt"
|
|
6
|
-
"strings"
|
|
7
|
-
|
|
8
|
-
"github.com/rogeriopvl/fizzy/internal/app"
|
|
9
|
-
"github.com/spf13/cobra"
|
|
10
|
-
)
|
|
11
|
-
|
|
12
|
-
var notificationUnreadCmd = &cobra.Command{
|
|
13
|
-
Use: "unread <notification_id>",
|
|
14
|
-
Short: "Mark notification as unread",
|
|
15
|
-
Long: `Mark a notification as unread`,
|
|
16
|
-
Args: cobra.ExactArgs(1),
|
|
17
|
-
Run: func(cmd *cobra.Command, args []string) {
|
|
18
|
-
if err := handleUnreadNotification(cmd, args[0]); err != nil {
|
|
19
|
-
fmt.Fprintf(cmd.OutOrStderr(), "Error: %v\n", err)
|
|
20
|
-
}
|
|
21
|
-
},
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
func handleUnreadNotification(cmd *cobra.Command, notificationID string) error {
|
|
25
|
-
a := app.FromContext(cmd.Context())
|
|
26
|
-
if a == nil || a.Client == nil {
|
|
27
|
-
return fmt.Errorf("API client not available")
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
_, err := a.Client.DeleteNotificationReading(context.Background(), notificationID)
|
|
31
|
-
if err != nil {
|
|
32
|
-
if strings.Contains(err.Error(), "404") {
|
|
33
|
-
return fmt.Errorf("notification not found")
|
|
34
|
-
}
|
|
35
|
-
return fmt.Errorf("marking notification as unread: %w", err)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
fmt.Fprintf(cmd.OutOrStdout(), "✓ Notification marked as unread successfully\n")
|
|
39
|
-
return nil
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
func init() {
|
|
43
|
-
notificationCmd.AddCommand(notificationUnreadCmd)
|
|
44
|
-
}
|