fizzy-cli 0.2.1 → 0.4.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 +29 -0
- package/IMPLEMENTATION_PLAN.md +338 -0
- package/bin/fizzy +0 -0
- package/cmd/board.go +1 -1
- package/cmd/card_assign.go +55 -0
- package/cmd/card_assign_test.go +130 -0
- package/cmd/card_golden.go +46 -0
- package/cmd/card_golden_test.go +92 -0
- package/cmd/card_not_now.go +46 -0
- package/cmd/card_not_now_test.go +92 -0
- package/cmd/card_tag.go +51 -0
- package/cmd/card_tag_test.go +112 -0
- package/cmd/card_triage.go +46 -0
- package/cmd/card_ungolden.go +46 -0
- package/cmd/card_ungolden_test.go +92 -0
- package/cmd/card_untriage.go +46 -0
- package/cmd/card_untriage_test.go +92 -0
- package/cmd/card_unwatch.go +46 -0
- package/cmd/card_unwatch_test.go +92 -0
- package/cmd/card_update.go +0 -1
- package/cmd/card_update_test.go +0 -2
- package/cmd/card_watch.go +46 -0
- package/cmd/card_watch_test.go +92 -0
- package/cmd/login.go +2 -1
- package/cmd/notification.go +14 -0
- package/cmd/notification_list.go +69 -0
- package/cmd/notification_list_test.go +288 -0
- package/cmd/notification_read.go +51 -0
- package/cmd/notification_read_all.go +38 -0
- package/cmd/notification_read_all_test.go +75 -0
- package/cmd/notification_read_test.go +138 -0
- package/cmd/notification_unread.go +44 -0
- package/cmd/notification_unread_test.go +99 -0
- package/cmd/tag.go +15 -0
- package/cmd/tag_list.go +47 -0
- package/cmd/tag_list_test.go +109 -0
- package/docs/API.md +180 -1
- package/go.mod +1 -1
- package/internal/api/client.go +275 -0
- package/internal/config/config.go +1 -0
- package/internal/ui/notification_list.go +27 -0
- package/package.json +1 -1
package/internal/api/client.go
CHANGED
|
@@ -307,6 +307,120 @@ func (c *Client) PostCardsClosure(ctx context.Context, cardNumber int) (bool, er
|
|
|
307
307
|
return true, nil
|
|
308
308
|
}
|
|
309
309
|
|
|
310
|
+
func (c *Client) PostCardNotNow(ctx context.Context, cardNumber int) (bool, error) {
|
|
311
|
+
endpointURL := fmt.Sprintf("%s/cards/%d/not_now", c.AccountBaseURL, cardNumber)
|
|
312
|
+
|
|
313
|
+
req, err := c.newRequest(ctx, http.MethodPost, endpointURL, nil)
|
|
314
|
+
if err != nil {
|
|
315
|
+
return false, fmt.Errorf("failed to create post not now request: %w", err)
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
_, err = c.decodeResponse(req, nil, http.StatusNoContent)
|
|
319
|
+
if err != nil {
|
|
320
|
+
return false, err
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return true, nil
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
func (c *Client) PostCardTriage(ctx context.Context, cardNumber int, columnID string) (bool, error) {
|
|
327
|
+
endpointURL := fmt.Sprintf("%s/cards/%d/triage", c.AccountBaseURL, cardNumber)
|
|
328
|
+
|
|
329
|
+
body := map[string]any{"column_id": columnID}
|
|
330
|
+
|
|
331
|
+
req, err := c.newRequest(ctx, http.MethodPost, endpointURL, body)
|
|
332
|
+
if err != nil {
|
|
333
|
+
return false, fmt.Errorf("failed to create post triage request: %w", err)
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
_, err = c.decodeResponse(req, nil, http.StatusNoContent)
|
|
337
|
+
if err != nil {
|
|
338
|
+
return false, err
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return true, nil
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
func (c *Client) DeleteCardTriage(ctx context.Context, cardNumber int) (bool, error) {
|
|
345
|
+
endpointURL := fmt.Sprintf("%s/cards/%d/triage", c.AccountBaseURL, cardNumber)
|
|
346
|
+
|
|
347
|
+
req, err := c.newRequest(ctx, http.MethodDelete, endpointURL, nil)
|
|
348
|
+
if err != nil {
|
|
349
|
+
return false, fmt.Errorf("failed to create delete triage request: %w", err)
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
_, err = c.decodeResponse(req, nil, http.StatusNoContent)
|
|
353
|
+
if err != nil {
|
|
354
|
+
return false, err
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
return true, nil
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
func (c *Client) PostCardWatch(ctx context.Context, cardNumber int) (bool, error) {
|
|
361
|
+
endpointURL := fmt.Sprintf("%s/cards/%d/watch", c.AccountBaseURL, cardNumber)
|
|
362
|
+
|
|
363
|
+
req, err := c.newRequest(ctx, http.MethodPost, endpointURL, nil)
|
|
364
|
+
if err != nil {
|
|
365
|
+
return false, fmt.Errorf("failed to create post watch request: %w", err)
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
_, err = c.decodeResponse(req, nil, http.StatusNoContent)
|
|
369
|
+
if err != nil {
|
|
370
|
+
return false, err
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
return true, nil
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
func (c *Client) DeleteCardWatch(ctx context.Context, cardNumber int) (bool, error) {
|
|
377
|
+
endpointURL := fmt.Sprintf("%s/cards/%d/watch", c.AccountBaseURL, cardNumber)
|
|
378
|
+
|
|
379
|
+
req, err := c.newRequest(ctx, http.MethodDelete, endpointURL, nil)
|
|
380
|
+
if err != nil {
|
|
381
|
+
return false, fmt.Errorf("failed to create delete watch request: %w", err)
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
_, err = c.decodeResponse(req, nil, http.StatusNoContent)
|
|
385
|
+
if err != nil {
|
|
386
|
+
return false, err
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
return true, nil
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
func (c *Client) PostCardGoldenness(ctx context.Context, cardNumber int) (bool, error) {
|
|
393
|
+
endpointURL := fmt.Sprintf("%s/cards/%d/goldness", c.AccountBaseURL, cardNumber)
|
|
394
|
+
|
|
395
|
+
req, err := c.newRequest(ctx, http.MethodPost, endpointURL, nil)
|
|
396
|
+
if err != nil {
|
|
397
|
+
return false, fmt.Errorf("failed to create post goldness request: %w", err)
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
_, err = c.decodeResponse(req, nil, http.StatusNoContent)
|
|
401
|
+
if err != nil {
|
|
402
|
+
return false, err
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
return true, nil
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
func (c *Client) DeleteCardGoldenness(ctx context.Context, cardNumber int) (bool, error) {
|
|
409
|
+
endpointURL := fmt.Sprintf("%s/cards/%d/goldness", c.AccountBaseURL, cardNumber)
|
|
410
|
+
|
|
411
|
+
req, err := c.newRequest(ctx, http.MethodDelete, endpointURL, nil)
|
|
412
|
+
if err != nil {
|
|
413
|
+
return false, fmt.Errorf("failed to create delete goldness request: %w", err)
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
_, err = c.decodeResponse(req, nil, http.StatusNoContent)
|
|
417
|
+
if err != nil {
|
|
418
|
+
return false, err
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
return true, nil
|
|
422
|
+
}
|
|
423
|
+
|
|
310
424
|
func (c *Client) DeleteCardsClosure(ctx context.Context, cardNumber int) (bool, error) {
|
|
311
425
|
endpointURL := fmt.Sprintf("%s/cards/%d/closure", c.AccountBaseURL, cardNumber)
|
|
312
426
|
|
|
@@ -323,6 +437,42 @@ func (c *Client) DeleteCardsClosure(ctx context.Context, cardNumber int) (bool,
|
|
|
323
437
|
return true, nil
|
|
324
438
|
}
|
|
325
439
|
|
|
440
|
+
func (c *Client) PostCardAssignments(ctx context.Context, cardNumber int, userID string) (bool, error) {
|
|
441
|
+
endpointURL := fmt.Sprintf("%s/cards/%d/assignments", c.AccountBaseURL, cardNumber)
|
|
442
|
+
|
|
443
|
+
body := map[string]string{"assignee_id": userID}
|
|
444
|
+
|
|
445
|
+
req, err := c.newRequest(ctx, http.MethodPost, endpointURL, body)
|
|
446
|
+
if err != nil {
|
|
447
|
+
return false, fmt.Errorf("failed to create assignment request: %w", err)
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
_, err = c.decodeResponse(req, nil, http.StatusNoContent)
|
|
451
|
+
if err != nil {
|
|
452
|
+
return false, err
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
return true, nil
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
func (c *Client) PostCardTagging(ctx context.Context, cardNumber int, tagTitle string) (bool, error) {
|
|
459
|
+
endpointURL := fmt.Sprintf("%s/cards/%d/taggings", c.AccountBaseURL, cardNumber)
|
|
460
|
+
|
|
461
|
+
body := map[string]string{"tag_title": tagTitle}
|
|
462
|
+
|
|
463
|
+
req, err := c.newRequest(ctx, http.MethodPost, endpointURL, body)
|
|
464
|
+
if err != nil {
|
|
465
|
+
return false, fmt.Errorf("failed to create tagging request: %w", err)
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
_, err = c.decodeResponse(req, nil, http.StatusNoContent)
|
|
469
|
+
if err != nil {
|
|
470
|
+
return false, err
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
return true, nil
|
|
474
|
+
}
|
|
475
|
+
|
|
326
476
|
func (c *Client) GetMyIdentity(ctx context.Context) (*GetMyIdentityResponse, error) {
|
|
327
477
|
endpointURL := c.BaseURL + "/my/identity"
|
|
328
478
|
|
|
@@ -340,6 +490,105 @@ func (c *Client) GetMyIdentity(ctx context.Context) (*GetMyIdentityResponse, err
|
|
|
340
490
|
return &response, nil
|
|
341
491
|
}
|
|
342
492
|
|
|
493
|
+
func (c *Client) GetNotifications(ctx context.Context) ([]Notification, error) {
|
|
494
|
+
endpointURL := c.AccountBaseURL + "/notifications"
|
|
495
|
+
|
|
496
|
+
req, err := c.newRequest(ctx, http.MethodGet, endpointURL, nil)
|
|
497
|
+
if err != nil {
|
|
498
|
+
return nil, fmt.Errorf("failed to create get notifications request: %w", err)
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
var response []Notification
|
|
502
|
+
_, err = c.decodeResponse(req, &response)
|
|
503
|
+
if err != nil {
|
|
504
|
+
return nil, err
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
return response, nil
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
func (c *Client) GetNotification(ctx context.Context, notificationID string) (*Notification, error) {
|
|
511
|
+
endpointURL := fmt.Sprintf("%s/notifications/%s", c.AccountBaseURL, notificationID)
|
|
512
|
+
|
|
513
|
+
req, err := c.newRequest(ctx, http.MethodGet, endpointURL, nil)
|
|
514
|
+
if err != nil {
|
|
515
|
+
return nil, fmt.Errorf("failed to create get notification request: %w", err)
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
var response Notification
|
|
519
|
+
_, err = c.decodeResponse(req, &response)
|
|
520
|
+
if err != nil {
|
|
521
|
+
return nil, err
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
return &response, nil
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
func (c *Client) PostNotificationReading(ctx context.Context, notificationID string) (bool, error) {
|
|
528
|
+
endpointURL := fmt.Sprintf("%s/notifications/%s/reading", c.AccountBaseURL, notificationID)
|
|
529
|
+
|
|
530
|
+
req, err := c.newRequest(ctx, http.MethodPost, endpointURL, nil)
|
|
531
|
+
if err != nil {
|
|
532
|
+
return false, fmt.Errorf("failed to create mark notification as read request: %w", err)
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
_, err = c.decodeResponse(req, nil, http.StatusNoContent)
|
|
536
|
+
if err != nil {
|
|
537
|
+
return false, err
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
return true, nil
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
func (c *Client) DeleteNotificationReading(ctx context.Context, notificationID string) (bool, error) {
|
|
544
|
+
endpointURL := fmt.Sprintf("%s/notifications/%s/reading", c.AccountBaseURL, notificationID)
|
|
545
|
+
|
|
546
|
+
req, err := c.newRequest(ctx, http.MethodDelete, endpointURL, nil)
|
|
547
|
+
if err != nil {
|
|
548
|
+
return false, fmt.Errorf("failed to create delete notification request: %w", err)
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
_, err = c.decodeResponse(req, nil, http.StatusNoContent)
|
|
552
|
+
if err != nil {
|
|
553
|
+
return false, err
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
return true, nil
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
func (c *Client) PostBulkNotificationsReading(ctx context.Context) (bool, error) {
|
|
560
|
+
endpointURL := c.AccountBaseURL + "/notifications/bulk_reading"
|
|
561
|
+
|
|
562
|
+
req, err := c.newRequest(ctx, http.MethodPost, endpointURL, nil)
|
|
563
|
+
if err != nil {
|
|
564
|
+
return false, fmt.Errorf("failed to create bulk notifications reading request: %w", err)
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
_, err = c.decodeResponse(req, nil, http.StatusNoContent)
|
|
568
|
+
if err != nil {
|
|
569
|
+
return false, err
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
return true, nil
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
func (c *Client) GetTags(ctx context.Context) ([]Tag, error) {
|
|
576
|
+
endpointURL := c.AccountBaseURL + "/tags"
|
|
577
|
+
|
|
578
|
+
req, err := c.newRequest(ctx, http.MethodGet, endpointURL, nil)
|
|
579
|
+
if err != nil {
|
|
580
|
+
return nil, fmt.Errorf("failed to create get tags request: %w", err)
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
var response []Tag
|
|
584
|
+
_, err = c.decodeResponse(req, &response)
|
|
585
|
+
if err != nil {
|
|
586
|
+
return nil, err
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
return response, nil
|
|
590
|
+
}
|
|
591
|
+
|
|
343
592
|
type Board struct {
|
|
344
593
|
ID string `json:"id"`
|
|
345
594
|
Name string `json:"name"`
|
|
@@ -447,6 +696,32 @@ type User struct {
|
|
|
447
696
|
URL string `json:"url"`
|
|
448
697
|
}
|
|
449
698
|
|
|
699
|
+
type Notification struct {
|
|
700
|
+
ID string `json:"id"`
|
|
701
|
+
Read bool `json:"read"`
|
|
702
|
+
ReadAt string `json:"read_at"`
|
|
703
|
+
CreatedAt string `json:"created_at"`
|
|
704
|
+
Title string `json:"title"`
|
|
705
|
+
Body string `json:"body"`
|
|
706
|
+
Creator User `json:"creator"`
|
|
707
|
+
Card CardReference `json:"card"`
|
|
708
|
+
URL string `json:"url"`
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
type CardReference struct {
|
|
712
|
+
ID string `json:"id"`
|
|
713
|
+
Title string `json:"title"`
|
|
714
|
+
Status string `json:"status"`
|
|
715
|
+
URL string `json:"url"`
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
type Tag struct {
|
|
719
|
+
ID string `json:"id"`
|
|
720
|
+
Title string `json:"title"`
|
|
721
|
+
CreatedAt string `json:"created_at"`
|
|
722
|
+
URL string `json:"url"`
|
|
723
|
+
}
|
|
724
|
+
|
|
450
725
|
type Color string
|
|
451
726
|
|
|
452
727
|
// Color constants using centralized definitions
|
|
@@ -16,6 +16,7 @@ const (
|
|
|
16
16
|
type Config struct {
|
|
17
17
|
SelectedAccount string `json:"selected_account"`
|
|
18
18
|
SelectedBoard string `json:"selected_board"`
|
|
19
|
+
CurrentUserID string `json:"current_user_id"`
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
// Load reads the config file from $HOME/.config/fizzy-cli/config.json.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
package ui
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
|
|
6
|
+
"github.com/rogeriopvl/fizzy/internal/api"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
func DisplayNotifications(notifications []api.Notification) error {
|
|
10
|
+
for _, notification := range notifications {
|
|
11
|
+
fmt.Printf("%s (%s)\n", notification.Title, DisplayID(notification.ID))
|
|
12
|
+
}
|
|
13
|
+
return nil
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
func DisplayNotification(notification *api.Notification) error {
|
|
17
|
+
status := "read"
|
|
18
|
+
if !notification.Read {
|
|
19
|
+
status = "unread"
|
|
20
|
+
}
|
|
21
|
+
fmt.Printf("%s (%s)\n", notification.Title, DisplayID(notification.ID))
|
|
22
|
+
fmt.Printf("Status: %s\n", status)
|
|
23
|
+
fmt.Printf("Card: %s\n", notification.Card.Title)
|
|
24
|
+
fmt.Printf("From: %s\n", notification.Creator.Name)
|
|
25
|
+
fmt.Printf("Message: %s\n", notification.Body)
|
|
26
|
+
return nil
|
|
27
|
+
}
|