Skip to content

Commit

Permalink
Merge pull request #3 from brandur/parameter-validation
Browse files Browse the repository at this point in the history
Parameter validation
  • Loading branch information
brandur authored Jul 21, 2017
2 parents b345f7d + 9738f13 commit ef4fa1a
Show file tree
Hide file tree
Showing 82 changed files with 9,795 additions and 158 deletions.
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ before_install:

script:
- go generate
- go test
- go vet
- make test
- make vet
- scripts/check_gofmt.sh

notifications:
Expand Down
8 changes: 8 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# These tasks will no longer be needed after Go 1.9, whereby `vendor/` is no
# longer considered to be part of `./...`.

test:
go test $(shell go list ./... | egrep -v '/vendor/')

vet:
go vet $(shell go list ./... | egrep -v '/vendor/')
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ curl -i http://localhost:6065/v1/charges
Run the test suite:

``` sh
go test
go test ./...
```

### Binary data
Expand Down
20 changes: 11 additions & 9 deletions generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"fmt"
"sort"
"strings"

"github.com/brandur/stripestub/spec"
)

var errExpansionNotSupported = fmt.Errorf("Expansion not supported")
Expand All @@ -12,16 +14,16 @@ var errNotSupported = fmt.Errorf("Expected response to be a list or include $ref
// DataGenerator generates fixture response data based off a response schema, a
// set of definitions, and a fixture store.
type DataGenerator struct {
definitions map[string]*JSONSchema
fixtures *Fixtures
definitions map[string]*spec.JSONSchema
fixtures *spec.Fixtures
}

// Generate generates a fixture response.
func (g *DataGenerator) Generate(schema *JSONSchema, requestPath string, expansions *ExpansionLevel) (interface{}, error) {
func (g *DataGenerator) Generate(schema *spec.JSONSchema, requestPath string, expansions *ExpansionLevel) (interface{}, error) {
return g.generateInternal(schema, requestPath, expansions, nil)
}

func (g *DataGenerator) generateInternal(schema *JSONSchema, requestPath string, expansions *ExpansionLevel, existingData interface{}) (interface{}, error) {
func (g *DataGenerator) generateInternal(schema *spec.JSONSchema, requestPath string, expansions *ExpansionLevel, existingData interface{}) (interface{}, error) {
schema, err := g.maybeDereference(schema)
if err != nil {
return nil, err
Expand Down Expand Up @@ -62,7 +64,7 @@ func (g *DataGenerator) generateInternal(schema *JSONSchema, requestPath string,
var ok bool
subExpansions, ok = expansions.expansions[key]

var expansion *JSONSchema
var expansion *spec.JSONSchema
if property.XExpansionResources != nil {
expansion = property.XExpansionResources.OneOf[0]
}
Expand Down Expand Up @@ -90,7 +92,7 @@ func (g *DataGenerator) generateInternal(schema *JSONSchema, requestPath string,
return data, nil
}

func (g *DataGenerator) generateResource(schema *JSONSchema) (interface{}, error) {
func (g *DataGenerator) generateResource(schema *spec.JSONSchema) (interface{}, error) {
if schema.XResourceID == "" {
// Technically type can also be just a string, but we're not going to
// support this for now.
Expand All @@ -107,15 +109,15 @@ func (g *DataGenerator) generateResource(schema *JSONSchema) (interface{}, error
return map[string]interface{}{}, nil
}

fixture, ok := g.fixtures.Resources[ResourceID(schema.XResourceID)]
fixture, ok := g.fixtures.Resources[spec.ResourceID(schema.XResourceID)]
if !ok {
return map[string]interface{}{}, nil
}

return fixture, nil
}

func (g *DataGenerator) maybeDereference(schema *JSONSchema) (*JSONSchema, error) {
func (g *DataGenerator) maybeDereference(schema *spec.JSONSchema) (*spec.JSONSchema, error) {
if schema.Ref != "" {
definition, err := definitionFromJSONPointer(schema.Ref)
if err != nil {
Expand All @@ -131,7 +133,7 @@ func (g *DataGenerator) maybeDereference(schema *JSONSchema) (*JSONSchema, error
return schema, nil
}

func (g *DataGenerator) maybeGenerateList(properties map[string]*JSONSchema, existingData interface{}, requestPath string, expansions *ExpansionLevel) (interface{}, error) {
func (g *DataGenerator) maybeGenerateList(properties map[string]*spec.JSONSchema, existingData interface{}, requestPath string, expansions *ExpansionLevel) (interface{}, error) {
object, ok := properties["object"]
if !ok {
return nil, nil
Expand Down
41 changes: 21 additions & 20 deletions generator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@ import (
"fmt"
"testing"

"github.com/brandur/stripestub/spec"
assert "github.com/stretchr/testify/require"
)

var listSchema *JSONSchema
var listSchema *spec.JSONSchema

func init() {
listSchema = &JSONSchema{
Properties: map[string]*JSONSchema{
listSchema = &spec.JSONSchema{
Properties: map[string]*spec.JSONSchema{
"data": {
Items: &JSONSchema{
Items: &spec.JSONSchema{
Ref: "#/definitions/charge",
},
},
Expand All @@ -35,7 +36,7 @@ func TestGenerateResponseData(t *testing.T) {
// basic reference
generator = DataGenerator{testSpec.Definitions, testFixtures}
data, err = generator.Generate(
&JSONSchema{Ref: "#/definitions/charge"}, "", nil)
&spec.JSONSchema{Ref: "#/definitions/charge"}, "", nil)
assert.Nil(t, err)
assert.Equal(t,
testFixtures.Resources["charge"].(map[string]interface{})["id"],
Expand All @@ -49,7 +50,7 @@ func TestGenerateResponseData(t *testing.T) {
// expansion
generator = DataGenerator{testSpec.Definitions, testFixtures}
data, err = generator.Generate(
&JSONSchema{Ref: "#/definitions/charge"},
&spec.JSONSchema{Ref: "#/definitions/charge"},
"",
&ExpansionLevel{expansions: map[string]*ExpansionLevel{"customer": nil}})
assert.Nil(t, err)
Expand All @@ -60,23 +61,23 @@ func TestGenerateResponseData(t *testing.T) {
// bad expansion
generator = DataGenerator{testSpec.Definitions, testFixtures}
data, err = generator.Generate(
&JSONSchema{Ref: "#/definitions/charge"},
&spec.JSONSchema{Ref: "#/definitions/charge"},
"",
&ExpansionLevel{expansions: map[string]*ExpansionLevel{"id": nil}})
assert.Equal(t, err, errExpansionNotSupported)

// bad nested expansion
generator = DataGenerator{testSpec.Definitions, testFixtures}
data, err = generator.Generate(
&JSONSchema{Ref: "#/definitions/charge"},
&spec.JSONSchema{Ref: "#/definitions/charge"},
"",
&ExpansionLevel{expansions: map[string]*ExpansionLevel{"customer.id": nil}})
assert.Equal(t, err, errExpansionNotSupported)

// wildcard expansion
generator = DataGenerator{testSpec.Definitions, testFixtures}
data, err = generator.Generate(
&JSONSchema{Ref: "#/definitions/charge"},
&spec.JSONSchema{Ref: "#/definitions/charge"},
"",
&ExpansionLevel{wildcard: true})
assert.Nil(t, err)
Expand All @@ -97,10 +98,10 @@ func TestGenerateResponseData(t *testing.T) {
// nested list
generator = DataGenerator{
testSpec.Definitions,
&Fixtures{
Resources: map[ResourceID]interface{}{
ResourceID("charge"): map[string]interface{}{"id": "ch_123"},
ResourceID("with_charges_list"): map[string]interface{}{
&spec.Fixtures{
Resources: map[spec.ResourceID]interface{}{
spec.ResourceID("charge"): map[string]interface{}{"id": "ch_123"},
spec.ResourceID("with_charges_list"): map[string]interface{}{
"charges_list": map[string]interface{}{
"url": "/v1/from_charges_list",
},
Expand All @@ -109,8 +110,8 @@ func TestGenerateResponseData(t *testing.T) {
},
}
data, err = generator.Generate(
&JSONSchema{
Properties: map[string]*JSONSchema{
&spec.JSONSchema{
Properties: map[string]*spec.JSONSchema{
"charges_list": listSchema,
},
XResourceID: "with_charges_list",
Expand All @@ -127,27 +128,27 @@ func TestGenerateResponseData(t *testing.T) {
generator = DataGenerator{
testSpec.Definitions,
// this is an empty set of fixtures
&Fixtures{
Resources: map[ResourceID]interface{}{},
&spec.Fixtures{
Resources: map[spec.ResourceID]interface{}{},
},
}
data, err = generator.Generate(
&JSONSchema{Ref: "#/definitions/charge"}, "", nil)
&spec.JSONSchema{Ref: "#/definitions/charge"}, "", nil)
assert.Nil(t, err)
assert.Equal(t, map[string]interface{}{}, data)

// error: unhandled JSON schema type
generator = DataGenerator{testSpec.Definitions, testFixtures}
data, err = generator.Generate(
&JSONSchema{Type: []string{"string"}}, "", nil)
&spec.JSONSchema{Type: []string{"string"}}, "", nil)
assert.Equal(t,
fmt.Errorf("Expected response to be a list or include $ref"),
err)

// error: no definition in OpenAPI
generator = DataGenerator{testSpec.Definitions, testFixtures}
data, err = generator.Generate(
&JSONSchema{Ref: "#/definitions/doesnt-exist"}, "", nil)
&spec.JSONSchema{Ref: "#/definitions/doesnt-exist"}, "", nil)
assert.Equal(t,
fmt.Errorf("Couldn't dereference: #/definitions/doesnt-exist"),
err)
Expand Down
15 changes: 10 additions & 5 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"net"
"net/http"
"strconv"

"github.com/brandur/stripestub/spec"
)

const defaultPort = 6065
Expand Down Expand Up @@ -37,8 +39,8 @@ func main() {
log.Fatalf("Error loading spec: %v", err)
}

var spec OpenAPISpec
err = json.Unmarshal(data, &spec)
var stripeSpec spec.Spec
err = json.Unmarshal(data, &stripeSpec)
if err != nil {
log.Fatalf("Error decoding spec: %v", err)
}
Expand All @@ -49,14 +51,17 @@ func main() {
log.Fatalf("Error loading fixtures: %v", err)
}

var fixtures Fixtures
var fixtures spec.Fixtures
err = json.Unmarshal(data, &fixtures)
if err != nil {
log.Fatalf("Error decoding spec: %v", err)
}

stub := StubServer{fixtures: &fixtures, spec: &spec}
stub.initializeRouter()
stub := StubServer{fixtures: &fixtures, spec: &stripeSpec}
err = stub.initializeRouter()
if err != nil {
log.Fatalf("Error initializing router: %v", err)
}

var listener net.Listener
if unix != "" {
Expand Down
75 changes: 53 additions & 22 deletions main_test.go
Original file line number Diff line number Diff line change
@@ -1,39 +1,70 @@
package main

var chargeAllMethod *OpenAPIMethod
var chargeCreateMethod *OpenAPIMethod
var chargeDeleteMethod *OpenAPIMethod
var chargeGetMethod *OpenAPIMethod
var testSpec *OpenAPISpec
var testFixtures *Fixtures
import (
"github.com/brandur/stripestub/spec"
)

var chargeAllMethod *spec.Method
var chargeCreateMethod *spec.Method
var chargeDeleteMethod *spec.Method
var chargeGetMethod *spec.Method
var testSpec *spec.Spec
var testFixtures *spec.Fixtures

func init() {
chargeAllMethod = &OpenAPIMethod{}
chargeCreateMethod = &OpenAPIMethod{}
chargeDeleteMethod = &OpenAPIMethod{}
chargeGetMethod = &OpenAPIMethod{}
chargeAllMethod = &spec.Method{}
chargeCreateMethod = &spec.Method{
Parameters: []*spec.Parameter{
{
In: "body",
Schema: &spec.JSONSchema{
RawFields: map[string]interface{}{
"properties": map[string]interface{}{
"amount": map[string]interface{}{
"type": []interface{}{
"integer",
},
},
},
"required": []interface{}{
"amount",
},
},
},
},
},
Responses: map[spec.StatusCode]spec.Response{
"200": {
Schema: &spec.JSONSchema{
Ref: "#/definitions/customer",
},
},
},
}
chargeDeleteMethod = &spec.Method{}
chargeGetMethod = &spec.Method{}

testFixtures =
&Fixtures{
Resources: map[ResourceID]interface{}{
ResourceID("charge"): map[string]interface{}{
&spec.Fixtures{
Resources: map[spec.ResourceID]interface{}{
spec.ResourceID("charge"): map[string]interface{}{
"customer": "cus_123",
"id": "ch_123",
},
ResourceID("customer"): map[string]interface{}{"id": "cus_123"},
spec.ResourceID("customer"): map[string]interface{}{"id": "cus_123"},
},
}

testSpec = &OpenAPISpec{
Definitions: map[string]*JSONSchema{
testSpec = &spec.Spec{
Definitions: map[string]*spec.JSONSchema{
"charge": {
Properties: map[string]*JSONSchema{
Properties: map[string]*spec.JSONSchema{
// Normally a customer ID, but expandable to a full
// customer resource
"customer": {
Type: []string{"string"},
XExpansionResources: &JSONSchema{
OneOf: []*JSONSchema{
XExpansionResources: &spec.JSONSchema{
OneOf: []*spec.JSONSchema{
{Ref: "#/definitions/customer"},
},
},
Expand All @@ -46,12 +77,12 @@ func init() {
XResourceID: "customer",
},
},
Paths: map[OpenAPIPath]map[HTTPVerb]*OpenAPIMethod{
OpenAPIPath("/v1/charges"): {
Paths: map[spec.Path]map[spec.HTTPVerb]*spec.Method{
spec.Path("/v1/charges"): {
"get": chargeAllMethod,
"post": chargeCreateMethod,
},
OpenAPIPath("/v1/charges/{id}"): {
spec.Path("/v1/charges/{id}"): {
"get": chargeGetMethod,
"delete": chargeDeleteMethod,
},
Expand Down
Loading

0 comments on commit ef4fa1a

Please sign in to comment.