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
This commit is contained in:
137
internal/api/projects.go
Normal file
137
internal/api/projects.go
Normal file
@@ -0,0 +1,137 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user