Skip to content

Commit

Permalink
Add ExpansionLevel construct to prepare for expansions
Browse files Browse the repository at this point in the history
  • Loading branch information
brandur committed Jun 23, 2017
1 parent 7877319 commit 754b9fd
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 14 deletions.
14 changes: 7 additions & 7 deletions generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,11 @@ func (g *DataGenerator) generateResource(schema *JSONSchema) (interface{}, error
return fixture, nil
}

func (g *DataGenerator) Generate(schema *JSONSchema, requestPath string) (interface{}, error) {
return g.generateInternal(schema, requestPath, nil)
func (g *DataGenerator) Generate(schema *JSONSchema, requestPath string, expansions *ExpansionLevel) (interface{}, error) {
return g.generateInternal(schema, requestPath, expansions, nil)
}

func (g *DataGenerator) generateInternal(schema *JSONSchema, requestPath string, existingData interface{}) (interface{}, error) {
func (g *DataGenerator) generateInternal(schema *JSONSchema, requestPath string, expansions *ExpansionLevel, existingData interface{}) (interface{}, error) {
schema, err := g.maybeDereference(schema)
if err != nil {
return nil, err
Expand All @@ -70,7 +70,7 @@ func (g *DataGenerator) generateInternal(schema *JSONSchema, requestPath string,

if schema.Properties != nil {
listData, err := g.maybeGenerateList(
schema.Properties, existingData, requestPath)
schema.Properties, existingData, requestPath, expansions)
if err != nil {
return nil, err
}
Expand All @@ -80,7 +80,7 @@ func (g *DataGenerator) generateInternal(schema *JSONSchema, requestPath string,

for key, property := range schema.Properties {
dataMap := data.(map[string]interface{})
keyData, err := g.generateInternal(property, requestPath, dataMap[key])
keyData, err := g.generateInternal(property, requestPath, expansions, dataMap[key])
if err == notSupportedErr {
continue
}
Expand All @@ -94,7 +94,7 @@ func (g *DataGenerator) generateInternal(schema *JSONSchema, requestPath string,
return data, nil
}

func (g *DataGenerator) maybeGenerateList(properties map[string]*JSONSchema, existingData interface{}, requestPath string) (interface{}, error) {
func (g *DataGenerator) maybeGenerateList(properties map[string]*JSONSchema, existingData interface{}, requestPath string, expansions *ExpansionLevel) (interface{}, error) {
object, ok := properties["object"]
if !ok {
return nil, nil
Expand Down Expand Up @@ -122,7 +122,7 @@ func (g *DataGenerator) maybeGenerateList(properties map[string]*JSONSchema, exi
return nil, err
}

itemsData, err := g.Generate(itemsSchema, requestPath)
itemsData, err := g.generateInternal(itemsSchema, requestPath, expansions, nil)
if err != nil {
return nil, err
}
Expand Down
12 changes: 6 additions & 6 deletions generator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func TestGenerateResponseData(t *testing.T) {
// basic reference
generator = DataGenerator{testSpec.Definitions, testFixtures}
data, err = generator.Generate(
&JSONSchema{Ref: "#/definitions/charge"}, "")
&JSONSchema{Ref: "#/definitions/charge"}, "", nil)

assert.Nil(t, err)
assert.Equal(t,
Expand All @@ -44,7 +44,7 @@ func TestGenerateResponseData(t *testing.T) {

// list
generator = DataGenerator{testSpec.Definitions, testFixtures}
data, err = generator.Generate(listSchema, "/v1/charges")
data, err = generator.Generate(listSchema, "/v1/charges", nil)
assert.Nil(t, err)
assert.Equal(t, "list", data.(map[string]interface{})["object"])
assert.Equal(t, "/v1/charges", data.(map[string]interface{})["url"])
Expand Down Expand Up @@ -72,7 +72,7 @@ func TestGenerateResponseData(t *testing.T) {
"charges_list": listSchema,
},
XResourceID: "with_charges_list",
}, "")
}, "", nil)
assert.Nil(t, err)
chargesList := data.(map[string]interface{})["charges_list"]
assert.Equal(t, "list", chargesList.(map[string]interface{})["object"])
Expand All @@ -90,22 +90,22 @@ func TestGenerateResponseData(t *testing.T) {
},
}
data, err = generator.Generate(
&JSONSchema{Ref: "#/definitions/charge"}, "")
&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"}}, "")
&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"}, "")
&JSONSchema{Ref: "#/definitions/doesnt-exist"}, "", nil)
assert.Equal(t,
fmt.Errorf("Couldn't dereference: #/definitions/doesnt-exist"),
err)
Expand Down
50 changes: 49 additions & 1 deletion server.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,43 @@ import (
"log"
"net/http"
"regexp"
"sort"
"strings"
"time"
)

// ExpansionLevel represents expansions on a single "level" of resource. It may
// have subexpansions that are meant to take effect on resources that are
// nested below it (on other levels).
type ExpansionLevel struct {
expansions map[string]*ExpansionLevel
}

// ParseExpasionLevel parses a set of raw expansions from a request query
// string or form and produces a structure more useful for performing actual
// expansions.
func ParseExpansionLevel(raw []string) *ExpansionLevel {
sort.Strings(raw)

level := &ExpansionLevel{expansions: make(map[string]*ExpansionLevel)}
groups := make(map[string][]string)

for _, expansion := range raw {
parts := strings.Split(expansion, ".")
if len(parts) == 1 {
level.expansions[parts[0]] = nil
} else {
groups[parts[0]] = append(groups[parts[0]], strings.Join(parts[1:], "."))
}
}

for key, subexpansions := range groups {
level.expansions[key] = ParseExpansionLevel(subexpansions)
}

return level
}

// StubServer handles incoming HTTP requests and responds to them appropriately
// based off the set of OpenAPI routes that it's been configured with.
type StubServer struct {
Expand Down Expand Up @@ -56,8 +89,16 @@ func (s *StubServer) handleRequest(w http.ResponseWriter, r *http.Request) {
log.Printf("Response schema: %+v", response.Schema)
}

err := r.ParseForm()
if err != nil {
log.Printf("Couldn't parse query/body: %v", err)
writeResponse(w, start, http.StatusInternalServerError, nil)
return
}

expansions := extractExpansions(r)
generator := DataGenerator{s.spec.Definitions, s.fixtures}
data, err := generator.Generate(response.Schema, r.URL.Path)
data, err := generator.Generate(response.Schema, r.URL.Path, expansions)
if err != nil {
log.Printf("Couldn't generate response: %v", err)
writeResponse(w, start, http.StatusInternalServerError, nil)
Expand Down Expand Up @@ -125,6 +166,13 @@ func compilePath(path OpenAPIPath) *regexp.Regexp {
return regexp.MustCompile(pattern + `\z`)
}

func extractExpansions(r *http.Request) *ExpansionLevel {
var expansions []string
expansions = append(expansions, r.Form["expand"]...)
expansions = append(expansions, r.Form["expand[]"]...)
return ParseExpansionLevel(expansions)
}

func writeResponse(w http.ResponseWriter, start time.Time, status int, data interface{}) {
if data == nil {
data = []byte(http.StatusText(status))
Expand Down
29 changes: 29 additions & 0 deletions server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,32 @@ func TestCompilePath(t *testing.T) {
assert.Equal(t, `\A/v1/charges/(?P<id>\w+)\z`,
compilePath(OpenAPIPath("/v1/charges/{id}")).String())
}

func TestParseExpansionLevel(t *testing.T) {
assert.Equal(t,
&ExpansionLevel{expansions: map[string]*ExpansionLevel{
"charge": nil,
"customer": nil,
}},
ParseExpansionLevel([]string{"charge", "customer"}))

assert.Equal(t,
&ExpansionLevel{expansions: map[string]*ExpansionLevel{
"charge": &ExpansionLevel{expansions: map[string]*ExpansionLevel{
"customer": nil,
"source": nil,
}},
"customer": nil,
}},
ParseExpansionLevel([]string{"charge.customer", "customer", "charge.source"}))

assert.Equal(t,
&ExpansionLevel{expansions: map[string]*ExpansionLevel{
"charge": &ExpansionLevel{expansions: map[string]*ExpansionLevel{
"customer": &ExpansionLevel{expansions: map[string]*ExpansionLevel{
"default_source": nil,
}},
}},
}},
ParseExpansionLevel([]string{"charge.customer.default_source", "charge"}))
}

0 comments on commit 754b9fd

Please sign in to comment.