| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219 |
- 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
- )
- // DefaultHTTPClient is the default HTTP client implementation
- type DefaultHTTPClient struct {
- client *http.Client
- }
- // NewDefaultHTTPClient creates a new default HTTP client
- func NewDefaultHTTPClient(timeout time.Duration) *DefaultHTTPClient {
- return &DefaultHTTPClient{
- client: &http.Client{
- Timeout: timeout,
- },
- }
- }
- // Do executes an HTTP request
- 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)
- }
- // Set headers
- 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()
- // Read response body
- var body bytes.Buffer
- _, err = body.ReadFrom(resp.Body)
- if err != nil {
- return nil, fmt.Errorf("failed to read response body: %w", err)
- }
- // Convert headers
- 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
- }
- // NewEnterpriseClient creates a new IO.NET API client targeting the enterprise API base URL.
- func NewEnterpriseClient(apiKey string) *Client {
- return NewClientWithConfig(apiKey, DefaultEnterpriseBaseURL, nil)
- }
- // NewClient creates a new IO.NET API client targeting the public API base URL.
- func NewClient(apiKey string) *Client {
- return NewClientWithConfig(apiKey, DefaultBaseURL, nil)
- }
- // NewClientWithConfig creates a new IO.NET API client with custom configuration
- 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,
- }
- }
- // makeRequest performs an HTTP request and handles common response processing
- 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)
- }
- // Handle API errors
- if resp.StatusCode >= 400 {
- var apiErr APIError
- if len(resp.Body) > 0 {
- // Try to parse the actual error format: {"detail": "message"}
- 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 {
- // Fallback: use raw body as details
- 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
- }
- // buildQueryParams builds query parameters for GET requests
- 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 ""
- }
|