github.com/MontFerret/ferret@v0.18.0/pkg/drivers/response.go (about)

     1  package drivers
     2  
     3  import (
     4  	"context"
     5  
     6  	"github.com/wI2L/jettison"
     7  
     8  	"github.com/MontFerret/ferret/pkg/runtime/core"
     9  	"github.com/MontFerret/ferret/pkg/runtime/values"
    10  	"github.com/MontFerret/ferret/pkg/runtime/values/types"
    11  )
    12  
    13  // HTTPResponse HTTP response object.
    14  type (
    15  	HTTPResponse struct {
    16  		URL          string
    17  		StatusCode   int
    18  		Status       string
    19  		Headers      *HTTPHeaders
    20  		Body         []byte
    21  		ResponseTime float64
    22  	}
    23  
    24  	// responseMarshal is a structure that repeats HTTPResponse. It allows
    25  	// easily Marshal the HTTPResponse object.
    26  	responseMarshal struct {
    27  		URL          string       `json:"url"`
    28  		StatusCode   int          `json:"status_code"`
    29  		Status       string       `json:"status"`
    30  		Headers      *HTTPHeaders `json:"headers"`
    31  		Body         []byte       `json:"body"`
    32  		ResponseTime float64      `json:"response_time"`
    33  	}
    34  )
    35  
    36  func (resp *HTTPResponse) Type() core.Type {
    37  	return HTTPResponseType
    38  }
    39  
    40  func (resp *HTTPResponse) String() string {
    41  	return resp.Status
    42  }
    43  
    44  func (resp *HTTPResponse) Compare(other core.Value) int64 {
    45  	if other.Type() != HTTPResponseType {
    46  		return Compare(HTTPResponseType, other.Type())
    47  	}
    48  
    49  	// this is a safe cast. Only *HTTPResponse implements core.Value.
    50  	// HTTPResponse does not.
    51  	otherResp := other.(*HTTPResponse)
    52  
    53  	comp := resp.Headers.Compare(otherResp.Headers)
    54  	if comp != 0 {
    55  		return comp
    56  	}
    57  
    58  	// it makes no sense to compare Status strings
    59  	// because they are always equal if StatusCode's are equal
    60  	return values.NewInt(resp.StatusCode).
    61  		Compare(values.NewInt(resp.StatusCode))
    62  }
    63  
    64  func (resp *HTTPResponse) Unwrap() interface{} {
    65  	return resp
    66  }
    67  
    68  func (resp *HTTPResponse) Copy() core.Value {
    69  	cop := *resp
    70  	return &cop
    71  }
    72  
    73  func (resp *HTTPResponse) Hash() uint64 {
    74  	return values.Parse(resp).Hash()
    75  }
    76  
    77  func (resp *HTTPResponse) MarshalJSON() ([]byte, error) {
    78  	if resp == nil {
    79  		return values.None.MarshalJSON()
    80  	}
    81  
    82  	return jettison.MarshalOpts(responseMarshal(*resp), jettison.NoHTMLEscaping())
    83  }
    84  
    85  func (resp *HTTPResponse) GetIn(ctx context.Context, path []core.Value) (core.Value, core.PathError) {
    86  	if len(path) == 0 {
    87  		return resp, nil
    88  	}
    89  
    90  	segmentIdx := 0
    91  	segment := path[segmentIdx]
    92  
    93  	if typ := segment.Type(); typ != types.String {
    94  		return values.None, core.NewPathError(core.TypeError(typ, types.String), segmentIdx)
    95  	}
    96  
    97  	field := segment.String()
    98  
    99  	switch field {
   100  	case "url", "URL":
   101  		return values.NewString(resp.URL), nil
   102  	case "status":
   103  		return values.NewString(resp.Status), nil
   104  	case "statusCode":
   105  		return values.NewInt(resp.StatusCode), nil
   106  	case "headers":
   107  		if len(path) == 1 {
   108  			return resp.Headers, nil
   109  		}
   110  
   111  		out, pathErr := resp.Headers.GetIn(ctx, path[1:])
   112  
   113  		if pathErr != nil {
   114  			return values.None, core.NewPathErrorFrom(pathErr, segmentIdx)
   115  		}
   116  
   117  		return out, nil
   118  	case "body":
   119  		return values.NewBinary(resp.Body), nil
   120  	case "responseTime":
   121  		return values.NewFloat(resp.ResponseTime), nil
   122  	}
   123  
   124  	return values.None, nil
   125  }