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 }