github.com/Files-com/files-sdk-go/v3@v3.1.81/backend.go (about)

     1  package files_sdk
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"net/http"
     9  	"strconv"
    10  
    11  	"github.com/Files-com/files-sdk-go/v3/lib"
    12  	"github.com/hashicorp/go-retryablehttp"
    13  	"moul.io/http2curl/v2"
    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, request, opts...)
    48  	if err != nil {
    49  		return nil, response, err
    50  	}
    51  	data, res, err := ParseResponse(response, resource)
    52  	var responseError ResponseError
    53  	ok := errors.As(err, &responseError)
    54  	if ok {
    55  		err = responseError
    56  	}
    57  	return data, res, err
    58  }
    59  
    60  func ParseResponse(res *http.Response, resource string) (*[]byte, *http.Response, error) {
    61  	defaultValue := make([]byte, 0)
    62  	if res.StatusCode == 204 {
    63  		return &defaultValue, res, nil
    64  	}
    65  	nonOkError := lib.NonOkErrorCustom(func(err error) error {
    66  		return fmt.Errorf("%v - %v", resource, err)
    67  	})
    68  
    69  	if err := lib.ResponseErrors(res, APIError(), nonOkError); err != nil {
    70  		return &defaultValue, res, err
    71  	}
    72  	data, err := io.ReadAll(res.Body)
    73  	return &data, res, err
    74  }
    75  
    76  type CallParams struct {
    77  	Method  string
    78  	Config  Config
    79  	Uri     string
    80  	Params  lib.Values
    81  	BodyIo  io.ReadCloser
    82  	Headers *http.Header
    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  	retryRequest := &retryablehttp.Request{Request: request}
    92  	retryRequest.Body = request.Body
    93  	return params.Config.Client.Do(retryRequest)
    94  }
    95  
    96  func buildRequest(opts *CallParams) (*http.Request, error) {
    97  	var bodyIsJson bool
    98  	if opts.Headers == nil {
    99  		opts.Headers = &http.Header{}
   100  	}
   101  
   102  	var req *http.Request
   103  	var err error
   104  	if opts.Context != nil {
   105  		req, err = http.NewRequestWithContext(opts.Context, opts.Method, opts.Uri, nil)
   106  	} else {
   107  		req, err = http.NewRequest(opts.Method, opts.Uri, nil)
   108  	}
   109  
   110  	if err != nil {
   111  		return &http.Request{}, err
   112  	}
   113  	req.Header = *opts.Headers
   114  	if req.Header.Get("Content-Length") != "" {
   115  		c, _ := strconv.ParseInt(req.Header.Get("Content-Length"), 10, 64)
   116  		req.ContentLength = c
   117  	}
   118  
   119  	switch opts.Method {
   120  	case "GET", "HEAD", "DELETE":
   121  		if opts.Params != nil {
   122  			values, err := opts.Params.ToValues()
   123  			if err != nil {
   124  				return nil, err
   125  			}
   126  			req.URL.RawQuery = values.Encode()
   127  		}
   128  	default:
   129  		if opts.BodyIo == nil {
   130  			bodyIsJson = true
   131  			jsonBody, err := opts.Params.ToJSON()
   132  			if err != nil {
   133  				return &http.Request{}, err
   134  			}
   135  			req.Body = io.NopCloser(jsonBody)
   136  			req.Header.Add("Content-Type", "application/json")
   137  		} else {
   138  			if req.ContentLength != 0 {
   139  				req.Body = opts.BodyIo
   140  			}
   141  		}
   142  	}
   143  
   144  	if opts.Config.InDebug() {
   145  		defer debugLog(bodyIsJson, req, opts.Config, opts.Params)
   146  	}
   147  	return req, nil
   148  }
   149  
   150  func debugLog(bodyIsJson bool, req *http.Request, config Config, params lib.Values) {
   151  	clonedReq := req.Clone(context.Background())
   152  	clonedReq.Body = nil
   153  	if bodyIsJson {
   154  		jsonBody, err := params.ToJSON()
   155  		if err != nil {
   156  			panic(err)
   157  		}
   158  		clonedReq.Body = io.NopCloser(jsonBody)
   159  	}
   160  	command, err := http2curl.GetCurlCommand(clonedReq)
   161  	if err != nil {
   162  		panic(err)
   163  	}
   164  	config.Printf(" %v", command)
   165  }