diff --git a/generator.go b/generator.go index db01d31e..7e0e4fa9 100644 --- a/generator.go +++ b/generator.go @@ -8,41 +8,32 @@ import ( var notSupportedErr = fmt.Errorf("Expected response to be a list or include $ref") type DataGenerator struct { - definitions map[string]JSONSchema + definitions map[string]*JSONSchema fixtures *Fixtures } -func (g *DataGenerator) maybeDereference(schema JSONSchema) (JSONSchema, error) { - ref, ok := schema["$ref"].(string) - if ok { - definition, err := definitionFromJSONPointer(ref) +func (g *DataGenerator) maybeDereference(schema *JSONSchema) (*JSONSchema, error) { + if schema.Ref != "" { + definition, err := definitionFromJSONPointer(schema.Ref) if err != nil { return nil, err } - schema, ok = g.definitions[definition] + newSchema, ok := g.definitions[definition] if !ok { - return nil, fmt.Errorf("Couldn't dereference: %v", ref) + return nil, fmt.Errorf("Couldn't dereference: %v", schema.Ref) } + schema = newSchema } return schema, nil } -func (g *DataGenerator) generateResource(schema JSONSchema) (interface{}, error) { - xResourceID, ok := schema["x-resourceId"].(string) - if !ok { - schemaType, ok := schema["type"].(string) - if ok { - if schemaType == "object" { - return map[string]interface{}{}, nil - } - return nil, notSupportedErr - } - - // Types are also allowed to be an array of types - schemaTypes, ok := schema["type"].([]string) - if ok { - for _, schemaType := range schemaTypes { +func (g *DataGenerator) generateResource(schema *JSONSchema) (interface{}, error) { + if schema.XResourceID == "" { + // Technically type can also be just a string, but we're not going to + // support this for now. + if schema.Type != nil { + for _, schemaType := range schema.Type { if schemaType == "object" { return map[string]interface{}{}, nil } @@ -54,14 +45,14 @@ func (g *DataGenerator) generateResource(schema JSONSchema) (interface{}, error) return map[string]interface{}{}, nil } - fixture, ok := g.fixtures.Resources[ResourceID(xResourceID)] + fixture, ok := g.fixtures.Resources[ResourceID(schema.XResourceID)] if !ok { - return nil, fmt.Errorf("Expected fixtures to include %v", xResourceID) + return nil, fmt.Errorf("Expected fixtures to include %v", schema.XResourceID) } return fixture, nil } -func (g *DataGenerator) Generate(schema JSONSchema, requestPath string) (interface{}, error) { +func (g *DataGenerator) Generate(schema *JSONSchema, requestPath string) (interface{}, error) { schema, err := g.maybeDereference(schema) if err != nil { return nil, err @@ -72,9 +63,8 @@ func (g *DataGenerator) Generate(schema JSONSchema, requestPath string) (interfa return nil, err } - properties, ok := schema["properties"].(map[string]interface{}) - if ok { - listData, err := g.maybeGenerateList(properties, requestPath) + if schema.Properties != nil { + listData, err := g.maybeGenerateList(schema.Properties, requestPath) if err != nil { return nil, err } @@ -82,8 +72,8 @@ func (g *DataGenerator) Generate(schema JSONSchema, requestPath string) (interfa return listData, nil } - for key, property := range properties { - keyData, err := g.Generate(property.(JSONSchema), requestPath) + for key, property := range schema.Properties { + keyData, err := g.Generate(property, requestPath) if err == notSupportedErr { continue } @@ -97,32 +87,30 @@ func (g *DataGenerator) Generate(schema JSONSchema, requestPath string) (interfa return data, nil } -func (g *DataGenerator) maybeGenerateList(properties map[string]interface{}, requestPath string) (interface{}, error) { - object, ok := properties["object"].(map[string]interface{}) +func (g *DataGenerator) maybeGenerateList(properties map[string]*JSONSchema, requestPath string) (interface{}, error) { + object, ok := properties["object"] if !ok { return nil, nil } - objectEnum, ok := object["enum"].([]interface{}) - if !ok { + if object.Enum == nil { return nil, nil } - if objectEnum[0] != interface{}("list") { + if object.Enum[0] != "list" { return nil, nil } - data, ok := properties["data"].(map[string]interface{}) + data, ok := properties["data"] if !ok { return nil, nil } - items, ok := data["items"].(map[string]interface{}) - if !ok { + if data.Items == nil { return nil, nil } - itemsSchema, err := g.maybeDereference(items) + itemsSchema, err := g.maybeDereference(data.Items) if err != nil { return nil, err } diff --git a/generator_test.go b/generator_test.go index 0d1c6415..5f70614d 100644 --- a/generator_test.go +++ b/generator_test.go @@ -7,24 +7,24 @@ import ( assert "github.com/stretchr/testify/require" ) -var listSchema JSONSchema +var listSchema *JSONSchema func init() { - listSchema = JSONSchema(map[string]interface{}{ - "properties": map[string]interface{}{ - "data": map[string]interface{}{ - "items": map[string]interface{}{ - "$ref": "#/definitions/charge", + listSchema = &JSONSchema{ + Properties: map[string]*JSONSchema{ + "data": &JSONSchema{ + Items: &JSONSchema{ + Ref: "#/definitions/charge", }, }, "has_more": nil, - "object": map[string]interface{}{ - "enum": []interface{}{"list"}, + "object": &JSONSchema{ + Enum: []string{"list"}, }, "total_count": nil, "url": nil, }, - }) + } } func TestGenerateResponseData(t *testing.T) { @@ -35,7 +35,7 @@ func TestGenerateResponseData(t *testing.T) { // basic reference generator = DataGenerator{testSpec.Definitions, testFixtures} data, err = generator.Generate( - JSONSchema(map[string]interface{}{"$ref": "#/definitions/charge"}), "") + &JSONSchema{Ref: "#/definitions/charge"}, "") assert.Nil(t, err) assert.Equal(t, @@ -55,11 +55,11 @@ func TestGenerateResponseData(t *testing.T) { // nested list generator = DataGenerator{testSpec.Definitions, testFixtures} data, err = generator.Generate( - JSONSchema(map[string]interface{}{ - "properties": map[string]interface{}{ + &JSONSchema{ + Properties: map[string]*JSONSchema{ "charges_list": listSchema, }, - }), "/v1/charges") + }, "/v1/charges") assert.Nil(t, err) chargesList := data.(map[string]interface{})["charges_list"] assert.Equal(t, "list", chargesList.(map[string]interface{})["object"]) @@ -71,7 +71,7 @@ func TestGenerateResponseData(t *testing.T) { // error: unhandled JSON schema type generator = DataGenerator{testSpec.Definitions, testFixtures} data, err = generator.Generate( - JSONSchema(map[string]interface{}{"type": "string"}), "") + &JSONSchema{Type: []string{"string"}}, "") assert.Equal(t, fmt.Errorf("Expected response to be a list or include $ref"), err) @@ -79,7 +79,7 @@ func TestGenerateResponseData(t *testing.T) { // error: no definition in OpenAPI generator = DataGenerator{testSpec.Definitions, testFixtures} data, err = generator.Generate( - JSONSchema(map[string]interface{}{"$ref": "#/definitions/doesnt-exist"}), "") + &JSONSchema{Ref: "#/definitions/doesnt-exist"}, "") assert.Equal(t, fmt.Errorf("Couldn't dereference: #/definitions/doesnt-exist"), err) @@ -93,7 +93,7 @@ func TestGenerateResponseData(t *testing.T) { }, } data, err = generator.Generate( - JSONSchema(map[string]interface{}{"$ref": "#/definitions/charge"}), "") + &JSONSchema{Ref: "#/definitions/charge"}, "") assert.Equal(t, fmt.Errorf("Expected fixtures to include charge"), err) diff --git a/main.go b/main.go index f9da45eb..99644ffb 100644 --- a/main.go +++ b/main.go @@ -21,14 +21,25 @@ type Fixtures struct { type HTTPVerb string -type JSONSchema map[string]interface{} +type JSONSchema struct { + Enum []string `json:"enum"` + Items *JSONSchema `json:"items"` + Properties map[string]*JSONSchema `json:"properties"` + Type []string `json:"type"` + + // Ref is populated if this JSON Schema is actually a JSON reference, and + // it defines the location of the actual schema definition. + Ref string `json:"$ref"` + + XResourceID string `json:"x-resourceId"` +} type OpenAPIParameter struct { - Description string `json:"description"` - In string `json:"in"` - Name string `json:"name"` - Required bool `json:"required"` - Schema JSONSchema `json:"schema"` + Description string `json:"description"` + In string `json:"in"` + Name string `json:"name"` + Required bool `json:"required"` + Schema *JSONSchema `json:"schema"` } type OpenAPIMethod struct { @@ -41,12 +52,12 @@ type OpenAPIMethod struct { type OpenAPIPath string type OpenAPIResponse struct { - Description string `json:"description"` - Schema JSONSchema `json:"schema"` + Description string `json:"description"` + Schema *JSONSchema `json:"schema"` } type OpenAPISpec struct { - Definitions map[string]JSONSchema `json:"definitions"` + Definitions map[string]*JSONSchema `json:"definitions"` Paths map[OpenAPIPath]map[HTTPVerb]*OpenAPIMethod `json:"paths"` } diff --git a/main_test.go b/main_test.go index e1d19f93..f3baf25d 100644 --- a/main_test.go +++ b/main_test.go @@ -29,8 +29,8 @@ func init() { } testSpec = &OpenAPISpec{ - Definitions: map[string]JSONSchema{ - "charge": {"x-resourceId": "charge"}, + Definitions: map[string]*JSONSchema{ + "charge": {XResourceID: "charge"}, }, Paths: map[OpenAPIPath]map[HTTPVerb]*OpenAPIMethod{ OpenAPIPath("/v1/charges"): {