Skip to content

Commit

Permalink
Add validation on request body parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
Peltoche committed Sep 30, 2018
1 parent 668c2f5 commit 5912491
Show file tree
Hide file tree
Showing 4 changed files with 150 additions and 4 deletions.
51 changes: 48 additions & 3 deletions analyzer.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
package oaichecker

import (
"encoding/json"
"errors"
"fmt"
"net/http"

"github.com/go-openapi/analysis"
"github.com/go-openapi/spec"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/validate"
)

type Analyzer struct {
analyzer *analysis.Spec
schema *spec.Schema
}

func NewAnalyzer(specs *Specs) *Analyzer {
Expand All @@ -18,7 +22,8 @@ func NewAnalyzer(specs *Specs) *Analyzer {
}

return &Analyzer{
analyzer: analysis.New(specs.document.OrigSpec()),
analyzer: specs.document.Analyzer,
schema: specs.document.Schema(),
}
}

Expand All @@ -36,7 +41,47 @@ func (t *Analyzer) Analyze(req *http.Request) error {
return errors.New("operation not defined inside the specs")
}

fmt.Printf("operation: %#v\n", operation)
for _, param := range operation.Parameters {
var err error

switch param.In {
case "body":
err = t.validateBodyParameter(req, &param)
}
if err != nil {
return err
}
}

return nil
}

func (t *Analyzer) validateBodyParameter(req *http.Request, param *spec.Parameter) error {
bodyReader, err := req.GetBody()
if err != nil {
return err
}

input := map[string]interface{}{}
err = json.NewDecoder(bodyReader).Decode(&input)
if err != nil {
return err
}

paramRef := param.ParamProps.Schema.Ref.String()

var schema *spec.Schema
for _, def := range t.analyzer.AllDefinitions() {
if paramRef == def.Ref.String() {
schema = def.Schema
break
}
}

err = validate.AgainstSchema(schema, input, strfmt.Default)
if err != nil {
return err
}

return nil
}
49 changes: 49 additions & 0 deletions analyzer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package oaichecker

import (
"net/http"
"strings"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -40,3 +41,51 @@ func Test_Analyzer_Analyze_with_request_not_in_specs(t *testing.T) {

assert.EqualError(t, err, "operation not defined inside the specs")
}

func Test_Analyzer_Analyze_with_body_parameters(t *testing.T) {
specs, err := NewSpecsFromFile("./dataset/petstore.json")
require.NoError(t, err)

analyzer := NewAnalyzer(specs)

req, err := http.NewRequest("POST", "/pet", strings.NewReader(`{
"name": "foobar",
"photoUrls": ["tutu"]
}`))
require.NoError(t, err)

err = analyzer.Analyze(req)

assert.NoError(t, err)
}

func Test_Analyzer_Analyze_with_invalid_body_parameters(t *testing.T) {
specs, err := NewSpecsFromFile("./dataset/petstore.json")
require.NoError(t, err)

analyzer := NewAnalyzer(specs)

req, err := http.NewRequest("POST", "/pet", strings.NewReader(`{
"name": "foobar"
}`))
require.NoError(t, err)

err = analyzer.Analyze(req)

assert.EqualError(t, err, "validation failure list:\n"+
".photoUrls in body is required")
}

func Test_Analyzer_Analyze_with_invalid_body_format(t *testing.T) {
specs, err := NewSpecsFromFile("./dataset/petstore.json")
require.NoError(t, err)

analyzer := NewAnalyzer(specs)

req, err := http.NewRequest("POST", "/pet", strings.NewReader(`not a json`))
require.NoError(t, err)

err = analyzer.Analyze(req)

assert.EqualError(t, err, "invalid character 'o' in literal null (expecting 'u')")
}
31 changes: 30 additions & 1 deletion transport.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package oaichecker

import "net/http"
import (
"bytes"
"io"
"io/ioutil"
"net/http"
)

type Transport struct {
Transport http.RoundTripper
Expand All @@ -15,6 +20,30 @@ func NewTransport(specs *Specs) *Transport {
}

func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
var err error

// GetBody is an optional func to return a new copy of Body
switch req.Body.(type) {
case nil:
req.GetBody = func() (io.ReadCloser, error) {
return http.NoBody, nil
}
default:
body, err := ioutil.ReadAll(req.Body)
if err != nil {
return nil, err
}

req.GetBody = func() (io.ReadCloser, error) {
return ioutil.NopCloser(bytes.NewReader(body)), nil
}
}

req.Body, err = req.GetBody()
if err != nil {
return nil, err
}

res, err := t.Transport.RoundTrip(req)
if err != nil {
return nil, err
Expand Down
23 changes: 23 additions & 0 deletions transport_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"io/ioutil"
"net/http"
"net/http/httptest"
"strings"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -101,3 +102,25 @@ func Test_Transport_with_an_analyzer_error(t *testing.T) {
assert.Nil(t, res)
assert.EqualError(t, err, fmt.Sprintf("Get %s/invalid-path: operation not defined inside the specs", ts.URL))
}

func Test_Transport_with_a_body(t *testing.T) {
ts := newServer(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("some-response"))
})
defer ts.Close()

specs, err := NewSpecsFromFile("./dataset/petstore.json")
require.NoError(t, err)

client := http.Client{
Transport: NewTransport(specs),
}

res, err := client.Post(ts.URL+"/pet", "application/json", strings.NewReader(`{
"name": "foobar",
"photoUrls": ["some-url"]
}`))

assert.NoError(t, err)
assert.Equal(t, "some-response", resBody(t, res))
}

0 comments on commit 5912491

Please sign in to comment.