270 lines
6.2 KiB
Go
270 lines
6.2 KiB
Go
package client
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/RyanCopley/skybridge/user/internal/domain"
|
|
)
|
|
|
|
// UserClient provides an interface to interact with the user service
|
|
type UserClient struct {
|
|
baseURL string
|
|
httpClient *http.Client
|
|
userEmail string // For authentication
|
|
}
|
|
|
|
// NewUserClient creates a new user service client
|
|
func NewUserClient(baseURL, userEmail string) *UserClient {
|
|
return &UserClient{
|
|
baseURL: baseURL,
|
|
httpClient: &http.Client{
|
|
Timeout: 30 * time.Second,
|
|
},
|
|
userEmail: userEmail,
|
|
}
|
|
}
|
|
|
|
// NewUserClientWithTimeout creates a new user service client with custom timeout
|
|
func NewUserClientWithTimeout(baseURL, userEmail string, timeout time.Duration) *UserClient {
|
|
return &UserClient{
|
|
baseURL: baseURL,
|
|
httpClient: &http.Client{
|
|
Timeout: timeout,
|
|
},
|
|
userEmail: userEmail,
|
|
}
|
|
}
|
|
|
|
// CreateUser creates a new user
|
|
func (c *UserClient) CreateUser(req *domain.CreateUserRequest) (*domain.User, error) {
|
|
body, err := json.Marshal(req)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to marshal request: %w", err)
|
|
}
|
|
|
|
httpReq, err := c.newRequest("POST", "/api/users", bytes.NewBuffer(body))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var user domain.User
|
|
err = c.doRequest(httpReq, &user)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &user, nil
|
|
}
|
|
|
|
// GetUserByID retrieves a user by ID
|
|
func (c *UserClient) GetUserByID(id uuid.UUID) (*domain.User, error) {
|
|
path := fmt.Sprintf("/api/users/%s", id.String())
|
|
httpReq, err := c.newRequest("GET", path, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var user domain.User
|
|
err = c.doRequest(httpReq, &user)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &user, nil
|
|
}
|
|
|
|
// GetUserByEmail retrieves a user by email
|
|
func (c *UserClient) GetUserByEmail(email string) (*domain.User, error) {
|
|
path := fmt.Sprintf("/api/users/email/%s", url.PathEscape(email))
|
|
httpReq, err := c.newRequest("GET", path, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var user domain.User
|
|
err = c.doRequest(httpReq, &user)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &user, nil
|
|
}
|
|
|
|
// UpdateUser updates an existing user
|
|
func (c *UserClient) UpdateUser(id uuid.UUID, req *domain.UpdateUserRequest) (*domain.User, error) {
|
|
body, err := json.Marshal(req)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to marshal request: %w", err)
|
|
}
|
|
|
|
path := fmt.Sprintf("/api/users/%s", id.String())
|
|
httpReq, err := c.newRequest("PUT", path, bytes.NewBuffer(body))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var user domain.User
|
|
err = c.doRequest(httpReq, &user)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &user, nil
|
|
}
|
|
|
|
// DeleteUser deletes a user by ID
|
|
func (c *UserClient) DeleteUser(id uuid.UUID) error {
|
|
path := fmt.Sprintf("/api/users/%s", id.String())
|
|
httpReq, err := c.newRequest("DELETE", path, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return c.doRequest(httpReq, nil)
|
|
}
|
|
|
|
// ListUsers retrieves a list of users with optional filters
|
|
func (c *UserClient) ListUsers(req *domain.ListUsersRequest) (*domain.ListUsersResponse, error) {
|
|
// Build query parameters
|
|
params := url.Values{}
|
|
|
|
if req.Status != nil {
|
|
params.Set("status", string(*req.Status))
|
|
}
|
|
if req.Role != nil {
|
|
params.Set("role", string(*req.Role))
|
|
}
|
|
if req.Search != "" {
|
|
params.Set("search", req.Search)
|
|
}
|
|
if req.Limit > 0 {
|
|
params.Set("limit", strconv.Itoa(req.Limit))
|
|
}
|
|
if req.Offset > 0 {
|
|
params.Set("offset", strconv.Itoa(req.Offset))
|
|
}
|
|
if req.OrderBy != "" {
|
|
params.Set("order_by", req.OrderBy)
|
|
}
|
|
if req.OrderDir != "" {
|
|
params.Set("order_dir", req.OrderDir)
|
|
}
|
|
|
|
path := "/api/users"
|
|
if len(params) > 0 {
|
|
path += "?" + params.Encode()
|
|
}
|
|
|
|
httpReq, err := c.newRequest("GET", path, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var response domain.ListUsersResponse
|
|
err = c.doRequest(httpReq, &response)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &response, nil
|
|
}
|
|
|
|
// ExistsByEmail checks if a user exists with the given email
|
|
func (c *UserClient) ExistsByEmail(email string) (bool, error) {
|
|
path := fmt.Sprintf("/api/users/exists/%s", url.PathEscape(email))
|
|
httpReq, err := c.newRequest("GET", path, nil)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
var response map[string]interface{}
|
|
err = c.doRequest(httpReq, &response)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
exists, ok := response["exists"].(bool)
|
|
if !ok {
|
|
return false, fmt.Errorf("invalid response format")
|
|
}
|
|
|
|
return exists, nil
|
|
}
|
|
|
|
// Health checks the health of the user service
|
|
func (c *UserClient) Health() (map[string]interface{}, error) {
|
|
httpReq, err := c.newRequest("GET", "/health", nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var response map[string]interface{}
|
|
err = c.doRequest(httpReq, &response)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return response, nil
|
|
}
|
|
|
|
// newRequest creates a new HTTP request with authentication headers
|
|
func (c *UserClient) newRequest(method, path string, body io.Reader) (*http.Request, error) {
|
|
url := c.baseURL + path
|
|
req, err := http.NewRequest(method, url, body)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create request: %w", err)
|
|
}
|
|
|
|
req.Header.Set("Content-Type", "application/json")
|
|
req.Header.Set("Accept", "application/json")
|
|
|
|
// Add authentication header
|
|
if c.userEmail != "" {
|
|
req.Header.Set("X-User-Email", c.userEmail)
|
|
}
|
|
|
|
return req, nil
|
|
}
|
|
|
|
// doRequest executes an HTTP request and handles the response
|
|
func (c *UserClient) doRequest(req *http.Request, target interface{}) error {
|
|
resp, err := c.httpClient.Do(req)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to execute request: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to read response body: %w", err)
|
|
}
|
|
|
|
if resp.StatusCode >= 400 {
|
|
var errorResponse map[string]interface{}
|
|
if json.Unmarshal(body, &errorResponse) == nil {
|
|
if errorMsg, ok := errorResponse["error"].(string); ok {
|
|
return fmt.Errorf("API error (status %d): %s", resp.StatusCode, errorMsg)
|
|
}
|
|
}
|
|
return fmt.Errorf("HTTP error (status %d): %s", resp.StatusCode, string(body))
|
|
}
|
|
|
|
// If target is nil, we don't need to unmarshal (e.g., for DELETE requests)
|
|
if target == nil {
|
|
return nil
|
|
}
|
|
|
|
if err := json.Unmarshal(body, target); err != nil {
|
|
return fmt.Errorf("failed to unmarshal response: %w", err)
|
|
}
|
|
|
|
return nil
|
|
} |