github.com/khulnasoft/codebase@v0.0.0-20231214144635-a707781cbb24/go-bitbucket/client.go (about) 1 /* 2 * Bitbucket API 3 * 4 * Code against the Bitbucket API to automate simple tasks, embed Bitbucket data into your own site, build mobile or desktop apps, or even add custom UI add-ons into Bitbucket itself using the Connect framework. 5 * 6 * API version: 2.0 7 * Contact: support@bitbucket.org 8 */ 9 10 // Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. 11 12 package bitbucket 13 14 import ( 15 "bytes" 16 "context" 17 "encoding/json" 18 "encoding/xml" 19 "errors" 20 "fmt" 21 "io" 22 "log" 23 "mime/multipart" 24 "net/http" 25 "net/http/httputil" 26 "net/url" 27 "os" 28 "path/filepath" 29 "reflect" 30 "regexp" 31 "strconv" 32 "strings" 33 "time" 34 "unicode/utf8" 35 36 "golang.org/x/oauth2" 37 ) 38 39 var ( 40 jsonCheck = regexp.MustCompile(`(?i:(?:application|text)/(?:vnd\.[^;]+\+)?json)`) 41 xmlCheck = regexp.MustCompile(`(?i:(?:application|text)/xml)`) 42 ) 43 44 // APIClient manages communication with the Bitbucket API API v2.0 45 // In most cases there should be only one, shared, APIClient. 46 type APIClient struct { 47 cfg *Configuration 48 common service // Reuse a single struct instead of allocating one for each service on the heap. 49 50 // API Services 51 // NOTE : all services except ReportsApi were removed 52 ReportsApi *ReportsApiService 53 } 54 55 type service struct { 56 client *APIClient 57 } 58 59 // NewAPIClient creates a new API client. Requires a userAgent string describing your application. 60 // optionally a custom http.Client to allow for advanced features such as caching. 61 func NewAPIClient(cfg *Configuration) *APIClient { 62 if cfg.HTTPClient == nil { 63 cfg.HTTPClient = http.DefaultClient 64 } 65 66 c := &APIClient{} 67 c.cfg = cfg 68 c.common.client = c 69 70 // API Services 71 // NOTE : all services except ReportsApi were removed 72 c.ReportsApi = (*ReportsApiService)(&c.common) 73 74 return c 75 } 76 77 func atoi(in string) (int, error) { 78 return strconv.Atoi(in) 79 } 80 81 // selectHeaderContentType select a content type from the available list. 82 func selectHeaderContentType(contentTypes []string) string { 83 if len(contentTypes) == 0 { 84 return "" 85 } 86 if contains(contentTypes, "application/json") { 87 return "application/json" 88 } 89 return contentTypes[0] // use the first content type specified in 'consumes' 90 } 91 92 // selectHeaderAccept join all accept types and return 93 func selectHeaderAccept(accepts []string) string { 94 if len(accepts) == 0 { 95 return "" 96 } 97 98 if contains(accepts, "application/json") { 99 return "application/json" 100 } 101 102 return strings.Join(accepts, ",") 103 } 104 105 // contains is a case insenstive match, finding needle in a haystack 106 func contains(haystack []string, needle string) bool { 107 for _, a := range haystack { 108 if strings.EqualFold(a, needle) { 109 return true 110 } 111 } 112 return false 113 } 114 115 // Verify optional parameters are of the correct type. 116 func typeCheckParameter(obj interface{}, expected string, name string) error { 117 // Make sure there is an object. 118 if obj == nil { 119 return nil 120 } 121 122 // Check the type is as expected. 123 if reflect.TypeOf(obj).String() != expected { 124 return fmt.Errorf("expected %s to be of type %s but received %s", name, expected, reflect.TypeOf(obj).String()) 125 } 126 return nil 127 } 128 129 // parameterToString convert interface{} parameters to string, using a delimiter if format is provided. 130 func parameterToString(obj interface{}, collectionFormat string) string { 131 var delimiter string 132 133 switch collectionFormat { 134 case "pipes": 135 delimiter = "|" 136 case "ssv": 137 delimiter = " " 138 case "tsv": 139 delimiter = "\t" 140 case "csv": 141 delimiter = "," 142 } 143 144 if reflect.TypeOf(obj).Kind() == reflect.Slice { 145 return strings.Trim(strings.Replace(fmt.Sprint(obj), " ", delimiter, -1), "[]") 146 } else if t, ok := obj.(time.Time); ok { 147 return t.Format(time.RFC3339) 148 } 149 150 return fmt.Sprintf("%v", obj) 151 } 152 153 // helper for converting interface{} parameters to json strings 154 func parameterToJson(obj interface{}) (string, error) { 155 jsonBuf, err := json.Marshal(obj) 156 if err != nil { 157 return "", err 158 } 159 return string(jsonBuf), err 160 } 161 162 // callAPI do the request. 163 func (c *APIClient) callAPI(request *http.Request) (*http.Response, error) { 164 if c.cfg.Debug { 165 dump, err := httputil.DumpRequestOut(request, true) 166 if err != nil { 167 return nil, err 168 } 169 log.Printf("\n%s\n", string(dump)) 170 } 171 172 resp, err := c.cfg.HTTPClient.Do(request) 173 if err != nil { 174 return resp, err 175 } 176 177 if c.cfg.Debug { 178 dump, err := httputil.DumpResponse(resp, true) 179 if err != nil { 180 return resp, err 181 } 182 log.Printf("\n%s\n", string(dump)) 183 } 184 return resp, err 185 } 186 187 // Allow modification of underlying config for alternate implementations and testing 188 // Caution: modifying the configuration while live can cause data races and potentially unwanted behavior 189 func (c *APIClient) GetConfig() *Configuration { 190 return c.cfg 191 } 192 193 // prepareRequest build the request 194 func (c *APIClient) prepareRequest( 195 ctx context.Context, 196 path string, method string, 197 postBody interface{}, 198 headerParams map[string]string, 199 queryParams url.Values, 200 formParams url.Values, 201 formFileName string, 202 fileName string, 203 fileBytes []byte) (localVarRequest *http.Request, err error) { 204 205 var body *bytes.Buffer 206 207 // Detect postBody type and post. 208 if postBody != nil { 209 contentType := headerParams["Content-Type"] 210 if contentType == "" { 211 contentType = detectContentType(postBody) 212 headerParams["Content-Type"] = contentType 213 } 214 215 body, err = setBody(postBody, contentType) 216 if err != nil { 217 return nil, err 218 } 219 } 220 221 // add form parameters and file if available. 222 if strings.HasPrefix(headerParams["Content-Type"], "multipart/form-data") && len(formParams) > 0 || (len(fileBytes) > 0 && fileName != "") { 223 if body != nil { 224 return nil, errors.New("cannot specify postBody and multipart form at the same time") 225 } 226 body = &bytes.Buffer{} 227 w := multipart.NewWriter(body) 228 229 for k, v := range formParams { 230 for _, iv := range v { 231 if strings.HasPrefix(k, "@") { // file 232 err = addFile(w, k[1:], iv) 233 if err != nil { 234 return nil, err 235 } 236 } else { // form value 237 w.WriteField(k, iv) 238 } 239 } 240 } 241 if len(fileBytes) > 0 && fileName != "" { 242 w.Boundary() 243 //_, fileNm := filepath.Split(fileName) 244 part, err := w.CreateFormFile(formFileName, filepath.Base(fileName)) 245 if err != nil { 246 return nil, err 247 } 248 _, err = part.Write(fileBytes) 249 if err != nil { 250 return nil, err 251 } 252 } 253 254 // Set the Boundary in the Content-Type 255 headerParams["Content-Type"] = w.FormDataContentType() 256 257 // Set Content-Length 258 headerParams["Content-Length"] = fmt.Sprintf("%d", body.Len()) 259 w.Close() 260 } 261 262 if strings.HasPrefix(headerParams["Content-Type"], "application/x-www-form-urlencoded") && len(formParams) > 0 { 263 if body != nil { 264 return nil, errors.New("cannot specify postBody and x-www-form-urlencoded form at the same time") 265 } 266 body = &bytes.Buffer{} 267 body.WriteString(formParams.Encode()) 268 // Set Content-Length 269 headerParams["Content-Length"] = fmt.Sprintf("%d", body.Len()) 270 } 271 272 // Setup path and query parameters 273 url, err := url.Parse(path) 274 if err != nil { 275 return nil, err 276 } 277 278 // Override request host, if applicable 279 if c.cfg.Host != "" { 280 url.Host = c.cfg.Host 281 } 282 283 // Override request scheme, if applicable 284 if c.cfg.Scheme != "" { 285 url.Scheme = c.cfg.Scheme 286 } 287 288 // Adding Query Param 289 query := url.Query() 290 for k, v := range queryParams { 291 for _, iv := range v { 292 query.Add(k, iv) 293 } 294 } 295 296 // Encode the parameters. 297 url.RawQuery = query.Encode() 298 299 // Generate a new request 300 if body != nil { 301 localVarRequest, err = http.NewRequest(method, url.String(), body) 302 } else { 303 localVarRequest, err = http.NewRequest(method, url.String(), nil) 304 } 305 if err != nil { 306 return nil, err 307 } 308 309 // add header parameters, if any 310 if len(headerParams) > 0 { 311 headers := http.Header{} 312 for h, v := range headerParams { 313 headers.Set(h, v) 314 } 315 localVarRequest.Header = headers 316 } 317 318 // Add the user agent to the request. 319 localVarRequest.Header.Add("User-Agent", c.cfg.UserAgent) 320 321 if ctx != nil { 322 // add context to the request 323 localVarRequest = localVarRequest.WithContext(ctx) 324 325 // Walk through any authentication. 326 327 // OAuth2 authentication 328 if tok, ok := ctx.Value(ContextOAuth2).(oauth2.TokenSource); ok { 329 // We were able to grab an oauth2 token from the context 330 var latestToken *oauth2.Token 331 if latestToken, err = tok.Token(); err != nil { 332 return nil, err 333 } 334 335 latestToken.SetAuthHeader(localVarRequest) 336 } 337 338 // Basic HTTP Authentication 339 if auth, ok := ctx.Value(ContextBasicAuth).(BasicAuth); ok { 340 localVarRequest.SetBasicAuth(auth.UserName, auth.Password) 341 } 342 343 // AccessToken Authentication 344 if auth, ok := ctx.Value(ContextAccessToken).(string); ok { 345 localVarRequest.Header.Add("Authorization", "Bearer "+auth) 346 } 347 348 } 349 350 for header, value := range c.cfg.DefaultHeader { 351 localVarRequest.Header.Add(header, value) 352 } 353 return localVarRequest, nil 354 } 355 356 func (c *APIClient) decode(v interface{}, b []byte, contentType string) (err error) { 357 if len(b) == 0 { 358 return nil 359 } 360 if s, ok := v.(*string); ok { 361 *s = string(b) 362 return nil 363 } 364 if xmlCheck.MatchString(contentType) { 365 if err = xml.Unmarshal(b, v); err != nil { 366 return err 367 } 368 return nil 369 } 370 if jsonCheck.MatchString(contentType) { 371 if actualObj, ok := v.(interface{ GetActualInstance() interface{} }); ok { // oneOf, anyOf schemas 372 if unmarshalObj, ok := actualObj.(interface{ UnmarshalJSON([]byte) error }); ok { // make sure it has UnmarshalJSON defined 373 if err = unmarshalObj.UnmarshalJSON(b); err != nil { 374 return err 375 } 376 } else { 377 return errors.New("unknown type with GetActualInstance but no unmarshalObj.UnmarshalJSON defined") 378 } 379 } else if err = json.Unmarshal(b, v); err != nil { // simple model 380 return err 381 } 382 return nil 383 } 384 return errors.New("undefined response type") 385 } 386 387 // Add a file to the multipart request 388 func addFile(w *multipart.Writer, fieldName, path string) error { 389 file, err := os.Open(path) 390 if err != nil { 391 return err 392 } 393 defer file.Close() 394 395 part, err := w.CreateFormFile(fieldName, filepath.Base(path)) 396 if err != nil { 397 return err 398 } 399 _, err = io.Copy(part, file) 400 401 return err 402 } 403 404 // Prevent trying to import "fmt" 405 func reportError(format string, a ...interface{}) error { 406 return fmt.Errorf(format, a...) 407 } 408 409 // Set request body from an interface{} 410 func setBody(body interface{}, contentType string) (bodyBuf *bytes.Buffer, err error) { 411 if bodyBuf == nil { 412 bodyBuf = &bytes.Buffer{} 413 } 414 415 if reader, ok := body.(io.Reader); ok { 416 _, err = bodyBuf.ReadFrom(reader) 417 } else if b, ok := body.([]byte); ok { 418 _, err = bodyBuf.Write(b) 419 } else if s, ok := body.(string); ok { 420 _, err = bodyBuf.WriteString(s) 421 } else if s, ok := body.(*string); ok { 422 _, err = bodyBuf.WriteString(*s) 423 } else if jsonCheck.MatchString(contentType) { 424 err = json.NewEncoder(bodyBuf).Encode(body) 425 } else if xmlCheck.MatchString(contentType) { 426 err = xml.NewEncoder(bodyBuf).Encode(body) 427 } 428 429 if err != nil { 430 return nil, err 431 } 432 433 if bodyBuf.Len() == 0 { 434 err = fmt.Errorf("invalid body type %s", contentType) 435 return nil, err 436 } 437 return bodyBuf, nil 438 } 439 440 // detectContentType method is used to figure out `Request.Body` content type for request header 441 func detectContentType(body interface{}) string { 442 contentType := "text/plain; charset=utf-8" 443 kind := reflect.TypeOf(body).Kind() 444 445 switch kind { 446 case reflect.Struct, reflect.Map, reflect.Ptr: 447 contentType = "application/json; charset=utf-8" 448 case reflect.String: 449 contentType = "text/plain; charset=utf-8" 450 default: 451 if b, ok := body.([]byte); ok { 452 contentType = http.DetectContentType(b) 453 } else if kind == reflect.Slice { 454 contentType = "application/json; charset=utf-8" 455 } 456 } 457 458 return contentType 459 } 460 461 // Ripped from https://github.com/gregjones/httpcache/blob/master/httpcache.go 462 type cacheControl map[string]string 463 464 func parseCacheControl(headers http.Header) cacheControl { 465 cc := cacheControl{} 466 ccHeader := headers.Get("Cache-Control") 467 for _, part := range strings.Split(ccHeader, ",") { 468 part = strings.Trim(part, " ") 469 if part == "" { 470 continue 471 } 472 if strings.ContainsRune(part, '=') { 473 keyval := strings.Split(part, "=") 474 cc[strings.Trim(keyval[0], " ")] = strings.Trim(keyval[1], ",") 475 } else { 476 cc[part] = "" 477 } 478 } 479 return cc 480 } 481 482 // CacheExpires helper function to determine remaining time before repeating a request. 483 func CacheExpires(r *http.Response) time.Time { 484 // Figure out when the cache expires. 485 var expires time.Time 486 now, err := time.Parse(time.RFC1123, r.Header.Get("date")) 487 if err != nil { 488 return time.Now() 489 } 490 respCacheControl := parseCacheControl(r.Header) 491 492 if maxAge, ok := respCacheControl["max-age"]; ok { 493 lifetime, err := time.ParseDuration(maxAge + "s") 494 if err != nil { 495 expires = now 496 } else { 497 expires = now.Add(lifetime) 498 } 499 } else { 500 expiresHeader := r.Header.Get("Expires") 501 if expiresHeader != "" { 502 expires, err = time.Parse(time.RFC1123, expiresHeader) 503 if err != nil { 504 expires = now 505 } 506 } 507 } 508 return expires 509 } 510 511 func strlen(s string) int { 512 return utf8.RuneCountInString(s) 513 } 514 515 // GenericOpenAPIError Provides access to the body, error and model on returned errors. 516 type GenericOpenAPIError struct { 517 body []byte 518 error string 519 model interface{} 520 } 521 522 // Error returns non-empty string if there was an error. 523 func (e GenericOpenAPIError) Error() string { 524 return e.error 525 } 526 527 // Body returns the raw bytes of the response 528 func (e GenericOpenAPIError) Body() []byte { 529 return e.body 530 } 531 532 // Model returns the unpacked model of the error 533 func (e GenericOpenAPIError) Model() interface{} { 534 return e.model 535 }