Fixed read request body, added read request body wf function, added more tests
This commit is contained in:
+109
@@ -0,0 +1,109 @@
|
||||
package automation
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
// Definitions file that controls how this file is generated:
|
||||
// automation/automation/apigw_body_handler.yaml
|
||||
|
||||
import (
|
||||
"context"
|
||||
atypes "github.com/cortezaproject/corteza-server/automation/types"
|
||||
"github.com/cortezaproject/corteza-server/pkg/expr"
|
||||
"github.com/cortezaproject/corteza-server/pkg/http"
|
||||
"github.com/cortezaproject/corteza-server/pkg/wfexec"
|
||||
)
|
||||
|
||||
var _ wfexec.ExecResponse
|
||||
|
||||
type (
|
||||
apigwBodyHandlerRegistry interface {
|
||||
AddFunctions(ff ...*atypes.Function)
|
||||
Type(ref string) expr.Type
|
||||
}
|
||||
)
|
||||
|
||||
func (h apigwBodyHandler) register() {
|
||||
h.reg.AddFunctions(
|
||||
h.Read(),
|
||||
)
|
||||
}
|
||||
|
||||
type (
|
||||
apigwBodyReadArgs struct {
|
||||
hasRequest bool
|
||||
Request *http.Request
|
||||
}
|
||||
|
||||
apigwBodyReadResults struct {
|
||||
Body string
|
||||
}
|
||||
)
|
||||
|
||||
// Read function Read request body from integration gateway
|
||||
//
|
||||
// expects implementation of read function:
|
||||
// func (h apigwBodyHandler) read(ctx context.Context, args *apigwBodyReadArgs) (results *apigwBodyReadResults, err error) {
|
||||
// return
|
||||
// }
|
||||
func (h apigwBodyHandler) Read() *atypes.Function {
|
||||
return &atypes.Function{
|
||||
Ref: "apigwBodyRead",
|
||||
Kind: "function",
|
||||
Labels: map[string]string(nil),
|
||||
Meta: &atypes.FunctionMeta{
|
||||
Short: "Read request body from integration gateway",
|
||||
},
|
||||
|
||||
Parameters: []*atypes.Param{
|
||||
{
|
||||
Name: "request",
|
||||
Types: []string{"HttpRequest"}, Required: true,
|
||||
},
|
||||
},
|
||||
|
||||
Results: []*atypes.Param{
|
||||
|
||||
{
|
||||
Name: "body",
|
||||
Types: []string{"String"},
|
||||
},
|
||||
},
|
||||
|
||||
Handler: func(ctx context.Context, in *expr.Vars) (out *expr.Vars, err error) {
|
||||
var (
|
||||
args = &apigwBodyReadArgs{
|
||||
hasRequest: in.Has("request"),
|
||||
}
|
||||
)
|
||||
|
||||
if err = in.Decode(args); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var results *apigwBodyReadResults
|
||||
if results, err = h.read(ctx, args); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
out = &expr.Vars{}
|
||||
|
||||
{
|
||||
// converting results.Body (string) to String
|
||||
var (
|
||||
tval expr.TypedValue
|
||||
)
|
||||
|
||||
if tval, err = h.reg.Type("String").Cast(results.Body); err != nil {
|
||||
return
|
||||
} else if err = expr.Assign(out, "body", tval); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package automation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
type (
|
||||
apigwBodyHandler struct {
|
||||
reg apigwBodyHandlerRegistry
|
||||
}
|
||||
)
|
||||
|
||||
func ApigwBodyHandler(reg queueHandlerRegistry) *apigwBodyHandler {
|
||||
h := &apigwBodyHandler{
|
||||
reg: reg,
|
||||
}
|
||||
|
||||
h.register()
|
||||
return h
|
||||
}
|
||||
|
||||
func (h apigwBodyHandler) read(ctx context.Context, args *apigwBodyReadArgs) (res *apigwBodyReadResults, err error) {
|
||||
res = &apigwBodyReadResults{}
|
||||
|
||||
if !args.hasRequest {
|
||||
err = fmt.Errorf("could not read body, contents missing")
|
||||
return
|
||||
}
|
||||
|
||||
bb, err := io.ReadAll(args.Request.Body)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
res.Body = string(bb)
|
||||
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
name: apigwBody
|
||||
|
||||
imports:
|
||||
- github.com/cortezaproject/corteza-server/pkg/http
|
||||
|
||||
functions:
|
||||
read:
|
||||
meta:
|
||||
short: Read request body from integration gateway
|
||||
params:
|
||||
request:
|
||||
required: true
|
||||
types:
|
||||
- { wf: HttpRequest, go: '*http.Request' }
|
||||
results:
|
||||
body:
|
||||
wf: String
|
||||
Generated
-578
@@ -11,7 +11,6 @@ package automation
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza-server/automation/types"
|
||||
. "github.com/cortezaproject/corteza-server/pkg/expr"
|
||||
"sync"
|
||||
)
|
||||
@@ -67,580 +66,3 @@ func (t *EmailMessage) Assign(val interface{}) error {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// HttpRequest is an expression type, wrapper for *types.HttpRequest type
|
||||
type HttpRequest struct {
|
||||
value *types.HttpRequest
|
||||
mux sync.RWMutex
|
||||
}
|
||||
|
||||
// NewHttpRequest creates new instance of HttpRequest expression type
|
||||
func NewHttpRequest(val interface{}) (*HttpRequest, error) {
|
||||
if c, err := CastToHttpRequest(val); err != nil {
|
||||
return nil, fmt.Errorf("unable to create HttpRequest: %w", err)
|
||||
} else {
|
||||
return &HttpRequest{value: c}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Get return underlying value on HttpRequest
|
||||
func (t *HttpRequest) Get() interface{} {
|
||||
t.mux.RLock()
|
||||
defer t.mux.RUnlock()
|
||||
return t.value
|
||||
}
|
||||
|
||||
// GetValue returns underlying value on HttpRequest
|
||||
func (t *HttpRequest) GetValue() *types.HttpRequest {
|
||||
t.mux.RLock()
|
||||
defer t.mux.RUnlock()
|
||||
return t.value
|
||||
}
|
||||
|
||||
// Type return type name
|
||||
func (HttpRequest) Type() string { return "HttpRequest" }
|
||||
|
||||
// Cast converts value to *types.HttpRequest
|
||||
func (HttpRequest) Cast(val interface{}) (TypedValue, error) {
|
||||
return NewHttpRequest(val)
|
||||
}
|
||||
|
||||
// Assign new value to HttpRequest
|
||||
//
|
||||
// value is first passed through CastToHttpRequest
|
||||
func (t *HttpRequest) Assign(val interface{}) error {
|
||||
if c, err := CastToHttpRequest(val); err != nil {
|
||||
return err
|
||||
} else {
|
||||
t.value = c
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (t *HttpRequest) AssignFieldValue(key string, val TypedValue) error {
|
||||
t.mux.Lock()
|
||||
defer t.mux.Unlock()
|
||||
return assignToHttpRequest(t.value, key, val)
|
||||
}
|
||||
|
||||
// SelectGVal implements gval.Selector requirements
|
||||
//
|
||||
// It allows gval lib to access HttpRequest's underlying value (*types.HttpRequest)
|
||||
// and it's fields
|
||||
//
|
||||
func (t *HttpRequest) SelectGVal(ctx context.Context, k string) (interface{}, error) {
|
||||
t.mux.RLock()
|
||||
defer t.mux.RUnlock()
|
||||
return httpRequestGValSelector(t.value, k)
|
||||
}
|
||||
|
||||
// Select is field accessor for *types.HttpRequest
|
||||
//
|
||||
// Similar to SelectGVal but returns typed values
|
||||
func (t *HttpRequest) Select(k string) (TypedValue, error) {
|
||||
t.mux.RLock()
|
||||
defer t.mux.RUnlock()
|
||||
return httpRequestTypedValueSelector(t.value, k)
|
||||
}
|
||||
|
||||
func (t *HttpRequest) Has(k string) bool {
|
||||
t.mux.RLock()
|
||||
defer t.mux.RUnlock()
|
||||
switch k {
|
||||
case "Method":
|
||||
return true
|
||||
case "URL":
|
||||
return true
|
||||
case "Header":
|
||||
return true
|
||||
case "Body":
|
||||
return true
|
||||
case "Form":
|
||||
return true
|
||||
case "PostForm":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// httpRequestGValSelector is field accessor for *types.HttpRequest
|
||||
func httpRequestGValSelector(res *types.HttpRequest, k string) (interface{}, error) {
|
||||
if res == nil {
|
||||
return nil, nil
|
||||
}
|
||||
switch k {
|
||||
case "Method":
|
||||
return res.Method, nil
|
||||
case "URL":
|
||||
return res.URL, nil
|
||||
case "Header":
|
||||
return res.Header, nil
|
||||
case "Body":
|
||||
return res.Body, nil
|
||||
case "Form":
|
||||
return res.Form, nil
|
||||
case "PostForm":
|
||||
return res.PostForm, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unknown field '%s'", k)
|
||||
}
|
||||
|
||||
// httpRequestTypedValueSelector is field accessor for *types.HttpRequest
|
||||
func httpRequestTypedValueSelector(res *types.HttpRequest, k string) (TypedValue, error) {
|
||||
if res == nil {
|
||||
return nil, nil
|
||||
}
|
||||
switch k {
|
||||
case "Method":
|
||||
return NewString(res.Method)
|
||||
case "URL":
|
||||
return NewUrl(res.URL)
|
||||
case "Header":
|
||||
return NewKVV(res.Header)
|
||||
case "Body":
|
||||
return NewHttpRequestBody(res.Body)
|
||||
case "Form":
|
||||
return NewKVV(res.Form)
|
||||
case "PostForm":
|
||||
return NewKVV(res.PostForm)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unknown field '%s'", k)
|
||||
}
|
||||
|
||||
// assignToHttpRequest is field value setter for *types.HttpRequest
|
||||
func assignToHttpRequest(res *types.HttpRequest, k string, val interface{}) error {
|
||||
switch k {
|
||||
case "Method":
|
||||
aux, err := CastToString(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res.Method = aux
|
||||
return nil
|
||||
case "URL":
|
||||
aux, err := CastToUrl(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res.URL = aux
|
||||
return nil
|
||||
case "Header":
|
||||
aux, err := CastToKVV(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res.Header = aux
|
||||
return nil
|
||||
case "Body":
|
||||
aux, err := CastToHttpRequestBody(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res.Body = aux
|
||||
return nil
|
||||
case "Form":
|
||||
aux, err := CastToKVV(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res.Form = aux
|
||||
return nil
|
||||
case "PostForm":
|
||||
aux, err := CastToKVV(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res.PostForm = aux
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("unknown field '%s'", k)
|
||||
}
|
||||
|
||||
// HttpRequestBody is an expression type, wrapper for *types.HttpRequestBody type
|
||||
type HttpRequestBody struct {
|
||||
value *types.HttpRequestBody
|
||||
mux sync.RWMutex
|
||||
}
|
||||
|
||||
// NewHttpRequestBody creates new instance of HttpRequestBody expression type
|
||||
func NewHttpRequestBody(val interface{}) (*HttpRequestBody, error) {
|
||||
if c, err := CastToHttpRequestBody(val); err != nil {
|
||||
return nil, fmt.Errorf("unable to create HttpRequestBody: %w", err)
|
||||
} else {
|
||||
return &HttpRequestBody{value: c}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Get return underlying value on HttpRequestBody
|
||||
func (t *HttpRequestBody) Get() interface{} {
|
||||
t.mux.RLock()
|
||||
defer t.mux.RUnlock()
|
||||
return t.value
|
||||
}
|
||||
|
||||
// GetValue returns underlying value on HttpRequestBody
|
||||
func (t *HttpRequestBody) GetValue() *types.HttpRequestBody {
|
||||
t.mux.RLock()
|
||||
defer t.mux.RUnlock()
|
||||
return t.value
|
||||
}
|
||||
|
||||
// Type return type name
|
||||
func (HttpRequestBody) Type() string { return "HttpRequestBody" }
|
||||
|
||||
// Cast converts value to *types.HttpRequestBody
|
||||
func (HttpRequestBody) Cast(val interface{}) (TypedValue, error) {
|
||||
return NewHttpRequestBody(val)
|
||||
}
|
||||
|
||||
// Assign new value to HttpRequestBody
|
||||
//
|
||||
// value is first passed through CastToHttpRequestBody
|
||||
func (t *HttpRequestBody) Assign(val interface{}) error {
|
||||
if c, err := CastToHttpRequestBody(val); err != nil {
|
||||
return err
|
||||
} else {
|
||||
t.value = c
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (t *HttpRequestBody) AssignFieldValue(key string, val TypedValue) error {
|
||||
t.mux.Lock()
|
||||
defer t.mux.Unlock()
|
||||
return assignToHttpRequestBody(t.value, key, val)
|
||||
}
|
||||
|
||||
// SelectGVal implements gval.Selector requirements
|
||||
//
|
||||
// It allows gval lib to access HttpRequestBody's underlying value (*types.HttpRequestBody)
|
||||
// and it's fields
|
||||
//
|
||||
func (t *HttpRequestBody) SelectGVal(ctx context.Context, k string) (interface{}, error) {
|
||||
t.mux.RLock()
|
||||
defer t.mux.RUnlock()
|
||||
return httpRequestBodyGValSelector(t.value, k)
|
||||
}
|
||||
|
||||
// Select is field accessor for *types.HttpRequestBody
|
||||
//
|
||||
// Similar to SelectGVal but returns typed values
|
||||
func (t *HttpRequestBody) Select(k string) (TypedValue, error) {
|
||||
t.mux.RLock()
|
||||
defer t.mux.RUnlock()
|
||||
return httpRequestBodyTypedValueSelector(t.value, k)
|
||||
}
|
||||
|
||||
func (t *HttpRequestBody) Has(k string) bool {
|
||||
t.mux.RLock()
|
||||
defer t.mux.RUnlock()
|
||||
switch k {
|
||||
case "Body":
|
||||
return true
|
||||
case "Buffer":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// httpRequestBodyGValSelector is field accessor for *types.HttpRequestBody
|
||||
func httpRequestBodyGValSelector(res *types.HttpRequestBody, k string) (interface{}, error) {
|
||||
if res == nil {
|
||||
return nil, nil
|
||||
}
|
||||
switch k {
|
||||
case "Body":
|
||||
return res.Body, nil
|
||||
case "Buffer":
|
||||
return res.Buffer, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unknown field '%s'", k)
|
||||
}
|
||||
|
||||
// httpRequestBodyTypedValueSelector is field accessor for *types.HttpRequestBody
|
||||
func httpRequestBodyTypedValueSelector(res *types.HttpRequestBody, k string) (TypedValue, error) {
|
||||
if res == nil {
|
||||
return nil, nil
|
||||
}
|
||||
switch k {
|
||||
case "Body":
|
||||
return NewReader(res.Body)
|
||||
case "Buffer":
|
||||
return NewBytes(res.Buffer)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unknown field '%s'", k)
|
||||
}
|
||||
|
||||
// assignToHttpRequestBody is field value setter for *types.HttpRequestBody
|
||||
func assignToHttpRequestBody(res *types.HttpRequestBody, k string, val interface{}) error {
|
||||
switch k {
|
||||
case "Body":
|
||||
aux, err := CastToReader(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res.Body = aux
|
||||
return nil
|
||||
case "Buffer":
|
||||
aux, err := CastToBytes(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res.Buffer = aux
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("unknown field '%s'", k)
|
||||
}
|
||||
|
||||
// Url is an expression type, wrapper for *types.Url type
|
||||
type Url struct {
|
||||
value *types.Url
|
||||
mux sync.RWMutex
|
||||
}
|
||||
|
||||
// NewUrl creates new instance of Url expression type
|
||||
func NewUrl(val interface{}) (*Url, error) {
|
||||
if c, err := CastToUrl(val); err != nil {
|
||||
return nil, fmt.Errorf("unable to create Url: %w", err)
|
||||
} else {
|
||||
return &Url{value: c}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Get return underlying value on Url
|
||||
func (t *Url) Get() interface{} {
|
||||
t.mux.RLock()
|
||||
defer t.mux.RUnlock()
|
||||
return t.value
|
||||
}
|
||||
|
||||
// GetValue returns underlying value on Url
|
||||
func (t *Url) GetValue() *types.Url {
|
||||
t.mux.RLock()
|
||||
defer t.mux.RUnlock()
|
||||
return t.value
|
||||
}
|
||||
|
||||
// Type return type name
|
||||
func (Url) Type() string { return "Url" }
|
||||
|
||||
// Cast converts value to *types.Url
|
||||
func (Url) Cast(val interface{}) (TypedValue, error) {
|
||||
return NewUrl(val)
|
||||
}
|
||||
|
||||
// Assign new value to Url
|
||||
//
|
||||
// value is first passed through CastToUrl
|
||||
func (t *Url) Assign(val interface{}) error {
|
||||
if c, err := CastToUrl(val); err != nil {
|
||||
return err
|
||||
} else {
|
||||
t.value = c
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Url) AssignFieldValue(key string, val TypedValue) error {
|
||||
t.mux.Lock()
|
||||
defer t.mux.Unlock()
|
||||
return assignToUrl(t.value, key, val)
|
||||
}
|
||||
|
||||
// SelectGVal implements gval.Selector requirements
|
||||
//
|
||||
// It allows gval lib to access Url's underlying value (*types.Url)
|
||||
// and it's fields
|
||||
//
|
||||
func (t *Url) SelectGVal(ctx context.Context, k string) (interface{}, error) {
|
||||
t.mux.RLock()
|
||||
defer t.mux.RUnlock()
|
||||
return urlGValSelector(t.value, k)
|
||||
}
|
||||
|
||||
// Select is field accessor for *types.Url
|
||||
//
|
||||
// Similar to SelectGVal but returns typed values
|
||||
func (t *Url) Select(k string) (TypedValue, error) {
|
||||
t.mux.RLock()
|
||||
defer t.mux.RUnlock()
|
||||
return urlTypedValueSelector(t.value, k)
|
||||
}
|
||||
|
||||
func (t *Url) Has(k string) bool {
|
||||
t.mux.RLock()
|
||||
defer t.mux.RUnlock()
|
||||
switch k {
|
||||
case "Scheme":
|
||||
return true
|
||||
case "Opaque":
|
||||
return true
|
||||
case "Host":
|
||||
return true
|
||||
case "Path":
|
||||
return true
|
||||
case "RawPath":
|
||||
return true
|
||||
case "ForceQuery":
|
||||
return true
|
||||
case "RawQuery":
|
||||
return true
|
||||
case "Fragment":
|
||||
return true
|
||||
case "RawFragment":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// urlGValSelector is field accessor for *types.Url
|
||||
func urlGValSelector(res *types.Url, k string) (interface{}, error) {
|
||||
if res == nil {
|
||||
return nil, nil
|
||||
}
|
||||
switch k {
|
||||
case "Scheme":
|
||||
return res.Scheme, nil
|
||||
case "Opaque":
|
||||
return res.Opaque, nil
|
||||
case "Host":
|
||||
return res.Host, nil
|
||||
case "Path":
|
||||
return res.Path, nil
|
||||
case "RawPath":
|
||||
return res.RawPath, nil
|
||||
case "ForceQuery":
|
||||
return res.ForceQuery, nil
|
||||
case "RawQuery":
|
||||
return res.RawQuery, nil
|
||||
case "Fragment":
|
||||
return res.Fragment, nil
|
||||
case "RawFragment":
|
||||
return res.RawFragment, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unknown field '%s'", k)
|
||||
}
|
||||
|
||||
// urlTypedValueSelector is field accessor for *types.Url
|
||||
func urlTypedValueSelector(res *types.Url, k string) (TypedValue, error) {
|
||||
if res == nil {
|
||||
return nil, nil
|
||||
}
|
||||
switch k {
|
||||
case "Scheme":
|
||||
return NewString(res.Scheme)
|
||||
case "Opaque":
|
||||
return NewString(res.Opaque)
|
||||
case "Host":
|
||||
return NewString(res.Host)
|
||||
case "Path":
|
||||
return NewString(res.Path)
|
||||
case "RawPath":
|
||||
return NewString(res.RawPath)
|
||||
case "ForceQuery":
|
||||
return NewBoolean(res.ForceQuery)
|
||||
case "RawQuery":
|
||||
return NewString(res.RawQuery)
|
||||
case "Fragment":
|
||||
return NewString(res.Fragment)
|
||||
case "RawFragment":
|
||||
return NewString(res.RawFragment)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unknown field '%s'", k)
|
||||
}
|
||||
|
||||
// assignToUrl is field value setter for *types.Url
|
||||
func assignToUrl(res *types.Url, k string, val interface{}) error {
|
||||
switch k {
|
||||
case "Scheme":
|
||||
aux, err := CastToString(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res.Scheme = aux
|
||||
return nil
|
||||
case "Opaque":
|
||||
aux, err := CastToString(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res.Opaque = aux
|
||||
return nil
|
||||
case "Host":
|
||||
aux, err := CastToString(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res.Host = aux
|
||||
return nil
|
||||
case "Path":
|
||||
aux, err := CastToString(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res.Path = aux
|
||||
return nil
|
||||
case "RawPath":
|
||||
aux, err := CastToString(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res.RawPath = aux
|
||||
return nil
|
||||
case "ForceQuery":
|
||||
aux, err := CastToBoolean(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res.ForceQuery = aux
|
||||
return nil
|
||||
case "RawQuery":
|
||||
aux, err := CastToString(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res.RawQuery = aux
|
||||
return nil
|
||||
case "Fragment":
|
||||
aux, err := CastToString(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res.Fragment = aux
|
||||
return nil
|
||||
case "RawFragment":
|
||||
aux, err := CastToString(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res.RawFragment = aux
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("unknown field '%s'", k)
|
||||
}
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
package automation
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/automation/types"
|
||||
atypes "github.com/cortezaproject/corteza-server/pkg/apigw/types"
|
||||
"github.com/cortezaproject/corteza-server/pkg/expr"
|
||||
"github.com/cortezaproject/corteza-server/pkg/http"
|
||||
"gopkg.in/mail.v2"
|
||||
)
|
||||
|
||||
@@ -37,74 +33,6 @@ func CastToEmailMessage(val interface{}) (out *emailMessage, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
func CastToHttpRequest(val interface{}) (out *types.HttpRequest, err error) {
|
||||
switch val := val.(type) {
|
||||
case expr.Iterator:
|
||||
out = &types.HttpRequest{}
|
||||
return out, val.Each(func(k string, v expr.TypedValue) error {
|
||||
return assignToHttpRequest(out, k, v)
|
||||
})
|
||||
}
|
||||
|
||||
switch val := expr.UntypedValue(val).(type) {
|
||||
case *http.Request:
|
||||
rr := &types.HttpRequest{}
|
||||
|
||||
assignToHttpRequest(rr, "Method", val.Method)
|
||||
assignToHttpRequest(rr, "URL", val.URL)
|
||||
assignToHttpRequest(rr, "Header", val.Header)
|
||||
assignToHttpRequest(rr, "Body", val.Body)
|
||||
assignToHttpRequest(rr, "Form", val.Form)
|
||||
assignToHttpRequest(rr, "PostForm", val.PostForm)
|
||||
|
||||
return rr, nil
|
||||
case *types.HttpRequest:
|
||||
return val, nil
|
||||
case nil:
|
||||
return &types.HttpRequest{}, nil
|
||||
default:
|
||||
return &types.HttpRequest{}, fmt.Errorf("unable to cast type %T to %T", val, out)
|
||||
}
|
||||
}
|
||||
|
||||
func CastToHttpRequestBody(val interface{}) (out *types.HttpRequestBody, err error) {
|
||||
switch val := val.(type) {
|
||||
case io.ReadCloser:
|
||||
rr := &types.HttpRequestBody{}
|
||||
return rr, assignToHttpRequestBody(rr, "Body", val)
|
||||
}
|
||||
|
||||
switch val := expr.UntypedValue(val).(type) {
|
||||
case *io.ReadCloser:
|
||||
rr := &types.HttpRequestBody{}
|
||||
return rr, assignToHttpRequestBody(rr, "Body", val)
|
||||
case *types.HttpRequestBody:
|
||||
return val, nil
|
||||
case nil:
|
||||
return &types.HttpRequestBody{}, nil
|
||||
default:
|
||||
return &types.HttpRequestBody{}, fmt.Errorf("unable to cast type %T to %T", val, out)
|
||||
}
|
||||
}
|
||||
|
||||
func CastToUrl(val interface{}) (out *types.Url, err error) {
|
||||
switch val := expr.UntypedValue(val).(type) {
|
||||
case *url.URL:
|
||||
u := &types.Url{}
|
||||
|
||||
m, _ := json.Marshal(val)
|
||||
_ = json.Unmarshal(m, u)
|
||||
|
||||
return u, nil
|
||||
case *types.Url:
|
||||
return val, nil
|
||||
case nil:
|
||||
return &types.Url{}, nil
|
||||
default:
|
||||
return &types.Url{}, fmt.Errorf("unable to cast type %T to %T", val, out)
|
||||
}
|
||||
}
|
||||
|
||||
func ReadRequestBody(in interface{}) (s string) {
|
||||
var (
|
||||
b []byte
|
||||
@@ -112,18 +40,8 @@ func ReadRequestBody(in interface{}) (s string) {
|
||||
)
|
||||
|
||||
switch val := in.(type) {
|
||||
case *HttpRequest:
|
||||
b, err = val.Get().(*types.HttpRequest).ReadBody()
|
||||
case *HttpRequestBody:
|
||||
b, err = val.Get().(*types.HttpRequestBody).Read()
|
||||
case *atypes.HttpRequest:
|
||||
b, err = val.ReadBody()
|
||||
case *types.HttpRequest:
|
||||
b, err = val.ReadBody()
|
||||
case *atypes.HttpRequestBody:
|
||||
b, err = val.Read()
|
||||
case *types.HttpRequestBody:
|
||||
b, err = val.Read()
|
||||
case *http.Request:
|
||||
b, err = io.ReadAll(val.Body)
|
||||
case io.Reader:
|
||||
b, err = io.ReadAll(val)
|
||||
default:
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
package: automation
|
||||
|
||||
imports:
|
||||
- github.com/cortezaproject/corteza-server/automation/types
|
||||
|
||||
types:
|
||||
EmailMessage:
|
||||
# using ad-hoc type for now, we'll port this to something internal
|
||||
@@ -19,31 +16,4 @@ types:
|
||||
# - { name: 'parts', exprType: 'Any', goType: 'map[string]io.Reader', mode: ro }
|
||||
# - { name: 'embedded', exprType: 'Any', goType: 'map[string]io.Reader', mode: ro }
|
||||
# - { name: 'attachments', exprType: 'Any', goType: 'map[string]io.Reader', mode: ro }
|
||||
HttpRequest:
|
||||
as: '*types.HttpRequest'
|
||||
struct:
|
||||
- { name: 'Method', exprType: 'String', goType: 'string' }
|
||||
- { name: 'URL', exprType: 'Url', goType: 'url.Url' }
|
||||
- { name: 'Header', exprType: 'KVV', goType: 'map[string][]string' }
|
||||
- { name: 'Body', exprType: 'HttpRequestBody', goType: '*types.HttpRequestBody' }
|
||||
- { name: 'Form', exprType: 'KVV', goType: 'map[string][]string' }
|
||||
- { name: 'PostForm', exprType: 'KVV', goType: 'map[string][]string' }
|
||||
|
||||
HttpRequestBody:
|
||||
as: '*types.HttpRequestBody'
|
||||
struct:
|
||||
- { name: 'Body', exprType: 'Reader', goType: 'io.Reader' }
|
||||
- { name: 'Buffer', exprType: 'Bytes', goType: '[]byte' }
|
||||
|
||||
Url:
|
||||
as: '*types.Url'
|
||||
struct:
|
||||
- { name: 'Scheme', exprType: 'String', goType: 'string' }
|
||||
- { name: 'Opaque', exprType: 'String', goType: 'string' }
|
||||
- { name: 'Host', exprType: 'String', goType: 'string' }
|
||||
- { name: 'Path', exprType: 'String', goType: 'string' }
|
||||
- { name: 'RawPath', exprType: 'String', goType: 'string' }
|
||||
- { name: 'ForceQuery', exprType: 'Boolean', goType: 'bool' }
|
||||
- { name: 'RawQuery', exprType: 'String', goType: 'string' }
|
||||
- { name: 'Fragment', exprType: 'String', goType: 'string' }
|
||||
- { name: 'RawFragment', exprType: 'String', goType: 'string' }
|
||||
|
||||
@@ -63,8 +63,6 @@ func (h jsenvHandler) execute(ctx context.Context, args *jsenvExecuteArgs) (res
|
||||
}
|
||||
|
||||
switch vv := out.(type) {
|
||||
case uint64:
|
||||
res.ResultInt = int64(vv)
|
||||
case int64:
|
||||
res.ResultInt = int64(vv)
|
||||
case string:
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
package automation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/expr"
|
||||
h "github.com/cortezaproject/corteza-server/pkg/http"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_jsenvHandler(t *testing.T) {
|
||||
type (
|
||||
exp struct {
|
||||
s string
|
||||
i int64
|
||||
a interface{}
|
||||
}
|
||||
|
||||
tf struct {
|
||||
name string
|
||||
exp *exp
|
||||
err error
|
||||
params *jsenvExecuteArgs
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
handler = &jsenvHandler{}
|
||||
tcc = []tf{
|
||||
{
|
||||
name: "jsenv handler check payload",
|
||||
err: errors.New(`could not process payload, scope missing`),
|
||||
params: &jsenvExecuteArgs{
|
||||
hasScope: false,
|
||||
hasSource: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "jsenv handler check payload",
|
||||
err: errors.New(`could not process payload, function missing`),
|
||||
params: &jsenvExecuteArgs{
|
||||
hasScope: true,
|
||||
hasSource: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "jsenv handler invalid function",
|
||||
err: errors.New(`could not register jsenv function: SyntaxError: SyntaxError: (anonymous): Line 1:74 Unexpected token function (and 1 more errors)`),
|
||||
params: &jsenvExecuteArgs{
|
||||
hasScope: true,
|
||||
hasSource: true,
|
||||
Source: `invalid function here...`,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "jsenv handler check payload",
|
||||
err: errors.New(``),
|
||||
params: &jsenvExecuteArgs{
|
||||
hasScope: true,
|
||||
hasSource: true,
|
||||
Source: `return nonexistent`,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "jsenv handler parse request body json to string",
|
||||
exp: &exp{
|
||||
s: `bar`,
|
||||
i: 0,
|
||||
},
|
||||
params: &jsenvExecuteArgs{
|
||||
hasScope: true,
|
||||
hasSource: true,
|
||||
Scope: mustAny(expr.NewAny(makeRequest(t, `{"foo":"bar"}`))),
|
||||
Source: `const b = JSON.parse(readRequestBody(input)); return b.foo;`,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "jsenv handler parse request body json to int",
|
||||
exp: &exp{
|
||||
s: ``,
|
||||
i: 42,
|
||||
},
|
||||
params: &jsenvExecuteArgs{
|
||||
hasScope: true,
|
||||
hasSource: true,
|
||||
Scope: mustAny(expr.NewAny(makeRequest(t, `{"foo":42}`))),
|
||||
Source: `const b = JSON.parse(readRequestBody(input)); return b.foo;`,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "jsenv handler parse request body json to int",
|
||||
exp: &exp{
|
||||
s: ``,
|
||||
i: 42,
|
||||
},
|
||||
params: &jsenvExecuteArgs{
|
||||
hasScope: true,
|
||||
hasSource: true,
|
||||
Scope: mustAny(expr.NewAny(makeRequest(t, `42`))),
|
||||
Source: `const b = readRequestBody(input); return parseInt(b);`,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "jsenv handler parse request body try catch",
|
||||
exp: &exp{
|
||||
s: `caught`,
|
||||
i: 0,
|
||||
},
|
||||
params: &jsenvExecuteArgs{
|
||||
hasScope: true,
|
||||
hasSource: true,
|
||||
Scope: mustAny(expr.NewAny(makeRequest(t, `42`))),
|
||||
Source: `try { const b = readRequestBody(input_NONEXISTENT); } catch (e) { return 'caught'; }`,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "jsenv handler parse request body json to float",
|
||||
exp: &exp{
|
||||
s: ``,
|
||||
i: 0,
|
||||
a: 42.690,
|
||||
},
|
||||
params: &jsenvExecuteArgs{
|
||||
hasScope: true,
|
||||
hasSource: true,
|
||||
Scope: mustAny(expr.NewAny(makeRequest(t, `42.690`))),
|
||||
Source: `const b = readRequestBody(input); return parseFloat(b);`,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "jsenv handler input scope",
|
||||
exp: &exp{
|
||||
s: ``,
|
||||
i: 41,
|
||||
},
|
||||
params: &jsenvExecuteArgs{
|
||||
hasScope: true,
|
||||
hasSource: true,
|
||||
Scope: mustAny(expr.NewAny(makeRequest(t, `42`))),
|
||||
Source: `const b = readRequestBody(input); return b - 1;`,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
for _, tc := range tcc {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
ctx = context.Background()
|
||||
)
|
||||
|
||||
handler.preloadVm()
|
||||
|
||||
out, err := handler.execute(ctx, tc.params)
|
||||
|
||||
if tc.err == nil {
|
||||
req.NoError(err)
|
||||
} else {
|
||||
req.Error(err)
|
||||
}
|
||||
|
||||
if tc.exp != nil {
|
||||
req.Equal(tc.exp.s, out.ResultString)
|
||||
req.Equal(tc.exp.i, out.ResultInt)
|
||||
|
||||
if tc.exp.a != nil {
|
||||
req.Equal(tc.exp.a, out.ResultAny)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func makeRequest(t *testing.T, b string) *h.Request {
|
||||
r, err := http.NewRequest("POST", "/foo", ioutil.NopCloser(strings.NewReader(b)))
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
ar, err := h.NewRequest(r)
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
return ar
|
||||
}
|
||||
|
||||
func mustAny(v *expr.Any, err error) *expr.Any {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return v
|
||||
}
|
||||
@@ -113,10 +113,9 @@ func Initialize(ctx context.Context, log *zap.Logger, s store.Storer, ws websock
|
||||
&expr.KVV{},
|
||||
&expr.Reader{},
|
||||
&expr.Vars{},
|
||||
&expr.HttpRequest{},
|
||||
|
||||
&automation.EmailMessage{},
|
||||
&automation.HttpRequest{},
|
||||
&automation.HttpRequestBody{},
|
||||
)
|
||||
|
||||
automation.HttpRequestHandler(Registry())
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/automation/service"
|
||||
atypes "github.com/cortezaproject/corteza-server/automation/types"
|
||||
agctx "github.com/cortezaproject/corteza-server/pkg/apigw/ctx"
|
||||
"github.com/cortezaproject/corteza-server/pkg/apigw/types"
|
||||
@@ -194,7 +193,7 @@ func NewJsonResponse(reg typesRegistry) (e *jsonResponse) {
|
||||
}
|
||||
|
||||
func (j jsonResponse) New() types.Handler {
|
||||
return NewJsonResponse(service.Registry())
|
||||
return NewJsonResponse(j.reg)
|
||||
}
|
||||
|
||||
func (j jsonResponse) String() string {
|
||||
|
||||
@@ -7,13 +7,16 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/automation/service"
|
||||
agctx "github.com/cortezaproject/corteza-server/pkg/apigw/ctx"
|
||||
"github.com/cortezaproject/corteza-server/pkg/apigw/types"
|
||||
"github.com/cortezaproject/corteza-server/pkg/expr"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type (
|
||||
mockHandlerRegistry struct{}
|
||||
)
|
||||
|
||||
func Test_redirectionMerge(t *testing.T) {
|
||||
var (
|
||||
tcc = []tf{
|
||||
@@ -141,7 +144,7 @@ func Test_jsonResponse(t *testing.T) {
|
||||
|
||||
r = r.WithContext(agctx.ScopeToContext(context.Background(), scope))
|
||||
|
||||
h := getHandler(NewJsonResponse(service.Registry()))
|
||||
h := getHandler(NewJsonResponse(&mockHandlerRegistry{}))
|
||||
h, err := h.Merge([]byte(tc.expr))
|
||||
|
||||
req.NoError(err)
|
||||
@@ -164,3 +167,7 @@ func Test_jsonResponse(t *testing.T) {
|
||||
func getHandler(h types.Handler) types.Handler {
|
||||
return h
|
||||
}
|
||||
|
||||
func (r *mockHandlerRegistry) Type(ref string) expr.Type {
|
||||
return expr.Any{}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/automation/automation"
|
||||
atypes "github.com/cortezaproject/corteza-server/automation/types"
|
||||
agctx "github.com/cortezaproject/corteza-server/pkg/apigw/ctx"
|
||||
"github.com/cortezaproject/corteza-server/pkg/apigw/types"
|
||||
@@ -29,6 +29,7 @@ type (
|
||||
}
|
||||
|
||||
WfExecer interface {
|
||||
Load(ctx context.Context) error
|
||||
Exec(ctx context.Context, workflowID uint64, p atypes.WorkflowExecParams) (*expr.Vars, atypes.Stacktrace, error)
|
||||
}
|
||||
|
||||
@@ -36,6 +37,7 @@ type (
|
||||
types.FilterMeta
|
||||
|
||||
vm jsenv.Vm
|
||||
fn *jsenv.Fn
|
||||
log *zap.Logger
|
||||
|
||||
params struct {
|
||||
@@ -80,7 +82,12 @@ func (h workflow) Meta() types.FilterMeta {
|
||||
func (h *workflow) Merge(params []byte) (types.Handler, error) {
|
||||
err := json.NewDecoder(bytes.NewBuffer(params)).Decode(&h.params)
|
||||
|
||||
return h, err
|
||||
if err != nil {
|
||||
return h, err
|
||||
}
|
||||
|
||||
// preload workflow cache
|
||||
return h, h.d.Load(context.Background())
|
||||
}
|
||||
|
||||
func (h workflow) Handler() types.HandlerFunc {
|
||||
@@ -91,21 +98,10 @@ func (h workflow) Handler() types.HandlerFunc {
|
||||
scope = agctx.ScopeFromContext(ctx)
|
||||
)
|
||||
|
||||
// original request with body as io.Reader
|
||||
// read-only
|
||||
ar, err := scope.Get("request")
|
||||
// cleanup scope for wf
|
||||
scp := filterScope(scope, "opts")
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// setup scope for workflow
|
||||
vv := map[string]interface{}{
|
||||
"request": ar,
|
||||
}
|
||||
|
||||
// get the request data and put it into vars
|
||||
in, err := expr.NewVars(vv)
|
||||
in, err := expr.NewVars(scp.Dict())
|
||||
|
||||
if err != nil {
|
||||
return pe.Internal("could not validate request data: %v", err)
|
||||
@@ -143,17 +139,10 @@ func (h workflow) Handler() types.HandlerFunc {
|
||||
scope.Set(k, v)
|
||||
}
|
||||
|
||||
ss := scope.Filter(func(k string, v interface{}) bool {
|
||||
if k == "eventType" || k == "resourceType" {
|
||||
return false
|
||||
}
|
||||
scope = filterScope(scope, "eventType", "resourceType", "invoker")
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
scope = ss
|
||||
|
||||
scope.Set("request", ar)
|
||||
// update scope for next items in pipeline
|
||||
r.WithContext(agctx.ScopeToContext(ctx, scope))
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -162,7 +151,6 @@ func (h workflow) Handler() types.HandlerFunc {
|
||||
func NewPayload(l *zap.Logger) (p *processerPayload) {
|
||||
p = &processerPayload{}
|
||||
|
||||
// todo - check the consequences of doing this here
|
||||
p.vm = jsenv.New(jsenv.NewTransformer(jsenv.LoaderJS, jsenv.TargetES2016))
|
||||
p.log = l
|
||||
|
||||
@@ -179,11 +167,7 @@ func NewPayload(l *zap.Logger) (p *processerPayload) {
|
||||
}
|
||||
|
||||
// register a request body reader
|
||||
// since it's a readcloser, it can be read only once
|
||||
p.vm.Register("readRequestBody", func(rc io.ReadCloser) string {
|
||||
b, _ := io.ReadAll(rc)
|
||||
return string(b)
|
||||
})
|
||||
p.vm.Register("readRequestBody", automation.ReadRequestBody)
|
||||
|
||||
return
|
||||
}
|
||||
@@ -211,6 +195,8 @@ func (h *processerPayload) Merge(params []byte) (types.Handler, error) {
|
||||
return nil, errors.New("could not register function, body empty")
|
||||
}
|
||||
|
||||
h.fn, _ = h.vm.RegisterFunction(h.params.Func)
|
||||
|
||||
return h, err
|
||||
}
|
||||
|
||||
@@ -221,15 +207,18 @@ func (h processerPayload) Handler() types.HandlerFunc {
|
||||
scope = agctx.ScopeFromContext(ctx)
|
||||
)
|
||||
|
||||
scope.Set("request", r)
|
||||
|
||||
fn, err := h.vm.RegisterFunction(h.params.Func)
|
||||
// cleanup scope for js
|
||||
scp := filterScope(scope, "opts")
|
||||
|
||||
// check fn type
|
||||
if err != nil {
|
||||
return pe.InvalidData("could not register function: %v", err)
|
||||
}
|
||||
|
||||
out, err := fn.Exec(h.vm.New(scope))
|
||||
// need to find a consistent approach to the workflow jsenv function
|
||||
// wf: input expr Var and the resulting variable in the jsenv is `input`
|
||||
// apigw: input types.Scope and the resulting variable in the jsenv is `input['some_var']`
|
||||
out, err := h.fn.Exec(h.vm.New(scp))
|
||||
|
||||
if err != nil {
|
||||
return pe.Internal("could not exec payload function: %v", err)
|
||||
@@ -240,7 +229,7 @@ func (h processerPayload) Handler() types.HandlerFunc {
|
||||
|
||||
// check if string
|
||||
switch out.(type) {
|
||||
case string:
|
||||
case string, []byte:
|
||||
// handling the newline, to keep the consistency with the json encoder
|
||||
// which automatically appends the newline
|
||||
_, err = rw.Write([]byte(fmt.Sprintf("%s\n", out)))
|
||||
@@ -259,3 +248,17 @@ func (h processerPayload) Handler() types.HandlerFunc {
|
||||
func (h processerPayload) VM() jsenv.Vm {
|
||||
return h.vm
|
||||
}
|
||||
|
||||
func filterScope(scope *types.Scp, kk ...string) (s *types.Scp) {
|
||||
s = scope.Filter(func(k string, v interface{}) bool {
|
||||
for _, v := range kk {
|
||||
if k == v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -3,19 +3,108 @@ package filter
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
atypes "github.com/cortezaproject/corteza-server/automation/types"
|
||||
agctx "github.com/cortezaproject/corteza-server/pkg/apigw/ctx"
|
||||
"github.com/cortezaproject/corteza-server/pkg/apigw/types"
|
||||
"github.com/cortezaproject/corteza-server/pkg/expr"
|
||||
h "github.com/cortezaproject/corteza-server/pkg/http"
|
||||
"github.com/cortezaproject/corteza-server/pkg/options"
|
||||
"github.com/cortezaproject/corteza-server/pkg/wfexec"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type (
|
||||
wfServicer struct {
|
||||
load func(ctx context.Context) error
|
||||
exec func(ctx context.Context, workflowID uint64, p atypes.WorkflowExecParams) (*expr.Vars, atypes.Stacktrace, error)
|
||||
}
|
||||
)
|
||||
|
||||
func Test_processerWorkflow(t *testing.T) {
|
||||
type (
|
||||
tf struct {
|
||||
name string
|
||||
err string
|
||||
params string
|
||||
wfs wfServicer
|
||||
exp []string
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
tcc = []tf{
|
||||
{
|
||||
name: "workflow processer",
|
||||
exp: []string{"opts", "request"},
|
||||
params: `{"workflow":"1"}`,
|
||||
err: `could not exec workflow: mocked error`,
|
||||
wfs: wfServicer{
|
||||
load: func(ctx context.Context) error {
|
||||
return nil
|
||||
},
|
||||
exec: func(ctx context.Context, workflowID uint64, p atypes.WorkflowExecParams) (*expr.Vars, atypes.Stacktrace, error) {
|
||||
return must(expr.NewVars(map[string]interface{}{"foo": "bar"})), make([]*wfexec.Frame, 0), errors.New("mocked error")
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "workflow processer",
|
||||
exp: []string{"foo", "request", "opts"},
|
||||
params: `{"workflow":"1"}`,
|
||||
wfs: wfServicer{
|
||||
load: func(ctx context.Context) error {
|
||||
return nil
|
||||
},
|
||||
exec: func(ctx context.Context, workflowID uint64, p atypes.WorkflowExecParams) (*expr.Vars, atypes.Stacktrace, error) {
|
||||
return must(expr.NewVars(map[string]interface{}{"foo": "bar"})), make([]*wfexec.Frame, 0), nil
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
for _, tc := range tcc {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
rc = httptest.NewRecorder()
|
||||
rq, _ = http.NewRequest("POST", "/foo", http.NoBody)
|
||||
ar, err = h.NewRequest(rq)
|
||||
pp = NewWorkflow(tc.wfs)
|
||||
)
|
||||
|
||||
_, err = pp.Merge([]byte(tc.params))
|
||||
req.NoError(err)
|
||||
|
||||
scope := &types.Scp{
|
||||
"opts": options.Apigw(),
|
||||
"request": ar,
|
||||
}
|
||||
|
||||
rq = rq.WithContext(agctx.ScopeToContext(context.Background(), scope))
|
||||
|
||||
hn := pp.Handler()
|
||||
err = hn(rc, rq)
|
||||
|
||||
if tc.err != "" {
|
||||
req.EqualError(err, tc.err)
|
||||
} else {
|
||||
req.NoError(err)
|
||||
}
|
||||
|
||||
req.ElementsMatch(tc.exp, agctx.ScopeFromContext(rq.Context()).Keys())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_processerPayload(t *testing.T) {
|
||||
type (
|
||||
tf struct {
|
||||
@@ -31,26 +120,26 @@ func Test_processerPayload(t *testing.T) {
|
||||
var (
|
||||
tcc = []tf{
|
||||
{
|
||||
name: "payload processer",
|
||||
name: "payload processer parse request body",
|
||||
rq: &http.Request{
|
||||
Method: "POST",
|
||||
Body: ioutil.NopCloser(strings.NewReader(`[1,2,3]`)),
|
||||
},
|
||||
exp: "2\n",
|
||||
params: prepareFuncPayload(t, `
|
||||
var b = JSON.parse(readRequestBody(input.Get('request').Body));
|
||||
const b = JSON.parse(readRequestBody(input.Get('request')));
|
||||
return b[1];
|
||||
`),
|
||||
},
|
||||
{
|
||||
name: "payload processer js map",
|
||||
name: "payload processer js map request body",
|
||||
rq: &http.Request{
|
||||
Method: "POST",
|
||||
Body: ioutil.NopCloser(strings.NewReader(`[{"name":"johnny", "surname":"mnemonic"},{"name":"johnny", "surname":"knoxville"}]`)),
|
||||
},
|
||||
exp: "{\"count\":2,\"results\":[{\"fullname\":\"Johnny Mnemonic\"},{\"fullname\":\"Johnny Knoxville\"}]}\n",
|
||||
params: prepareFuncPayload(t, `
|
||||
var b = JSON.parse(readRequestBody(input.Get('request').Body));
|
||||
const b = JSON.parse(readRequestBody(input.Get('request')));
|
||||
|
||||
return {
|
||||
"results":
|
||||
@@ -78,12 +167,13 @@ func Test_processerPayload(t *testing.T) {
|
||||
for _, tc := range tcc {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
rc = httptest.NewRecorder()
|
||||
req = require.New(t)
|
||||
rc = httptest.NewRecorder()
|
||||
ar, err = h.NewRequest(tc.rq)
|
||||
)
|
||||
|
||||
pp := NewPayload(zap.NewNop())
|
||||
_, err := pp.Merge([]byte(tc.params))
|
||||
_, err = pp.Merge([]byte(tc.params))
|
||||
|
||||
if tc.errv != "" {
|
||||
req.EqualError(err, tc.errv)
|
||||
@@ -93,7 +183,8 @@ func Test_processerPayload(t *testing.T) {
|
||||
}
|
||||
|
||||
scope := &types.Scp{
|
||||
"opts": options.Apigw(),
|
||||
"opts": options.Apigw(),
|
||||
"request": ar,
|
||||
}
|
||||
|
||||
tc.rq = tc.rq.WithContext(agctx.ScopeToContext(context.Background(), scope))
|
||||
@@ -118,3 +209,19 @@ func prepareFuncPayload(t *testing.T, s string) string {
|
||||
}
|
||||
return string(aux)
|
||||
}
|
||||
|
||||
func (f wfServicer) Load(ctx context.Context) error {
|
||||
return f.load(ctx)
|
||||
}
|
||||
|
||||
func (f wfServicer) Exec(ctx context.Context, workflowID uint64, p atypes.WorkflowExecParams) (*expr.Vars, atypes.Stacktrace, error) {
|
||||
return f.exec(ctx, workflowID, p)
|
||||
}
|
||||
|
||||
func must(v *expr.Vars, err error) *expr.Vars {
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
@@ -8,8 +8,6 @@ import (
|
||||
"github.com/cortezaproject/corteza-server/pkg/apigw/filter"
|
||||
"github.com/cortezaproject/corteza-server/pkg/apigw/filter/proxy"
|
||||
"github.com/cortezaproject/corteza-server/pkg/apigw/types"
|
||||
"github.com/cortezaproject/corteza-server/pkg/logger"
|
||||
"github.com/cortezaproject/corteza-server/pkg/options"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -74,7 +72,5 @@ func (r *Registry) Preload() {
|
||||
}
|
||||
|
||||
func NewWorkflow() (wf filter.WfExecer) {
|
||||
// implementation assumes that Corredor & Workflow options can not be changed
|
||||
// in the run-time.
|
||||
return service.Workflow(logger.Default().Named("workflow"), *options.Corredor(), *options.Workflow())
|
||||
return service.DefaultWorkflow
|
||||
}
|
||||
|
||||
+3
-5
@@ -8,8 +8,8 @@ import (
|
||||
actx "github.com/cortezaproject/corteza-server/pkg/apigw/ctx"
|
||||
"github.com/cortezaproject/corteza-server/pkg/apigw/types"
|
||||
"github.com/cortezaproject/corteza-server/pkg/auth"
|
||||
h "github.com/cortezaproject/corteza-server/pkg/http"
|
||||
"github.com/cortezaproject/corteza-server/pkg/options"
|
||||
"github.com/cortezaproject/corteza-server/system/automation"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
@@ -42,12 +42,10 @@ func (r route) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
|
||||
r.log.Debug("started serving route")
|
||||
|
||||
// create a new automation HttpRequest
|
||||
ar, err := automation.NewHttpRequest(req)
|
||||
ar, err := h.NewRequest(req)
|
||||
|
||||
if err != nil {
|
||||
r.log.Error("could not prepare a request holder", zap.Error(err))
|
||||
return
|
||||
r.log.Error("could not get initial request", zap.Error(err))
|
||||
}
|
||||
|
||||
scope.Set("opts", r.opts)
|
||||
|
||||
Generated
+248
@@ -11,7 +11,9 @@ package expr
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza-server/pkg/http"
|
||||
"io"
|
||||
"net/url"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
@@ -464,6 +466,203 @@ func (t Handle) Compare(to TypedValue) (int, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// HttpRequest is an expression type, wrapper for *http.Request type
|
||||
type HttpRequest struct {
|
||||
value *http.Request
|
||||
mux sync.RWMutex
|
||||
}
|
||||
|
||||
// NewHttpRequest creates new instance of HttpRequest expression type
|
||||
func NewHttpRequest(val interface{}) (*HttpRequest, error) {
|
||||
if c, err := CastToHttpRequest(val); err != nil {
|
||||
return nil, fmt.Errorf("unable to create HttpRequest: %w", err)
|
||||
} else {
|
||||
return &HttpRequest{value: c}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Get return underlying value on HttpRequest
|
||||
func (t *HttpRequest) Get() interface{} {
|
||||
t.mux.RLock()
|
||||
defer t.mux.RUnlock()
|
||||
return t.value
|
||||
}
|
||||
|
||||
// GetValue returns underlying value on HttpRequest
|
||||
func (t *HttpRequest) GetValue() *http.Request {
|
||||
t.mux.RLock()
|
||||
defer t.mux.RUnlock()
|
||||
return t.value
|
||||
}
|
||||
|
||||
// Type return type name
|
||||
func (HttpRequest) Type() string { return "HttpRequest" }
|
||||
|
||||
// Cast converts value to *http.Request
|
||||
func (HttpRequest) Cast(val interface{}) (TypedValue, error) {
|
||||
return NewHttpRequest(val)
|
||||
}
|
||||
|
||||
// Assign new value to HttpRequest
|
||||
//
|
||||
// value is first passed through CastToHttpRequest
|
||||
func (t *HttpRequest) Assign(val interface{}) error {
|
||||
if c, err := CastToHttpRequest(val); err != nil {
|
||||
return err
|
||||
} else {
|
||||
t.value = c
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (t *HttpRequest) AssignFieldValue(key string, val TypedValue) error {
|
||||
t.mux.Lock()
|
||||
defer t.mux.Unlock()
|
||||
return assignToHttpRequest(t.value, key, val)
|
||||
}
|
||||
|
||||
// SelectGVal implements gval.Selector requirements
|
||||
//
|
||||
// It allows gval lib to access HttpRequest's underlying value (*http.Request)
|
||||
// and it's fields
|
||||
//
|
||||
func (t *HttpRequest) SelectGVal(ctx context.Context, k string) (interface{}, error) {
|
||||
t.mux.RLock()
|
||||
defer t.mux.RUnlock()
|
||||
return httpRequestGValSelector(t.value, k)
|
||||
}
|
||||
|
||||
// Select is field accessor for *http.Request
|
||||
//
|
||||
// Similar to SelectGVal but returns typed values
|
||||
func (t *HttpRequest) Select(k string) (TypedValue, error) {
|
||||
t.mux.RLock()
|
||||
defer t.mux.RUnlock()
|
||||
return httpRequestTypedValueSelector(t.value, k)
|
||||
}
|
||||
|
||||
func (t *HttpRequest) Has(k string) bool {
|
||||
t.mux.RLock()
|
||||
defer t.mux.RUnlock()
|
||||
switch k {
|
||||
case "Method":
|
||||
return true
|
||||
case "URL":
|
||||
return true
|
||||
case "Header":
|
||||
return true
|
||||
case "Body":
|
||||
return true
|
||||
case "Form":
|
||||
return true
|
||||
case "PostForm":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// httpRequestGValSelector is field accessor for *http.Request
|
||||
func httpRequestGValSelector(res *http.Request, k string) (interface{}, error) {
|
||||
if res == nil {
|
||||
return nil, nil
|
||||
}
|
||||
switch k {
|
||||
case "Method":
|
||||
return res.Method, nil
|
||||
case "URL":
|
||||
return res.URL, nil
|
||||
case "Header":
|
||||
return res.Header, nil
|
||||
case "Body":
|
||||
return res.Body, nil
|
||||
case "Form":
|
||||
return res.Form, nil
|
||||
case "PostForm":
|
||||
return res.PostForm, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unknown field '%s'", k)
|
||||
}
|
||||
|
||||
// httpRequestTypedValueSelector is field accessor for *http.Request
|
||||
func httpRequestTypedValueSelector(res *http.Request, k string) (TypedValue, error) {
|
||||
if res == nil {
|
||||
return nil, nil
|
||||
}
|
||||
switch k {
|
||||
case "Method":
|
||||
return NewString(res.Method)
|
||||
case "URL":
|
||||
return NewUrl(res.URL)
|
||||
case "Header":
|
||||
return NewKVV(res.Header)
|
||||
case "Body":
|
||||
return NewReader(res.Body)
|
||||
case "Form":
|
||||
return NewKVV(res.Form)
|
||||
case "PostForm":
|
||||
return NewKVV(res.PostForm)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unknown field '%s'", k)
|
||||
}
|
||||
|
||||
// assignToHttpRequest is field value setter for *http.Request
|
||||
func assignToHttpRequest(res *http.Request, k string, val interface{}) error {
|
||||
switch k {
|
||||
case "Method":
|
||||
aux, err := CastToString(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res.Method = aux
|
||||
return nil
|
||||
case "URL":
|
||||
aux, err := CastToUrl(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res.URL = aux
|
||||
return nil
|
||||
case "Header":
|
||||
aux, err := CastToKVV(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res.Header = aux
|
||||
return nil
|
||||
case "Body":
|
||||
aux, err := CastToReader(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res.Body = aux
|
||||
return nil
|
||||
case "Form":
|
||||
aux, err := CastToKVV(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res.Form = aux
|
||||
return nil
|
||||
case "PostForm":
|
||||
aux, err := CastToKVV(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res.PostForm = aux
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("unknown field '%s'", k)
|
||||
}
|
||||
|
||||
// ID is an expression type, wrapper for uint64 type
|
||||
type ID struct {
|
||||
value uint64
|
||||
@@ -883,6 +1082,55 @@ func (t UnsignedInteger) Compare(to TypedValue) (int, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Url is an expression type, wrapper for *url.URL type
|
||||
type Url struct {
|
||||
value *url.URL
|
||||
mux sync.RWMutex
|
||||
}
|
||||
|
||||
// NewUrl creates new instance of Url expression type
|
||||
func NewUrl(val interface{}) (*Url, error) {
|
||||
if c, err := CastToUrl(val); err != nil {
|
||||
return nil, fmt.Errorf("unable to create Url: %w", err)
|
||||
} else {
|
||||
return &Url{value: c}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Get return underlying value on Url
|
||||
func (t *Url) Get() interface{} {
|
||||
t.mux.RLock()
|
||||
defer t.mux.RUnlock()
|
||||
return t.value
|
||||
}
|
||||
|
||||
// GetValue returns underlying value on Url
|
||||
func (t *Url) GetValue() *url.URL {
|
||||
t.mux.RLock()
|
||||
defer t.mux.RUnlock()
|
||||
return t.value
|
||||
}
|
||||
|
||||
// Type return type name
|
||||
func (Url) Type() string { return "Url" }
|
||||
|
||||
// Cast converts value to *url.URL
|
||||
func (Url) Cast(val interface{}) (TypedValue, error) {
|
||||
return NewUrl(val)
|
||||
}
|
||||
|
||||
// Assign new value to Url
|
||||
//
|
||||
// value is first passed through CastToUrl
|
||||
func (t *Url) Assign(val interface{}) error {
|
||||
if c, err := CastToUrl(val); err != nil {
|
||||
return err
|
||||
} else {
|
||||
t.value = c
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Vars is an expression type, wrapper for map[string]TypedValue type
|
||||
type Vars struct {
|
||||
value map[string]TypedValue
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/PaesslerAG/gval"
|
||||
"github.com/cortezaproject/corteza-server/pkg/errors"
|
||||
"github.com/cortezaproject/corteza-server/pkg/handle"
|
||||
h "github.com/cortezaproject/corteza-server/pkg/http"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
@@ -654,6 +655,36 @@ func CastToReader(val interface{}) (out io.Reader, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
func CastToHttpRequest(val interface{}) (out *h.Request, err error) {
|
||||
switch val := val.(type) {
|
||||
case Iterator:
|
||||
out = &h.Request{}
|
||||
return out, val.Each(func(k string, v TypedValue) error {
|
||||
return assignToHttpRequest(out, k, v)
|
||||
})
|
||||
}
|
||||
|
||||
switch val := UntypedValue(val).(type) {
|
||||
case *h.Request:
|
||||
return val, nil
|
||||
case nil:
|
||||
return &h.Request{}, nil
|
||||
default:
|
||||
return &h.Request{}, fmt.Errorf("unable to cast type %T to %T", val, out)
|
||||
}
|
||||
}
|
||||
|
||||
func CastToUrl(val interface{}) (out *url.URL, err error) {
|
||||
switch val := UntypedValue(val).(type) {
|
||||
case *url.URL:
|
||||
return val, nil
|
||||
case nil:
|
||||
return &url.URL{}, nil
|
||||
default:
|
||||
return &url.URL{}, fmt.Errorf("unable to cast type %T to %T", val, out)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *KVV) Each(fn func(k string, v TypedValue) error) (err error) {
|
||||
if t == nil || t.value == nil {
|
||||
return
|
||||
|
||||
@@ -2,6 +2,8 @@ package: expr
|
||||
imports:
|
||||
- io
|
||||
- time
|
||||
- github.com/cortezaproject/corteza-server/pkg/http
|
||||
- net/url
|
||||
|
||||
types:
|
||||
Any:
|
||||
@@ -76,3 +78,15 @@ types:
|
||||
Reader:
|
||||
as: 'io.Reader'
|
||||
|
||||
HttpRequest:
|
||||
as: '*http.Request'
|
||||
struct:
|
||||
- { name: 'Method', exprType: 'String', goType: 'string' }
|
||||
- { name: 'URL', exprType: 'Url', goType: 'url.Url' }
|
||||
- { name: 'Header', exprType: 'KVV', goType: 'map[string][]string' }
|
||||
- { name: 'Body', exprType: 'Reader', goType: '*types.BufferedReader' }
|
||||
- { name: 'Form', exprType: 'KVV', goType: 'map[string][]string' }
|
||||
- { name: 'PostForm', exprType: 'KVV', goType: 'map[string][]string' }
|
||||
|
||||
Url:
|
||||
as: '*url.URL'
|
||||
|
||||
+3
-31
@@ -5,7 +5,6 @@ import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
@@ -363,37 +362,10 @@ func (t *Vars) MarshalJSON() ([]byte, error) {
|
||||
|
||||
aux[k] = &typedValueWrap{Type: v.Type()}
|
||||
|
||||
rv := v.Get()
|
||||
// @todo this is a temporary solution.
|
||||
// The JSON marshling failed due to some receiver functions on the
|
||||
// HTTP request struct.
|
||||
if hv, ok := rv.(*http.Request); ok {
|
||||
aux[k].Value = map[string]interface{}{
|
||||
"Method": hv.Method,
|
||||
"URL": hv.URL,
|
||||
"Proto": hv.Proto,
|
||||
"ProtoMajor": hv.ProtoMajor,
|
||||
"ProtoMinor": hv.ProtoMinor,
|
||||
"Header": hv.Header,
|
||||
"ContentLength": hv.ContentLength,
|
||||
"TransferEncoding": hv.TransferEncoding,
|
||||
"Close": hv.Close,
|
||||
"Host": hv.Host,
|
||||
"Form": hv.Form,
|
||||
"PostForm": hv.PostForm,
|
||||
"MultipartForm": hv.MultipartForm,
|
||||
"Trailer": hv.Trailer,
|
||||
"RemoteAddr": hv.RemoteAddr,
|
||||
"RequestURI": hv.RequestURI,
|
||||
"TLS": hv.TLS,
|
||||
"Response": hv.Response,
|
||||
}
|
||||
if _, is := v.(json.Marshaler); is {
|
||||
aux[k].Value = v
|
||||
} else {
|
||||
if _, is := v.(json.Marshaler); is {
|
||||
aux[k].Value = v
|
||||
} else {
|
||||
aux[k].Value = v.Get()
|
||||
}
|
||||
aux[k].Value = v.Get()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,8 +28,6 @@ type (
|
||||
config *Config
|
||||
}
|
||||
|
||||
Request http.Request
|
||||
|
||||
DebugLevel string
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
type (
|
||||
Request struct {
|
||||
*http.Request
|
||||
Body io.Reader
|
||||
}
|
||||
|
||||
BufferedReader struct {
|
||||
buffer []byte
|
||||
}
|
||||
)
|
||||
|
||||
// NewBufferedReader creates a new reader from readcloser
|
||||
func NewBufferedReader(r io.ReadCloser) (b *BufferedReader, err error) {
|
||||
var bb []byte
|
||||
|
||||
if bb, err = io.ReadAll(r); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
b = &BufferedReader{bb}
|
||||
return
|
||||
}
|
||||
|
||||
// NewRequest creates a new Request with the buffered ready body
|
||||
func NewRequest(r *http.Request) (rr *Request, err error) {
|
||||
rs, err := NewBufferedReader(r.Body)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
rr = &Request{r, rs}
|
||||
return
|
||||
}
|
||||
|
||||
func (bb *BufferedReader) Read(p []byte) (n int, err error) {
|
||||
if len(bb.buffer) <= n {
|
||||
err = io.EOF
|
||||
return
|
||||
}
|
||||
|
||||
if c := cap(p); c > 0 {
|
||||
for n < c {
|
||||
if len(bb.buffer) <= n {
|
||||
err = io.EOF
|
||||
break
|
||||
}
|
||||
|
||||
p[n] = bb.buffer[n]
|
||||
n++
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (bb *Request) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(&struct {
|
||||
Method string
|
||||
URL *url.URL
|
||||
Header http.Header
|
||||
ContentLength int64
|
||||
Host string
|
||||
Form url.Values
|
||||
PostForm url.Values
|
||||
MultipartForm *multipart.Form
|
||||
RemoteAddr string
|
||||
RequestURI string
|
||||
}{
|
||||
Method: bb.Method,
|
||||
URL: bb.URL,
|
||||
Header: bb.Header,
|
||||
ContentLength: bb.ContentLength,
|
||||
Host: bb.Host,
|
||||
Form: bb.Form,
|
||||
PostForm: bb.PostForm,
|
||||
MultipartForm: bb.MultipartForm,
|
||||
RemoteAddr: bb.RemoteAddr,
|
||||
RequestURI: bb.RequestURI,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"io"
|
||||
h "net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_requestReadMultiple(t *testing.T) {
|
||||
var req = require.New(t)
|
||||
r, _ := h.NewRequest("POST", "/foo", strings.NewReader(`foo body`))
|
||||
|
||||
rs, err := NewBufferedReader(r.Body)
|
||||
|
||||
req.NoError(err)
|
||||
req.Equal(`foo body`, must(io.ReadAll(rs)))
|
||||
req.Equal(`foo body`, must(io.ReadAll(rs)))
|
||||
}
|
||||
|
||||
func Test_requestReadMultipleNoBody(t *testing.T) {
|
||||
var req = require.New(t)
|
||||
r, _ := h.NewRequest("POST", "/foo", h.NoBody)
|
||||
|
||||
rs, err := NewBufferedReader(r.Body)
|
||||
|
||||
req.NoError(err)
|
||||
req.Equal(``, must(io.ReadAll(rs)))
|
||||
req.Equal(``, must(io.ReadAll(rs)))
|
||||
}
|
||||
|
||||
func must(b []byte, e error) string {
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
|
||||
return string(b)
|
||||
}
|
||||
Generated
-1
@@ -11,7 +11,6 @@ package rdbms
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
"github.com/cortezaproject/corteza-server/pkg/errors"
|
||||
"github.com/cortezaproject/corteza-server/pkg/filter"
|
||||
|
||||
@@ -51,7 +51,6 @@ var (
|
||||
testApp *app.CortezaApp
|
||||
r chi.Router
|
||||
|
||||
// defStore store.Storer
|
||||
eventBus = eventbus.New()
|
||||
)
|
||||
|
||||
@@ -69,7 +68,6 @@ func InitTestApp() {
|
||||
return err
|
||||
}
|
||||
|
||||
// defStore = app.Store
|
||||
eventbus.Set(eventBus)
|
||||
return nil
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user