Skip to content

Commit

Permalink
refactor(eval): extract a Clear method and document the ResponseBody …
Browse files Browse the repository at this point in the history
…wrapper
  • Loading branch information
caiorcferreira authored and cupello committed Sep 28, 2020
1 parent d54d9ec commit 3cd9b8a
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 11 deletions.
3 changes: 1 addition & 2 deletions internal/eval/aggregators.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,7 @@ func parseOrigin(origin interface{}) interface{} {
func cleanOriginResult(origin interface{}) interface{} {
switch origin := origin.(type) {
case restql.DoneResource:
origin.ResponseBody.SetValue(nil)
origin.ResponseBody.SetBytes(nil)
origin.ResponseBody.Clear()
return origin
case restql.DoneResources:
result := make(restql.DoneResources, len(origin))
Expand Down
59 changes: 51 additions & 8 deletions pkg/restql/response.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,42 +4,68 @@ import (
"encoding/json"
)

// ResponseBody is a wrapper that allows restQL to defer JSON parsing
// the HTTP body of an upstream response.
//
// Internally it stores two values: a byte slice and a interface. The most
// common scenario will be of creating a ResponseBody from the byte slice red
// from the HTTP response and returning it to downstream.
//
// When features need to access the content of the JSON response, they can
// use the Unmarshal method to get it.
//
// If the byte slice is unmarshalled or a new value is set on the response body
// with SetValue method, then the Marshal and Unmarshal function will operate
// using this value rather then the byte slice.
type ResponseBody struct {
log Logger
jsonBytes []byte
jsonValue interface{}
}

// NewResponseBodyFromBytes creates a ResponseBody wrapper from
// an HTTP response data.
func NewResponseBodyFromBytes(log Logger, b []byte) *ResponseBody {
r := &ResponseBody{log: log}
r := &ResponseBody{log: log, jsonBytes: b}

r.SetBytes(b)
return r
}

// NewResponseBodyFromValue creates a ResponseBody wrapper from
// a generic value, usually from an error.
func NewResponseBodyFromValue(log Logger, v interface{}) *ResponseBody {
r := &ResponseBody{log: log}

r.SetValue(v)
return r
}

func (r *ResponseBody) GetBytes() []byte {
// Bytes return the bytes data wrapped.
func (r *ResponseBody) Bytes() []byte {
return r.jsonBytes
}

func (r *ResponseBody) SetBytes(b []byte) {
r.jsonBytes = b
}

func (r *ResponseBody) GetValue() interface{} {
// Value return the generic data wrapped.
func (r *ResponseBody) Value() interface{} {
return r.jsonValue
}

// SetValue defines a generic value to replace
// the byte slice data.
func (r *ResponseBody) SetValue(v interface{}) {
r.jsonValue = v
}

// Marshal returns the content of ResponseBody ready to
// be sent to downstream.
//
// This method can process the content in 4 ways:
// - If there is a generic data, marshal it using a JSON parser
// and return the result as a json.RawMessage.
// - Else, if the byte slice is empty, return nil.
// - Else, if the byte slice is not an valid JSON, stringify it.
// - Finally, if the byte slice is not empty and is a valid json,
// return it as a json.RawMessage.
func (r *ResponseBody) Marshal() (interface{}, error) {
if r.jsonValue != nil {
b, err := json.Marshal(r.jsonValue)
Expand All @@ -61,6 +87,15 @@ func (r *ResponseBody) Marshal() (interface{}, error) {
return json.RawMessage(r.jsonBytes), nil
}

// Unmarshal returns the content of ResponseBody ready to
// be manipulated internally by restQL.
//
// This method can process the content in 4 ways:
// - If there is a generic data, return it.
// - Else, if the byte slice is empty or is not a valid json,
// return it as a string.
// - Finally, if it is valid to be manipulated, then unmarshal it
// and return.
func (r *ResponseBody) Unmarshal() interface{} {
if r.jsonValue != nil {
return r.jsonValue
Expand All @@ -84,6 +119,8 @@ func (r *ResponseBody) Unmarshal() interface{} {
return responseBody
}

// Valid return true if the ResponseBody content
// can be manipulated by restQL.
func (r *ResponseBody) Valid() bool {
if r.jsonValue != nil {
return true
Expand All @@ -92,6 +129,12 @@ func (r *ResponseBody) Valid() bool {
return len(r.jsonBytes) > 0 && json.Valid(r.jsonBytes)
}

// Clear removes all internal content.
func (r *ResponseBody) Clear() {
r.jsonBytes = nil
r.jsonValue = nil
}

// ResourceCacheControlValue represents the values a cache control
// directive is able to have.
// It can either be present and have a integer time value or
Expand Down
2 changes: 1 addition & 1 deletion test/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ var mappingComparer = cmp.Comparer(func(x, y restql.Mapping) bool {
})

var responseBodyComparer = cmp.Comparer(func(x, y restql.ResponseBody) bool {
return bytes.Equal(x.GetBytes(), y.GetBytes()) || cmp.Equal(x.GetValue(), y.GetBytes())
return bytes.Equal(x.Bytes(), y.Bytes()) || cmp.Equal(x.Value(), y.Bytes())
})

func Equal(t *testing.T, got, expected interface{}) {
Expand Down

0 comments on commit 3cd9b8a

Please sign in to comment.