| package ionet |
|
|
| import ( |
| "bytes" |
| "encoding/json" |
| "fmt" |
| "net/http" |
| "net/url" |
| "strconv" |
| "time" |
| ) |
|
|
| const ( |
| DefaultEnterpriseBaseURL = "https://api.io.solutions/enterprise/v1/io-cloud/caas" |
| DefaultBaseURL = "https://api.io.solutions/v1/io-cloud/caas" |
| DefaultTimeout = 30 * time.Second |
| ) |
|
|
| |
| type DefaultHTTPClient struct { |
| client *http.Client |
| } |
|
|
| |
| func NewDefaultHTTPClient(timeout time.Duration) *DefaultHTTPClient { |
| return &DefaultHTTPClient{ |
| client: &http.Client{ |
| Timeout: timeout, |
| }, |
| } |
| } |
|
|
| |
| func (c *DefaultHTTPClient) Do(req *HTTPRequest) (*HTTPResponse, error) { |
| httpReq, err := http.NewRequest(req.Method, req.URL, bytes.NewReader(req.Body)) |
| if err != nil { |
| return nil, fmt.Errorf("failed to create HTTP request: %w", err) |
| } |
|
|
| |
| for key, value := range req.Headers { |
| httpReq.Header.Set(key, value) |
| } |
|
|
| resp, err := c.client.Do(httpReq) |
| if err != nil { |
| return nil, fmt.Errorf("HTTP request failed: %w", err) |
| } |
| defer resp.Body.Close() |
|
|
| |
| var body bytes.Buffer |
| _, err = body.ReadFrom(resp.Body) |
| if err != nil { |
| return nil, fmt.Errorf("failed to read response body: %w", err) |
| } |
|
|
| |
| headers := make(map[string]string) |
| for key, values := range resp.Header { |
| if len(values) > 0 { |
| headers[key] = values[0] |
| } |
| } |
|
|
| return &HTTPResponse{ |
| StatusCode: resp.StatusCode, |
| Headers: headers, |
| Body: body.Bytes(), |
| }, nil |
| } |
|
|
| |
| func NewEnterpriseClient(apiKey string) *Client { |
| return NewClientWithConfig(apiKey, DefaultEnterpriseBaseURL, nil) |
| } |
|
|
| |
| func NewClient(apiKey string) *Client { |
| return NewClientWithConfig(apiKey, DefaultBaseURL, nil) |
| } |
|
|
| |
| func NewClientWithConfig(apiKey, baseURL string, httpClient HTTPClient) *Client { |
| if baseURL == "" { |
| baseURL = DefaultBaseURL |
| } |
| if httpClient == nil { |
| httpClient = NewDefaultHTTPClient(DefaultTimeout) |
| } |
| return &Client{ |
| BaseURL: baseURL, |
| APIKey: apiKey, |
| HTTPClient: httpClient, |
| } |
| } |
|
|
| |
| func (c *Client) makeRequest(method, endpoint string, body interface{}) (*HTTPResponse, error) { |
| var reqBody []byte |
| var err error |
|
|
| if body != nil { |
| reqBody, err = json.Marshal(body) |
| if err != nil { |
| return nil, fmt.Errorf("failed to marshal request body: %w", err) |
| } |
| } |
|
|
| headers := map[string]string{ |
| "X-API-KEY": c.APIKey, |
| "Content-Type": "application/json", |
| } |
|
|
| req := &HTTPRequest{ |
| Method: method, |
| URL: c.BaseURL + endpoint, |
| Headers: headers, |
| Body: reqBody, |
| } |
|
|
| resp, err := c.HTTPClient.Do(req) |
| if err != nil { |
| return nil, fmt.Errorf("request failed: %w", err) |
| } |
|
|
| |
| if resp.StatusCode >= 400 { |
| var apiErr APIError |
| if len(resp.Body) > 0 { |
| |
| var errorResp struct { |
| Detail string `json:"detail"` |
| } |
| if err := json.Unmarshal(resp.Body, &errorResp); err == nil && errorResp.Detail != "" { |
| apiErr = APIError{ |
| Code: resp.StatusCode, |
| Message: errorResp.Detail, |
| } |
| } else { |
| |
| apiErr = APIError{ |
| Code: resp.StatusCode, |
| Message: fmt.Sprintf("API request failed with status %d", resp.StatusCode), |
| Details: string(resp.Body), |
| } |
| } |
| } else { |
| apiErr = APIError{ |
| Code: resp.StatusCode, |
| Message: fmt.Sprintf("API request failed with status %d", resp.StatusCode), |
| } |
| } |
| return nil, &apiErr |
| } |
|
|
| return resp, nil |
| } |
|
|
| |
| func buildQueryParams(params map[string]interface{}) string { |
| if len(params) == 0 { |
| return "" |
| } |
|
|
| values := url.Values{} |
| for key, value := range params { |
| if value == nil { |
| continue |
| } |
| switch v := value.(type) { |
| case string: |
| if v != "" { |
| values.Add(key, v) |
| } |
| case int: |
| if v != 0 { |
| values.Add(key, strconv.Itoa(v)) |
| } |
| case int64: |
| if v != 0 { |
| values.Add(key, strconv.FormatInt(v, 10)) |
| } |
| case float64: |
| if v != 0 { |
| values.Add(key, strconv.FormatFloat(v, 'f', -1, 64)) |
| } |
| case bool: |
| values.Add(key, strconv.FormatBool(v)) |
| case time.Time: |
| if !v.IsZero() { |
| values.Add(key, v.Format(time.RFC3339)) |
| } |
| case *time.Time: |
| if v != nil && !v.IsZero() { |
| values.Add(key, v.Format(time.RFC3339)) |
| } |
| case []int: |
| if len(v) > 0 { |
| if encoded, err := json.Marshal(v); err == nil { |
| values.Add(key, string(encoded)) |
| } |
| } |
| case []string: |
| if len(v) > 0 { |
| if encoded, err := json.Marshal(v); err == nil { |
| values.Add(key, string(encoded)) |
| } |
| } |
| default: |
| values.Add(key, fmt.Sprint(v)) |
| } |
| } |
|
|
| if len(values) > 0 { |
| return "?" + values.Encode() |
| } |
| return "" |
| } |
|
|