bunnyshell.com/sdk@v0.16.0/client.go (about)

     1  /*
     2  API Bunnyshell Environments
     3  
     4  Interact with Bunnyshell Platform
     5  
     6  API version: 1.1.0
     7  Contact: osi+support@bunnyshell.com
     8  */
     9  
    10  // Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT.
    11  
    12  package sdk
    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  	"gopkg.in/yaml.v3"
    37  
    38  )
    39  
    40  var (
    41  	jsonCheck = regexp.MustCompile(`(?i:(?:application|text)/(?:(?:hal|problem|vnd\.[^;]+)\+)?json)`)
    42  	xmlCheck  = regexp.MustCompile(`(?i:(?:application|text)/xml)`)
    43  	yamlCheck = regexp.MustCompile(`(?i:(?:application|text)/x\+yaml)`)
    44  	queryParamSplit = regexp.MustCompile(`(^|&)([^&]+)`)
    45  	queryDescape    = strings.NewReplacer( "%5B", "[", "%5D", "]" )
    46  )
    47  
    48  // APIClient manages communication with the API Bunnyshell Environments API v1.1.0
    49  // In most cases there should be only one, shared, APIClient.
    50  type APIClient struct {
    51  	cfg    *Configuration
    52  	common service // Reuse a single struct instead of allocating one for each service on the heap.
    53  
    54  	// API Services
    55  
    56  	ComponentAPI *ComponentAPIService
    57  
    58  	ComponentEndpointAPI *ComponentEndpointAPIService
    59  
    60  	ComponentGitAPI *ComponentGitAPIService
    61  
    62  	EnvironmentAPI *EnvironmentAPIService
    63  
    64  	EnvironmentVariableAPI *EnvironmentVariableAPIService
    65  
    66  	EventAPI *EventAPIService
    67  
    68  	KubernetesIntegrationAPI *KubernetesIntegrationAPIService
    69  
    70  	OrganizationAPI *OrganizationAPIService
    71  
    72  	PipelineAPI *PipelineAPIService
    73  
    74  	ProjectAPI *ProjectAPIService
    75  
    76  	ProjectVariableAPI *ProjectVariableAPIService
    77  
    78  	RegistryIntegrationAPI *RegistryIntegrationAPIService
    79  
    80  	SecretAPI *SecretAPIService
    81  
    82  	ServiceComponentVariableAPI *ServiceComponentVariableAPIService
    83  
    84  	TemplateAPI *TemplateAPIService
    85  
    86  	TemplatesRepositoryAPI *TemplatesRepositoryAPIService
    87  
    88  	WorkflowAPI *WorkflowAPIService
    89  }
    90  
    91  type service struct {
    92  	client *APIClient
    93  }
    94  
    95  // NewAPIClient creates a new API client. Requires a userAgent string describing your application.
    96  // optionally a custom http.Client to allow for advanced features such as caching.
    97  func NewAPIClient(cfg *Configuration) *APIClient {
    98  	if cfg.HTTPClient == nil {
    99  		cfg.HTTPClient = http.DefaultClient
   100  	}
   101  
   102  	c := &APIClient{}
   103  	c.cfg = cfg
   104  	c.common.client = c
   105  
   106  	// API Services
   107  	c.ComponentAPI = (*ComponentAPIService)(&c.common)
   108  	c.ComponentEndpointAPI = (*ComponentEndpointAPIService)(&c.common)
   109  	c.ComponentGitAPI = (*ComponentGitAPIService)(&c.common)
   110  	c.EnvironmentAPI = (*EnvironmentAPIService)(&c.common)
   111  	c.EnvironmentVariableAPI = (*EnvironmentVariableAPIService)(&c.common)
   112  	c.EventAPI = (*EventAPIService)(&c.common)
   113  	c.KubernetesIntegrationAPI = (*KubernetesIntegrationAPIService)(&c.common)
   114  	c.OrganizationAPI = (*OrganizationAPIService)(&c.common)
   115  	c.PipelineAPI = (*PipelineAPIService)(&c.common)
   116  	c.ProjectAPI = (*ProjectAPIService)(&c.common)
   117  	c.ProjectVariableAPI = (*ProjectVariableAPIService)(&c.common)
   118  	c.RegistryIntegrationAPI = (*RegistryIntegrationAPIService)(&c.common)
   119  	c.SecretAPI = (*SecretAPIService)(&c.common)
   120  	c.ServiceComponentVariableAPI = (*ServiceComponentVariableAPIService)(&c.common)
   121  	c.TemplateAPI = (*TemplateAPIService)(&c.common)
   122  	c.TemplatesRepositoryAPI = (*TemplatesRepositoryAPIService)(&c.common)
   123  	c.WorkflowAPI = (*WorkflowAPIService)(&c.common)
   124  
   125  	return c
   126  }
   127  
   128  func atoi(in string) (int, error) {
   129  	return strconv.Atoi(in)
   130  }
   131  
   132  // selectHeaderContentType select a content type from the available list.
   133  func selectHeaderContentType(contentTypes []string) string {
   134  	if len(contentTypes) == 0 {
   135  		return ""
   136  	}
   137  	if contains(contentTypes, "application/json") {
   138  		return "application/json"
   139  	}
   140  	return contentTypes[0] // use the first content type specified in 'consumes'
   141  }
   142  
   143  // selectHeaderAccept join all accept types and return
   144  func selectHeaderAccept(accepts []string) string {
   145  	if len(accepts) == 0 {
   146  		return ""
   147  	}
   148  
   149  	if contains(accepts, "application/json") {
   150  		return "application/json"
   151  	}
   152  
   153  	return strings.Join(accepts, ",")
   154  }
   155  
   156  // contains is a case insensitive match, finding needle in a haystack
   157  func contains(haystack []string, needle string) bool {
   158  	for _, a := range haystack {
   159  		if strings.EqualFold(a, needle) {
   160  			return true
   161  		}
   162  	}
   163  	return false
   164  }
   165  
   166  // Verify optional parameters are of the correct type.
   167  func typeCheckParameter(obj interface{}, expected string, name string) error {
   168  	// Make sure there is an object.
   169  	if obj == nil {
   170  		return nil
   171  	}
   172  
   173  	// Check the type is as expected.
   174  	if reflect.TypeOf(obj).String() != expected {
   175  		return fmt.Errorf("expected %s to be of type %s but received %s", name, expected, reflect.TypeOf(obj).String())
   176  	}
   177  	return nil
   178  }
   179  
   180  func parameterValueToString( obj interface{}, key string ) string {
   181  	if reflect.TypeOf(obj).Kind() != reflect.Ptr {
   182  		return fmt.Sprintf("%v", obj)
   183  	}
   184  	var param,ok = obj.(MappedNullable)
   185  	if !ok {
   186  		return ""
   187  	}
   188  	dataMap,err := param.ToMap()
   189  	if err != nil {
   190  		return ""
   191  	}
   192  	return fmt.Sprintf("%v", dataMap[key])
   193  }
   194  
   195  // parameterAddToHeaderOrQuery adds the provided object to the request header or url query
   196  // supporting deep object syntax
   197  func parameterAddToHeaderOrQuery(headerOrQueryParams interface{}, keyPrefix string, obj interface{}, collectionType string) {
   198  	var v = reflect.ValueOf(obj)
   199  	var value = ""
   200  	if v == reflect.ValueOf(nil) {
   201  		value = "null"
   202  	} else {
   203  		switch v.Kind() {
   204  			case reflect.Invalid:
   205  				value = "invalid"
   206  
   207  			case reflect.Struct:
   208  				if t,ok := obj.(MappedNullable); ok {
   209  					dataMap,err := t.ToMap()
   210  					if err != nil {
   211  						return
   212  					}
   213  					parameterAddToHeaderOrQuery(headerOrQueryParams, keyPrefix, dataMap, collectionType)
   214  					return
   215  				}
   216  				if t, ok := obj.(time.Time); ok {
   217  					parameterAddToHeaderOrQuery(headerOrQueryParams, keyPrefix, t.Format(time.RFC3339), collectionType)
   218  					return
   219  				}
   220  				value = v.Type().String() + " value"
   221  			case reflect.Slice:
   222  				var indValue = reflect.ValueOf(obj)
   223  				if indValue == reflect.ValueOf(nil) {
   224  					return
   225  				}
   226  				var lenIndValue = indValue.Len()
   227  				for i:=0;i<lenIndValue;i++ {
   228  					var arrayValue = indValue.Index(i)
   229  					parameterAddToHeaderOrQuery(headerOrQueryParams, keyPrefix, arrayValue.Interface(), collectionType)
   230  				}
   231  				return
   232  
   233  			case reflect.Map:
   234  				var indValue = reflect.ValueOf(obj)
   235  				if indValue == reflect.ValueOf(nil) {
   236  					return
   237  				}
   238  				iter := indValue.MapRange()
   239  				for iter.Next() {
   240  					k,v := iter.Key(), iter.Value()
   241  					parameterAddToHeaderOrQuery(headerOrQueryParams, fmt.Sprintf("%s[%s]", keyPrefix, k.String()), v.Interface(), collectionType)
   242  				}
   243  				return
   244  
   245  			case reflect.Interface:
   246  				fallthrough
   247  			case reflect.Ptr:
   248  				parameterAddToHeaderOrQuery(headerOrQueryParams, keyPrefix, v.Elem().Interface(), collectionType)
   249  				return
   250  
   251  			case reflect.Int, reflect.Int8, reflect.Int16,
   252  				reflect.Int32, reflect.Int64:
   253  				value = strconv.FormatInt(v.Int(), 10)
   254  			case reflect.Uint, reflect.Uint8, reflect.Uint16,
   255  				reflect.Uint32, reflect.Uint64, reflect.Uintptr:
   256  				value = strconv.FormatUint(v.Uint(), 10)
   257  			case reflect.Float32, reflect.Float64:
   258  				value = strconv.FormatFloat(v.Float(), 'g', -1, 32)
   259  			case reflect.Bool:
   260  				value = strconv.FormatBool(v.Bool())
   261  			case reflect.String:
   262  				value = v.String()
   263  			default:
   264  				value = v.Type().String() + " value"
   265  		}
   266  	}
   267  
   268  	switch valuesMap := headerOrQueryParams.(type) {
   269  		case url.Values:
   270  			if collectionType == "csv" && valuesMap.Get(keyPrefix) != "" {
   271  				valuesMap.Set(keyPrefix, valuesMap.Get(keyPrefix) + "," + value)
   272  			} else {
   273  				valuesMap.Add(keyPrefix, value)
   274  			}
   275  			break
   276  		case map[string]string:
   277  			valuesMap[keyPrefix] = value
   278  			break
   279  	}
   280  }
   281  
   282  // helper for converting interface{} parameters to json strings
   283  func parameterToJson(obj interface{}) (string, error) {
   284  	jsonBuf, err := json.Marshal(obj)
   285  	if err != nil {
   286  		return "", err
   287  	}
   288  	return string(jsonBuf), err
   289  }
   290  
   291  // callAPI do the request.
   292  func (c *APIClient) callAPI(request *http.Request) (*http.Response, error) {
   293  	if c.cfg.Debug {
   294  		dump, err := httputil.DumpRequestOut(request, true)
   295  		if err != nil {
   296  			return nil, err
   297  		}
   298  		log.Printf("\n%s\n", string(dump))
   299  	}
   300  
   301  	resp, err := c.cfg.HTTPClient.Do(request)
   302  	if err != nil {
   303  		return resp, err
   304  	}
   305  
   306  	if c.cfg.Debug {
   307  		dump, err := httputil.DumpResponse(resp, true)
   308  		if err != nil {
   309  			return resp, err
   310  		}
   311  		log.Printf("\n%s\n", string(dump))
   312  	}
   313  	return resp, err
   314  }
   315  
   316  // Allow modification of underlying config for alternate implementations and testing
   317  // Caution: modifying the configuration while live can cause data races and potentially unwanted behavior
   318  func (c *APIClient) GetConfig() *Configuration {
   319  	return c.cfg
   320  }
   321  
   322  type formFile struct {
   323  		fileBytes []byte
   324  		fileName string
   325  		formFileName string
   326  }
   327  
   328  // prepareRequest build the request
   329  func (c *APIClient) prepareRequest(
   330  	ctx context.Context,
   331  	path string, method string,
   332  	postBody interface{},
   333  	headerParams map[string]string,
   334  	queryParams url.Values,
   335  	formParams url.Values,
   336  	formFiles []formFile) (localVarRequest *http.Request, err error) {
   337  
   338  	var body *bytes.Buffer
   339  
   340  	// Detect postBody type and post.
   341  	if postBody != nil {
   342  		contentType := headerParams["Content-Type"]
   343  		if contentType == "" {
   344  			contentType = detectContentType(postBody)
   345  			headerParams["Content-Type"] = contentType
   346  		}
   347  
   348  		body, err = setBody(postBody, contentType)
   349  		if err != nil {
   350  			return nil, err
   351  		}
   352  	}
   353  
   354  	// add form parameters and file if available.
   355  	if strings.HasPrefix(headerParams["Content-Type"], "multipart/form-data") && len(formParams) > 0 || (len(formFiles) > 0) {
   356  		if body != nil {
   357  			return nil, errors.New("Cannot specify postBody and multipart form at the same time.")
   358  		}
   359  		body = &bytes.Buffer{}
   360  		w := multipart.NewWriter(body)
   361  
   362  		for k, v := range formParams {
   363  			for _, iv := range v {
   364  				if strings.HasPrefix(k, "@") { // file
   365  					err = addFile(w, k[1:], iv)
   366  					if err != nil {
   367  						return nil, err
   368  					}
   369  				} else { // form value
   370  					w.WriteField(k, iv)
   371  				}
   372  			}
   373  		}
   374  		for _, formFile := range formFiles {
   375  			if len(formFile.fileBytes) > 0 && formFile.fileName != "" {
   376  				w.Boundary()
   377  				part, err := w.CreateFormFile(formFile.formFileName, filepath.Base(formFile.fileName))
   378  				if err != nil {
   379  						return nil, err
   380  				}
   381  				_, err = part.Write(formFile.fileBytes)
   382  				if err != nil {
   383  						return nil, err
   384  				}
   385  			}
   386  		}
   387  
   388  		// Set the Boundary in the Content-Type
   389  		headerParams["Content-Type"] = w.FormDataContentType()
   390  
   391  		// Set Content-Length
   392  		headerParams["Content-Length"] = fmt.Sprintf("%d", body.Len())
   393  		w.Close()
   394  	}
   395  
   396  	if strings.HasPrefix(headerParams["Content-Type"], "application/x-www-form-urlencoded") && len(formParams) > 0 {
   397  		if body != nil {
   398  			return nil, errors.New("Cannot specify postBody and x-www-form-urlencoded form at the same time.")
   399  		}
   400  		body = &bytes.Buffer{}
   401  		body.WriteString(formParams.Encode())
   402  		// Set Content-Length
   403  		headerParams["Content-Length"] = fmt.Sprintf("%d", body.Len())
   404  	}
   405  
   406  	// Setup path and query parameters
   407  	url, err := url.Parse(path)
   408  	if err != nil {
   409  		return nil, err
   410  	}
   411  
   412  	// Override request host, if applicable
   413  	if c.cfg.Host != "" {
   414  		url.Host = c.cfg.Host
   415  	}
   416  
   417  	// Override request scheme, if applicable
   418  	if c.cfg.Scheme != "" {
   419  		url.Scheme = c.cfg.Scheme
   420  	}
   421  
   422  	// Adding Query Param
   423  	query := url.Query()
   424  	for k, v := range queryParams {
   425  		for _, iv := range v {
   426  			query.Add(k, iv)
   427  		}
   428  	}
   429  
   430  	// Encode the parameters.
   431  	url.RawQuery = queryParamSplit.ReplaceAllStringFunc(query.Encode(), func(s string) string {
   432  		pieces := strings.Split(s, "=")
   433  		pieces[0] = queryDescape.Replace(pieces[0])
   434  		return strings.Join(pieces, "=")
   435  	})
   436  
   437  	// Generate a new request
   438  	if body != nil {
   439  		localVarRequest, err = http.NewRequest(method, url.String(), body)
   440  	} else {
   441  		localVarRequest, err = http.NewRequest(method, url.String(), nil)
   442  	}
   443  	if err != nil {
   444  		return nil, err
   445  	}
   446  
   447  	// add header parameters, if any
   448  	if len(headerParams) > 0 {
   449  		headers := http.Header{}
   450  		for h, v := range headerParams {
   451  			headers[h] = []string{v}
   452  		}
   453  		localVarRequest.Header = headers
   454  	}
   455  
   456  	// Add the user agent to the request.
   457  	localVarRequest.Header.Add("User-Agent", c.cfg.UserAgent)
   458  
   459  	if ctx != nil {
   460  		// add context to the request
   461  		localVarRequest = localVarRequest.WithContext(ctx)
   462  
   463  		// Walk through any authentication.
   464  
   465  	}
   466  
   467  	for header, value := range c.cfg.DefaultHeader {
   468  		localVarRequest.Header.Add(header, value)
   469  	}
   470  	return localVarRequest, nil
   471  }
   472  
   473  func (c *APIClient) decode(v interface{}, b []byte, contentType string) (err error) {
   474  	if len(b) == 0 {
   475  		return nil
   476  	}
   477  	if s, ok := v.(*string); ok {
   478  		*s = string(b)
   479  		return nil
   480  	}
   481  	if f, ok := v.(*os.File); ok {
   482  		f, err = os.CreateTemp("", "HttpClientFile")
   483  		if err != nil {
   484  			return
   485  		}
   486  		_, err = f.Write(b)
   487  		if err != nil {
   488  			return
   489  		}
   490  		_, err = f.Seek(0, io.SeekStart)
   491  		err = os.Remove(f.Name())
   492  		return
   493  	}
   494  	if f, ok := v.(**os.File); ok {
   495  		*f, err = os.CreateTemp("", "HttpClientFile")
   496  		if err != nil {
   497  			return
   498  		}
   499  		_, err = (*f).Write(b)
   500  		if err != nil {
   501  			return
   502  		}
   503  		_, err = (*f).Seek(0, io.SeekStart)
   504  		err = os.Remove((*f).Name())
   505  		return
   506  	}
   507  	if xmlCheck.MatchString(contentType) {
   508  		if err = xml.Unmarshal(b, v); err != nil {
   509  			return err
   510  		}
   511  		return nil
   512  	}
   513  	if jsonCheck.MatchString(contentType) {
   514  		if actualObj, ok := v.(interface{ GetActualInstance() interface{} }); ok { // oneOf, anyOf schemas
   515  			if unmarshalObj, ok := actualObj.(interface{ UnmarshalJSON([]byte) error }); ok { // make sure it has UnmarshalJSON defined
   516  				if err = unmarshalObj.UnmarshalJSON(b); err != nil {
   517  					return err
   518  				}
   519  			} else {
   520  				return errors.New("Unknown type with GetActualInstance but no unmarshalObj.UnmarshalJSON defined")
   521  			}
   522  		} else if err = json.Unmarshal(b, v); err != nil { // simple model
   523  			return err
   524  		}
   525  		return nil
   526  	}
   527  	if yamlCheck.MatchString(contentType) {
   528  		if actualObj, ok := v.(interface{ GetActualInstance() interface{} }); ok { // oneOf, anyOf schemas
   529  			if unmarshalObj, ok := actualObj.(interface{ UnmarshalYAML([]byte) error }); ok { // make sure it has UnmarshalYAML defined
   530  				if err = unmarshalObj.UnmarshalYAML(b); err != nil {
   531  					return err
   532  				}
   533  			} else {
   534  				return errors.New("unknown type with GetActualInstance but no unmarshalObj.UnmarshalYAML defined")
   535  			}
   536  		} else if err = yaml.Unmarshal(b, v); err != nil { // simple model
   537  			return err
   538  		}
   539  
   540  		return nil
   541  	}
   542  	return errors.New("undefined response type")
   543  }
   544  
   545  // Add a file to the multipart request
   546  func addFile(w *multipart.Writer, fieldName, path string) error {
   547  	file, err := os.Open(filepath.Clean(path))
   548  	if err != nil {
   549  		return err
   550  	}
   551  	err = file.Close()
   552  	if err != nil {
   553  		return err
   554  	}
   555  
   556  	part, err := w.CreateFormFile(fieldName, filepath.Base(path))
   557  	if err != nil {
   558  		return err
   559  	}
   560  	_, err = io.Copy(part, file)
   561  
   562  	return err
   563  }
   564  
   565  // Prevent trying to import "fmt"
   566  func reportError(format string, a ...interface{}) error {
   567  	return fmt.Errorf(format, a...)
   568  }
   569  
   570  // A wrapper for strict JSON decoding
   571  func newStrictDecoder(data []byte) *json.Decoder {
   572  	dec := json.NewDecoder(bytes.NewBuffer(data))
   573  	dec.DisallowUnknownFields()
   574  	return dec
   575  }
   576  
   577  // Set request body from an interface{}
   578  func setBody(body interface{}, contentType string) (bodyBuf *bytes.Buffer, err error) {
   579  	if bodyBuf == nil {
   580  		bodyBuf = &bytes.Buffer{}
   581  	}
   582  
   583  	if reader, ok := body.(io.Reader); ok {
   584  		_, err = bodyBuf.ReadFrom(reader)
   585  	} else if fp, ok := body.(*os.File); ok {
   586  		_, err = bodyBuf.ReadFrom(fp)
   587  	} else if b, ok := body.([]byte); ok {
   588  		_, err = bodyBuf.Write(b)
   589  	} else if s, ok := body.(string); ok {
   590  		_, err = bodyBuf.WriteString(s)
   591  	} else if s, ok := body.(*string); ok {
   592  		_, err = bodyBuf.WriteString(*s)
   593  	} else if jsonCheck.MatchString(contentType) {
   594  		err = json.NewEncoder(bodyBuf).Encode(body)
   595  	} else if xmlCheck.MatchString(contentType) {
   596  		var bs []byte
   597  		bs, err = xml.Marshal(body)
   598  		if err == nil {
   599  			bodyBuf.Write(bs)
   600  		}
   601  	} else if yamlCheck.MatchString(contentType) {
   602  		var bs []byte
   603  		bs, err = yaml.Marshal(body)
   604  		if err == nil {
   605  			bodyBuf.Write(bs)
   606  		}
   607  	}
   608  
   609  	if err != nil {
   610  		return nil, err
   611  	}
   612  
   613  	if bodyBuf.Len() == 0 {
   614  		err = fmt.Errorf("invalid body type %s\n", contentType)
   615  		return nil, err
   616  	}
   617  	return bodyBuf, nil
   618  }
   619  
   620  // detectContentType method is used to figure out `Request.Body` content type for request header
   621  func detectContentType(body interface{}) string {
   622  	contentType := "text/plain; charset=utf-8"
   623  	kind := reflect.TypeOf(body).Kind()
   624  
   625  	switch kind {
   626  	case reflect.Struct, reflect.Map, reflect.Ptr:
   627  		contentType = "application/json; charset=utf-8"
   628  	case reflect.String:
   629  		contentType = "text/plain; charset=utf-8"
   630  	default:
   631  		if b, ok := body.([]byte); ok {
   632  			contentType = http.DetectContentType(b)
   633  		} else if kind == reflect.Slice {
   634  			contentType = "application/json; charset=utf-8"
   635  		}
   636  	}
   637  
   638  	return contentType
   639  }
   640  
   641  // Ripped from https://github.com/gregjones/httpcache/blob/master/httpcache.go
   642  type cacheControl map[string]string
   643  
   644  func parseCacheControl(headers http.Header) cacheControl {
   645  	cc := cacheControl{}
   646  	ccHeader := headers.Get("Cache-Control")
   647  	for _, part := range strings.Split(ccHeader, ",") {
   648  		part = strings.Trim(part, " ")
   649  		if part == "" {
   650  			continue
   651  		}
   652  		if strings.ContainsRune(part, '=') {
   653  			keyval := strings.Split(part, "=")
   654  			cc[strings.Trim(keyval[0], " ")] = strings.Trim(keyval[1], ",")
   655  		} else {
   656  			cc[part] = ""
   657  		}
   658  	}
   659  	return cc
   660  }
   661  
   662  // CacheExpires helper function to determine remaining time before repeating a request.
   663  func CacheExpires(r *http.Response) time.Time {
   664  	// Figure out when the cache expires.
   665  	var expires time.Time
   666  	now, err := time.Parse(time.RFC1123, r.Header.Get("date"))
   667  	if err != nil {
   668  		return time.Now()
   669  	}
   670  	respCacheControl := parseCacheControl(r.Header)
   671  
   672  	if maxAge, ok := respCacheControl["max-age"]; ok {
   673  		lifetime, err := time.ParseDuration(maxAge + "s")
   674  		if err != nil {
   675  			expires = now
   676  		} else {
   677  			expires = now.Add(lifetime)
   678  		}
   679  	} else {
   680  		expiresHeader := r.Header.Get("Expires")
   681  		if expiresHeader != "" {
   682  			expires, err = time.Parse(time.RFC1123, expiresHeader)
   683  			if err != nil {
   684  				expires = now
   685  			}
   686  		}
   687  	}
   688  	return expires
   689  }
   690  
   691  func strlen(s string) int {
   692  	return utf8.RuneCountInString(s)
   693  }
   694  
   695  // GenericOpenAPIError Provides access to the body, error and model on returned errors.
   696  type GenericOpenAPIError struct {
   697  	body  []byte
   698  	error string
   699  	model interface{}
   700  }
   701  
   702  // Error returns non-empty string if there was an error.
   703  func (e GenericOpenAPIError) Error() string {
   704  	return e.error
   705  }
   706  
   707  // Body returns the raw bytes of the response
   708  func (e GenericOpenAPIError) Body() []byte {
   709  	return e.body
   710  }
   711  
   712  // Model returns the unpacked model of the error
   713  func (e GenericOpenAPIError) Model() interface{} {
   714  	return e.model
   715  }
   716  
   717  // format error message using title and detail when model implements rfc7807
   718  func formatErrorMessage(status string, v interface{}) string {
   719  	str := ""
   720  	metaValue := reflect.ValueOf(v).Elem()
   721  
   722  	if metaValue.Kind() == reflect.Struct {
   723  		field := metaValue.FieldByName("Title")
   724  		if field != (reflect.Value{}) {
   725  			str = fmt.Sprintf("%s", field.Interface())
   726  		}
   727  
   728  		field = metaValue.FieldByName("Detail")
   729  		if field != (reflect.Value{}) {
   730  			str = fmt.Sprintf("%s (%s)", str, field.Interface())
   731  		}
   732  	}
   733  
   734  	return strings.TrimSpace(fmt.Sprintf("%s %s", status, str))
   735  }