Files
claudia-docs-cli/internal/api/projects.go
Claudia CLI Bot aca95d90f3 Initial MVP implementation of Claudia Docs CLI
- Auth commands: login, logout, status
- Document commands: create, list, get, update, delete
- Project commands: list, get
- Folder commands: list, create
- Tag commands: list, create, add
- Search command
- Reasoning save command
- JSON output format with --output text option
- Viper-based configuration management
- Cobra CLI framework
2026-03-31 01:25:15 +00:00

138 lines
3.0 KiB
Go

package api
import (
"fmt"
"io"
"net/http"
"time"
"github.com/claudia/docs-cli/pkg/types"
)
// ProjectsClient handles project API calls
type ProjectsClient struct {
client *Client
}
// NewProjectsClient creates a new projects client
func NewProjectsClient(c *Client) *ProjectsClient {
return &ProjectsClient{client: c}
}
// List lists all projects
func (p *ProjectsClient) List() ([]types.Project, error) {
resp, err := p.client.doRequest(http.MethodGet, "/projects", nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response: %w", err)
}
if resp.StatusCode != http.StatusOK {
return nil, handleError(resp, body)
}
var projResp types.Response
if err := decodeJSON(body, &projResp); err != nil {
return nil, err
}
return sliceToProjects(projResp.Data), nil
}
// Get gets a project by ID
func (p *ProjectsClient) Get(id string) (*types.Project, error) {
resp, err := p.client.doRequest(http.MethodGet, "/projects/"+id, nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response: %w", err)
}
if resp.StatusCode != http.StatusOK {
return nil, handleError(resp, body)
}
var projResp types.Response
if err := decodeJSON(body, &projResp); err != nil {
return nil, err
}
proj, ok := projResp.Data.(map[string]interface{})
if !ok {
return nil, fmt.Errorf("unexpected response format")
}
return mapToProject(proj), nil
}
// Create creates a new project
func (p *ProjectsClient) Create(req types.CreateProjectRequest) (*types.Project, error) {
resp, err := p.client.doRequest(http.MethodPost, "/projects", req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response: %w", err)
}
if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusOK {
return nil, handleError(resp, body)
}
var projResp types.Response
if err := decodeJSON(body, &projResp); err != nil {
return nil, err
}
proj, ok := projResp.Data.(map[string]interface{})
if !ok {
return nil, fmt.Errorf("unexpected response format")
}
return mapToProject(proj), nil
}
func mapToProject(m map[string]interface{}) *types.Project {
proj := &types.Project{}
if v, ok := m["id"].(string); ok {
proj.ID = v
}
if v, ok := m["name"].(string); ok {
proj.Name = v
}
if v, ok := m["description"].(string); ok {
proj.Description = v
}
if v, ok := m["created_at"].(string); ok {
proj.CreatedAt, _ = time.Parse(time.RFC3339, v)
}
if v, ok := m["updated_at"].(string); ok {
proj.UpdatedAt, _ = time.Parse(time.RFC3339, v)
}
return proj
}
func sliceToProjects(data interface{}) []types.Project {
var projects []types.Project
if arr, ok := data.([]interface{}); ok {
for _, item := range arr {
if m, ok := item.(map[string]interface{}); ok {
projects = append(projects, *mapToProject(m))
}
}
}
return projects
}