Files
wild-cli/internal/client/client.go

162 lines
3.9 KiB
Go

// Package client provides HTTP client for Wild Central daemon API
package client
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
)
// Client is the HTTP client for the Wild Central daemon
type Client struct {
baseURL string
httpClient *http.Client
}
// NewClient creates a new API client
func NewClient(baseURL string) *Client {
return &Client{
baseURL: baseURL,
httpClient: &http.Client{
Timeout: 30 * time.Second,
},
}
}
// APIResponse is the API response format
type APIResponse struct {
Data map[string]interface{}
Error string `json:"error,omitempty"`
}
// Get makes a GET request to the API
func (c *Client) Get(path string) (*APIResponse, error) {
return c.doRequest("GET", path, nil)
}
// Post makes a POST request to the API
func (c *Client) Post(path string, body interface{}) (*APIResponse, error) {
return c.doRequest("POST", path, body)
}
// Put makes a PUT request to the API
func (c *Client) Put(path string, body interface{}) (*APIResponse, error) {
return c.doRequest("PUT", path, body)
}
// Delete makes a DELETE request to the API
func (c *Client) Delete(path string) (*APIResponse, error) {
return c.doRequest("DELETE", path, nil)
}
// Patch makes a PATCH request to the API
func (c *Client) Patch(path string, body interface{}) (*APIResponse, error) {
return c.doRequest("PATCH", path, body)
}
// doRequest performs the actual HTTP request
func (c *Client) doRequest(method, path string, body interface{}) (*APIResponse, error) {
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.NewBuffer(jsonData)
}
req, err := http.NewRequest(method, url, reqBody)
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
if body != nil {
req.Header.Set("Content-Type", "application/json")
}
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("request failed: %w", err)
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response: %w", err)
}
// Check for HTTP error status
if resp.StatusCode >= 400 {
// Try to parse error response
var errResp map[string]interface{}
if err := json.Unmarshal(respBody, &errResp); err == nil {
if errMsg, ok := errResp["error"].(string); ok {
return nil, fmt.Errorf("API error: %s", errMsg)
}
}
return nil, fmt.Errorf("API request failed with status %d: %s", resp.StatusCode, string(respBody))
}
// Parse response data directly (daemon doesn't wrap in "data" field)
var data map[string]interface{}
if err := json.Unmarshal(respBody, &data); err != nil {
return nil, fmt.Errorf("failed to parse response: %w\nResponse: %s", err, string(respBody))
}
return &APIResponse{Data: data}, nil
}
// GetData extracts data from API response
func (r *APIResponse) GetData(key string) interface{} {
if r.Data == nil {
return nil
}
return r.Data[key]
}
// GetString extracts string data from API response
func (r *APIResponse) GetString(key string) string {
val := r.GetData(key)
if str, ok := val.(string); ok {
return str
}
return ""
}
// GetMap extracts map data from API response
func (r *APIResponse) GetMap(key string) map[string]interface{} {
val := r.GetData(key)
if m, ok := val.(map[string]interface{}); ok {
return m
}
return nil
}
// GetArray extracts array data from API response
func (r *APIResponse) GetArray(key string) []interface{} {
val := r.GetData(key)
if arr, ok := val.([]interface{}); ok {
return arr
}
return nil
}
// GetBool extracts boolean data from API response
func (r *APIResponse) GetBool(key string) bool {
val := r.GetData(key)
if b, ok := val.(bool); ok {
return b
}
return false
}
// BaseURL returns the base URL of the client
func (c *Client) BaseURL() string {
return c.baseURL
}