github.com/opentelekomcloud/gophertelekomcloud@v0.9.3/results.go (about)

     1  package golangsdk
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"net/http"
     9  	"strconv"
    10  	"time"
    11  
    12  	"github.com/opentelekomcloud/gophertelekomcloud/internal/extract"
    13  )
    14  
    15  /*
    16  Result is an internal type to be used by individual resource packages, but its
    17  methods will be available on a wide variety of user-facing embedding types.
    18  
    19  It acts as a base struct that other Result types, returned from request
    20  functions, can embed for convenience. All Results capture basic information
    21  from the HTTP transaction that was performed, including the response body,
    22  HTTP headers, and any errors that happened.
    23  
    24  Generally, each Result type will have an Extract method that can be used to
    25  further interpret the result's payload in a specific context. Extensions or
    26  providers can then provide additional extraction functions to pull out
    27  provider- or extension-specific information as well.
    28  
    29  Deprecated: use functions from internal/extract package instead
    30  */
    31  type Result struct {
    32  	// Body is the payload of the HTTP response from the server.
    33  	Body []byte
    34  
    35  	// Header contains the HTTP header structure from the original response.
    36  	Header http.Header
    37  
    38  	// Err is an error that occurred during the operation. It's deferred until
    39  	// extraction to make it easier to chain the Extract call.
    40  	Err error
    41  }
    42  
    43  // BodyReader returns cached body as *bytes.Reader
    44  func (r Result) BodyReader() io.Reader {
    45  	return bytes.NewReader(r.Body)
    46  }
    47  
    48  type JsonRDSInstanceStatus struct {
    49  	Instances  []JsonRDSInstanceField `json:"instances"`
    50  	TotalCount int                    `json:"total_count"`
    51  }
    52  
    53  type JsonRDSInstanceField struct {
    54  	Id     string `json:"id"`
    55  	Name   string `json:"name"`
    56  	Status string `json:"status"`
    57  }
    58  
    59  // ExtractInto allows users to provide an object into which `Extract` will extract
    60  // the `Result.Body`. This would be useful for OpenStack providers that have
    61  // different fields in the response object than OpenStack proper.
    62  //
    63  // Deprecated: use extract.Into function instead
    64  func (r Result) ExtractInto(to interface{}) error {
    65  	if r.Err != nil {
    66  		return r.Err
    67  	}
    68  
    69  	return extract.Into(bytes.NewReader(r.Body), to)
    70  }
    71  
    72  // ExtractIntoStructPtr will unmarshal the Result (r) into the provided
    73  // interface{} (to).
    74  //
    75  // NOTE: For internal use only
    76  //
    77  // `to` must be a pointer to an underlying struct type
    78  //
    79  // If provided, `label` will be filtered out of the response
    80  // body prior to `r` being unmarshalled into `to`.
    81  //
    82  // Deprecated: use extract.IntoStructPtr function instead
    83  func (r Result) ExtractIntoStructPtr(to interface{}, label string) error {
    84  	if r.Err != nil {
    85  		return r.Err
    86  	}
    87  
    88  	return extract.IntoStructPtr(bytes.NewReader(r.Body), to, label)
    89  }
    90  
    91  // ExtractIntoSlicePtr will unmarshal the Result (r) into the provided
    92  // interface{} (to).
    93  //
    94  // NOTE: For internal use only
    95  //
    96  // `to` must be a pointer to an underlying slice type
    97  //
    98  // If provided, `label` will be filtered out of the response
    99  // body prior to `r` being unmarshalled into `to`.
   100  //
   101  // Deprecated: use extract.IntoSlicePtr function instead
   102  func (r Result) ExtractIntoSlicePtr(to interface{}, label string) error {
   103  	if r.Err != nil {
   104  		return r.Err
   105  	}
   106  
   107  	return extract.IntoSlicePtr(bytes.NewReader(r.Body), to, label)
   108  }
   109  
   110  func PrettyPrintJSON(body interface{}) string {
   111  	pretty, err := json.MarshalIndent(body, "", "  ")
   112  	if err != nil {
   113  		panic(err.Error())
   114  	}
   115  	return string(pretty)
   116  }
   117  
   118  // PrettyPrintJSON creates a string containing the full response body as
   119  // pretty-printed JSON. It's useful for capturing test fixtures and for
   120  // debugging extraction bugs. If you include its output in an issue related to
   121  // a buggy extraction function, we will all love you forever.
   122  func (r Result) PrettyPrintJSON() string {
   123  	return PrettyPrintJSON(r.Body)
   124  }
   125  
   126  // ErrResult is an internal type to be used by individual resource packages, but
   127  // its methods will be available on a wide variety of user-facing embedding
   128  // types.
   129  //
   130  // It represents results that only contain a potential error and
   131  // nothing else. Usually, if the operation executed successfully, the Err field
   132  // will be nil; otherwise it will be stocked with a relevant error. Use the
   133  // ExtractErr method
   134  // to cleanly pull it out.
   135  //
   136  // Deprecated: use plain err return instead
   137  type ErrResult struct {
   138  	Result
   139  }
   140  
   141  // ExtractErr is a function that extracts error information, or nil, from a result.
   142  func (r ErrResult) ExtractErr() error {
   143  	return r.Err
   144  }
   145  
   146  // ----------------------------------------------------------------------------
   147  
   148  type ErrRespond struct {
   149  	ErrorCode string `json:"error_code"`
   150  	ErrorMsg  string `json:"error_msg"`
   151  }
   152  
   153  type ErrWithResult struct {
   154  	ErrResult
   155  }
   156  
   157  func (r Result) Extract() (*ErrRespond, error) {
   158  	var s = ErrRespond{}
   159  	if r.Err != nil {
   160  		return nil, r.Err
   161  	}
   162  	err := r.ExtractInto(&s)
   163  	if err != nil {
   164  		return nil, fmt.Errorf("failed to extract Error with Result")
   165  	}
   166  	return &s, nil
   167  }
   168  
   169  // ----------------------------------------------------------------------------
   170  
   171  /*
   172  HeaderResult is an internal type to be used by individual resource packages, but
   173  its methods will be available on a wide variety of user-facing embedding types.
   174  
   175  It represents a result that only contains an error (possibly nil) and an
   176  http.Header. This is used, for example, by the objectstorage packages in
   177  openstack, because most of the operations don't return response bodies, but do
   178  have relevant information in headers.
   179  */
   180  type HeaderResult struct {
   181  	Result
   182  }
   183  
   184  // ExtractInto allows users to provide an object into which `Extract` will
   185  // extract the http.Header headers of the result.
   186  func (r HeaderResult) ExtractInto(to interface{}) error {
   187  	if r.Err != nil {
   188  		return r.Err
   189  	}
   190  
   191  	tmpHeaderMap := map[string]string{}
   192  	for k, v := range r.Header {
   193  		if len(v) > 0 {
   194  			tmpHeaderMap[k] = v[0]
   195  		}
   196  	}
   197  
   198  	b, err := extract.JsonMarshal(tmpHeaderMap)
   199  	if err != nil {
   200  		return err
   201  	}
   202  	err = json.Unmarshal(b, to)
   203  
   204  	return err
   205  }
   206  
   207  // RFC3339Milli describes a common time format used by some API responses.
   208  const RFC3339Milli = "2006-01-02T15:04:05.999999Z"
   209  
   210  type JSONRFC3339Milli time.Time
   211  
   212  func (jt *JSONRFC3339Milli) UnmarshalJSON(data []byte) error {
   213  	b := bytes.NewBuffer(data)
   214  	dec := json.NewDecoder(b)
   215  	var s string
   216  	if err := dec.Decode(&s); err != nil {
   217  		return err
   218  	}
   219  	t, err := time.Parse(RFC3339Milli, s)
   220  	if err != nil {
   221  		return err
   222  	}
   223  	*jt = JSONRFC3339Milli(t)
   224  	return nil
   225  }
   226  
   227  const RFC3339MilliNoZ = "2006-01-02T15:04:05.999999"
   228  
   229  type JSONRFC3339MilliNoZ time.Time
   230  
   231  func (jt *JSONRFC3339MilliNoZ) UnmarshalJSON(data []byte) error {
   232  	var s string
   233  	if err := json.Unmarshal(data, &s); err != nil {
   234  		return err
   235  	}
   236  	if s == "" {
   237  		return nil
   238  	}
   239  	t, err := time.Parse(RFC3339MilliNoZ, s)
   240  	if err != nil {
   241  		return err
   242  	}
   243  	*jt = JSONRFC3339MilliNoZ(t)
   244  	return nil
   245  }
   246  
   247  type JSONRFC1123 time.Time
   248  
   249  func (jt *JSONRFC1123) UnmarshalJSON(data []byte) error {
   250  	var s string
   251  	if err := json.Unmarshal(data, &s); err != nil {
   252  		return err
   253  	}
   254  	if s == "" {
   255  		return nil
   256  	}
   257  	t, err := time.Parse(time.RFC1123, s)
   258  	if err != nil {
   259  		return err
   260  	}
   261  	*jt = JSONRFC1123(t)
   262  	return nil
   263  }
   264  
   265  type JSONUnix time.Time
   266  
   267  func (jt *JSONUnix) UnmarshalJSON(data []byte) error {
   268  	var s string
   269  	if err := json.Unmarshal(data, &s); err != nil {
   270  		return err
   271  	}
   272  	if s == "" {
   273  		return nil
   274  	}
   275  	unix, err := strconv.ParseInt(s, 10, 64)
   276  	if err != nil {
   277  		return err
   278  	}
   279  	t = time.Unix(unix, 0)
   280  	*jt = JSONUnix(t)
   281  	return nil
   282  }
   283  
   284  // RFC3339NoZ is the time format used in Heat (Orchestration).
   285  const RFC3339NoZ = "2006-01-02T15:04:05"
   286  
   287  type JSONRFC3339NoZ time.Time
   288  
   289  func (jt *JSONRFC3339NoZ) UnmarshalJSON(data []byte) error {
   290  	var s string
   291  	if err := json.Unmarshal(data, &s); err != nil {
   292  		return err
   293  	}
   294  	if s == "" {
   295  		return nil
   296  	}
   297  	t, err := time.Parse(RFC3339NoZ, s)
   298  	if err != nil {
   299  		return err
   300  	}
   301  	*jt = JSONRFC3339NoZ(t)
   302  	return nil
   303  }
   304  
   305  // RFC3339ZNoT is the time format used in Zun (Containers Service).
   306  const RFC3339ZNoT = "2006-01-02 15:04:05-07:00"
   307  
   308  type JSONRFC3339ZNoT time.Time
   309  
   310  func (jt *JSONRFC3339ZNoT) UnmarshalJSON(data []byte) error {
   311  	var s string
   312  	if err := json.Unmarshal(data, &s); err != nil {
   313  		return err
   314  	}
   315  	if s == "" {
   316  		return nil
   317  	}
   318  	t, err := time.Parse(RFC3339ZNoT, s)
   319  	if err != nil {
   320  		return err
   321  	}
   322  	*jt = JSONRFC3339ZNoT(t)
   323  	return nil
   324  }
   325  
   326  // RFC3339ZNoTNoZ is another time format used in Zun (Containers Service).
   327  const RFC3339ZNoTNoZ = "2006-01-02 15:04:05"
   328  
   329  type JSONRFC3339ZNoTNoZ time.Time
   330  
   331  func (jt *JSONRFC3339ZNoTNoZ) UnmarshalJSON(data []byte) error {
   332  	var s string
   333  	if err := json.Unmarshal(data, &s); err != nil {
   334  		return err
   335  	}
   336  	if s == "" {
   337  		return nil
   338  	}
   339  	t, err := time.Parse(RFC3339ZNoTNoZ, s)
   340  	if err != nil {
   341  		return err
   342  	}
   343  	*jt = JSONRFC3339ZNoTNoZ(t)
   344  	return nil
   345  }
   346  
   347  /*
   348  Link is an internal type to be used in packages of collection resources that are
   349  paginated in a certain way.
   350  
   351  It's a response substructure common to many paginated collection results that is
   352  used to point to related pages. Usually, the one we care about is the one with
   353  Rel field set to "next".
   354  */
   355  type Link struct {
   356  	Href string `json:"href"`
   357  	Rel  string `json:"rel"`
   358  }
   359  
   360  /*
   361  ExtractNextURL is an internal function useful for packages of collection
   362  resources that are paginated in a certain way.
   363  
   364  It attempts to extract the "next" URL from slice of Link structs, or
   365  "" if no such URL is present.
   366  */
   367  func ExtractNextURL(links []Link) (string, error) {
   368  	var url string
   369  
   370  	for _, l := range links {
   371  		if l.Rel == "next" {
   372  			url = l.Href
   373  		}
   374  	}
   375  
   376  	if url == "" {
   377  		return "", nil
   378  	}
   379  
   380  	return url, nil
   381  }