| 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 ""
|
| }
|
|
|