diff --git a/Sources/Enums.swift b/Sources/Enums.swift index cc24f7c..6c957da 100644 --- a/Sources/Enums.swift +++ b/Sources/Enums.swift @@ -24,6 +24,7 @@ public enum DataType: String { case boolean = "boolean" case file = "file" case allOf = "allOf" + case pointer = "pointer" case any = "any" } diff --git a/Sources/Items.swift b/Sources/Items.swift index 07971a2..0aabdcb 100644 --- a/Sources/Items.swift +++ b/Sources/Items.swift @@ -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() } } diff --git a/Sources/Metadata.swift b/Sources/Metadata.swift index 646667b..26f119f 100644 --- a/Sources/Metadata.swift +++ b/Sources/Metadata.swift @@ -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 diff --git a/Sources/Schema.swift b/Sources/Schema.swift index 88a7b7a..a5c4a7b 100644 --- a/Sources/Schema.swift +++ b/Sources/Schema.swift @@ -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) + indirect case structure(metadata: Metadata, structure: Structure) indirect case object(ObjectSchema) indirect case array(ArraySchema) indirect case allOf(AllOfSchema) @@ -19,9 +19,9 @@ enum SchemaBuilder: Builder { typealias Building = Schema + indirect case pointer(MetadataBuilder, Pointer) indirect case object(ObjectSchemaBuilder) indirect case array(ArraySchemaBuilder) - indirect case pointer(Pointer) indirect case allOf(AllOfSchemaBuilder) case string(metadata: MetadataBuilder, format: StringFormat?) case number(metadata: MetadataBuilder, format: NumberFormat?) @@ -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(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(map: map)) case .object: self = .object(try ObjectSchemaBuilder(map: map)) case .array: @@ -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): diff --git a/Tests/SwaggerParserTests/Fixtures/test_pointer_metadata.json b/Tests/SwaggerParserTests/Fixtures/test_pointer_metadata.json new file mode 100644 index 0000000..4e56ee6 --- /dev/null +++ b/Tests/SwaggerParserTests/Fixtures/test_pointer_metadata.json @@ -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" + } + } + } + } +} diff --git a/Tests/SwaggerParserTests/StructureSchemaTests.swift b/Tests/SwaggerParserTests/StructureSchemaTests.swift new file mode 100644 index 0000000..c576639 --- /dev/null +++ b/Tests/SwaggerParserTests/StructureSchemaTests.swift @@ -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) + } +} + diff --git a/Tests/SwaggerParserTests/TestHelpers.swift b/Tests/SwaggerParserTests/TestHelpers.swift index d85b3ab..dcc77fd 100644 --- a/Tests/SwaggerParserTests/TestHelpers.swift +++ b/Tests/SwaggerParserTests/TestHelpers.swift @@ -37,7 +37,7 @@ func getBaseAndChildSchemas(withDefinition definition: Structure) 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) } @@ -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.") } @@ -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 {