github.com/sharovik/devbot@v1.0.1-0.20240308094637-4a0387c40516/internal/client/http.go (about)

     1  package client
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/base64"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"net/http"
    10  	"net/url"
    11  	"strings"
    12  
    13  	"github.com/sharovik/devbot/internal/log"
    14  )
    15  
    16  // BaseHTTPClientInterface base interface for all http clients
    17  type BaseHTTPClientInterface interface {
    18  	//Configuration methods
    19  	SetOauthToken(token string)
    20  	SetBaseURL(baseURL string)
    21  	BasicAuth(username string, password string) string
    22  	GetClientID() string
    23  	GetClientSecret() string
    24  	GetOAuthToken() string
    25  
    26  	//Http methods
    27  	Request(string, string, interface{}, map[string]string) ([]byte, int, error)
    28  	Post(string, interface{}, map[string]string) ([]byte, int, error)
    29  	Get(string, map[string]string) ([]byte, int, error)
    30  	Put(string, interface{}, map[string]string) ([]byte, int, error)
    31  }
    32  
    33  // HTTPClient main http client
    34  type HTTPClient struct {
    35  	Client *http.Client
    36  
    37  	//Configuration of client
    38  	OAuthToken   string
    39  	BaseURL      string
    40  	ClientID     string
    41  	ClientSecret string
    42  }
    43  
    44  // SetOauthToken method sets the oauth token and retrieves its self
    45  func (client *HTTPClient) SetOauthToken(token string) {
    46  	client.OAuthToken = token
    47  }
    48  
    49  // GetClientID method retrieves the clientID
    50  func (client HTTPClient) GetClientID() string {
    51  	return client.ClientID
    52  }
    53  
    54  // GetClientSecret method retrieves the clientSecret
    55  func (client HTTPClient) GetClientSecret() string {
    56  	return client.ClientSecret
    57  }
    58  
    59  // GetOAuthToken method retrieves the oauth token
    60  func (client HTTPClient) GetOAuthToken() string {
    61  	return client.OAuthToken
    62  }
    63  
    64  // SetBaseURL method sets the base url and retrieves its self
    65  func (client *HTTPClient) SetBaseURL(baseURL string) {
    66  	client.BaseURL = baseURL
    67  }
    68  
    69  // BasicAuth retrieves encode string for basic auth
    70  func (client HTTPClient) BasicAuth(username string, password string) string {
    71  	return base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", username, password)))
    72  }
    73  
    74  // Request method for API requests
    75  //
    76  // This method accepts parameters:
    77  // method - the method of request. Ex: POST, GET, PUT, DELETE, and etc
    78  // endpoint - endpoint to which we should do a request
    79  // body - it's a request body. Accepted types of body: string, url.Values(for form_data requests), byte
    80  // headers - request headers
    81  func (client HTTPClient) Request(method string, endpoint string, body interface{}, headers map[string]string) ([]byte, int, error) {
    82  
    83  	log.Logger().StartMessage("Http request")
    84  
    85  	var (
    86  		resp    *http.Response
    87  		request *http.Request
    88  		err     error
    89  	)
    90  
    91  	switch b := body.(type) {
    92  	case string:
    93  		log.Logger().Debug().
    94  			Str("endpoint", endpoint).
    95  			Str("method", method).
    96  			Str("body", b).
    97  			Msg("Endpoint call")
    98  		request, err = http.NewRequest(method, endpoint, strings.NewReader(b))
    99  		if err != nil {
   100  			log.Logger().AddError(err).Msg("Error during the request generation")
   101  			log.Logger().FinishMessage("Http request")
   102  			return nil, 0, err
   103  		}
   104  
   105  		request.Header.Set("Content-Type", "application/json")
   106  	case url.Values:
   107  		log.Logger().Debug().
   108  			Str("endpoint", endpoint).
   109  			Str("method", method).
   110  			Interface("body", b).
   111  			Msg("Endpoint call")
   112  
   113  		request, err = http.NewRequest(method, endpoint, strings.NewReader(b.Encode()))
   114  		if err != nil {
   115  			log.Logger().AddError(err).Msg("Error during the request generation")
   116  			log.Logger().FinishMessage("Http request")
   117  			return nil, 0, err
   118  		}
   119  		request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
   120  	case bytes.Buffer:
   121  		log.Logger().Debug().
   122  			Str("endpoint", endpoint).
   123  			Str("method", method).
   124  			Interface("body", b).
   125  			Msg("Endpoint call")
   126  
   127  		buff := b
   128  		request, err = http.NewRequest(method, endpoint, &buff)
   129  		if err != nil {
   130  			log.Logger().AddError(err).Msg("Error during the request generation")
   131  			log.Logger().FinishMessage("Http request")
   132  			return nil, 0, err
   133  		}
   134  		request.Header.Set("Content-Type", "multipart/form-data")
   135  	default:
   136  		log.Logger().Debug().
   137  			Str("endpoint", endpoint).
   138  			Str("method", method).
   139  			Str("body", string(body.([]byte))).
   140  			Msg("Endpoint call")
   141  		request, err = http.NewRequest(method, endpoint, bytes.NewReader(body.([]byte)))
   142  		if err != nil {
   143  			log.Logger().AddError(err).Msg("Error during the request generation")
   144  			log.Logger().FinishMessage("Http request")
   145  			return nil, 0, err
   146  		}
   147  		request.Header.Set("Content-Type", "application/json")
   148  	}
   149  
   150  	for attribute, value := range headers {
   151  		request.Header.Set(attribute, value)
   152  	}
   153  
   154  	if client.OAuthToken != "" {
   155  		request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", client.OAuthToken))
   156  	}
   157  
   158  	resp, errorResponse := client.Client.Do(request)
   159  
   160  	if resp == nil {
   161  		err = errors.New("ResponseInterface cannot be null ")
   162  		errMsg := err.Error()
   163  		if errorResponse != nil {
   164  			errMsg = errorResponse.Error()
   165  		}
   166  		log.Logger().AddError(errorResponse).
   167  			Str("response_error", errMsg).
   168  			Msg("Error during response body parse")
   169  
   170  		log.Logger().FinishMessage("Http request")
   171  		return nil, 0, err
   172  	}
   173  
   174  	defer resp.Body.Close()
   175  	byteResp, errorConversion := io.ReadAll(resp.Body)
   176  	if errorConversion != nil {
   177  		log.Logger().AddError(errorConversion).
   178  			Err(errorConversion).
   179  			Msg("Error during response body parse")
   180  		log.Logger().FinishMessage("Http request")
   181  		return byteResp, 0, errorConversion
   182  	}
   183  
   184  	var response []byte
   185  	if string(byteResp) == "" {
   186  		response = []byte(`{}`)
   187  	} else {
   188  		response = byteResp
   189  	}
   190  
   191  	log.Logger().FinishMessage("Http request")
   192  	return response, resp.StatusCode, nil
   193  }
   194  
   195  // Post method for POST http requests
   196  func (client HTTPClient) Post(endpoint string, body interface{}, headers map[string]string) ([]byte, int, error) {
   197  	return client.Request(http.MethodPost, client.generateAPIUrl(endpoint), body, headers)
   198  }
   199  
   200  // Put method for PUT http requests
   201  func (client HTTPClient) Put(endpoint string, body interface{}, headers map[string]string) ([]byte, int, error) {
   202  	return client.Request(http.MethodPut, client.generateAPIUrl(endpoint), body, headers)
   203  }
   204  
   205  // Get method for GET http requests
   206  func (client *HTTPClient) Get(endpoint string, query map[string]string) ([]byte, int, error) {
   207  	if client.OAuthToken != "" {
   208  		query["access_token"] = client.OAuthToken
   209  	}
   210  
   211  	var queryString = ""
   212  	for fieldName, value := range query {
   213  		if queryString == "" {
   214  			queryString += "?"
   215  		} else {
   216  			queryString += "&"
   217  		}
   218  
   219  		queryString += fmt.Sprintf("%s=%s", fieldName, value)
   220  	}
   221  
   222  	return client.Request(http.MethodGet, client.generateAPIUrl(endpoint)+queryString, []byte(``), map[string]string{})
   223  }
   224  
   225  func (client HTTPClient) generateAPIUrl(endpoint string) string {
   226  	log.Logger().Debug().
   227  		Str("base_url", client.BaseURL).
   228  		Str("endpoint", endpoint).
   229  		Msg("Generate API url")
   230  
   231  	return client.BaseURL + endpoint
   232  }