Skip to content

Commit

Permalink
Add Metadata to $ref schemas (#14)
Browse files Browse the repository at this point in the history
* Add Metadata to `$ref` schemas

This allows things like descriptions or nullability to be applied to a reference to another schema.

* Minor updates based on PR feedback.

* Use `(Metadata, Structure<Schema>)` instead of `SchemaStructure`.

* Break long line onto multiple lines.
  • Loading branch information
n8chur authored Jun 27, 2017
1 parent 4b1425d commit 956e53a
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 15 deletions.
1 change: 1 addition & 0 deletions Sources/Enums.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public enum DataType: String {
case boolean = "boolean"
case file = "file"
case allOf = "allOf"
case pointer = "pointer"
case any = "any"
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/Items.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ indirect enum ItemsBuilder: Builder {
self = .array(builder: try ArrayItemBuilder(map: map))
case .boolean:
self = .boolean(builder: metadata)
case .enumeration, .object, .allOf, .file, .any:
case .enumeration, .object, .allOf, .pointer, .file, .any:
throw DecodingError()
}
}
Expand Down
2 changes: 2 additions & 0 deletions Sources/Metadata.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ struct MetadataBuilder: Builder {
init(map: Map) throws {
if let typeString: String = try? map.value("type"), let mappedType = DataType(rawValue: typeString) {
type = mappedType
} else if map.JSON["$ref"] != nil {
type = .pointer
} else if map.JSON["items"] != nil {
// Implicit array
type = .array
Expand Down
18 changes: 7 additions & 11 deletions Sources/Schema.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import ObjectMapper

/// Schemas are used to define the types used in body parameters. They are more expressive than Items.
public enum Schema {
indirect case structure(Structure<Schema>)
indirect case structure(metadata: Metadata, structure: Structure<Schema>)
indirect case object(ObjectSchema)
indirect case array(ArraySchema)
indirect case allOf(AllOfSchema)
Expand All @@ -19,9 +19,9 @@ enum SchemaBuilder: Builder {

typealias Building = Schema

indirect case pointer(MetadataBuilder, Pointer<SchemaBuilder>)
indirect case object(ObjectSchemaBuilder)
indirect case array(ArraySchemaBuilder)
indirect case pointer(Pointer<SchemaBuilder>)
indirect case allOf(AllOfSchemaBuilder)
case string(metadata: MetadataBuilder, format: StringFormat?)
case number(metadata: MetadataBuilder, format: NumberFormat?)
Expand All @@ -32,15 +32,10 @@ enum SchemaBuilder: Builder {
case any(metadata: MetadataBuilder)

public init(map: Map) throws {
// Check if a reference
if let pointer = try? Pointer<SchemaBuilder>(map: map) {
self = .pointer(pointer)
return
}

// Map according to the type:
let metadata = try MetadataBuilder(map: map)
switch metadata.type {
case .pointer:
self = .pointer(metadata, try Pointer<SchemaBuilder>(map: map))
case .object:
self = .object(try ObjectSchemaBuilder(map: map))
case .array:
Expand All @@ -66,14 +61,15 @@ enum SchemaBuilder: Builder {

func build(_ swagger: SwaggerBuilder) throws -> Schema {
switch self {
case .pointer(let metadataBuilder, let pointer):
let structure = try SchemaBuilder.resolve(swagger, pointer: pointer)
return .structure(metadata: try metadataBuilder.build(swagger), structure: structure)
case .object(let builder):
return .object(try builder.build(swagger))
case .array(let builder):
return .array(try builder.build(swagger))
case .allOf(let builder):
return .allOf(try builder.build(swagger))
case .pointer(let pointer):
return .structure(try SchemaBuilder.resolve(swagger, pointer: pointer))
case .string(let metadataBuilder, let format):
return .string(metadata: try metadataBuilder.build(swagger), format: format)
case .number(let metadataBuilder, let format):
Expand Down
30 changes: 30 additions & 0 deletions Tests/SwaggerParserTests/Fixtures/test_pointer_metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"swagger": "2.0",
"info": {
"title": "",
"description": "",
"version": "1.0.0"
},
"host": "api.test.com",
"schemes": ["https"],
"produces": ["application/json"],
"paths": {},
"definitions": {
"Foo": {
"properties": {
"bar": {
"$ref": "#/definitions/Bar",
"description": "A nullable reference to Bar.",
"x-nullable": true
}
}
},
"Bar": {
"properties": {
"baz": {
"type" : "string"
}
}
}
}
}
35 changes: 35 additions & 0 deletions Tests/SwaggerParserTests/StructureSchemaTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import XCTest
@testable import SwaggerParser

class StructureSchemaTests: XCTestCase {
func testPointerMetadata() throws {
let jsonString = try fixture(named: "test_pointer_metadata.json")
let swagger = try Swagger(JSONString: jsonString)

guard
let fooDefinition = swagger.definitions.first(where: { $0.name == "Foo" }),
case .object(let foo) = fooDefinition.structure else
{
return XCTFail("Foo is not an object schema.")
}

guard
let barProperty = foo.properties["bar"],
case .structure(let metadata, let barReference) = barProperty,
case .object(let bar) = barReference.structure else
{
return XCTFail("Bar property is not a reference to an object schema.")
}

guard
let bazProperty = bar.properties["baz"],
case .string = bazProperty else
{
return XCTFail("Baz property is not a string.")
}

XCTAssertEqual(metadata.description, "A nullable reference to Bar.")
XCTAssertTrue(metadata.nullable)
}
}

6 changes: 3 additions & 3 deletions Tests/SwaggerParserTests/TestHelpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func getBaseAndChildSchemas(withDefinition definition: Structure<Schema>) throws
switch subschema {
case .object(let childSchema):
child = childSchema
case .structure(let structure):
case .structure(_, let structure):
guard case .object(let baseSchema) = structure.structure else {
throw GetBaseAndChildSchemasError.badSubschemaType(subschema)
}
Expand Down Expand Up @@ -78,7 +78,7 @@ func validate(that parameter: Parameter, named parameterName: String, isAnObject
return XCTFail("\(parameterName) is not a .body.")
}

guard case .structure(let structure) = schema else {
guard case .structure(_, let structure) = schema else {
return XCTFail("\(parameterName)'s schema is not a .structure.")
}

Expand All @@ -100,7 +100,7 @@ func validate(that childSchema: Schema, named childName: String, withProperties

guard
let childsParent = childAllOf.subschemas.first,
case .structure(let childsParentStructure) = childsParent,
case .structure(_, let childsParentStructure) = childsParent,
childsParentStructure.name == parentName,
case .object(let childsParentSchema) = childsParentStructure.structure else
{
Expand Down

0 comments on commit 956e53a

Please sign in to comment.