Released v1
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"git.franklin.lab/steve.cliff/pcli/model"
|
||||
)
|
||||
|
||||
func (c *Client) ListBoards(ctx context.Context) ([]model.Board, error) {
|
||||
data, err := c.DoNoBody(ctx, "GET", "/api/projects")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var response struct {
|
||||
Included struct {
|
||||
Boards []model.Board `json:"boards"`
|
||||
} `json:"included"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, &response); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal projects response: %w", err)
|
||||
}
|
||||
|
||||
return response.Included.Boards, nil
|
||||
}
|
||||
|
||||
func (c *Client) GetBoard(ctx context.Context, id string) (*model.Board, error) {
|
||||
data, err := c.DoNoBody(ctx, "GET", fmt.Sprintf("/api/boards/%s", id))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var response struct {
|
||||
Item model.Board `json:"item"`
|
||||
Included struct {
|
||||
Lists []model.List `json:"lists"`
|
||||
Cards []model.Card `json:"cards"`
|
||||
} `json:"included"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, &response); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal board response: %w", err)
|
||||
}
|
||||
|
||||
response.Item.Lists = response.Included.Lists
|
||||
response.Item.Cards = response.Included.Cards
|
||||
|
||||
return &response.Item, nil
|
||||
}
|
||||
|
||||
func (c *Client) ListBoardActions(ctx context.Context, boardId string, limit int) ([]model.Action, error) {
|
||||
var all []model.Action
|
||||
var beforeId string
|
||||
|
||||
for {
|
||||
path := fmt.Sprintf("/api/boards/%s/actions", boardId)
|
||||
if beforeId != "" {
|
||||
path = fmt.Sprintf("%s?beforeId=%s", path, beforeId)
|
||||
}
|
||||
|
||||
c.Logger.Debug("Fetching board actions page",
|
||||
slog.String("boardId", boardId),
|
||||
slog.String("beforeId", beforeId),
|
||||
)
|
||||
|
||||
data, err := c.DoNoBody(ctx, "GET", path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var response struct {
|
||||
Items []model.Action `json:"items"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, &response); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal board actions response: %w", err)
|
||||
}
|
||||
|
||||
if len(response.Items) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
all = append(all, response.Items...)
|
||||
|
||||
if limit > 0 && len(all) >= limit {
|
||||
all = all[:limit]
|
||||
break
|
||||
}
|
||||
|
||||
beforeId = response.Items[len(response.Items)-1].ID
|
||||
}
|
||||
|
||||
return all, nil
|
||||
}
|
||||
+250
@@ -0,0 +1,250 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"git.franklin.lab/steve.cliff/pcli/model"
|
||||
)
|
||||
|
||||
func (c *Client) GetCard(ctx context.Context, id string) (*model.Card, error) {
|
||||
data, err := c.DoNoBody(ctx, "GET", fmt.Sprintf("/api/cards/%s", id))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var response struct {
|
||||
Item model.Card `json:"item"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, &response); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal card response: %w", err)
|
||||
}
|
||||
|
||||
return &response.Item, nil
|
||||
}
|
||||
|
||||
func (c *Client) CreateCard(ctx context.Context, listId string, fields map[string]any) (*model.Card, error) {
|
||||
data, err := c.Do(ctx, "POST", fmt.Sprintf("/api/lists/%s/cards", listId), fields)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var response struct {
|
||||
Item model.Card `json:"item"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, &response); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal card response: %w", err)
|
||||
}
|
||||
|
||||
return &response.Item, nil
|
||||
}
|
||||
|
||||
func (c *Client) UpdateCard(ctx context.Context, id string, fields map[string]any) (*model.Card, error) {
|
||||
data, err := c.Do(ctx, "PATCH", fmt.Sprintf("/api/cards/%s", id), fields)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var response struct {
|
||||
Item model.Card `json:"item"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, &response); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal card response: %w", err)
|
||||
}
|
||||
|
||||
return &response.Item, nil
|
||||
}
|
||||
|
||||
func (c *Client) DeleteCard(ctx context.Context, id string) error {
|
||||
_, err := c.DoNoBody(ctx, "DELETE", fmt.Sprintf("/api/cards/%s", id))
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Client) DuplicateCard(ctx context.Context, id string, name *string, position *float64) (*model.Card, error) {
|
||||
fields := make(map[string]any)
|
||||
if name != nil {
|
||||
fields["name"] = *name
|
||||
}
|
||||
if position != nil {
|
||||
fields["position"] = *position
|
||||
}
|
||||
|
||||
data, err := c.Do(ctx, "POST", fmt.Sprintf("/api/cards/%s/duplicate", id), fields)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var response struct {
|
||||
Item model.Card `json:"item"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, &response); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal card response: %w", err)
|
||||
}
|
||||
|
||||
return &response.Item, nil
|
||||
}
|
||||
|
||||
func (c *Client) ListCards(ctx context.Context, listId string, limit int) ([]model.Card, error) {
|
||||
var all []model.Card
|
||||
var before string
|
||||
|
||||
for {
|
||||
path := fmt.Sprintf("/api/lists/%s/cards", listId)
|
||||
if before != "" {
|
||||
path = fmt.Sprintf("%s?before=%s", path, before)
|
||||
}
|
||||
|
||||
c.Logger.Debug("Fetching cards page",
|
||||
slog.String("listId", listId),
|
||||
slog.String("before", before),
|
||||
)
|
||||
|
||||
data, err := c.DoNoBody(ctx, "GET", path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var response struct {
|
||||
Items []model.Card `json:"items"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, &response); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal cards response: %w", err)
|
||||
}
|
||||
|
||||
if len(response.Items) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
all = append(all, response.Items...)
|
||||
|
||||
if limit > 0 && len(all) >= limit {
|
||||
all = all[:limit]
|
||||
break
|
||||
}
|
||||
|
||||
before = response.Items[len(response.Items)-1].ID
|
||||
}
|
||||
|
||||
return all, nil
|
||||
}
|
||||
|
||||
func (c *Client) ListCardActions(ctx context.Context, cardId string, limit int) ([]model.Action, error) {
|
||||
var all []model.Action
|
||||
var beforeId string
|
||||
|
||||
for {
|
||||
path := fmt.Sprintf("/api/cards/%s/actions", cardId)
|
||||
if beforeId != "" {
|
||||
path = fmt.Sprintf("%s?beforeId=%s", path, beforeId)
|
||||
}
|
||||
|
||||
c.Logger.Debug("Fetching card actions page",
|
||||
slog.String("cardId", cardId),
|
||||
slog.String("beforeId", beforeId),
|
||||
)
|
||||
|
||||
data, err := c.DoNoBody(ctx, "GET", path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var response struct {
|
||||
Items []model.Action `json:"items"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, &response); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal card actions response: %w", err)
|
||||
}
|
||||
|
||||
if len(response.Items) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
all = append(all, response.Items...)
|
||||
|
||||
if limit > 0 && len(all) >= limit {
|
||||
all = all[:limit]
|
||||
break
|
||||
}
|
||||
|
||||
beforeId = response.Items[len(response.Items)-1].ID
|
||||
}
|
||||
|
||||
return all, nil
|
||||
}
|
||||
|
||||
func (c *Client) ListCardsByBoard(ctx context.Context, boardId string, limit int) ([]model.CardWithList, error) {
|
||||
board, err := c.GetBoard(ctx, boardId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get board: %w", err)
|
||||
}
|
||||
|
||||
listNames := make(map[string]string)
|
||||
for _, list := range board.Lists {
|
||||
name := ""
|
||||
if list.Name != nil {
|
||||
name = *list.Name
|
||||
}
|
||||
listNames[list.ID] = name
|
||||
}
|
||||
|
||||
var allCards []model.CardWithList
|
||||
for _, card := range board.Cards {
|
||||
cardWithList := model.CardWithList{
|
||||
Card: card,
|
||||
ListName: listNames[card.ListID],
|
||||
}
|
||||
allCards = append(allCards, cardWithList)
|
||||
|
||||
if limit > 0 && len(allCards) >= limit {
|
||||
return allCards[:limit], nil
|
||||
}
|
||||
}
|
||||
|
||||
return allCards, nil
|
||||
}
|
||||
|
||||
func (c *Client) AddCardLabel(ctx context.Context, cardId, labelId string) error {
|
||||
fields := map[string]any{
|
||||
"labelId": labelId,
|
||||
}
|
||||
|
||||
_, err := c.DoWithFallback(ctx, "POST",
|
||||
fmt.Sprintf("/api/cards/%s/card-labels", cardId),
|
||||
fmt.Sprintf("/api/cards/%s/labels", cardId),
|
||||
fields)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Client) RemoveCardLabel(ctx context.Context, cardId, labelId string) error {
|
||||
_, err := c.DoNoBodyWithFallback(ctx, "DELETE",
|
||||
fmt.Sprintf("/api/cards/%s/card-labels/labelId:%s", cardId, labelId),
|
||||
fmt.Sprintf("/api/cards/%s/labels/%s", cardId, labelId))
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Client) AddCardMember(ctx context.Context, cardId, userId string) error {
|
||||
fields := map[string]any{
|
||||
"userId": userId,
|
||||
}
|
||||
|
||||
_, err := c.DoWithFallback(ctx, "POST",
|
||||
fmt.Sprintf("/api/cards/%s/card-memberships", cardId),
|
||||
fmt.Sprintf("/api/cards/%s/memberships", cardId),
|
||||
fields)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Client) RemoveCardMember(ctx context.Context, cardId, userId string) error {
|
||||
_, err := c.DoNoBodyWithFallback(ctx, "DELETE",
|
||||
fmt.Sprintf("/api/cards/%s/card-memberships/userId:%s", cardId, userId),
|
||||
fmt.Sprintf("/api/cards/%s/memberships/%s", cardId, userId))
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.franklin.lab/steve.cliff/pcli/model"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
BaseURL string
|
||||
APIKey string
|
||||
HTTPClient *http.Client
|
||||
Logger *slog.Logger
|
||||
}
|
||||
|
||||
func NewClient(baseURL, apiKey string, logger *slog.Logger) *Client {
|
||||
transport := &http.Transport{
|
||||
MaxIdleConns: 10,
|
||||
MaxIdleConnsPerHost: 5,
|
||||
IdleConnTimeout: 30 * time.Second,
|
||||
}
|
||||
return &Client{
|
||||
BaseURL: baseURL,
|
||||
APIKey: apiKey,
|
||||
HTTPClient: &http.Client{
|
||||
Timeout: 30 * time.Second,
|
||||
Transport: transport,
|
||||
},
|
||||
Logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) Do(ctx context.Context, method, path string, body any) (json.RawMessage, error) {
|
||||
start := time.Now()
|
||||
|
||||
url := c.BaseURL + path
|
||||
|
||||
var reqBody io.Reader
|
||||
if body != nil {
|
||||
jsonData, err := json.Marshal(body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal request body: %w", err)
|
||||
}
|
||||
reqBody = bytes.NewReader(jsonData)
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, method, url, reqBody)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
|
||||
req.Header.Set("x-api-key", c.APIKey)
|
||||
if body != nil {
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
}
|
||||
|
||||
c.Logger.Debug("HTTP request",
|
||||
slog.String("method", method),
|
||||
slog.String("path", path),
|
||||
)
|
||||
|
||||
resp, err := c.HTTPClient.Do(req)
|
||||
if err != nil {
|
||||
c.Logger.Warn("HTTP request failed",
|
||||
slog.String("method", method),
|
||||
slog.String("path", path),
|
||||
slog.String("error", err.Error()),
|
||||
)
|
||||
return nil, fmt.Errorf("HTTP request failed: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
duration := time.Since(start)
|
||||
|
||||
c.Logger.Debug("HTTP response",
|
||||
slog.String("method", method),
|
||||
slog.String("path", path),
|
||||
slog.Int("status", resp.StatusCode),
|
||||
slog.Duration("duration", duration),
|
||||
)
|
||||
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read response body: %w", err)
|
||||
}
|
||||
|
||||
contentType := resp.Header.Get("Content-Type")
|
||||
if len(respBody) > 0 && !isJSONContentType(contentType) && respBody[0] == '<' {
|
||||
c.Logger.Warn("Non-JSON response received",
|
||||
slog.String("method", method),
|
||||
slog.String("path", path),
|
||||
slog.Int("status", resp.StatusCode),
|
||||
slog.String("content_type", contentType),
|
||||
)
|
||||
return nil, &model.APIError{
|
||||
StatusCode: resp.StatusCode,
|
||||
Message: fmt.Sprintf("unexpected non-JSON response (content-type: %s) — endpoint may not exist", contentType),
|
||||
}
|
||||
}
|
||||
|
||||
if resp.StatusCode >= 400 {
|
||||
apiErr := &model.APIError{
|
||||
StatusCode: resp.StatusCode,
|
||||
Message: string(respBody),
|
||||
}
|
||||
|
||||
var errResp map[string]any
|
||||
if json.Unmarshal(respBody, &errResp) == nil {
|
||||
if msg, ok := errResp["message"].(string); ok {
|
||||
apiErr.Message = msg
|
||||
} else if errMsg, ok := errResp["error"].(string); ok {
|
||||
apiErr.Message = errMsg
|
||||
}
|
||||
}
|
||||
|
||||
c.Logger.Warn("API error",
|
||||
slog.String("method", method),
|
||||
slog.String("path", path),
|
||||
slog.Int("status", resp.StatusCode),
|
||||
slog.String("message", apiErr.Message),
|
||||
)
|
||||
|
||||
return nil, apiErr
|
||||
}
|
||||
|
||||
return json.RawMessage(respBody), nil
|
||||
}
|
||||
|
||||
func (c *Client) DoNoBody(ctx context.Context, method, path string) (json.RawMessage, error) {
|
||||
return c.Do(ctx, method, path, nil)
|
||||
}
|
||||
|
||||
func (c *Client) DoWithFallback(ctx context.Context, method, primary, fallback string, body any) (json.RawMessage, error) {
|
||||
data, err := c.Do(ctx, method, primary, body)
|
||||
if err != nil {
|
||||
if apiErr, ok := err.(*model.APIError); ok && apiErr.StatusCode == 404 {
|
||||
c.Logger.Debug("Primary path failed, trying fallback",
|
||||
slog.String("primary", primary),
|
||||
slog.String("fallback", fallback),
|
||||
)
|
||||
return c.Do(ctx, method, fallback, body)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (c *Client) DoNoBodyWithFallback(ctx context.Context, method, primary, fallback string) (json.RawMessage, error) {
|
||||
return c.DoWithFallback(ctx, method, primary, fallback, nil)
|
||||
}
|
||||
|
||||
func isJSONContentType(contentType string) bool {
|
||||
ct := strings.ToLower(strings.TrimSpace(contentType))
|
||||
return strings.HasPrefix(ct, "application/json") ||
|
||||
strings.HasPrefix(ct, "text/json")
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"git.franklin.lab/steve.cliff/pcli/model"
|
||||
)
|
||||
|
||||
func (c *Client) ListComments(ctx context.Context, cardId string, limit int) ([]model.Comment, error) {
|
||||
var all []model.Comment
|
||||
var beforeId string
|
||||
|
||||
for {
|
||||
path := fmt.Sprintf("/api/cards/%s/comments", cardId)
|
||||
if beforeId != "" {
|
||||
path = fmt.Sprintf("%s?beforeId=%s", path, beforeId)
|
||||
}
|
||||
fallback := fmt.Sprintf("/api/cards/%s/comment-actions", cardId)
|
||||
if beforeId != "" {
|
||||
fallback = fmt.Sprintf("%s?beforeId=%s", fallback, beforeId)
|
||||
}
|
||||
|
||||
c.Logger.Debug("Fetching comments page",
|
||||
slog.String("cardId", cardId),
|
||||
slog.String("beforeId", beforeId),
|
||||
)
|
||||
|
||||
data, err := c.DoNoBodyWithFallback(ctx, "GET", path, fallback)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var response struct {
|
||||
Items []model.Comment `json:"items"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, &response); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal comments response: %w", err)
|
||||
}
|
||||
|
||||
if len(response.Items) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
all = append(all, response.Items...)
|
||||
|
||||
if limit > 0 && len(all) >= limit {
|
||||
all = all[:limit]
|
||||
break
|
||||
}
|
||||
|
||||
beforeId = response.Items[len(response.Items)-1].ID
|
||||
}
|
||||
|
||||
return all, nil
|
||||
}
|
||||
|
||||
func (c *Client) CreateComment(ctx context.Context, cardId, text string) (*model.Comment, error) {
|
||||
fields := map[string]any{
|
||||
"text": text,
|
||||
}
|
||||
|
||||
data, err := c.DoWithFallback(ctx, "POST",
|
||||
fmt.Sprintf("/api/cards/%s/comments", cardId),
|
||||
fmt.Sprintf("/api/cards/%s/comment-actions", cardId),
|
||||
fields)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var response struct {
|
||||
Item model.Comment `json:"item"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, &response); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal comment response: %w", err)
|
||||
}
|
||||
|
||||
return &response.Item, nil
|
||||
}
|
||||
|
||||
func (c *Client) UpdateComment(ctx context.Context, id, text string) (*model.Comment, error) {
|
||||
fields := map[string]any{
|
||||
"text": text,
|
||||
}
|
||||
|
||||
data, err := c.DoWithFallback(ctx, "PATCH",
|
||||
fmt.Sprintf("/api/comments/%s", id),
|
||||
fmt.Sprintf("/api/comment-actions/%s", id),
|
||||
fields)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var response struct {
|
||||
Item model.Comment `json:"item"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, &response); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal comment response: %w", err)
|
||||
}
|
||||
|
||||
return &response.Item, nil
|
||||
}
|
||||
|
||||
func (c *Client) DeleteComment(ctx context.Context, id string) error {
|
||||
_, err := c.DoNoBodyWithFallback(ctx, "DELETE",
|
||||
fmt.Sprintf("/api/comments/%s", id),
|
||||
fmt.Sprintf("/api/comment-actions/%s", id))
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"git.franklin.lab/steve.cliff/pcli/model"
|
||||
)
|
||||
|
||||
func (c *Client) CreateLabel(ctx context.Context, boardId string, fields map[string]any) (*model.Label, error) {
|
||||
data, err := c.Do(ctx, "POST", fmt.Sprintf("/api/boards/%s/labels", boardId), fields)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var response struct {
|
||||
Item model.Label `json:"item"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, &response); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal label response: %w", err)
|
||||
}
|
||||
|
||||
return &response.Item, nil
|
||||
}
|
||||
|
||||
func (c *Client) UpdateLabel(ctx context.Context, id string, fields map[string]any) (*model.Label, error) {
|
||||
data, err := c.Do(ctx, "PATCH", fmt.Sprintf("/api/labels/%s", id), fields)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var response struct {
|
||||
Item model.Label `json:"item"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, &response); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal label response: %w", err)
|
||||
}
|
||||
|
||||
return &response.Item, nil
|
||||
}
|
||||
|
||||
func (c *Client) DeleteLabel(ctx context.Context, id string) error {
|
||||
_, err := c.DoNoBody(ctx, "DELETE", fmt.Sprintf("/api/labels/%s", id))
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"git.franklin.lab/steve.cliff/pcli/model"
|
||||
)
|
||||
|
||||
func (c *Client) ListProjects(ctx context.Context) ([]model.Project, error) {
|
||||
data, err := c.DoNoBody(ctx, "GET", "/api/projects")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var response struct {
|
||||
Items []model.Project `json:"items"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, &response); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal projects response: %w", err)
|
||||
}
|
||||
|
||||
return response.Items, nil
|
||||
}
|
||||
|
||||
func (c *Client) GetProject(ctx context.Context, id string) (*model.Project, error) {
|
||||
data, err := c.DoNoBody(ctx, "GET", fmt.Sprintf("/api/projects/%s", id))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var response struct {
|
||||
Item model.Project `json:"item"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, &response); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal project response: %w", err)
|
||||
}
|
||||
|
||||
return &response.Item, nil
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"git.franklin.lab/steve.cliff/pcli/model"
|
||||
)
|
||||
|
||||
func (c *Client) CreateTaskList(ctx context.Context, cardId string, fields map[string]any) (*model.TaskList, error) {
|
||||
data, err := c.Do(ctx, "POST", fmt.Sprintf("/api/cards/%s/task-lists", cardId), fields)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var response struct {
|
||||
Item model.TaskList `json:"item"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, &response); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal task list response: %w", err)
|
||||
}
|
||||
|
||||
return &response.Item, nil
|
||||
}
|
||||
|
||||
func (c *Client) GetTaskList(ctx context.Context, id string) (*model.TaskList, error) {
|
||||
data, err := c.DoNoBody(ctx, "GET", fmt.Sprintf("/api/task-lists/%s", id))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var response struct {
|
||||
Item model.TaskList `json:"item"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, &response); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal task list response: %w", err)
|
||||
}
|
||||
|
||||
return &response.Item, nil
|
||||
}
|
||||
|
||||
func (c *Client) UpdateTaskList(ctx context.Context, id string, fields map[string]any) (*model.TaskList, error) {
|
||||
data, err := c.Do(ctx, "PATCH", fmt.Sprintf("/api/task-lists/%s", id), fields)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var response struct {
|
||||
Item model.TaskList `json:"item"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, &response); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal task list response: %w", err)
|
||||
}
|
||||
|
||||
return &response.Item, nil
|
||||
}
|
||||
|
||||
func (c *Client) DeleteTaskList(ctx context.Context, id string) error {
|
||||
_, err := c.DoNoBody(ctx, "DELETE", fmt.Sprintf("/api/task-lists/%s", id))
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"git.franklin.lab/steve.cliff/pcli/model"
|
||||
)
|
||||
|
||||
func (c *Client) CreateTask(ctx context.Context, taskListId string, fields map[string]any) (*model.Task, error) {
|
||||
data, err := c.Do(ctx, "POST", fmt.Sprintf("/api/task-lists/%s/tasks", taskListId), fields)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var response struct {
|
||||
Item model.Task `json:"item"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, &response); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal task response: %w", err)
|
||||
}
|
||||
|
||||
return &response.Item, nil
|
||||
}
|
||||
|
||||
func (c *Client) UpdateTask(ctx context.Context, id string, fields map[string]any) (*model.Task, error) {
|
||||
data, err := c.Do(ctx, "PATCH", fmt.Sprintf("/api/tasks/%s", id), fields)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var response struct {
|
||||
Item model.Task `json:"item"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, &response); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal task response: %w", err)
|
||||
}
|
||||
|
||||
return &response.Item, nil
|
||||
}
|
||||
|
||||
func (c *Client) DeleteTask(ctx context.Context, id string) error {
|
||||
_, err := c.DoNoBody(ctx, "DELETE", fmt.Sprintf("/api/tasks/%s", id))
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user