Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Refactor] Introduce extract functions #391

Merged
merged 6 commits into from
Aug 10, 2022
Merged
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 61 additions & 31 deletions results.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ Generally, each Result type will have an Extract method that can be used to
further interpret the result's payload in a specific context. Extensions or
providers can then provide additional extraction functions to pull out
provider- or extension-specific information as well.

: use plain functions of this package instead
*/
type Result struct {
// Body is the payload of the HTTP response from the server. In most cases,
Expand All @@ -49,22 +51,15 @@ type JsonRDSInstanceField struct {
Status string `json:"status"`
}

// ExtractInto allows users to provide an object into which `Extract` will extract
// the `Result.Body`. This would be useful for OpenStack providers that have
// different fields in the response object than OpenStack proper.
func (r Result) ExtractInto(to interface{}) error {
if r.Err != nil {
return r.Err
}

if reader, ok := r.Body.(io.Reader); ok {
func ExtractInto(body interface{}, to interface{}) error {
if reader, ok := body.(io.Reader); ok {
if readCloser, ok := reader.(io.Closer); ok {
defer readCloser.Close()
}
return json.NewDecoder(reader).Decode(to)
}

b, err := jsonMarshal(r.Body)
b, err := jsonMarshal(body)
if err != nil {
return err
}
Expand All @@ -73,13 +68,26 @@ func (r Result) ExtractInto(to interface{}) error {
return err
}

func (r Result) extractIntoPtr(to interface{}, label string) error {
// ExtractInto allows users to provide an object into which `Extract` will extract
// the `Result.Body`. This would be useful for OpenStack providers that have
// different fields in the response object than OpenStack proper.
//
// : use ExtractInto function instead
func (r Result) ExtractInto(to interface{}) error {
if r.Err != nil {
return r.Err
}

return ExtractInto(r.Body, to)
}

func extractIntoPtr(body, to interface{}, label string) error {
if label == "" {
return r.ExtractInto(&to)
return ExtractInto(body, &to)
}

var m map[string]interface{}
err := r.ExtractInto(&m)
err := ExtractInto(body, &m)
if err != nil {
return err
}
Expand Down Expand Up @@ -162,6 +170,21 @@ func (r Result) extractIntoPtr(to interface{}, label string) error {
return err
}

// ExtractIntoStructPtr will unmarshal the given body into the provided
// interface{} (to).
func ExtractIntoStructPtr(body, to interface{}, label string) error {
t := reflect.TypeOf(to)
if k := t.Kind(); k != reflect.Ptr {
return fmt.Errorf("expected pointer, got %v", k)
}
switch t.Elem().Kind() {
case reflect.Struct:
return extractIntoPtr(body, to, label)
default:
return fmt.Errorf("expected pointer to struct, got: %v", t)
}
}

// ExtractIntoStructPtr will unmarshal the Result (r) into the provided
// interface{} (to).
//
Expand All @@ -171,20 +194,28 @@ func (r Result) extractIntoPtr(to interface{}, label string) error {
//
// If provided, `label` will be filtered out of the response
// body prior to `r` being unmarshalled into `to`.
//
// : use ExtractIntoStructPtr function instead
func (r Result) ExtractIntoStructPtr(to interface{}, label string) error {
if r.Err != nil {
return r.Err
}

return ExtractIntoStructPtr(r.Body, to, label)
}

// ExtractIntoSlicePtr will unmarshal the provided body into the provided
// interface{} (to).
func ExtractIntoSlicePtr(body, to interface{}, label string) error {
t := reflect.TypeOf(to)
if k := t.Kind(); k != reflect.Ptr {
return fmt.Errorf("Expected pointer, got %v", k)
return fmt.Errorf("expected pointer, got %v", k)
}
switch t.Elem().Kind() {
case reflect.Struct:
return r.extractIntoPtr(to, label)
case reflect.Slice:
return extractIntoPtr(body, to, label)
default:
return fmt.Errorf("Expected pointer to struct, got: %v", t)
return fmt.Errorf("expected pointer to slice, got: %v", t)
}
}

Expand All @@ -197,33 +228,30 @@ func (r Result) ExtractIntoStructPtr(to interface{}, label string) error {
//
// If provided, `label` will be filtered out of the response
// body prior to `r` being unmarshalled into `to`.
//
// : use ExtractIntoSlicePtr function instead
func (r Result) ExtractIntoSlicePtr(to interface{}, label string) error {
if r.Err != nil {
return r.Err
}

t := reflect.TypeOf(to)
if k := t.Kind(); k != reflect.Ptr {
return fmt.Errorf("Expected pointer, got %v", k)
}
switch t.Elem().Kind() {
case reflect.Slice:
return r.extractIntoPtr(to, label)
default:
return fmt.Errorf("Expected pointer to slice, got: %v", t)
return ExtractIntoSlicePtr(r.Body, to, label)
}

func PrettyPrintJSON(body interface{}) string {
pretty, err := json.MarshalIndent(body, "", " ")
if err != nil {
panic(err.Error())
}
return string(pretty)
}

// PrettyPrintJSON creates a string containing the full response body as
// pretty-printed JSON. It's useful for capturing test fixtures and for
// debugging extraction bugs. If you include its output in an issue related to
// a buggy extraction function, we will all love you forever.
func (r Result) PrettyPrintJSON() string {
pretty, err := json.MarshalIndent(r.Body, "", " ")
if err != nil {
panic(err.Error())
}
return string(pretty)
return PrettyPrintJSON(r.Body)
}

// ErrResult is an internal type to be used by individual resource packages, but
Expand All @@ -235,6 +263,8 @@ func (r Result) PrettyPrintJSON() string {
// will be nil; otherwise it will be stocked with a relevant error. Use the
// ExtractErr method
// to cleanly pull it out.
//
// : use plain err return instead
type ErrResult struct {
Result
}
Expand Down