fizzy-cli 0.8.0 → 1.0.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.
Files changed (159) hide show
  1. package/.golangci.yml +56 -0
  2. package/IMPLEMENTATION_PLAN.md +58 -0
  3. package/docs/API.md +89 -0
  4. package/package.json +2 -1
  5. package/.env +0 -1
  6. package/.github/workflows/release.yml +0 -29
  7. package/.github/workflows/tests.yml +0 -24
  8. package/AGENTS.md +0 -33
  9. package/CHANGELOG.md +0 -194
  10. package/Makefile +0 -43
  11. package/bin/fizzy +0 -0
  12. package/cmd/account.go +0 -14
  13. package/cmd/account_list.go +0 -44
  14. package/cmd/account_list_test.go +0 -118
  15. package/cmd/board.go +0 -49
  16. package/cmd/board_create.go +0 -59
  17. package/cmd/board_create_test.go +0 -141
  18. package/cmd/board_delete.go +0 -40
  19. package/cmd/board_delete_test.go +0 -121
  20. package/cmd/board_list.go +0 -44
  21. package/cmd/board_list_test.go +0 -115
  22. package/cmd/board_show.go +0 -40
  23. package/cmd/board_show_test.go +0 -113
  24. package/cmd/board_test.go +0 -92
  25. package/cmd/board_update.go +0 -72
  26. package/cmd/board_update_test.go +0 -233
  27. package/cmd/card.go +0 -24
  28. package/cmd/card_assign.go +0 -55
  29. package/cmd/card_assign_test.go +0 -130
  30. package/cmd/card_close.go +0 -46
  31. package/cmd/card_close_test.go +0 -92
  32. package/cmd/card_create.go +0 -72
  33. package/cmd/card_create_test.go +0 -186
  34. package/cmd/card_delete.go +0 -46
  35. package/cmd/card_delete_test.go +0 -92
  36. package/cmd/card_golden.go +0 -46
  37. package/cmd/card_golden_test.go +0 -92
  38. package/cmd/card_list.go +0 -114
  39. package/cmd/card_list_test.go +0 -373
  40. package/cmd/card_not_now.go +0 -46
  41. package/cmd/card_not_now_test.go +0 -92
  42. package/cmd/card_reaction.go +0 -13
  43. package/cmd/card_reaction_create.go +0 -46
  44. package/cmd/card_reaction_create_test.go +0 -148
  45. package/cmd/card_reaction_delete.go +0 -46
  46. package/cmd/card_reaction_delete_test.go +0 -112
  47. package/cmd/card_reaction_list.go +0 -51
  48. package/cmd/card_reaction_list_test.go +0 -127
  49. package/cmd/card_reopen.go +0 -46
  50. package/cmd/card_reopen_test.go +0 -92
  51. package/cmd/card_show.go +0 -46
  52. package/cmd/card_show_test.go +0 -92
  53. package/cmd/card_tag.go +0 -51
  54. package/cmd/card_tag_test.go +0 -112
  55. package/cmd/card_triage.go +0 -46
  56. package/cmd/card_ungolden.go +0 -46
  57. package/cmd/card_ungolden_test.go +0 -92
  58. package/cmd/card_untriage.go +0 -46
  59. package/cmd/card_untriage_test.go +0 -92
  60. package/cmd/card_unwatch.go +0 -46
  61. package/cmd/card_unwatch_test.go +0 -92
  62. package/cmd/card_update.go +0 -82
  63. package/cmd/card_update_test.go +0 -149
  64. package/cmd/card_watch.go +0 -46
  65. package/cmd/card_watch_test.go +0 -92
  66. package/cmd/column.go +0 -14
  67. package/cmd/column_create.go +0 -79
  68. package/cmd/column_create_test.go +0 -178
  69. package/cmd/column_delete.go +0 -40
  70. package/cmd/column_delete_test.go +0 -121
  71. package/cmd/column_list.go +0 -44
  72. package/cmd/column_list_test.go +0 -138
  73. package/cmd/column_show.go +0 -40
  74. package/cmd/column_show_test.go +0 -111
  75. package/cmd/column_update.go +0 -67
  76. package/cmd/column_update_test.go +0 -198
  77. package/cmd/comment.go +0 -14
  78. package/cmd/comment_create.go +0 -51
  79. package/cmd/comment_create_test.go +0 -129
  80. package/cmd/comment_delete.go +0 -46
  81. package/cmd/comment_delete_test.go +0 -92
  82. package/cmd/comment_list.go +0 -51
  83. package/cmd/comment_list_test.go +0 -132
  84. package/cmd/comment_show.go +0 -46
  85. package/cmd/comment_show_test.go +0 -104
  86. package/cmd/comment_update.go +0 -51
  87. package/cmd/comment_update_test.go +0 -130
  88. package/cmd/login.go +0 -81
  89. package/cmd/login_test.go +0 -98
  90. package/cmd/notification.go +0 -14
  91. package/cmd/notification_list.go +0 -69
  92. package/cmd/notification_list_test.go +0 -288
  93. package/cmd/notification_read.go +0 -51
  94. package/cmd/notification_read_all.go +0 -38
  95. package/cmd/notification_read_all_test.go +0 -75
  96. package/cmd/notification_read_test.go +0 -138
  97. package/cmd/notification_unread.go +0 -44
  98. package/cmd/notification_unread_test.go +0 -99
  99. package/cmd/reaction.go +0 -13
  100. package/cmd/reaction_create.go +0 -46
  101. package/cmd/reaction_create_test.go +0 -113
  102. package/cmd/reaction_delete.go +0 -46
  103. package/cmd/reaction_delete_test.go +0 -92
  104. package/cmd/reaction_list.go +0 -51
  105. package/cmd/reaction_list_test.go +0 -125
  106. package/cmd/root.go +0 -38
  107. package/cmd/step.go +0 -14
  108. package/cmd/step_create.go +0 -53
  109. package/cmd/step_create_test.go +0 -171
  110. package/cmd/step_delete.go +0 -46
  111. package/cmd/step_delete_test.go +0 -92
  112. package/cmd/step_update.go +0 -66
  113. package/cmd/step_update_test.go +0 -190
  114. package/cmd/tag.go +0 -15
  115. package/cmd/tag_list.go +0 -47
  116. package/cmd/tag_list_test.go +0 -109
  117. package/cmd/use.go +0 -85
  118. package/cmd/use_test.go +0 -186
  119. package/cmd/user.go +0 -22
  120. package/cmd/user_deactivate.go +0 -40
  121. package/cmd/user_deactivate_test.go +0 -121
  122. package/cmd/user_list.go +0 -44
  123. package/cmd/user_list_test.go +0 -126
  124. package/cmd/user_show.go +0 -40
  125. package/cmd/user_show_test.go +0 -110
  126. package/cmd/user_update.go +0 -71
  127. package/cmd/user_update_test.go +0 -177
  128. package/go.mod +0 -31
  129. package/go.sum +0 -53
  130. package/internal/api/boards.go +0 -93
  131. package/internal/api/cards.go +0 -322
  132. package/internal/api/client.go +0 -99
  133. package/internal/api/columns.go +0 -113
  134. package/internal/api/comments.go +0 -108
  135. package/internal/api/identity.go +0 -24
  136. package/internal/api/notifications.go +0 -89
  137. package/internal/api/reactions.go +0 -130
  138. package/internal/api/steps.go +0 -101
  139. package/internal/api/tags.go +0 -24
  140. package/internal/api/types.go +0 -195
  141. package/internal/api/users.go +0 -75
  142. package/internal/app/app.go +0 -49
  143. package/internal/colors/colors.go +0 -32
  144. package/internal/config/config.go +0 -70
  145. package/internal/testutil/client.go +0 -26
  146. package/internal/ui/account_list.go +0 -14
  147. package/internal/ui/account_selector.go +0 -63
  148. package/internal/ui/board_list.go +0 -14
  149. package/internal/ui/board_show.go +0 -17
  150. package/internal/ui/card_list.go +0 -14
  151. package/internal/ui/card_show.go +0 -23
  152. package/internal/ui/column_list.go +0 -28
  153. package/internal/ui/column_show.go +0 -16
  154. package/internal/ui/comment_list.go +0 -25
  155. package/internal/ui/format.go +0 -27
  156. package/internal/ui/notification_list.go +0 -27
  157. package/internal/ui/reaction_list.go +0 -14
  158. package/internal/ui/user_list.go +0 -19
  159. package/internal/ui/user_show.go +0 -23
@@ -1,93 +0,0 @@
1
- package api
2
-
3
- import (
4
- "context"
5
- "fmt"
6
- "net/http"
7
- )
8
-
9
- func (c *Client) GetBoards(ctx context.Context) ([]Board, error) {
10
- endpointURL := c.AccountBaseURL + "/boards"
11
-
12
- req, err := c.newRequest(ctx, http.MethodGet, endpointURL, nil)
13
- if err != nil {
14
- return nil, fmt.Errorf("failed to create request: %w", err)
15
- }
16
-
17
- var response []Board
18
- _, err = c.decodeResponse(req, &response)
19
- if err != nil {
20
- return nil, err
21
- }
22
-
23
- return response, nil
24
- }
25
-
26
- func (c *Client) GetBoard(ctx context.Context, boardID string) (*Board, error) {
27
- endpointURL := c.AccountBaseURL + "/boards/" + boardID
28
-
29
- req, err := c.newRequest(ctx, http.MethodGet, endpointURL, nil)
30
- if err != nil {
31
- return nil, fmt.Errorf("failed to create request: %w", err)
32
- }
33
-
34
- var response Board
35
- _, err = c.decodeResponse(req, &response)
36
- if err != nil {
37
- return nil, err
38
- }
39
-
40
- return &response, nil
41
- }
42
-
43
- func (c *Client) PostBoards(ctx context.Context, payload CreateBoardPayload) (bool, error) {
44
- endpointURL := c.AccountBaseURL + "/boards"
45
-
46
- body := map[string]CreateBoardPayload{"board": payload}
47
-
48
- req, err := c.newRequest(ctx, http.MethodPost, endpointURL, body)
49
- if err != nil {
50
- return false, fmt.Errorf("failed to create board request: %w", err)
51
- }
52
-
53
- _, err = c.decodeResponse(req, nil, http.StatusCreated)
54
- if err != nil {
55
- return false, err
56
- }
57
-
58
- return true, nil
59
- }
60
-
61
- func (c *Client) PutBoard(ctx context.Context, boardID string, payload UpdateBoardPayload) error {
62
- endpointURL := c.AccountBaseURL + "/boards/" + boardID
63
-
64
- body := map[string]UpdateBoardPayload{"board": payload}
65
-
66
- req, err := c.newRequest(ctx, http.MethodPut, endpointURL, body)
67
- if err != nil {
68
- return fmt.Errorf("failed to create update board request: %w", err)
69
- }
70
-
71
- _, err = c.decodeResponse(req, nil, http.StatusNoContent)
72
- if err != nil {
73
- return err
74
- }
75
-
76
- return nil
77
- }
78
-
79
- func (c *Client) DeleteBoard(ctx context.Context, boardID string) error {
80
- endpointURL := c.AccountBaseURL + "/boards/" + boardID
81
-
82
- req, err := c.newRequest(ctx, http.MethodDelete, endpointURL, nil)
83
- if err != nil {
84
- return fmt.Errorf("failed to create delete board request: %w", err)
85
- }
86
-
87
- _, err = c.decodeResponse(req, nil, http.StatusNoContent)
88
- if err != nil {
89
- return err
90
- }
91
-
92
- return nil
93
- }
@@ -1,322 +0,0 @@
1
- package api
2
-
3
- import (
4
- "context"
5
- "fmt"
6
- "net/http"
7
- )
8
-
9
- func (c *Client) GetCards(ctx context.Context, filters CardFilters) ([]Card, error) {
10
- endpointURL := c.AccountBaseURL + "/cards"
11
-
12
- req, err := c.newRequest(ctx, http.MethodGet, endpointURL, nil)
13
- if err != nil {
14
- return nil, fmt.Errorf("failed to create get cards request: %w", err)
15
- }
16
-
17
- q := req.URL.Query()
18
-
19
- for _, boardID := range filters.BoardIDs {
20
- q.Add("board_ids[]", boardID)
21
- }
22
- for _, tagID := range filters.TagIDs {
23
- q.Add("tag_ids[]", tagID)
24
- }
25
- for _, assigneeID := range filters.AssigneeIDs {
26
- q.Add("assignee_ids[]", assigneeID)
27
- }
28
- for _, creatorID := range filters.CreatorIDs {
29
- q.Add("creator_ids[]", creatorID)
30
- }
31
- for _, closerID := range filters.CloserIDs {
32
- q.Add("closer_ids[]", closerID)
33
- }
34
- for _, cardID := range filters.CardIDs {
35
- q.Add("card_ids[]", cardID)
36
- }
37
- for _, term := range filters.Terms {
38
- q.Add("terms[]", term)
39
- }
40
-
41
- if filters.IndexedBy != "" {
42
- q.Set("indexed_by", filters.IndexedBy)
43
- }
44
- if filters.SortedBy != "" {
45
- q.Set("sorted_by", filters.SortedBy)
46
- }
47
- if filters.AssignmentStatus != "" {
48
- q.Set("assignment_status", filters.AssignmentStatus)
49
- }
50
- if filters.CreationStatus != "" {
51
- q.Set("creation", filters.CreationStatus)
52
- }
53
- if filters.ClosureStatus != "" {
54
- q.Set("closure", filters.ClosureStatus)
55
- }
56
-
57
- req.URL.RawQuery = q.Encode()
58
-
59
- var response []Card
60
- _, err = c.decodeResponse(req, &response)
61
- if err != nil {
62
- return nil, err
63
- }
64
-
65
- return response, nil
66
- }
67
-
68
- func (c *Client) GetCard(ctx context.Context, cardNumber int) (*Card, error) {
69
- endpointURL := fmt.Sprintf("%s/cards/%d", c.AccountBaseURL, cardNumber)
70
-
71
- req, err := c.newRequest(ctx, http.MethodGet, endpointURL, nil)
72
- if err != nil {
73
- return nil, fmt.Errorf("failed to create get card by id request: %w", err)
74
- }
75
-
76
- var response Card
77
- _, err = c.decodeResponse(req, &response)
78
- if err != nil {
79
- return nil, err
80
- }
81
-
82
- return &response, nil
83
- }
84
-
85
- func (c *Client) PostCards(ctx context.Context, payload CreateCardPayload) (bool, error) {
86
- if c.BoardBaseURL == "" {
87
- return false, fmt.Errorf("please select a board first with 'fizzy use --board <board_name>'")
88
- }
89
-
90
- endpointURL := c.BoardBaseURL + "/cards"
91
-
92
- body := map[string]CreateCardPayload{"card": payload}
93
-
94
- req, err := c.newRequest(ctx, http.MethodPost, endpointURL, body)
95
- if err != nil {
96
- return false, fmt.Errorf("failed to create card request: %w", err)
97
- }
98
-
99
- _, err = c.decodeResponse(req, nil, http.StatusCreated)
100
- if err != nil {
101
- return false, err
102
- }
103
-
104
- return true, nil
105
- }
106
-
107
- func (c *Client) PutCard(ctx context.Context, cardNumber int, payload UpdateCardPayload) (*Card, error) {
108
- endpointURL := fmt.Sprintf("%s/cards/%d", c.AccountBaseURL, cardNumber)
109
-
110
- body := map[string]UpdateCardPayload{"card": payload}
111
-
112
- req, err := c.newRequest(ctx, http.MethodPut, endpointURL, body)
113
- if err != nil {
114
- return nil, fmt.Errorf("failed to create update card request: %w", err)
115
- }
116
-
117
- var response Card
118
- _, err = c.decodeResponse(req, &response, http.StatusOK)
119
- if err != nil {
120
- return nil, err
121
- }
122
-
123
- return &response, nil
124
- }
125
-
126
- func (c *Client) DeleteCard(ctx context.Context, cardNumber int) (bool, error) {
127
- endpointURL := fmt.Sprintf("%s/cards/%d", c.AccountBaseURL, cardNumber)
128
-
129
- req, err := c.newRequest(ctx, http.MethodDelete, endpointURL, nil)
130
- if err != nil {
131
- return false, fmt.Errorf("failed to create delete card request: %w", err)
132
- }
133
-
134
- _, err = c.decodeResponse(req, nil, http.StatusNoContent)
135
- if err != nil {
136
- return false, err
137
- }
138
-
139
- return true, nil
140
- }
141
-
142
- func (c *Client) PostCardsClosure(ctx context.Context, cardNumber int) (bool, error) {
143
- endpointURL := fmt.Sprintf("%s/cards/%d/closure", c.AccountBaseURL, cardNumber)
144
-
145
- req, err := c.newRequest(ctx, http.MethodPost, endpointURL, nil)
146
- if err != nil {
147
- return false, fmt.Errorf("failed to create closure card request: %w", err)
148
- }
149
-
150
- _, err = c.decodeResponse(req, nil, http.StatusNoContent)
151
- if err != nil {
152
- return false, err
153
- }
154
-
155
- return true, nil
156
- }
157
-
158
- func (c *Client) PostCardNotNow(ctx context.Context, cardNumber int) (bool, error) {
159
- endpointURL := fmt.Sprintf("%s/cards/%d/not_now", c.AccountBaseURL, cardNumber)
160
-
161
- req, err := c.newRequest(ctx, http.MethodPost, endpointURL, nil)
162
- if err != nil {
163
- return false, fmt.Errorf("failed to create post not now request: %w", err)
164
- }
165
-
166
- _, err = c.decodeResponse(req, nil, http.StatusNoContent)
167
- if err != nil {
168
- return false, err
169
- }
170
-
171
- return true, nil
172
- }
173
-
174
- func (c *Client) PostCardTriage(ctx context.Context, cardNumber int, columnID string) (bool, error) {
175
- endpointURL := fmt.Sprintf("%s/cards/%d/triage", c.AccountBaseURL, cardNumber)
176
-
177
- body := map[string]any{"column_id": columnID}
178
-
179
- req, err := c.newRequest(ctx, http.MethodPost, endpointURL, body)
180
- if err != nil {
181
- return false, fmt.Errorf("failed to create post triage request: %w", err)
182
- }
183
-
184
- _, err = c.decodeResponse(req, nil, http.StatusNoContent)
185
- if err != nil {
186
- return false, err
187
- }
188
-
189
- return true, nil
190
- }
191
-
192
- func (c *Client) DeleteCardTriage(ctx context.Context, cardNumber int) (bool, error) {
193
- endpointURL := fmt.Sprintf("%s/cards/%d/triage", c.AccountBaseURL, cardNumber)
194
-
195
- req, err := c.newRequest(ctx, http.MethodDelete, endpointURL, nil)
196
- if err != nil {
197
- return false, fmt.Errorf("failed to create delete triage request: %w", err)
198
- }
199
-
200
- _, err = c.decodeResponse(req, nil, http.StatusNoContent)
201
- if err != nil {
202
- return false, err
203
- }
204
-
205
- return true, nil
206
- }
207
-
208
- func (c *Client) PostCardWatch(ctx context.Context, cardNumber int) (bool, error) {
209
- endpointURL := fmt.Sprintf("%s/cards/%d/watch", c.AccountBaseURL, cardNumber)
210
-
211
- req, err := c.newRequest(ctx, http.MethodPost, endpointURL, nil)
212
- if err != nil {
213
- return false, fmt.Errorf("failed to create post watch request: %w", err)
214
- }
215
-
216
- _, err = c.decodeResponse(req, nil, http.StatusNoContent)
217
- if err != nil {
218
- return false, err
219
- }
220
-
221
- return true, nil
222
- }
223
-
224
- func (c *Client) DeleteCardWatch(ctx context.Context, cardNumber int) (bool, error) {
225
- endpointURL := fmt.Sprintf("%s/cards/%d/watch", c.AccountBaseURL, cardNumber)
226
-
227
- req, err := c.newRequest(ctx, http.MethodDelete, endpointURL, nil)
228
- if err != nil {
229
- return false, fmt.Errorf("failed to create delete watch request: %w", err)
230
- }
231
-
232
- _, err = c.decodeResponse(req, nil, http.StatusNoContent)
233
- if err != nil {
234
- return false, err
235
- }
236
-
237
- return true, nil
238
- }
239
-
240
- func (c *Client) PostCardGoldenness(ctx context.Context, cardNumber int) (bool, error) {
241
- endpointURL := fmt.Sprintf("%s/cards/%d/goldness", c.AccountBaseURL, cardNumber)
242
-
243
- req, err := c.newRequest(ctx, http.MethodPost, endpointURL, nil)
244
- if err != nil {
245
- return false, fmt.Errorf("failed to create post goldness request: %w", err)
246
- }
247
-
248
- _, err = c.decodeResponse(req, nil, http.StatusNoContent)
249
- if err != nil {
250
- return false, err
251
- }
252
-
253
- return true, nil
254
- }
255
-
256
- func (c *Client) DeleteCardGoldenness(ctx context.Context, cardNumber int) (bool, error) {
257
- endpointURL := fmt.Sprintf("%s/cards/%d/goldness", c.AccountBaseURL, cardNumber)
258
-
259
- req, err := c.newRequest(ctx, http.MethodDelete, endpointURL, nil)
260
- if err != nil {
261
- return false, fmt.Errorf("failed to create delete goldness request: %w", err)
262
- }
263
-
264
- _, err = c.decodeResponse(req, nil, http.StatusNoContent)
265
- if err != nil {
266
- return false, err
267
- }
268
-
269
- return true, nil
270
- }
271
-
272
- func (c *Client) DeleteCardsClosure(ctx context.Context, cardNumber int) (bool, error) {
273
- endpointURL := fmt.Sprintf("%s/cards/%d/closure", c.AccountBaseURL, cardNumber)
274
-
275
- req, err := c.newRequest(ctx, http.MethodDelete, endpointURL, nil)
276
- if err != nil {
277
- return false, fmt.Errorf("failed to create delete closure card request: %w", err)
278
- }
279
-
280
- _, err = c.decodeResponse(req, nil, http.StatusNoContent)
281
- if err != nil {
282
- return false, err
283
- }
284
-
285
- return true, nil
286
- }
287
-
288
- func (c *Client) PostCardAssignments(ctx context.Context, cardNumber int, userID string) (bool, error) {
289
- endpointURL := fmt.Sprintf("%s/cards/%d/assignments", c.AccountBaseURL, cardNumber)
290
-
291
- body := map[string]string{"assignee_id": userID}
292
-
293
- req, err := c.newRequest(ctx, http.MethodPost, endpointURL, body)
294
- if err != nil {
295
- return false, fmt.Errorf("failed to create assignment request: %w", err)
296
- }
297
-
298
- _, err = c.decodeResponse(req, nil, http.StatusNoContent)
299
- if err != nil {
300
- return false, err
301
- }
302
-
303
- return true, nil
304
- }
305
-
306
- func (c *Client) PostCardTagging(ctx context.Context, cardNumber int, tagTitle string) (bool, error) {
307
- endpointURL := fmt.Sprintf("%s/cards/%d/taggings", c.AccountBaseURL, cardNumber)
308
-
309
- body := map[string]string{"tag_title": tagTitle}
310
-
311
- req, err := c.newRequest(ctx, http.MethodPost, endpointURL, body)
312
- if err != nil {
313
- return false, fmt.Errorf("failed to create tagging request: %w", err)
314
- }
315
-
316
- _, err = c.decodeResponse(req, nil, http.StatusNoContent)
317
- if err != nil {
318
- return false, err
319
- }
320
-
321
- return true, nil
322
- }
@@ -1,99 +0,0 @@
1
- // Package api provides the Fizzy API client
2
- package api
3
-
4
- import (
5
- "bytes"
6
- "context"
7
- "encoding/json"
8
- "fmt"
9
- "io"
10
- "net/http"
11
- "os"
12
- "time"
13
- )
14
-
15
- type Client struct {
16
- BaseURL string
17
- AccountBaseURL string
18
- BoardBaseURL string
19
- AccessToken string
20
- HTTPClient *http.Client
21
- }
22
-
23
- func NewClient(accountSlug string, boardID string) (*Client, error) {
24
- baseURL := "https://app.fizzy.do"
25
- accountBaseURL := baseURL + accountSlug
26
-
27
- var boardBaseURL string
28
- if boardID != "" {
29
- boardBaseURL = accountBaseURL + "/boards" + "/" + boardID
30
- }
31
-
32
- token, isSet := os.LookupEnv("FIZZY_ACCESS_TOKEN")
33
- if !isSet || token == "" {
34
- return nil, fmt.Errorf("FIZZY_ACCESS_TOKEN environment variable is not set")
35
- }
36
-
37
- return &Client{
38
- BaseURL: baseURL,
39
- AccountBaseURL: accountBaseURL,
40
- BoardBaseURL: boardBaseURL,
41
- AccessToken: token,
42
- HTTPClient: &http.Client{Timeout: 30 * time.Second},
43
- }, nil
44
- }
45
-
46
- // newRequest makes an HTTP request with the required headers setup
47
- func (c *Client) newRequest(ctx context.Context, method, url string, body any) (*http.Request, error) {
48
- var bodyReader io.Reader
49
- if body != nil {
50
- data, err := json.Marshal(body)
51
- if err != nil {
52
- return nil, fmt.Errorf("failed to marshal request body: %w", err)
53
- }
54
- bodyReader = bytes.NewReader(data)
55
- }
56
-
57
- req, err := http.NewRequestWithContext(ctx, method, url, bodyReader)
58
- if err != nil {
59
- return nil, err
60
- }
61
-
62
- req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.AccessToken))
63
- req.Header.Set("Accept", "application/json")
64
- req.Header.Set("Content-Type", "application/json")
65
-
66
- return req, nil
67
- }
68
-
69
- // decodeResponse executes a request and decodes the JSON response into v
70
- // If expectedStatus is 0, it defaults to http.StatusOK
71
- // If v is nil, the response body is not decoded
72
- func (c *Client) decodeResponse(req *http.Request, v any, expectedStatus ...int) (int, error) {
73
- expectedCode := http.StatusOK
74
- if len(expectedStatus) > 0 {
75
- expectedCode = expectedStatus[0]
76
- }
77
-
78
- res, err := c.HTTPClient.Do(req)
79
- if err != nil {
80
- return 0, fmt.Errorf("failed to make request: %w", err)
81
- }
82
- defer res.Body.Close()
83
-
84
- if res.StatusCode != expectedCode {
85
- body, err := io.ReadAll(res.Body)
86
- if err != nil {
87
- return 0, fmt.Errorf("unexpected status code %d (failed to read error response: %w)", res.StatusCode, err)
88
- }
89
- return 0, fmt.Errorf("unexpected status code %d: %s", res.StatusCode, string(body))
90
- }
91
-
92
- if v != nil {
93
- if err := json.NewDecoder(res.Body).Decode(v); err != nil {
94
- return 0, fmt.Errorf("failed to decode response: %w", err)
95
- }
96
- }
97
-
98
- return res.StatusCode, nil
99
- }
@@ -1,113 +0,0 @@
1
- package api
2
-
3
- import (
4
- "context"
5
- "fmt"
6
- "net/http"
7
- )
8
-
9
- func (c *Client) GetColumns(ctx context.Context) ([]Column, error) {
10
- if c.BoardBaseURL == "" {
11
- return nil, fmt.Errorf("please select a board first with 'fizzy use --board <board_name>'")
12
- }
13
-
14
- endpointURL := c.BoardBaseURL + "/columns"
15
-
16
- req, err := c.newRequest(ctx, http.MethodGet, endpointURL, nil)
17
- if err != nil {
18
- return nil, fmt.Errorf("failed to create get columns request: %w", err)
19
- }
20
-
21
- var response []Column
22
- _, err = c.decodeResponse(req, &response)
23
- if err != nil {
24
- return nil, err
25
- }
26
-
27
- return response, nil
28
- }
29
-
30
- func (c *Client) GetColumn(ctx context.Context, columnID string) (*Column, error) {
31
- if c.BoardBaseURL == "" {
32
- return nil, fmt.Errorf("please select a board first with 'fizzy use --board <board_name>'")
33
- }
34
-
35
- endpointURL := c.BoardBaseURL + "/columns/" + columnID
36
-
37
- req, err := c.newRequest(ctx, http.MethodGet, endpointURL, nil)
38
- if err != nil {
39
- return nil, fmt.Errorf("failed to create request: %w", err)
40
- }
41
-
42
- var response Column
43
- _, err = c.decodeResponse(req, &response)
44
- if err != nil {
45
- return nil, err
46
- }
47
-
48
- return &response, nil
49
- }
50
-
51
- func (c *Client) PostColumns(ctx context.Context, payload CreateColumnPayload) (bool, error) {
52
- if c.BoardBaseURL == "" {
53
- return false, fmt.Errorf("please select a board first with 'fizzy use --board <board_name>'")
54
- }
55
-
56
- endpointURL := c.BoardBaseURL + "/columns"
57
-
58
- body := map[string]CreateColumnPayload{"column": payload}
59
-
60
- req, err := c.newRequest(ctx, http.MethodPost, endpointURL, body)
61
- if err != nil {
62
- return false, fmt.Errorf("failed to create column request: %w", err)
63
- }
64
-
65
- _, err = c.decodeResponse(req, nil, http.StatusCreated)
66
- if err != nil {
67
- return false, err
68
- }
69
-
70
- return true, nil
71
- }
72
-
73
- func (c *Client) PutColumn(ctx context.Context, columnID string, payload UpdateColumnPayload) error {
74
- if c.BoardBaseURL == "" {
75
- return fmt.Errorf("please select a board first with 'fizzy use --board <board_name>'")
76
- }
77
-
78
- endpointURL := c.BoardBaseURL + "/columns/" + columnID
79
-
80
- body := map[string]UpdateColumnPayload{"column": payload}
81
-
82
- req, err := c.newRequest(ctx, http.MethodPut, endpointURL, body)
83
- if err != nil {
84
- return fmt.Errorf("failed to create update column request: %w", err)
85
- }
86
-
87
- _, err = c.decodeResponse(req, nil, http.StatusNoContent)
88
- if err != nil {
89
- return err
90
- }
91
-
92
- return nil
93
- }
94
-
95
- func (c *Client) DeleteColumn(ctx context.Context, columnID string) error {
96
- if c.BoardBaseURL == "" {
97
- return fmt.Errorf("please select a board first with 'fizzy use --board <board_name>'")
98
- }
99
-
100
- endpointURL := c.BoardBaseURL + "/columns/" + columnID
101
-
102
- req, err := c.newRequest(ctx, http.MethodDelete, endpointURL, nil)
103
- if err != nil {
104
- return fmt.Errorf("failed to create delete column request: %w", err)
105
- }
106
-
107
- _, err = c.decodeResponse(req, nil, http.StatusNoContent)
108
- if err != nil {
109
- return err
110
- }
111
-
112
- return nil
113
- }