github.com/Files-com/files-sdk-go/v2@v2.1.2/backend.go (about)

     1  package files_sdk
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io"
     7  	"net/http"
     8  	"strconv"
     9  
    10  	lib "github.com/Files-com/files-sdk-go/v2/lib"
    11  	"github.com/hashicorp/go-retryablehttp"
    12  
    13  	"moul.io/http2curl"
    14  )
    15  
    16  func Resource(config Config, resource lib.Resource, opt ...RequestResponseOption) error {
    17  	out, err := resource.Out()
    18  	if err != nil {
    19  		return err
    20  	}
    21  	data, res, err := Call(resource.Method, config, out.Path, out.Values, opt...)
    22  	defer lib.CloseBody(res)
    23  	if err != nil {
    24  		return err
    25  	}
    26  	if res.StatusCode == 204 {
    27  		return err
    28  	}
    29  
    30  	return out.Entity.UnmarshalJSON(*data)
    31  }
    32  
    33  func Call(method string, config Config, resource string, params lib.Values, opts ...RequestResponseOption) (*[]byte, *http.Response, error) {
    34  	defaultHeaders := &http.Header{}
    35  	config.SetHeaders(defaultHeaders)
    36  	callParams := &CallParams{
    37  		Method:  method,
    38  		Config:  config,
    39  		Uri:     config.RootPath() + resource,
    40  		Params:  params,
    41  		Headers: defaultHeaders,
    42  	}
    43  	request, err := buildRequest(callParams)
    44  	if err != nil {
    45  		return nil, &http.Response{}, err
    46  	}
    47  	response, err := WrapRequestOptions(config.GetHttpClient(), request, opts...)
    48  	if err != nil {
    49  		return nil, response, err
    50  	}
    51  	data, res, err := ParseResponse(response, resource)
    52  	responseError, ok := err.(ResponseError)
    53  	if ok {
    54  		err = responseError
    55  	}
    56  	return data, res, err
    57  }
    58  
    59  func ParseResponse(res *http.Response, resource string) (*[]byte, *http.Response, error) {
    60  	defaultValue := make([]byte, 0)
    61  	if res.StatusCode == 204 {
    62  		return &defaultValue, res, nil
    63  	}
    64  	nonOkError := lib.NonOkErrorCustom(func(err error) error {
    65  		return fmt.Errorf("%v - %v", resource, err)
    66  	})
    67  
    68  	if err := lib.ResponseErrors(res, APIError(), nonOkError); err != nil {
    69  		return &defaultValue, res, err
    70  	}
    71  	data, err := io.ReadAll(res.Body)
    72  	return &data, res, err
    73  }
    74  
    75  type CallParams struct {
    76  	Method   string
    77  	Config   Config
    78  	Uri      string
    79  	Params   lib.Values
    80  	BodyIo   io.ReadCloser
    81  	Headers  *http.Header
    82  	StayOpen bool
    83  	context.Context
    84  }
    85  
    86  func CallRaw(params *CallParams) (*http.Response, error) {
    87  	request, err := buildRequest(params)
    88  	if err != nil {
    89  		return &http.Response{}, err
    90  	}
    91  	if request.Body != nil {
    92  		retryRequest := &retryablehttp.Request{Request: request}
    93  		retryRequest.Body = request.Body
    94  		return params.Config.GetRawClient().Do(retryRequest)
    95  	} else {
    96  		return params.Config.GetHttpClient().Do(request)
    97  	}
    98  }
    99  
   100  func buildRequest(opts *CallParams) (*http.Request, error) {
   101  	var bodyIsJson bool
   102  	if opts.Headers == nil {
   103  		opts.Headers = &http.Header{}
   104  	}
   105  
   106  	var req *http.Request
   107  	var err error
   108  	if opts.Context != nil {
   109  		req, err = http.NewRequestWithContext(opts.Context, opts.Method, opts.Uri, nil)
   110  	} else {
   111  		req, err = http.NewRequest(opts.Method, opts.Uri, nil)
   112  	}
   113  
   114  	if err != nil {
   115  		return &http.Request{}, err
   116  	}
   117  	req.Header = *opts.Headers
   118  	if req.Header.Get("Content-Length") != "" {
   119  		c, _ := strconv.ParseInt(req.Header.Get("Content-Length"), 10, 64)
   120  		req.ContentLength = c
   121  	}
   122  
   123  	switch opts.Method {
   124  	case "GET", "HEAD", "DELETE":
   125  		if opts.Params != nil {
   126  			values, err := opts.Params.ToValues()
   127  			if err != nil {
   128  				return nil, err
   129  			}
   130  			req.URL.RawQuery = values.Encode()
   131  		}
   132  	default:
   133  		if opts.BodyIo == nil {
   134  			bodyIsJson = true
   135  			jsonBody, err := opts.Params.ToJSON()
   136  			if err != nil {
   137  				return &http.Request{}, err
   138  			}
   139  			req.Body = io.NopCloser(jsonBody)
   140  			req.Header.Add("Content-Type", "application/json")
   141  		} else {
   142  			if req.ContentLength != 0 {
   143  				req.Body = opts.BodyIo
   144  			}
   145  		}
   146  	}
   147  
   148  	if !opts.StayOpen {
   149  		req.Header.Set("Connection", "close")
   150  		req.Close = true
   151  	}
   152  
   153  	if opts.Config.InDebug() {
   154  		defer debugLog(bodyIsJson, req, opts.Config, opts.Params)
   155  	}
   156  	return req, nil
   157  }
   158  
   159  func debugLog(bodyIsJson bool, req *http.Request, config Config, params lib.Values) {
   160  	clonedReq := req.Clone(context.Background())
   161  	clonedReq.Body = nil
   162  	if bodyIsJson {
   163  		jsonBody, err := params.ToJSON()
   164  		if err != nil {
   165  			panic(err)
   166  		}
   167  		clonedReq.Body = io.NopCloser(jsonBody)
   168  	}
   169  	command, err := http2curl.GetCurlCommand(clonedReq)
   170  	if err != nil {
   171  		panic(err)
   172  	}
   173  	config.Logger().Printf(" %v", command)
   174  }