From 7adbdb4505401e4098dbb7f0ad8c51fa86eb20b7 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Wed, 30 Jan 2019 17:54:26 -0800 Subject: [PATCH 1/7] Move JSONAPIArbitrary and JSONAPIOpenAPI into their own packages. --- Package.swift | 25 +- .../APIDescription+Arbitrary.swift | 24 - .../Attribute+Arbitrary.swift | 20 - .../JSONAPIArbitrary/Document+Arbitrary.swift | 110 -- .../JSONAPIArbitrary/Entity+Arbitrary.swift | 50 - .../JSONAPIArbitrary/Error+Arbitrary.swift | 15 - Sources/JSONAPIArbitrary/Id+Arbitrary.swift | 21 - .../JSONAPIArbitrary/Includes+Arbitrary.swift | 166 --- .../Relationship+Arbitrary.swift | 60 -- .../ResourceBody+Arbitrary.swift | 27 - .../JSONAPI/JSONAPIAttribute+OpenAPI.swift | 166 --- .../JSONAPI/JSONAPIInclude+OpenAPI.swift | 132 --- .../JSONAPI/JSONAPITypes+OpenAPI.swift | 160 --- .../JSONAPIOpenAPI/OpenAPI/Date+OpenAPI.swift | 40 - .../OpenAPI/OpenAPITypes+Codable.swift | 454 --------- .../JSONAPIOpenAPI/OpenAPI/OpenAPITypes.swift | 962 ------------------ .../OpenAPI/SwiftPrimitiveTypes+OpenAPI.swift | 132 --- Sources/JSONAPIOpenAPI/Optional+ZipWith.swift | 13 - .../Sampleable/Include+Sampleable.swift | 185 ---- .../Sampleable/JSONAPI+Sampleable.swift | 69 -- .../Sampleable/Sampleable+OpenAPI.swift | 162 --- .../PlaceholderTests.swift | 15 - .../XCTestManifests.swift | 15 - .../JSONAPIAttributeOpenAPITests.swift | 893 ---------------- .../JSONAPIDocumentOpenAPITests.swift | 377 ------- .../JSONAPIEntityOpenAPITests.swift | 365 ------- .../JSONAPIRelationshipsOpenAPITests.swift | 257 ----- ...NAPITransformedAttributeOpenAPITests.swift | 889 ---------------- .../OpenAPI/OpenAPITests.swift | 52 - .../Test Helpers/EntityTestTypes.swift | 14 - .../String+CreatableRawIdType.swift | 17 - .../JSONAPIOpenAPITests/XCTestManifests.swift | 136 --- 32 files changed, 2 insertions(+), 6021 deletions(-) delete mode 100644 Sources/JSONAPIArbitrary/APIDescription+Arbitrary.swift delete mode 100644 Sources/JSONAPIArbitrary/Attribute+Arbitrary.swift delete mode 100644 Sources/JSONAPIArbitrary/Document+Arbitrary.swift delete mode 100644 Sources/JSONAPIArbitrary/Entity+Arbitrary.swift delete mode 100644 Sources/JSONAPIArbitrary/Error+Arbitrary.swift delete mode 100644 Sources/JSONAPIArbitrary/Id+Arbitrary.swift delete mode 100644 Sources/JSONAPIArbitrary/Includes+Arbitrary.swift delete mode 100644 Sources/JSONAPIArbitrary/Relationship+Arbitrary.swift delete mode 100644 Sources/JSONAPIArbitrary/ResourceBody+Arbitrary.swift delete mode 100644 Sources/JSONAPIOpenAPI/JSONAPI/JSONAPIAttribute+OpenAPI.swift delete mode 100644 Sources/JSONAPIOpenAPI/JSONAPI/JSONAPIInclude+OpenAPI.swift delete mode 100644 Sources/JSONAPIOpenAPI/JSONAPI/JSONAPITypes+OpenAPI.swift delete mode 100644 Sources/JSONAPIOpenAPI/OpenAPI/Date+OpenAPI.swift delete mode 100644 Sources/JSONAPIOpenAPI/OpenAPI/OpenAPITypes+Codable.swift delete mode 100644 Sources/JSONAPIOpenAPI/OpenAPI/OpenAPITypes.swift delete mode 100644 Sources/JSONAPIOpenAPI/OpenAPI/SwiftPrimitiveTypes+OpenAPI.swift delete mode 100644 Sources/JSONAPIOpenAPI/Optional+ZipWith.swift delete mode 100644 Sources/JSONAPIOpenAPI/Sampleable/Include+Sampleable.swift delete mode 100644 Sources/JSONAPIOpenAPI/Sampleable/JSONAPI+Sampleable.swift delete mode 100644 Sources/JSONAPIOpenAPI/Sampleable/Sampleable+OpenAPI.swift delete mode 100644 Tests/JSONAPIArbitraryTests/PlaceholderTests.swift delete mode 100644 Tests/JSONAPIArbitraryTests/XCTestManifests.swift delete mode 100644 Tests/JSONAPIOpenAPITests/JSONAPIAttributeOpenAPITests.swift delete mode 100644 Tests/JSONAPIOpenAPITests/JSONAPIDocumentOpenAPITests.swift delete mode 100644 Tests/JSONAPIOpenAPITests/JSONAPIEntityOpenAPITests.swift delete mode 100644 Tests/JSONAPIOpenAPITests/JSONAPIRelationshipsOpenAPITests.swift delete mode 100644 Tests/JSONAPIOpenAPITests/JSONAPITransformedAttributeOpenAPITests.swift delete mode 100644 Tests/JSONAPIOpenAPITests/OpenAPI/OpenAPITests.swift delete mode 100644 Tests/JSONAPIOpenAPITests/Test Helpers/EntityTestTypes.swift delete mode 100644 Tests/JSONAPIOpenAPITests/Test Helpers/String+CreatableRawIdType.swift delete mode 100644 Tests/JSONAPIOpenAPITests/XCTestManifests.swift diff --git a/Package.swift b/Package.swift index 8ea47ab..8f0d1b8 100644 --- a/Package.swift +++ b/Package.swift @@ -11,19 +11,10 @@ let package = Package( targets: ["JSONAPI"]), .library( name: "JSONAPITesting", - targets: ["JSONAPITesting"]), - .library( - name: "JSONAPIArbitrary", - targets: ["JSONAPIArbitrary"]), - .library( - name: "JSONAPIOpenAPI", - targets: ["JSONAPIOpenAPI"]) + targets: ["JSONAPITesting"]) ], dependencies: [ .package(url: "https://github.com/mattpolzin/Poly.git", from: "1.0.0"), - .package(url: "https://github.com/mattpolzin/Sampleable.git", from: "1.0.0"), - .package(url: "https://github.com/Flight-School/AnyCodable.git", from: "0.1.0"), - .package(url: "https://github.com/typelift/SwiftCheck.git", from: "0.11.0") ], targets: [ .target( @@ -32,24 +23,12 @@ let package = Package( .target( name: "JSONAPITesting", dependencies: ["JSONAPI"]), - .target( - name: "JSONAPIArbitrary", - dependencies: ["JSONAPI", "SwiftCheck"]), - .target( - name: "JSONAPIOpenAPI", - dependencies: ["JSONAPI", "AnyCodable", "JSONAPIArbitrary", "Sampleable"]), .testTarget( name: "JSONAPITests", dependencies: ["JSONAPI", "JSONAPITesting"]), .testTarget( name: "JSONAPITestingTests", - dependencies: ["JSONAPI", "JSONAPITesting"]), - .testTarget( - name: "JSONAPIArbitraryTests", - dependencies: ["JSONAPI", "SwiftCheck", "JSONAPIArbitrary"]), - .testTarget( - name: "JSONAPIOpenAPITests", - dependencies: ["JSONAPI", "JSONAPIOpenAPI"]) + dependencies: ["JSONAPI", "JSONAPITesting"]) ], swiftLanguageVersions: [.v4_2] ) diff --git a/Sources/JSONAPIArbitrary/APIDescription+Arbitrary.swift b/Sources/JSONAPIArbitrary/APIDescription+Arbitrary.swift deleted file mode 100644 index 05aea1f..0000000 --- a/Sources/JSONAPIArbitrary/APIDescription+Arbitrary.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// APIDescription+Arbitrary.swift -// JSONAPIArbitrary -// -// Created by Mathew Polzin on 1/21/19. -// - -import SwiftCheck -import JSONAPI - -extension APIDescription: Arbitrary where Meta: Arbitrary { - public static var arbitrary: Gen> { - return Gen.compose { c in - APIDescription(version: c.generate(), - meta: c.generate()) - } - } -} - -extension NoAPIDescription: Arbitrary { - public static var arbitrary: Gen { - return Gen.pure(.none) - } -} diff --git a/Sources/JSONAPIArbitrary/Attribute+Arbitrary.swift b/Sources/JSONAPIArbitrary/Attribute+Arbitrary.swift deleted file mode 100644 index 90c6264..0000000 --- a/Sources/JSONAPIArbitrary/Attribute+Arbitrary.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// Attribute+Arbitrary.swift -// JSONAPIArbitrary -// -// Created by Mathew Polzin on 1/15/19. -// - -import SwiftCheck -import JSONAPI - -extension Attribute: Arbitrary where RawValue: Arbitrary { - public static var arbitrary: Gen> { - return RawValue.arbitrary.map { .init(value: $0) } - } -} - -// Cannot conform TransformedAttribute to Arbitrary here -// because there is no way to guarantee that an arbitrary -// RawValue will successfully transform or that an -// arbitrary Value will successfully reverse-transform. diff --git a/Sources/JSONAPIArbitrary/Document+Arbitrary.swift b/Sources/JSONAPIArbitrary/Document+Arbitrary.swift deleted file mode 100644 index 9920718..0000000 --- a/Sources/JSONAPIArbitrary/Document+Arbitrary.swift +++ /dev/null @@ -1,110 +0,0 @@ -// -// Document+Arbitrary.swift -// JSONAPIArbitrary -// -// Created by Mathew Polzin on 1/21/19. -// - -import SwiftCheck -import JSONAPI - -extension Document.Body.Data: Arbitrary where PrimaryResourceBody: Arbitrary, IncludeType: Arbitrary, MetaType: Arbitrary, LinksType: Arbitrary { - public static var arbitrary: Gen.Body.Data> { - return Gen.compose { c in - Document.Body.Data(primary: c.generate(), - includes: c.generate(), - meta: c.generate(), - links: c.generate()) - } - } -} - -extension Document.Body: Arbitrary where PrimaryResourceBody: Arbitrary, IncludeType: Arbitrary, MetaType: Arbitrary, LinksType: Arbitrary, Error: Arbitrary { - public static var arbitrary: Gen.Body> { - return Gen.one(of: [ - arbitraryData, - arbitraryErrors - ]) - } -} - -extension Document.Body where PrimaryResourceBody: Arbitrary, IncludeType: Arbitrary, MetaType: Arbitrary, LinksType: Arbitrary { - /// Arbitrary Document.Body with data (guaranteed to not - /// be an error body). - public static var arbitraryData: Gen.Body> { - return Document.Body.Data.arbitrary.map(Document.Body.data) - } -} - -extension Document.Body where MetaType: Arbitrary, LinksType: Arbitrary, Error: Arbitrary { - /// Arbitrary Document.Body with errors (guaranteed to not - /// be a data body). - public static var arbitraryErrors: Gen.Body> { - return Gen.compose { c in - Document.Body.errors(c.generate(), - meta: c.generate(), - links: c.generate()) - } - } -} - -extension Document.Body where Error: Arbitrary { - /// Arbitrary Document.Body with errors but no - /// metadata or links (also guaranteed to not - /// be a data body). - public static var arbitraryErrorsWithoutMetaOrLinks: Gen.Body> { - return Gen.compose { c in - Document.Body.errors(c.generate(), - meta: nil, - links: nil) - } - } -} - -extension Document: Arbitrary where PrimaryResourceBody: Arbitrary, IncludeType: Arbitrary, MetaType: Arbitrary, LinksType: Arbitrary, Error: Arbitrary, APIDescription: Arbitrary { - public static var arbitrary: Gen> { - return Gen.one(of: [ - arbitraryData, - arbitraryErrors - ]) - } -} - -extension Document where PrimaryResourceBody: Arbitrary, IncludeType: Arbitrary, MetaType: Arbitrary, LinksType: Arbitrary, APIDescription: Arbitrary { - /// Arbitrary Document with data (guaranteed to not - /// be an error body). - public static var arbitraryData: Gen> { - return Gen.compose { c in - Document(apiDescription: c.generate(), - body: c.generate(), - includes: c.generate(), - meta: c.generate(), - links: c.generate()) - } - } -} - -extension Document where MetaType: Arbitrary, LinksType: Arbitrary, Error: Arbitrary, APIDescription: Arbitrary { - /// Arbitrary Document with errors (guaranteed to not - /// be a data body). - public static var arbitraryErrors: Gen> { - return Gen.compose { c in - Document(apiDescription: c.generate(), - errors: c.generate(), - meta: c.generate(), - links: c.generate()) - } - } -} - -extension Document where Error: Arbitrary, APIDescription: Arbitrary { - /// Arbitrary Document with errors but no - /// metadata or links (also guaranteed to not - /// be a data body). - public static var arbitraryErrors: Gen> { - return Gen.compose { c in - Document(apiDescription: c.generate(), - errors: c.generate()) - } - } -} diff --git a/Sources/JSONAPIArbitrary/Entity+Arbitrary.swift b/Sources/JSONAPIArbitrary/Entity+Arbitrary.swift deleted file mode 100644 index ac22b5f..0000000 --- a/Sources/JSONAPIArbitrary/Entity+Arbitrary.swift +++ /dev/null @@ -1,50 +0,0 @@ -// -// Entity+Arbitrary.swift -// JSONAPIArbitrary -// -// Created by Mathew Polzin on 1/14/19. -// - -import SwiftCheck -import JSONAPI - -extension NoMetadata: Arbitrary { - public static var arbitrary: Gen { - return Gen.pure(.none) - } -} - -extension NoLinks: Arbitrary { - public static var arbitrary: Gen { - return Gen.pure(.none) - } -} - -extension NoAttributes: Arbitrary { - public static var arbitrary: Gen { - return Gen.pure(.none) - } -} - -extension NoRelationships: Arbitrary { - public static var arbitrary: Gen { - return Gen.pure(.none) - } -} - -// NOTE: Arbitrary conformance for MetaType, LinksType, Description.Attributes, -// and Description.Relationships must all be provided BY YOU for Entity to -// gain Arbitrary conformance (with the exception of NoMetadata, NoLinks, -// NoAttributes, and NoRelationships which all have Arbitrary conformance -// out of the box). -extension Entity: Arbitrary where MetaType: Arbitrary, LinksType: Arbitrary, Description.Attributes: Arbitrary, Description.Relationships: Arbitrary, EntityRawIdType: Arbitrary { - public static var arbitrary: Gen> { - return Gen.compose { c in - Entity(id: c.generate(), - attributes: c.generate(), - relationships: c.generate(), - meta: c.generate(), - links: c.generate()) - } - } -} diff --git a/Sources/JSONAPIArbitrary/Error+Arbitrary.swift b/Sources/JSONAPIArbitrary/Error+Arbitrary.swift deleted file mode 100644 index 0369c69..0000000 --- a/Sources/JSONAPIArbitrary/Error+Arbitrary.swift +++ /dev/null @@ -1,15 +0,0 @@ -// -// Error+Arbitrary.swift -// JSONAPIArbitrary -// -// Created by Mathew Polzin on 1/21/19. -// - -import SwiftCheck -import JSONAPI - -extension UnknownJSONAPIError: Arbitrary { - public static var arbitrary: Gen { - return Gen.pure(.unknownError) - } -} diff --git a/Sources/JSONAPIArbitrary/Id+Arbitrary.swift b/Sources/JSONAPIArbitrary/Id+Arbitrary.swift deleted file mode 100644 index fbf6c32..0000000 --- a/Sources/JSONAPIArbitrary/Id+Arbitrary.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// Id+Arbitrary.swift -// JSONAPIArbitrary -// -// Created by Mathew Polzin on 1/14/19. -// - -import SwiftCheck -import JSONAPI - -extension Unidentified: Arbitrary { - public static var arbitrary: Gen { - return Gen.pure(.init()) - } -} - -extension Id: Arbitrary where RawType: Arbitrary { - public static var arbitrary: Gen> { - return RawType.arbitrary.map { Id(rawValue: $0) } - } -} diff --git a/Sources/JSONAPIArbitrary/Includes+Arbitrary.swift b/Sources/JSONAPIArbitrary/Includes+Arbitrary.swift deleted file mode 100644 index e04ed04..0000000 --- a/Sources/JSONAPIArbitrary/Includes+Arbitrary.swift +++ /dev/null @@ -1,166 +0,0 @@ -// -// Includes+Arbitrary.swift -// JSONAPIArbitrary -// -// Created by Mathew Polzin on 1/21/19. -// - -import SwiftCheck -import JSONAPI - -extension Includes: Arbitrary where I: Arbitrary { - public static var arbitrary: Gen> { - return I - .arbitrary - .proliferate - .map(Includes.init(values:)) - } -} - -extension NoIncludes: Arbitrary { - public static var arbitrary: Gen { - return Gen.pure(NoIncludes()) - } -} - -extension Include1: Arbitrary where A: Arbitrary { - public static var arbitrary: Gen> { - return Gen.one(of: [ - A.arbitrary.map(Include1.init) - ]) - } -} - -extension Include2: Arbitrary where A: Arbitrary, B: Arbitrary { - public static var arbitrary: Gen> { - return Gen.one(of: [ - A.arbitrary.map(Include2.init), - B.arbitrary.map(Include2.init) - ]) - } -} - -extension Include3: Arbitrary where A: Arbitrary, B: Arbitrary, C: Arbitrary { - public static var arbitrary: Gen> { - return Gen.one(of: [ - A.arbitrary.map(Include3.init), - B.arbitrary.map(Include3.init), - C.arbitrary.map(Include3.init) - ]) - } -} - -extension Include4: Arbitrary where A: Arbitrary, B: Arbitrary, C: Arbitrary, D: Arbitrary { - public static var arbitrary: Gen> { - return Gen.one(of: [ - A.arbitrary.map(Include4.init), - B.arbitrary.map(Include4.init), - C.arbitrary.map(Include4.init), - D.arbitrary.map(Include4.init) - ]) - } -} - -extension Include5: Arbitrary where A: Arbitrary, B: Arbitrary, C: Arbitrary, D: Arbitrary, E: Arbitrary { - public static var arbitrary: Gen> { - return Gen.one(of: [ - A.arbitrary.map(Include5.init), - B.arbitrary.map(Include5.init), - C.arbitrary.map(Include5.init), - D.arbitrary.map(Include5.init), - E.arbitrary.map(Include5.init) - ]) - } -} - -extension Include6: Arbitrary where A: Arbitrary, B: Arbitrary, C: Arbitrary, D: Arbitrary, E: Arbitrary, F: Arbitrary { - public static var arbitrary: Gen> { - // Note broken up because compiler cannot typecheck entire array - // before it times out - let set1: [Gen>] = [ - A.arbitrary.map(Include6.init), - B.arbitrary.map(Include6.init), - C.arbitrary.map(Include6.init) - ] - - let set2: [Gen>] = [ - D.arbitrary.map(Include6.init), - E.arbitrary.map(Include6.init), - F.arbitrary.map(Include6.init) - ] - - return Gen.one(of: set1 + set2) - } -} - -extension Include7: Arbitrary where A: Arbitrary, B: Arbitrary, C: Arbitrary, D: Arbitrary, E: Arbitrary, F: Arbitrary, G: Arbitrary { - public static var arbitrary: Gen> { - // Note broken up because compiler cannot typecheck entire array - // before it times out - let set1: [Gen>] = [ - A.arbitrary.map(Include7.init), - B.arbitrary.map(Include7.init), - C.arbitrary.map(Include7.init) - ] - - let set2: [Gen>] = [ - D.arbitrary.map(Include7.init), - E.arbitrary.map(Include7.init), - F.arbitrary.map(Include7.init), - G.arbitrary.map(Include7.init) - ] - - return Gen.one(of: set1 + set2) - } -} - -extension Include8: Arbitrary where A: Arbitrary, B: Arbitrary, C: Arbitrary, D: Arbitrary, E: Arbitrary, F: Arbitrary, G: Arbitrary, H: Arbitrary { - public static var arbitrary: Gen> { - // Note broken up because compiler cannot typecheck entire array - // before it times out - let set1: [Gen>] = [ - A.arbitrary.map(Include8.init), - B.arbitrary.map(Include8.init), - C.arbitrary.map(Include8.init) - ] - - let set2: [Gen>] = [ - D.arbitrary.map(Include8.init), - E.arbitrary.map(Include8.init), - F.arbitrary.map(Include8.init) - ] - - let set3: [Gen>] = [ - G.arbitrary.map(Include8.init), - H.arbitrary.map(Include8.init) - ] - - return Gen.one(of: set1 + set2 + set3) - } -} - -extension Include9: Arbitrary where A: Arbitrary, B: Arbitrary, C: Arbitrary, D: Arbitrary, E: Arbitrary, F: Arbitrary, G: Arbitrary, H: Arbitrary, I: Arbitrary { - public static var arbitrary: Gen> { - // Note broken up because compiler cannot typecheck entire array - // before it times out - let set1: [Gen>] = [ - A.arbitrary.map(Include9.init), - B.arbitrary.map(Include9.init), - C.arbitrary.map(Include9.init) - ] - - let set2: [Gen>] = [ - D.arbitrary.map(Include9.init), - E.arbitrary.map(Include9.init), - F.arbitrary.map(Include9.init) - ] - - let set3: [Gen>] = [ - G.arbitrary.map(Include9.init), - H.arbitrary.map(Include9.init), - I.arbitrary.map(Include9.init) - ] - - return Gen.one(of: set1 + set2 + set3) - } -} diff --git a/Sources/JSONAPIArbitrary/Relationship+Arbitrary.swift b/Sources/JSONAPIArbitrary/Relationship+Arbitrary.swift deleted file mode 100644 index a1a8255..0000000 --- a/Sources/JSONAPIArbitrary/Relationship+Arbitrary.swift +++ /dev/null @@ -1,60 +0,0 @@ -// -// Relationship+Arbitrary.swift -// JSONAPIArbitrary -// -// Created by Mathew Polzin on 1/15/19. -// - -import SwiftCheck -import JSONAPI - -extension ToOneRelationship: Arbitrary where Identifiable.Identifier: Arbitrary, MetaType: Arbitrary, LinksType: Arbitrary { - public static var arbitrary: Gen> { - return Gen.compose { c in - return .init(id: c.generate(), - meta: c.generate(), - links: c.generate()) - } - } -} - -extension ToOneRelationship where MetaType: Arbitrary, LinksType: Arbitrary { - /// Create a generator of arbitrary ToOneRelationships that will all - /// point to one of the given entities. This allows you to create - /// arbitrary relationships that make sense in a broader context where - /// the relationship must actually point to another entity. - public static func arbitrary(givenEntities: [E]) -> Gen> where E.Id == Identifiable.Identifier { - - return Gen.compose { c in - let idGen = Gen.fromElements(of: givenEntities).map { $0.id } - return .init(id: c.generate(using: idGen), - meta: c.generate(), - links: c.generate()) - } - } -} - -extension ToManyRelationship: Arbitrary where Relatable.Identifier: Arbitrary, MetaType: Arbitrary, LinksType: Arbitrary { - public static var arbitrary: Gen> { - return Gen.compose { c in - return .init(ids: c.generate(), - meta: c.generate(), - links: c.generate()) - } - } -} - -extension ToManyRelationship where MetaType: Arbitrary, LinksType: Arbitrary { - /// Create a generator of arbitrary ToManyRelationships that will all - /// point to some number of the given entities. This allows you to create - /// arbitrary relationships that make sense in a broader context where - /// the relationship must actually point to other existing entities. - public static func arbitrary(givenEntities: [E]) -> Gen> where E.Id == Relatable.Identifier { - return Gen.compose { c in - let idsGen = Gen.fromElements(of: givenEntities).map { $0.id }.proliferate - return .init(ids: c.generate(using: idsGen), - meta: c.generate(), - links: c.generate()) - } - } -} diff --git a/Sources/JSONAPIArbitrary/ResourceBody+Arbitrary.swift b/Sources/JSONAPIArbitrary/ResourceBody+Arbitrary.swift deleted file mode 100644 index 1c84939..0000000 --- a/Sources/JSONAPIArbitrary/ResourceBody+Arbitrary.swift +++ /dev/null @@ -1,27 +0,0 @@ -// -// ResourceBody+Arbitrary.swift -// JSONAPIArbitrary -// -// Created by Mathew Polzin on 1/21/19. -// - -import SwiftCheck -import JSONAPI - -extension SingleResourceBody: Arbitrary where Entity: Arbitrary { - public static var arbitrary: Gen> { - return Entity.arbitrary.map(SingleResourceBody.init(entity:)) - } -} - -extension ManyResourceBody: Arbitrary where Entity: Arbitrary { - public static var arbitrary: Gen> { - return Entity.arbitrary.proliferate.map(ManyResourceBody.init(entities:)) - } -} - -extension NoResourceBody: Arbitrary { - public static var arbitrary: Gen { - return Gen.pure(.none) - } -} diff --git a/Sources/JSONAPIOpenAPI/JSONAPI/JSONAPIAttribute+OpenAPI.swift b/Sources/JSONAPIOpenAPI/JSONAPI/JSONAPIAttribute+OpenAPI.swift deleted file mode 100644 index b6a9a64..0000000 --- a/Sources/JSONAPIOpenAPI/JSONAPI/JSONAPIAttribute+OpenAPI.swift +++ /dev/null @@ -1,166 +0,0 @@ -// -// JSONAPIAttribute+OpenAPI.swift -// JSONAPIOpenAPI -// -// Created by Mathew Polzin on 1/28/19. -// - -import JSONAPI -import Foundation -import AnyCodable - -private protocol _Optional {} -extension Optional: _Optional {} - -private protocol Wrapper { - associatedtype Wrapped -} -extension Optional: Wrapper {} - -// MARK: Attribute -extension Attribute: OpenAPINodeType where RawValue: OpenAPINodeType { - static public func openAPINode() throws -> JSONNode { - // If the RawValue is not required, we actually consider it - // nullable. To be not required is for the Attribute itself - // to be optional. - if try !RawValue.openAPINode().required { - return try RawValue.openAPINode().requiredNode().nullableNode() - } - return try RawValue.openAPINode() - } -} - -extension Attribute: RawOpenAPINodeType where RawValue: RawRepresentable, RawValue.RawValue: OpenAPINodeType { - static public func rawOpenAPINode() throws -> JSONNode { - // If the RawValue is not required, we actually consider it - // nullable. To be not required is for the Attribute itself - // to be optional. - if try !RawValue.RawValue.openAPINode().required { - return try RawValue.RawValue.openAPINode().requiredNode().nullableNode() - } - return try RawValue.RawValue.openAPINode() - } -} - -extension Attribute: WrappedRawOpenAPIType where RawValue: RawOpenAPINodeType { - public static func wrappedOpenAPINode() throws -> JSONNode { - // If the RawValue is not required, we actually consider it - // nullable. To be not required is for the Attribute itself - // to be optional. - if try !RawValue.rawOpenAPINode().required { - return try RawValue.rawOpenAPINode().requiredNode().nullableNode() - } - return try RawValue.rawOpenAPINode() - } -} - -extension Attribute: GenericOpenAPINodeType where RawValue: GenericOpenAPINodeType { - public static func genericOpenAPINode(using encoder: JSONEncoder) throws -> JSONNode { - // If the RawValue is not required, we actually consider it - // nullable. To be not required is for the Attribute itself - // to be optional. - if try !RawValue.genericOpenAPINode(using: encoder).required { - return try RawValue.genericOpenAPINode(using: encoder).requiredNode().nullableNode() - } - return try RawValue.genericOpenAPINode(using: encoder) - } -} - -extension Attribute: DateOpenAPINodeType where RawValue: DateOpenAPINodeType { - public static func dateOpenAPINodeGuess(using encoder: JSONEncoder) -> JSONNode? { - // If the RawValue is not required, we actually consider it - // nullable. To be not required is for the Attribute itself - // to be optional. - if - !(RawValue.dateOpenAPINodeGuess(using: encoder)?.required ?? true) { - return RawValue.dateOpenAPINodeGuess(using: encoder)?.requiredNode().nullableNode() - } - return RawValue.dateOpenAPINodeGuess(using: encoder) - } -} - -extension Attribute: AnyJSONCaseIterable where RawValue: CaseIterable, RawValue: Codable { - public static func allCases(using encoder: JSONEncoder) -> [AnyCodable] { - return (try? allCases(from: Array(RawValue.allCases), using: encoder)) ?? [] - } -} - -extension Attribute: AnyWrappedJSONCaseIterable where RawValue: AnyJSONCaseIterable { - public static func allCases(using encoder: JSONEncoder) -> [AnyCodable] { - return RawValue.allCases(using: encoder) - } -} - -// MARK: - TransformedAttribute -extension TransformedAttribute: OpenAPINodeType where RawValue: OpenAPINodeType { - static public func openAPINode() throws -> JSONNode { - // If the RawValue is not required, we actually consider it - // nullable. To be not required is for the Attribute itself - // to be optional. - if try !RawValue.openAPINode().required { - return try RawValue.openAPINode().requiredNode().nullableNode() - } - return try RawValue.openAPINode() - } -} - -extension TransformedAttribute: RawOpenAPINodeType where RawValue: RawRepresentable, RawValue.RawValue: OpenAPINodeType { - static public func rawOpenAPINode() throws -> JSONNode { - // If the RawValue is not required, we actually consider it - // nullable. To be not required is for the Attribute itself - // to be optional. - if try !RawValue.RawValue.openAPINode().required { - return try RawValue.RawValue.openAPINode().requiredNode().nullableNode() - } - return try RawValue.RawValue.openAPINode() - } -} - -extension TransformedAttribute: WrappedRawOpenAPIType where RawValue: RawOpenAPINodeType { - public static func wrappedOpenAPINode() throws -> JSONNode { - // If the RawValue is not required, we actually consider it - // nullable. To be not required is for the Attribute itself - // to be optional. - if try !RawValue.rawOpenAPINode().required { - return try RawValue.rawOpenAPINode().requiredNode().nullableNode() - } - return try RawValue.rawOpenAPINode() - } -} - -extension TransformedAttribute: GenericOpenAPINodeType where RawValue: GenericOpenAPINodeType { - public static func genericOpenAPINode(using encoder: JSONEncoder) throws -> JSONNode { - // If the RawValue is not required, we actually consider it - // nullable. To be not required is for the Attribute itself - // to be optional. - if try !RawValue.genericOpenAPINode(using: encoder).required { - return try RawValue.genericOpenAPINode(using: encoder).requiredNode().nullableNode() - } - return try RawValue.genericOpenAPINode(using: encoder) - } -} - -extension TransformedAttribute: DateOpenAPINodeType where RawValue: DateOpenAPINodeType { - public static func dateOpenAPINodeGuess(using encoder: JSONEncoder) -> JSONNode? { - // If the RawValue is not required, we actually consider it - // nullable. To be not required is for the Attribute itself - // to be optional. - if - !(RawValue.dateOpenAPINodeGuess(using: encoder)?.required ?? true) { - return RawValue.dateOpenAPINodeGuess(using: encoder)?.requiredNode().nullableNode() - } - return RawValue.dateOpenAPINodeGuess(using: encoder) - } -} - -extension TransformedAttribute: AnyJSONCaseIterable where RawValue: CaseIterable, RawValue: Codable { - public static func allCases(using encoder: JSONEncoder) -> [AnyCodable] { - return (try? allCases(from: Array(RawValue.allCases), using: encoder)) ?? [] - } -} - -extension TransformedAttribute: AnyWrappedJSONCaseIterable where RawValue: AnyJSONCaseIterable { - public static func allCases(using encoder: JSONEncoder) -> [AnyCodable] { - return RawValue.allCases(using: encoder) - } -} diff --git a/Sources/JSONAPIOpenAPI/JSONAPI/JSONAPIInclude+OpenAPI.swift b/Sources/JSONAPIOpenAPI/JSONAPI/JSONAPIInclude+OpenAPI.swift deleted file mode 100644 index c7afff0..0000000 --- a/Sources/JSONAPIOpenAPI/JSONAPI/JSONAPIInclude+OpenAPI.swift +++ /dev/null @@ -1,132 +0,0 @@ -// -// JSONAPIInclude+OpenAPI.swift -// JSONAPIOpenAPI -// -// Created by Mathew Polzin on 1/22/19. -// - -import JSONAPI -import Foundation - -extension Includes: OpenAPIEncodedNodeType where I: OpenAPIEncodedNodeType { - public static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode { - let includeNode = try I.openAPINode(using: encoder) - - return .array(.init(format: .generic, - required: true), - .init(items: includeNode, - uniqueItems: true)) - } -} - -extension Include0: OpenAPIEncodedNodeType { - public static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode { - throw OpenAPITypeError.invalidNode - } -} - -extension Include1: OpenAPIEncodedNodeType where A: OpenAPIEncodedNodeType { - public static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode { - return try A.openAPINode(using: encoder) - } -} - -extension Include2: OpenAPIEncodedNodeType where A: OpenAPIEncodedNodeType, B: OpenAPIEncodedNodeType { - public static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode { - return try .one(of: [ - A.openAPINode(using: encoder), - B.openAPINode(using: encoder) - ]) - } -} - -extension Include3: OpenAPIEncodedNodeType where A: OpenAPIEncodedNodeType, B: OpenAPIEncodedNodeType, C: OpenAPIEncodedNodeType { - public static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode { - return try .one(of: [ - A.openAPINode(using: encoder), - B.openAPINode(using: encoder), - C.openAPINode(using: encoder) - ]) - } -} - -extension Include4: OpenAPIEncodedNodeType where A: OpenAPIEncodedNodeType, B: OpenAPIEncodedNodeType, C: OpenAPIEncodedNodeType, D: OpenAPIEncodedNodeType { - public static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode { - return try .one(of: [ - A.openAPINode(using: encoder), - B.openAPINode(using: encoder), - C.openAPINode(using: encoder), - D.openAPINode(using: encoder) - ]) - } -} - -extension Include5: OpenAPIEncodedNodeType where A: OpenAPIEncodedNodeType, B: OpenAPIEncodedNodeType, C: OpenAPIEncodedNodeType, D: OpenAPIEncodedNodeType, E: OpenAPIEncodedNodeType { - public static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode { - return try .one(of: [ - A.openAPINode(using: encoder), - B.openAPINode(using: encoder), - C.openAPINode(using: encoder), - D.openAPINode(using: encoder), - E.openAPINode(using: encoder) - ]) - } -} - -extension Include6: OpenAPIEncodedNodeType where A: OpenAPIEncodedNodeType, B: OpenAPIEncodedNodeType, C: OpenAPIEncodedNodeType, D: OpenAPIEncodedNodeType, E: OpenAPIEncodedNodeType, F: OpenAPIEncodedNodeType { - public static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode { - return try .one(of: [ - A.openAPINode(using: encoder), - B.openAPINode(using: encoder), - C.openAPINode(using: encoder), - D.openAPINode(using: encoder), - E.openAPINode(using: encoder), - F.openAPINode(using: encoder) - ]) - } -} - -extension Include7: OpenAPIEncodedNodeType where A: OpenAPIEncodedNodeType, B: OpenAPIEncodedNodeType, C: OpenAPIEncodedNodeType, D: OpenAPIEncodedNodeType, E: OpenAPIEncodedNodeType, F: OpenAPIEncodedNodeType, G: OpenAPIEncodedNodeType { - public static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode { - return try .one(of: [ - A.openAPINode(using: encoder), - B.openAPINode(using: encoder), - C.openAPINode(using: encoder), - D.openAPINode(using: encoder), - E.openAPINode(using: encoder), - F.openAPINode(using: encoder), - G.openAPINode(using: encoder) - ]) - } -} - -extension Include8: OpenAPIEncodedNodeType where A: OpenAPIEncodedNodeType, B: OpenAPIEncodedNodeType, C: OpenAPIEncodedNodeType, D: OpenAPIEncodedNodeType, E: OpenAPIEncodedNodeType, F: OpenAPIEncodedNodeType, G: OpenAPIEncodedNodeType, H: OpenAPIEncodedNodeType { - public static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode { - return try .one(of: [ - A.openAPINode(using: encoder), - B.openAPINode(using: encoder), - C.openAPINode(using: encoder), - D.openAPINode(using: encoder), - E.openAPINode(using: encoder), - F.openAPINode(using: encoder), - G.openAPINode(using: encoder), - H.openAPINode(using: encoder) - ]) - } -} - -extension Include9: OpenAPIEncodedNodeType where A: OpenAPIEncodedNodeType, B: OpenAPIEncodedNodeType, C: OpenAPIEncodedNodeType, D: OpenAPIEncodedNodeType, E: OpenAPIEncodedNodeType, F: OpenAPIEncodedNodeType, G: OpenAPIEncodedNodeType, H: OpenAPIEncodedNodeType, I: OpenAPIEncodedNodeType { - public static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode { - return try .one(of: [ - A.openAPINode(using: encoder), - B.openAPINode(using: encoder), - C.openAPINode(using: encoder), - D.openAPINode(using: encoder), - E.openAPINode(using: encoder), - F.openAPINode(using: encoder), - G.openAPINode(using: encoder), - H.openAPINode(using: encoder), - I.openAPINode(using: encoder) - ]) - } -} diff --git a/Sources/JSONAPIOpenAPI/JSONAPI/JSONAPITypes+OpenAPI.swift b/Sources/JSONAPIOpenAPI/JSONAPI/JSONAPITypes+OpenAPI.swift deleted file mode 100644 index cdd75c0..0000000 --- a/Sources/JSONAPIOpenAPI/JSONAPI/JSONAPITypes+OpenAPI.swift +++ /dev/null @@ -1,160 +0,0 @@ -// -// JSONAPIOpenAPITypes.swift -// JSONAPIOpenAPI -// -// Created by Mathew Polzin on 1/13/19. -// - -import JSONAPI -import Foundation -import AnyCodable -import Sampleable - -private protocol _Optional {} -extension Optional: _Optional {} - -private protocol Wrapper { - associatedtype Wrapped -} -extension Optional: Wrapper {} - -extension RelationshipType { - static func relationshipNode(nullable: Bool, jsonType: String) -> JSONNode { - let propertiesDict: [String: JSONNode] = [ - "id": .string(.init(format: .generic, - required: true), - .init()), - "type": .string(.init(format: .generic, - required: true, - allowedValues: [.init(jsonType)]), - .init()) - ] - - return .object(.init(format: .generic, - required: true, - nullable: nullable), - .init(properties: propertiesDict)) - } -} - -extension ToOneRelationship: OpenAPINodeType { - // NOTE: const for json `type` not supported by OpenAPI 3.0 - // Will use "enum" with one possible value for now. - - // TODO: metadata & links - static public func openAPINode() throws -> JSONNode { - let nullable = Identifiable.self is _Optional.Type - return .object(.init(format: .generic, - required: true), - .init(properties: [ - "data": ToOneRelationship.relationshipNode(nullable: nullable, jsonType: Identifiable.jsonType) - ])) - } -} - -extension ToManyRelationship: OpenAPINodeType { - // NOTE: const for json `type` not supported by OpenAPI 3.0 - // Will use "enum" with one possible value for now. - - // TODO: metadata & links - static public func openAPINode() throws -> JSONNode { - return .object(.init(format: .generic, - required: true), - .init(properties: [ - "data": .array(.init(format: .generic, - required: true), - .init(items: ToManyRelationship.relationshipNode(nullable: false, jsonType: Relatable.jsonType))) - ])) - } -} - -extension Entity: OpenAPIEncodedNodeType where Description.Attributes: Sampleable, Description.Relationships: Sampleable { - public static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode { - // NOTE: const for json `type` not supported by OpenAPI 3.0 - // Will use "enum" with one possible value for now. - - // TODO: metadata, links - - let idNode: JSONNode? = Id.RawType.self != Unidentified.self - ? JSONNode.string(.init(format: .generic, - required: true), - .init()) - : nil - let idProperty = idNode.map { ("id", $0) } - - let typeNode = JSONNode.string(.init(format: .generic, - required: true, - allowedValues: [.init(Entity.jsonType)]), - .init()) - let typeProperty = ("type", typeNode) - - let attributesNode: JSONNode? = Description.Attributes.self == NoAttributes.self - ? nil - : try Description.Attributes.genericOpenAPINode(using: encoder) - - let attributesProperty = attributesNode.map { ("attributes", $0) } - - let relationshipsNode: JSONNode? = Description.Relationships.self == NoRelationships.self - ? nil - : try Description.Relationships.genericOpenAPINode(using: encoder) - - let relationshipsProperty = relationshipsNode.map { ("relationships", $0) } - - let propertiesDict = Dictionary([ - idProperty, - typeProperty, - attributesProperty, - relationshipsProperty - ].compactMap { $0 }) { _, value in value } - - return .object(.init(format: .generic, - required: true), - .init(properties: propertiesDict)) - } -} - -extension SingleResourceBody: OpenAPIEncodedNodeType where Entity: OpenAPIEncodedNodeType { - public static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode { - return try Entity.openAPINode(using: encoder) - } -} - -extension ManyResourceBody: OpenAPIEncodedNodeType where Entity: OpenAPIEncodedNodeType { - public static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode { - return .array(.init(format: .generic, - required: true), - .init(items: try Entity.openAPINode(using: encoder))) - } -} - -extension Document: OpenAPIEncodedNodeType where PrimaryResourceBody: OpenAPIEncodedNodeType, IncludeType: OpenAPIEncodedNodeType { - public static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode { - // TODO: metadata, links, api description, errors - // TODO: represent data and errors as the two distinct possible outcomes - - let primaryDataNode: JSONNode? = try PrimaryResourceBody.openAPINode(using: encoder) - - let primaryDataProperty = primaryDataNode.map { ("data", $0) } - - let includeNode: JSONNode? - do { - includeNode = try Includes.openAPINode(using: encoder) - } catch let err as OpenAPITypeError { - guard case .invalidNode = err else { - throw err - } - includeNode = nil - } - - let includeProperty = includeNode.map { ("included", $0) } - - let propertiesDict = Dictionary([ - primaryDataProperty, - includeProperty - ].compactMap { $0 }) { _, value in value } - - return .object(.init(format: .generic, - required: true), - .init(properties: propertiesDict)) - } -} diff --git a/Sources/JSONAPIOpenAPI/OpenAPI/Date+OpenAPI.swift b/Sources/JSONAPIOpenAPI/OpenAPI/Date+OpenAPI.swift deleted file mode 100644 index c153fe4..0000000 --- a/Sources/JSONAPIOpenAPI/OpenAPI/Date+OpenAPI.swift +++ /dev/null @@ -1,40 +0,0 @@ -// -// Date+OpenAPI.swift -// JSONAPIOpenAPI -// -// Created by Mathew Polzin on 1/24/19. -// - -import Foundation - -extension Date: DateOpenAPINodeType { - public static func dateOpenAPINodeGuess(using encoder: JSONEncoder) -> JSONNode? { - - switch encoder.dateEncodingStrategy { - case .deferredToDate, .custom: - // I don't know if we can say anything about this case without - // encoding the Date and looking at it, which is what `primitiveGuess()` - // does. - return nil - - case .secondsSince1970, - .millisecondsSince1970: - return .number(.init(format: .double, - required: true), - .init()) - - case .iso8601: - return .string(.init(format: .dateTime, - required: true), - .init()) - - case .formatted(let formatter): - let hasTime = formatter.timeStyle != .none - let format: JSONTypeFormat.StringFormat = hasTime ? .dateTime : .date - - return .string(.init(format: format, - required: true), - .init()) - } - } -} diff --git a/Sources/JSONAPIOpenAPI/OpenAPI/OpenAPITypes+Codable.swift b/Sources/JSONAPIOpenAPI/OpenAPI/OpenAPITypes+Codable.swift deleted file mode 100644 index d764e67..0000000 --- a/Sources/JSONAPIOpenAPI/OpenAPI/OpenAPITypes+Codable.swift +++ /dev/null @@ -1,454 +0,0 @@ -// -// OpenAPITypes+Codable.swift -// JSONAPIOpenAPI -// -// Created by Mathew Polzin on 1/14/19. -// - -extension JSONNode.Context: Encodable { - - private enum CodingKeys: String, CodingKey { - case type - case format - case allowedValues = "enum" - case nullable - case example -// case constantValue = "const" - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - - try container.encode(format.jsonType, forKey: .type) - - if format != Format.unspecified { - try container.encode(format, forKey: .format) - } - - if allowedValues != nil { - try container.encode(allowedValues, forKey: .allowedValues) - } - -// if constantValue != nil { -// try container.encode(constantValue, forKey: .constantValue) -// } - - try container.encode(nullable, forKey: .nullable) - - if example != nil { - try container.encode(example, forKey: .example) - } - } -} - -extension JSONNode.NumericContext: Encodable { - private enum CodingKeys: String, CodingKey { - case multipleOf - case maximum - case exclusiveMaximum - case minimum - case exclusiveMinimum - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - - if multipleOf != nil { - try container.encode(multipleOf, forKey: .multipleOf) - } - - if maximum != nil { - try container.encode(maximum, forKey: .maximum) - } - - if exclusiveMaximum != nil { - try container.encode(exclusiveMaximum, forKey: .exclusiveMaximum) - } - - if minimum != nil { - try container.encode(minimum, forKey: .minimum) - } - - if exclusiveMinimum != nil { - try container.encode(exclusiveMinimum, forKey: .exclusiveMinimum) - } - } -} - -extension JSONNode.StringContext: Encodable { - private enum CodingKeys: String, CodingKey { - case maxLength - case minLength - case pattern - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - - if maxLength != nil { - try container.encode(maxLength, forKey: .maxLength) - } - - try container.encode(minLength, forKey: .minLength) - - if pattern != nil { - try container.encode(pattern, forKey: .pattern) - } - } -} - -extension JSONNode.ArrayContext: Encodable { - private enum CodingKeys: String, CodingKey { - case items - case maxItems - case minItems - case uniqueItems - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - - try container.encode(items, forKey: .items) - - if maxItems != nil { - try container.encode(maxItems, forKey: .maxItems) - } - - try container.encode(minItems, forKey: .minItems) - - try container.encode(uniqueItems, forKey: .uniqueItems) - } -} - -extension JSONNode.ObjectContext : Encodable { - private enum CodingKeys: String, CodingKey { - case maxProperties - case minProperties - case properties - case additionalProperties - case required - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - - if maxProperties != nil { - try container.encode(maxProperties, forKey: .maxProperties) - } - - try container.encode(properties, forKey: .properties) - - if additionalProperties != nil { - try container.encode(additionalProperties, forKey: .additionalProperties) - } - - try container.encode(requiredProperties, forKey: .required) - - try container.encode(minProperties, forKey: .minProperties) - } -} - -extension JSONNode: Encodable { - - private enum SubschemaCodingKeys: String, CodingKey { - case allOf - case oneOf - case anyOf - case not - } - - public func encode(to encoder: Encoder) throws { - switch self { - case .boolean(let context): - try context.encode(to: encoder) - - case .object(let contextA as Encodable, let contextB as Encodable), - .array(let contextA as Encodable, let contextB as Encodable), - .number(let contextA as Encodable, let contextB as Encodable), - .integer(let contextA as Encodable, let contextB as Encodable), - .string(let contextA as Encodable, let contextB as Encodable): - try contextA.encode(to: encoder) - try contextB.encode(to: encoder) - - case .all(of: let nodes): - var container = encoder.container(keyedBy: SubschemaCodingKeys.self) - - try container.encode(nodes, forKey: .allOf) - - case .one(of: let nodes): - var container = encoder.container(keyedBy: SubschemaCodingKeys.self) - - try container.encode(nodes, forKey: .oneOf) - - case .any(of: let nodes): - var container = encoder.container(keyedBy: SubschemaCodingKeys.self) - - try container.encode(nodes, forKey: .anyOf) - - case .not(let node): - var container = encoder.container(keyedBy: SubschemaCodingKeys.self) - - try container.encode(node, forKey: .not) - - case .reference(let reference): - var container = encoder.singleValueContainer() - - try container.encode(reference) - } - } -} - -extension JSONReference: Encodable { - private enum CodingKeys: String, CodingKey { - case ref = "$ref" - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - - let referenceString: String = { - switch self { - case .file(let reference): - return reference - case .node(let reference): - return "#/\(Root.refName)/\(reference.refName)/\(reference.selector)" - } - }() - - try container.encode(referenceString, forKey: .ref) - } -} - -extension RefDict: Encodable { - public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - - try container.encode(dict) - } -} - -extension OpenAPIResponse.Code: Encodable { - public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - - let string: String - switch self { - case .`default`: - string = "default" - - case .status(code: let code): - string = String(code) - } - - try container.encode(string) - } -} - -extension OpenAPIRequestBody: Encodable { - private enum CodingKeys: String, CodingKey { - case description - case content - case required - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - - if description != nil { - try container.encode(description, forKey: .description) - } - - // Hack to work around Dictionary encoding - // itself as an array in this case: - let stringKeyedDict = Dictionary( - content.map { ($0.key.rawValue, $0.value) }, - uniquingKeysWith: { $1 } - ) - try container.encode(stringKeyedDict, forKey: .content) - - try container.encode(required, forKey: .required) - } -} - -extension OpenAPIPathItem.PathProperties.Operation: Encodable { - private enum CodingKeys: String, CodingKey { - case tags - case summary - case description - case externalDocs - case operationId - case parameters - case requestBody - case responses - case callbacks - case deprecated - case security - case servers - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - - if tags != nil { - try container.encode(tags, forKey: .tags) - } - - if summary != nil { - try container.encode(summary, forKey: .summary) - } - - if description != nil { - try container.encode(description, forKey: .description) - } - - try container.encode(operationId, forKey: .operationId) - - try container.encode(parameters, forKey: .parameters) - - if requestBody != nil { - try container.encode(requestBody, forKey: .requestBody) - } - - // Hack to work around Dictionary encoding - // itself as an array in this case: - let stringKeyedDict = Dictionary( - responses.map { ($0.key.rawValue, $0.value) }, - uniquingKeysWith: { $1 } - ) - try container.encode(stringKeyedDict, forKey: .responses) - - try container.encode(deprecated, forKey: .deprecated) - } -} - -extension OpenAPIPathItem.PathProperties: Encodable { - private enum CodingKeys: String, CodingKey { - case summary - case description - case servers - case parameters - - case get - case put - case post - case delete - case options - case head - case patch - case trace - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - - if summary != nil { - try container.encode(summary, forKey: .summary) - } - - if description != nil { - try container.encode(description, forKey: .description) - } - - try container.encode(parameters, forKey: .parameters) - - if get != nil { - try container.encode(get, forKey: .get) - } - - if put != nil { - try container.encode(put, forKey: .put) - } - - if post != nil { - try container.encode(post, forKey: .post) - } - - if delete != nil { - try container.encode(delete, forKey: .delete) - } - - if options != nil { - try container.encode(options, forKey: .options) - } - - if head != nil { - try container.encode(head, forKey: .head) - } - - if patch != nil { - try container.encode(patch, forKey: .patch) - } - - if trace != nil { - try container.encode(trace, forKey: .trace) - } - } -} - -extension OpenAPIResponse: Encodable { - private enum CodingKeys: String, CodingKey { - case description - case headers - case content - case links - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - - try container.encode(description, forKey: .description) - - // Hack to work around Dictionary encoding - // itself as an array in this case: - let stringKeyedDict = Dictionary( - content.map { ($0.key.rawValue, $0.value) }, - uniquingKeysWith: { $1 } - ) - try container.encode(stringKeyedDict, forKey: .content) - } -} - -extension OpenAPIPathItem: Encodable { - public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - - switch self { - case .reference(let reference): - try container.encode(reference) - - case .operations(let operations): - try container.encode(operations) - } - } -} - -extension OpenAPISchema: Encodable { - private enum CodingKeys: String, CodingKey { - case openAPIVersion = "openapi" - case info - case servers - case paths - case components - case security - case tags - case externalDocs - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - - try container.encode(openAPIVersion, forKey: .openAPIVersion) - - try container.encode(info, forKey: .info) - - // Hack to work around Dictionary encoding - // itself as an array in this case: - let stringKeyedDict = Dictionary( - paths.map { ($0.key.rawValue, $0.value) }, - uniquingKeysWith: { $1 } - ) - try container.encode(stringKeyedDict, forKey: .paths) - - try container.encode(components, forKey: .components) - } -} diff --git a/Sources/JSONAPIOpenAPI/OpenAPI/OpenAPITypes.swift b/Sources/JSONAPIOpenAPI/OpenAPI/OpenAPITypes.swift deleted file mode 100644 index 1485026..0000000 --- a/Sources/JSONAPIOpenAPI/OpenAPI/OpenAPITypes.swift +++ /dev/null @@ -1,962 +0,0 @@ -// -// OpenAPITypes.swift -// JSONAPIOpenAPI -// -// Created by Mathew Polzin on 1/13/19. -// - -import AnyCodable -import Foundation -import Poly -import Sampleable - -// MARK: Node (i.e. schema) Protocols - -/// Anything conforming to `OpenAPINodeType` can provide an -/// OpenAPI schema representing itself. -public protocol OpenAPINodeType { - static func openAPINode() throws -> JSONNode -} - -/// Anything conforming to `OpenAPIEncodedNodeType` can provide an -/// OpenAPI schema representing itself but it may need an Encoder -/// to do its job. -public protocol OpenAPIEncodedNodeType { - static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode -} - -extension OpenAPIEncodedNodeType where Self: Sampleable, Self: Encodable { - public static func openAPINodeWithExample(using encoder: JSONEncoder = JSONEncoder()) throws -> JSONNode { - return try openAPINode(using: encoder).with(example: Self.successSample ?? Self.sample, using: encoder) - } -} - -/// Anything conforming to `RawOpenAPINodeType` can provide an -/// OpenAPI schema representing itself. This second protocol is -/// necessary so that one type can conditionally provide a -/// schema and then (under different conditions) provide a -/// different schema. The "different" conditions have to do -/// with Raw Representability, hence the name of this protocol. -public protocol RawOpenAPINodeType { - static func rawOpenAPINode() throws -> JSONNode -} - -/// Anything conforming to `RawOpenAPINodeType` can provide an -/// OpenAPI schema representing itself. This third protocol is -/// necessary so that one type can conditionally provide a -/// schema and then (under different conditions) provide a -/// different schema. The "different" conditions have to do -/// with Optionality, hence the name of this protocol. -public protocol WrappedRawOpenAPIType { - static func wrappedOpenAPINode() throws -> JSONNode -} - -/// Anything conforming to `RawOpenAPINodeType` can provide an -/// OpenAPI schema representing itself. This third protocol is -/// necessary so that one type can conditionally provide a -/// schema and then (under different conditions) provide a -/// different schema. The "different" conditions have to do -/// with Optionality, hence the name of this protocol. -public protocol DoubleWrappedRawOpenAPIType { - // NOTE: This is definitely a rabbit hole... hopefully I - // will realize I've been missing something obvious - // and dig my way back out at some point... - static func wrappedOpenAPINode() throws -> JSONNode -} - -/// A GenericOpenAPINodeType can take a stab at -/// determining its OpenAPINode because it is sampleable. -public protocol GenericOpenAPINodeType { - static func genericOpenAPINode(using encoder: JSONEncoder) throws -> JSONNode -} - -/// Anything conforming to `DateOpenAPINodeType` is -/// able to attempt to represent itself as a date OpenAPINode -public protocol DateOpenAPINodeType { - static func dateOpenAPINodeGuess(using encoder: JSONEncoder) -> JSONNode? -} - -/// Anything conforming to `AnyJSONCaseIterable` can provide a -/// list of its possible values. -public protocol AnyJSONCaseIterable { - static func allCases(using encoder: JSONEncoder) -> [AnyCodable] -} - -extension AnyJSONCaseIterable { - /// Given an array of Codable values, retrieve an array of AnyCodables. - static func allCases(from input: [T], using encoder: JSONEncoder) throws -> [AnyCodable] { - if let alreadyGoodToGo = input as? [AnyCodable] { - return alreadyGoodToGo - } - - // The following is messy, but it does get us the intended result: - // Given any array of things that can be encoded, we want - // to map to an array of AnyCodable so we can store later. We need to - // muck with JSONSerialization because something like an `enum` may - // very well be encoded as a string, and therefore representable - // by AnyCodable, but AnyCodable wants it to actually BE a String - // upon initialization. - - guard let arrayOfCodables = try JSONSerialization.jsonObject(with: encoder.encode(input), options: []) as? [Any] else { - throw OpenAPICodableError.allCasesArrayNotCodable - } - return arrayOfCodables.map(AnyCodable.init) - } -} - -/// Anything conforming to `AnyJSONCaseIterable` can provide a -/// list of its possible values. This second protocol is -/// necessary so that one type can conditionally provide a -/// list of possible values and then (under different conditions) -/// provide a different list of possible values. -/// The "different" conditions have to do -/// with Optionality, hence the name of this protocol. -public protocol AnyWrappedJSONCaseIterable { - static func allCases(using encoder: JSONEncoder) -> [AnyCodable] -} - -public protocol SwiftTyped { - associatedtype SwiftType: Codable, Equatable -} - -public protocol OpenAPIFormat: SwiftTyped, Codable, Equatable { - static var unspecified: Self { get } - - var jsonType: JSONType { get } -} - -public protocol JSONNodeContext { - var required: Bool { get } -} - -public enum JSONType: String, Codable { - case boolean = "boolean" - case object = "object" - case array = "array" - case number = "number" - case integer = "integer" - case string = "string" -} - -public enum JSONTypeFormat: Equatable { - case boolean(BooleanFormat) - case object(ObjectFormat) - case array(ArrayFormat) - case number(NumberFormat) - case integer(IntegerFormat) - case string(StringFormat) - - public enum BooleanFormat: String, Equatable, OpenAPIFormat { - case generic = "" - - public typealias SwiftType = Bool - - public static var unspecified: BooleanFormat { - return .generic - } - - public var jsonType: JSONType { - return .boolean - } - } - - public enum ObjectFormat: String, Equatable, OpenAPIFormat { - case generic = "" - - public typealias SwiftType = AnyCodable - - public static var unspecified: ObjectFormat { - return .generic - } - - public var jsonType: JSONType { - return .object - } - } - - public enum ArrayFormat: String, Equatable, OpenAPIFormat { - case generic = "" - - public typealias SwiftType = [AnyCodable] - - public static var unspecified: ArrayFormat { - return .generic - } - - public var jsonType: JSONType { - return .array - } - } - - public enum NumberFormat: String, Equatable, OpenAPIFormat { - case generic = "" - case float = "float" - case double = "double" - - public typealias SwiftType = Double - - public static var unspecified: NumberFormat { - return .generic - } - - public var jsonType: JSONType { - return .number - } - } - - public enum IntegerFormat: String, Equatable, OpenAPIFormat { - case generic = "" - case int32 = "int32" - case int64 = "int64" - - public typealias SwiftType = Int - - public static var unspecified: IntegerFormat { - return .generic - } - - public var jsonType: JSONType { - return .integer - } - } - - public enum StringFormat: String, Equatable, OpenAPIFormat { - case generic = "" - case byte = "byte" - case binary = "binary" - case date = "date" - case dateTime = "date-time" - case password = "password" - - public typealias SwiftType = String - - public static var unspecified: StringFormat { - return .generic - } - - public var jsonType: JSONType { - return .string - } - } - - public var jsonType: JSONType { - switch self { - case .boolean: - return .boolean - case .object: - return .object - case .array: - return .array - case .number: - return .number - case .integer: - return .integer - case .string: - return .string - } - } -} - -/// A JSON Node is what OpenAPI calls a -/// "Schema Object" -public enum JSONNode: Equatable { - case boolean(Context) - indirect case object(Context, ObjectContext) - indirect case array(Context, ArrayContext) - case number(Context, NumericContext) - case integer(Context, NumericContext) - case string(Context, StringContext) - indirect case all(of: [JSONNode]) - indirect case one(of: [JSONNode]) - indirect case any(of: [JSONNode]) - indirect case not(JSONNode) - case reference(JSONReference) - - public struct Context: JSONNodeContext, Equatable { - public let format: Format - public let required: Bool - public let nullable: Bool - - // NOTE: "const" is supported by the newest JSON Schema spec but not - // yet by OpenAPI. Instead, will use "enum" with one possible value for now. -// public let constantValue: Format.SwiftType? - - /// The OpenAPI spec calls this "enum" - /// If not specified, it is assumed that any - /// value of the given format is allowed. - /// NOTE: I would like the array of allowed - /// values to have the type `Format.SwiftType` - /// but this is not tractable because I also - /// want to be able to automatically turn any - /// Swift type that will get _encoded as - /// something compatible with_ `Format.SwiftType` - /// into an allowed value. - public let allowedValues: [AnyCodable]? - - // I wanted example to be AnyCodable, but alas that causes - // runtime problems when encoding in a very strange way. - // For now, a String (which is OK by the OpenAPI spec) will - // have to do. - public let example: String? - - public init(format: Format, - required: Bool, - nullable: Bool = false, -// constantValue: Format.SwiftType? = nil, - allowedValues: [AnyCodable]? = nil, - example: (codable: AnyCodable, encoder: JSONEncoder)? = nil) { - self.format = format - self.required = required - self.nullable = nullable -// self.constantValue = constantValue - self.allowedValues = allowedValues - self.example = example - .flatMap { try? $0.encoder.encode($0.codable)} - .flatMap { String(data: $0, encoding: .utf8) } - } - - /// Return the optional version of this Context - public func optionalContext() -> Context { - return .init(format: format, - required: false, - nullable: nullable, -// constantValue: constantValue, - allowedValues: allowedValues) - } - - /// Return the required version of this context - public func requiredContext() -> Context { - return .init(format: format, - required: true, - nullable: nullable, -// constantValue: constantValue, - allowedValues: allowedValues) - } - - /// Return the nullable version of this context - public func nullableContext() -> Context { - return .init(format: format, - required: required, - nullable: true, -// constantValue: constantValue, - allowedValues: allowedValues) - } - - /// Return this context with the given list of possible values - public func with(allowedValues: [AnyCodable]) -> Context { - return .init(format: format, - required: required, - nullable: nullable, -// constantValue: constantValue, - allowedValues: allowedValues) - } - - /// Return this context with the given example - public func with(example: AnyCodable, using encoder: JSONEncoder) -> Context { - return .init(format: format, - required: required, - nullable: nullable, -// constantValue: constantValue, - allowedValues: allowedValues, - example: (codable: example, encoder: encoder)) - } - } - - public struct NumericContext: Equatable { - /// A numeric instance is valid only if division by this keyword's value results in an integer. Defaults to nil. - public let multipleOf: Double? - public let maximum: Double? - public let exclusiveMaximum: Double? - public let minimum: Double? - public let exclusiveMinimum: Double? - - public init(multipleOf: Double? = nil, - maximum: Double? = nil, - exclusiveMaximum: Double? = nil, - minimum: Double? = nil, - exclusiveMinimum: Double? = nil) { - self.multipleOf = multipleOf - self.maximum = maximum - self.exclusiveMaximum = exclusiveMaximum - self.minimum = minimum - self.exclusiveMinimum = exclusiveMinimum - } - } - - public struct StringContext: Equatable { - public let maxLength: Int? - public let minLength: Int - - /// Regular expression - public let pattern: String? - - public init(maxLength: Int? = nil, - minLength: Int = 0, - pattern: String? = nil) { - self.maxLength = maxLength - self.minLength = minLength - self.pattern = pattern - } - } - - public struct ArrayContext: Equatable { - /// A JSON Type Node that describes - /// the type of each element in the array. - public let items: JSONNode - - /// Maximum number of items in array. - public let maxItems: Int? - - /// Minimum number of items in array. - /// Defaults to 0. - public let minItems: Int - - /// Setting to true indicates all - /// elements of the array are expected - /// to be unique. Defaults to false. - public let uniqueItems: Bool - - public init(items: JSONNode, - maxItems: Int? = nil, - minItems: Int = 0, - uniqueItems: Bool = false) { - self.items = items - self.maxItems = maxItems - self.minItems = minItems - self.uniqueItems = uniqueItems - } - } - - public struct ObjectContext: Equatable { - public let maxProperties: Int? - let _minProperties: Int - public let properties: [String: JSONNode] - public let additionalProperties: [String: JSONNode]? - - /* - // NOTE that an object's required properties - // array is determined by looking at its properties' - // required Bool. - */ - public var requiredProperties: [String] { - return Array(properties.filter { (name, node) in - node.required - }.keys) - } - - public var minProperties: Int { - return max(_minProperties, requiredProperties.count) - } - - public init(properties: [String: JSONNode], - additionalProperties: [String: JSONNode]? = nil, - maxProperties: Int? = nil, - minProperties: Int = 0) { - self.properties = properties - self.additionalProperties = additionalProperties - self.maxProperties = maxProperties - self._minProperties = minProperties - } - } - - public var jsonTypeFormat: JSONTypeFormat? { - switch self { - case .boolean(let context): - return .boolean(context.format) - case .object(let context, _): - return .object(context.format) - case .array(let context, _): - return .array(context.format) - case .number(let context, _): - return .number(context.format) - case .integer(let context, _): - return .integer(context.format) - case .string(let context, _): - return .string(context.format) - case .all, .one, .any, .not, .reference: - return nil - } - } - - public var required: Bool { - switch self { - case .boolean(let contextA as JSONNodeContext), - .object(let contextA as JSONNodeContext, _), - .array(let contextA as JSONNodeContext, _), - .number(let contextA as JSONNodeContext, _), - .integer(let contextA as JSONNodeContext, _), - .string(let contextA as JSONNodeContext, _): - return contextA.required - case .all, .one, .any, .not, .reference: - return true - } - } - - /// Return the optional version of this JSONNode - public func optionalNode() -> JSONNode { - switch self { - case .boolean(let context): - return .boolean(context.optionalContext()) - case .object(let contextA, let contextB): - return .object(contextA.optionalContext(), contextB) - case .array(let contextA, let contextB): - return .array(contextA.optionalContext(), contextB) - case .number(let context, let contextB): - return .number(context.optionalContext(), contextB) - case .integer(let context, let contextB): - return .integer(context.optionalContext(), contextB) - case .string(let context, let contextB): - return .string(context.optionalContext(), contextB) - case .all, .one, .any, .not, .reference: - return self - } - } - - /// Return the required version of this JSONNode - public func requiredNode() -> JSONNode { - switch self { - case .boolean(let context): - return .boolean(context.requiredContext()) - case .object(let contextA, let contextB): - return .object(contextA.requiredContext(), contextB) - case .array(let contextA, let contextB): - return .array(contextA.requiredContext(), contextB) - case .number(let context, let contextB): - return .number(context.requiredContext(), contextB) - case .integer(let context, let contextB): - return .integer(context.requiredContext(), contextB) - case .string(let context, let contextB): - return .string(context.requiredContext(), contextB) - case .all, .one, .any, .not, .reference: - return self - } - } - - /// Return the nullable version of this JSONNode - public func nullableNode() -> JSONNode { - switch self { - case .boolean(let context): - return .boolean(context.nullableContext()) - case .object(let contextA, let contextB): - return .object(contextA.nullableContext(), contextB) - case .array(let contextA, let contextB): - return .array(contextA.nullableContext(), contextB) - case .number(let context, let contextB): - return .number(context.nullableContext(), contextB) - case .integer(let context, let contextB): - return .integer(context.nullableContext(), contextB) - case .string(let context, let contextB): - return .string(context.nullableContext(), contextB) - case .all, .one, .any, .not, .reference: - return self - } - } - - public func with(allowedValues: [AnyCodable]) throws -> JSONNode { - - switch self { - case .boolean(let context): - return .boolean(context.with(allowedValues: allowedValues)) - case .object(let contextA, let contextB): - return .object(contextA.with(allowedValues: allowedValues), contextB) - case .array(let contextA, let contextB): - return .array(contextA.with(allowedValues: allowedValues), contextB) - case .number(let context, let contextB): - return .number(context.with(allowedValues: allowedValues), contextB) - case .integer(let context, let contextB): - return .integer(context.with(allowedValues: allowedValues), contextB) - case .string(let context, let contextB): - return .string(context.with(allowedValues: allowedValues), contextB) - case .all, .one, .any, .not, .reference: - return self - } - } - - public func with(example codableExample: T, - using encoder: JSONEncoder) throws -> JSONNode { - let example: AnyCodable - if let goodToGo = codableExample as? AnyCodable { - example = goodToGo - } else { - example = AnyCodable(try JSONSerialization.jsonObject(with: encoder.encode(codableExample), options: [])) - } - - switch self { - case .boolean(let context): - return .boolean(context.with(example: example, using: encoder)) - case .object(let contextA, let contextB): - return .object(contextA.with(example: example, using: encoder), contextB) - case .array(let contextA, let contextB): - return .array(contextA.with(example: example, using: encoder), contextB) - case .number(let context, let contextB): - return .number(context.with(example: example, using: encoder), contextB) - case .integer(let context, let contextB): - return .integer(context.with(example: example, using: encoder), contextB) - case .string(let context, let contextB): - return .string(context.with(example: example, using: encoder), contextB) - case .all, .one, .any, .not, .reference: - return self - } - } -} - -public enum OpenAPICodableError: Swift.Error, Equatable { - case allCasesArrayNotCodable - case exampleNotCodable - case primitiveGuessFailed -} - -public enum OpenAPITypeError: Swift.Error { - case invalidNode - case unknownNodeType(Any.Type) -} - -/// Anything conforming to RefName knows what to call itself -/// in the context of JSON References. -public protocol RefName { - static var refName: String { get } -} - -public protocol ReferenceRoot: RefName {} - -public protocol ReferenceDict: RefName { - associatedtype Value -} - -/// A RefDict knows what to call itself (Name) and where to -/// look for itself (Root) and it stores a dictionary of -/// JSONNodes (some of which might be other references). -public struct RefDict: ReferenceDict, Equatable { - public static var refName: String { return Name.refName } - - public typealias Value = RefType - public typealias Key = String - - let dict: [String: RefType] - - public init(_ dict: [String: RefType]) { - self.dict = dict - } - - public subscript(_ key: String) -> RefType? { - return dict[key] - } -} - -/// A Reference is the combination of -/// a path to a reference dictionary -/// and a selector that the dictionary is keyed off of. -public enum JSONReference: Equatable { - - case node(InternalReference) - case file(FileReference) - - public typealias FileReference = String - - public struct InternalReference: Equatable { - public let path: PartialKeyPath - public let selector: String - - public var refName: String { - // we require RD be a RefName in the initializer - // so it is safe to force cast here. - return (type(of: path).valueType as! RefName.Type).refName - } - - public init(type: KeyPath, - selector: String) where RD.Value == RefType { - self.path = type - self.selector = selector - } - } -} - -/// An OpenAPI Path Item -/// This type describes the endpoints a server has -/// bound to a particular path. -public enum OpenAPIPathItem: Equatable { - case reference(JSONReference) - case operations(PathProperties) - - public struct PathProperties: Equatable { - public let summary: String? - public let description: String? -// public let servers: - public let parameters: ParameterArray - - public let get: Operation? - public let put: Operation? - public let post: Operation? - public let delete: Operation? - public let options: Operation? - public let head: Operation? - public let patch: Operation? - public let trace: Operation? - - public init(summary: String? = nil, - description: String? = nil, - parameters: ParameterArray, - get: Operation? = nil, - put: Operation? = nil, - post: Operation? = nil, - delete: Operation? = nil, - options: Operation? = nil, - head: Operation? = nil, - patch: Operation? = nil, - trace: Operation? = nil) { - self.summary = summary - self.description = description - self.parameters = parameters - - self.get = get - self.put = put - self.post = post - self.delete = delete - self.options = options - self.head = head - self.patch = patch - self.trace = trace - } - - public typealias ParameterArray = [Either>] - - public struct Parameter: Equatable, Encodable { - private enum CodingKeys: String, CodingKey { - case name -// case parameterLocation = "in" - case description - case deprecated - } - - public let name: String -// public let parameterLocation: Location - public let description: String? - public let deprecated: Bool // default is false - // TODO: serialization rules - /* - Serialization Rules - */ - - public init(name: String, - description: String? = nil, - deprecated: Bool = false) { - self.name = name - self.description = description - self.deprecated = deprecated - } - -// public enum Location: Encodable { -// case query(required: Bool?) -// case header(required: Bool?) -// case path -// case cookie(required: Bool?) -// } - } - - public struct Operation: Equatable { - public let tags: [String]? - public let summary: String? - public let description: String? -// public let externalDocs: - public let operationId: String - public let parameters: ParameterArray - public let requestBody: OpenAPIRequestBody? - public let responses: ResponseMap -// public let callbacks: - public let deprecated: Bool // default is false -// public let security: -// public let servers: - - public init(tags: [String]? = nil, - summary: String? = nil, - description: String? = nil, - operationId: String, - parameters: ParameterArray, - requestBody: OpenAPIRequestBody? = nil, - responses: ResponseMap, - deprecated: Bool = false) { - self.tags = tags - self.summary = summary - self.description = description - self.operationId = operationId - self.parameters = parameters - self.requestBody = requestBody - self.responses = responses - self.deprecated = deprecated - } - - public typealias ResponseMap = [OpenAPIResponse.Code: Either>] - - public typealias ContentMap = [OpenAPIContentType: OpenAPIContent] - } - } -} - -public struct OpenAPIRequestBody: Equatable { - public let description: String? - public let content: OpenAPIPathItem.PathProperties.Operation.ContentMap - public let required: Bool - - public init(description: String? = nil, - content: OpenAPIPathItem.PathProperties.Operation.ContentMap, - required: Bool = true) { - self.description = description - self.content = content - self.required = required - } -} - -public struct OpenAPIResponse: Equatable { - public let description: String -// public let headers: - public let content: OpenAPIPathItem.PathProperties.Operation.ContentMap -// public let links: - - public init(description: String, - content: OpenAPIPathItem.PathProperties.Operation.ContentMap) { - self.description = description - self.content = content - } - - public enum Code: RawRepresentable, Equatable, Hashable { - public typealias RawValue = String - - case `default` - case status(code: Int) - - public var rawValue: String { - switch self { - case .default: - return "default" - - case .status(code: let code): - return String(code) - } - } - - public init?(rawValue: String) { - if let val = Int(rawValue) { - self = .status(code: val) - } else { - self = .default - } - } - } -} - -public enum OpenAPIContentType: String, Encodable, Equatable, Hashable { - case json = "application/json" -} - -public struct OpenAPIContent: Encodable, Equatable { - public let schema: Either> - // public let example: - // public let examples: - // public let encoding: - - public init(schema: Either>) { - self.schema = schema - } -} - -/// What the spec calls the "Components Object". -/// This is a place to put reusable components to -/// be referenced from other parts of the spec. -public struct OpenAPIComponents: Equatable, Encodable, ReferenceRoot { - public static var refName: String { return "components" } - - public let schemas: SchemasDict -// public let responses: - public let parameters: ParametersDict -// public let examples: -// public let requestBodies: -// public let headers: -// public let headers: -// public let securitySchemas: -// public let links: -// public let callbacks: - - public init(schemas: [String: SchemasDict.Value], parameters: [String: ParametersDict.Value]) { - self.schemas = SchemasDict(schemas) - self.parameters = ParametersDict(parameters) - } - - public enum SchemasName: RefName { - public static var refName: String { return "schemas" } - } - - public typealias SchemasDict = RefDict - - public enum ParametersName: RefName { - public static var refName: String { return "parameters" } - } - - public typealias ParametersDict = RefDict -} - -/// The root of an OpenAPI 3.0 document. -public struct OpenAPISchema { - public let openAPIVersion: Version - public let info: Info -// public let servers: - public let paths: [PathComponents: OpenAPIPathItem] - public let components: OpenAPIComponents -// public let security: -// public let tags: -// public let externalDocs: - - public init(openAPIVersion: Version = .v3_0_0, - info: Info, - paths: [PathComponents: OpenAPIPathItem], - components: OpenAPIComponents) { - self.openAPIVersion = openAPIVersion - self.info = info - self.paths = paths - self.components = components - } - - public enum Version: String, Encodable { - case v3_0_0 = "3.0.0" - } - - public struct Info: Encodable { - public let title: String - public let description: String? - public let termsOfService: URL? -// public let contact: -// public let license: - public let version: String - - public init(title: String, - description: String? = nil, - termsOfService: URL? = nil, - version: String) { - self.title = title - self.description = description - self.termsOfService = termsOfService - self.version = version - } - } - - public struct PathComponents: RawRepresentable, Encodable, Equatable, Hashable { - public let components: [String] - - public init(_ components: [String]) { - self.components = components - } - - public init?(rawValue: String) { - components = rawValue.split(separator: "/").map(String.init) - } - - public var rawValue: String { - return "/\(components.joined(separator: "/"))" - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - - try container.encode(rawValue) - } - } -} diff --git a/Sources/JSONAPIOpenAPI/OpenAPI/SwiftPrimitiveTypes+OpenAPI.swift b/Sources/JSONAPIOpenAPI/OpenAPI/SwiftPrimitiveTypes+OpenAPI.swift deleted file mode 100644 index 235cb82..0000000 --- a/Sources/JSONAPIOpenAPI/OpenAPI/SwiftPrimitiveTypes+OpenAPI.swift +++ /dev/null @@ -1,132 +0,0 @@ -// -// PrimitiveTypes.swift -// JSONAPIOpenAPI -// -// Created by Mathew Polzin on 01/13/19. -// - -import AnyCodable -import Foundation - -/** - -Notable omissions in this library's default offerings: - -Base 64 encoded characters: -.string(.byte) - -Any sequence of octets: -.string(.binary) - -RFC3339 full-date: -.string(.date) - -RFC3339 date-time: -.string(.dateTime) - -A hint to UIs to obscure input: -.string(.password) - -Any object: -.object(.generic) - -**/ - -extension Optional: OpenAPINodeType where Wrapped: OpenAPINodeType { - static public func openAPINode() throws -> JSONNode { - return try Wrapped.openAPINode().optionalNode() - } -} - -extension Optional: RawOpenAPINodeType where Wrapped: RawRepresentable, Wrapped.RawValue: OpenAPINodeType { - static public func rawOpenAPINode() throws -> JSONNode { - return try Wrapped.RawValue.openAPINode().optionalNode() - } -} - -extension Optional: WrappedRawOpenAPIType where Wrapped: RawOpenAPINodeType { - static public func wrappedOpenAPINode() throws -> JSONNode { - return try Wrapped.rawOpenAPINode().optionalNode() - } -} - -extension Optional: DoubleWrappedRawOpenAPIType where Wrapped: WrappedRawOpenAPIType { - static public func wrappedOpenAPINode() throws -> JSONNode { - return try Wrapped.wrappedOpenAPINode().optionalNode() - } -} - -extension Optional: AnyJSONCaseIterable where Wrapped: CaseIterable, Wrapped: Codable { - public static func allCases(using encoder: JSONEncoder) -> [AnyCodable] { - return (try? allCases(from: Array(Wrapped.allCases), using: encoder)) ?? [] - } -} - -extension Optional: DateOpenAPINodeType where Wrapped: DateOpenAPINodeType { - static public func dateOpenAPINodeGuess(using encoder: JSONEncoder) -> JSONNode? { - return Wrapped.dateOpenAPINodeGuess(using: encoder)?.optionalNode() - } -} - -extension String: OpenAPINodeType { - static public func openAPINode() throws -> JSONNode { - return .string(.init(format: .generic, - required: true), - .init()) - } -} - -extension Bool: OpenAPINodeType { - static public func openAPINode() throws -> JSONNode { - return .boolean(.init(format: .generic, - required: true)) - } -} - -extension Array: OpenAPINodeType where Element: OpenAPINodeType { - static public func openAPINode() throws -> JSONNode { - return .array(.init(format: .generic, - required: true), - .init(items: try Element.openAPINode())) - } -} - -extension Double: OpenAPINodeType { - static public func openAPINode() throws -> JSONNode { - return .number(.init(format: .double, - required: true), - .init()) - } -} - -extension Float: OpenAPINodeType { - static public func openAPINode() throws -> JSONNode { - return .number(.init(format: .float, - required: true), - .init()) - } -} - -extension Int: OpenAPINodeType { - static public func openAPINode() throws -> JSONNode { - return .integer(.init(format: .generic, - required: true), - .init()) - } -} - -extension Int32: OpenAPINodeType { - static public func openAPINode() throws -> JSONNode { - return .integer(.init(format: .int32, - required: true), - .init()) - } -} - -extension Int64: OpenAPINodeType { - static public func openAPINode() throws -> JSONNode { - return .integer(.init(format: .int64, - required: true), - .init()) - } -} diff --git a/Sources/JSONAPIOpenAPI/Optional+ZipWith.swift b/Sources/JSONAPIOpenAPI/Optional+ZipWith.swift deleted file mode 100644 index b04b7c2..0000000 --- a/Sources/JSONAPIOpenAPI/Optional+ZipWith.swift +++ /dev/null @@ -1,13 +0,0 @@ -// -// Optional+ZipWith.swift -// JSONAPIOpenAPI -// -// Created by Mathew Polzin on 1/19/19. -// - -/// Zip two optionals together with the given operation performed on -/// the unwrapped contents. If either optional is nil, the zip -/// yields nil. -func zip(_ left: X?, _ right: Y?, with fn: (X, Y) -> Z) -> Z? { - return left.flatMap { lft in right.map { rght in fn(lft, rght) }} -} diff --git a/Sources/JSONAPIOpenAPI/Sampleable/Include+Sampleable.swift b/Sources/JSONAPIOpenAPI/Sampleable/Include+Sampleable.swift deleted file mode 100644 index 750958a..0000000 --- a/Sources/JSONAPIOpenAPI/Sampleable/Include+Sampleable.swift +++ /dev/null @@ -1,185 +0,0 @@ -// -// Include+Sampleable.swift -// JSONAPIOpenAPI -// -// Created by Mathew Polzin on 1/23/19. -// - -import JSONAPI -import Sampleable - -extension Includes: Sampleable where I: Sampleable { - public static var sample: Includes { - guard I.self != NoIncludes.self else { - return .none - } - - return .init(values: I.samples) - } -} - -extension NoIncludes: Sampleable { - public static var sample: NoIncludes { - return NoIncludes() - } -} - -extension Include1: Sampleable where A: Sampleable { - public static var sample: Include1 { - return .init(A.sample) - } - - public static var samples: [Include1] { - return A.samples.map(Include1.init) - } -} - -extension Include2: Sampleable where A: Sampleable, B: Sampleable { - public static var sample: Include2 { - let randomChoice = Int.random(in: 0..] { - return A.samples.map(Include2.init) - + B.samples.map(Include2.init) - } -} - -extension Include3: Sampleable where A: Sampleable, B: Sampleable, C: Sampleable { - public static var sample: Include3 { - let randomChoice = Int.random(in: 0..] { - return A.samples.map(Include3.init) - + B.samples.map(Include3.init) - + C.samples.map(Include3.init) - } -} - -extension Include4: Sampleable where A: Sampleable, B: Sampleable, C: Sampleable, D: Sampleable { - public static var sample: Include4 { - let randomChoice = Int.random(in: 0..] { - return A.samples.map(Include4.init) - + B.samples.map(Include4.init) - + C.samples.map(Include4.init) - + D.samples.map(Include4.init) - } -} - -extension Include5: Sampleable where A: Sampleable, B: Sampleable, C: Sampleable, D: Sampleable, E: Sampleable { - public static var sample: Include5 { - let randomChoice = Int.random(in: 0..] { - let set1: [Include5] = A.samples.map(Include5.init) - + B.samples.map(Include5.init) - + C.samples.map(Include5.init) - - let set2: [Include5] = D.samples.map(Include5.init) - + E.samples.map(Include5.init) - - return set1 + set2 - } -} - -extension Include6: Sampleable where A: Sampleable, B: Sampleable, C: Sampleable, D: Sampleable, E: Sampleable, F: Sampleable { - public static var sample: Include6 { - let randomChoice = Int.random(in: 0..] { - let set1: [Include6] = A.samples.map(Include6.init) - + B.samples.map(Include6.init) - + C.samples.map(Include6.init) - - let set2: [Include6] = D.samples.map(Include6.init) - + E.samples.map(Include6.init) - + F.samples.map(Include6.init) - - return set1 + set2 - } -} - -extension Include7: Sampleable where A: Sampleable, B: Sampleable, C: Sampleable, D: Sampleable, E: Sampleable, F: Sampleable, G: Sampleable { - public static var sample: Include7 { - let randomChoice = Int.random(in: 0..] { - let set1: [Include7] = A.samples.map(Include7.init) - + B.samples.map(Include7.init) - + C.samples.map(Include7.init) - - let set2: [Include7] = D.samples.map(Include7.init) - + E.samples.map(Include7.init) - + F.samples.map(Include7.init) - - let set3: [Include7] = G.samples.map(Include7.init) - - return set1 + set2 + set3 - } -} - -extension Include8: Sampleable where A: Sampleable, B: Sampleable, C: Sampleable, D: Sampleable, E: Sampleable, F: Sampleable, G: Sampleable, H: Sampleable { - public static var sample: Include8 { - let randomChoice = Int.random(in: 0..] { - let set1: [Include8] = A.samples.map(Include8.init) - + B.samples.map(Include8.init) - + C.samples.map(Include8.init) - - let set2: [Include8] = D.samples.map(Include8.init) - + E.samples.map(Include8.init) - + F.samples.map(Include8.init) - - let set3: [Include8] = G.samples.map(Include8.init) - + H.samples.map(Include8.init) - - return set1 + set2 + set3 - } -} - -extension Include9: Sampleable where A: Sampleable, B: Sampleable, C: Sampleable, D: Sampleable, E: Sampleable, F: Sampleable, G: Sampleable, H: Sampleable, I: Sampleable { - public static var sample: Include9 { - let randomChoice = Int.random(in: 0..] { - let set1: [Include9] = A.samples.map(Include9.init) - + B.samples.map(Include9.init) - + C.samples.map(Include9.init) - - let set2: [Include9] = D.samples.map(Include9.init) - + E.samples.map(Include9.init) - + F.samples.map(Include9.init) - - let set3: [Include9] = G.samples.map(Include9.init) - + H.samples.map(Include9.init) - + I.samples.map(Include9.init) - - return set1 + set2 + set3 - } -} diff --git a/Sources/JSONAPIOpenAPI/Sampleable/JSONAPI+Sampleable.swift b/Sources/JSONAPIOpenAPI/Sampleable/JSONAPI+Sampleable.swift deleted file mode 100644 index d0d24b7..0000000 --- a/Sources/JSONAPIOpenAPI/Sampleable/JSONAPI+Sampleable.swift +++ /dev/null @@ -1,69 +0,0 @@ -// -// JSONAPI+Sampleable.swift -// JSONAPIOpenAPI -// -// Created by Mathew Polzin on 1/24/19. -// - -import JSONAPI -import Sampleable - -extension NoAttributes: Sampleable { - public static var sample: NoAttributes { - return .none - } -} - -extension NoRelationships: Sampleable { - public static var sample: NoRelationships { - return .none - } -} - -extension NoMetadata: Sampleable { - public static var sample: NoMetadata { - return .none - } -} - -extension NoLinks: Sampleable { - public static var sample: NoLinks { - return .none - } -} - -extension NoAPIDescription: Sampleable { - public static var sample: NoAPIDescription { - return .none - } -} - -extension UnknownJSONAPIError: Sampleable { - public static var sample: UnknownJSONAPIError { - return .unknownError - } -} - -extension Unidentified: Sampleable { - public static var sample: Unidentified { - return Unidentified() - } -} - -extension Attribute: Sampleable where RawValue: Sampleable { - public static var sample: Attribute { - return .init(value: RawValue.sample) - } -} - -extension SingleResourceBody: Sampleable where Entity: Sampleable { - public static var sample: SingleResourceBody { - return .init(entity: Entity.sample) - } -} - -extension ManyResourceBody: Sampleable where Entity: Sampleable { - public static var sample: ManyResourceBody { - return .init(entities: Entity.samples) - } -} diff --git a/Sources/JSONAPIOpenAPI/Sampleable/Sampleable+OpenAPI.swift b/Sources/JSONAPIOpenAPI/Sampleable/Sampleable+OpenAPI.swift deleted file mode 100644 index ee69d29..0000000 --- a/Sources/JSONAPIOpenAPI/Sampleable/Sampleable+OpenAPI.swift +++ /dev/null @@ -1,162 +0,0 @@ -// -// Sampleable+OpenAPI.swift -// JSONAPIOpenAPI -// -// Created by Mathew Polzin on 1/24/19. -// - -import Foundation -import AnyCodable -import Sampleable - -public typealias SampleableOpenAPIType = Sampleable & GenericOpenAPINodeType - -extension Sampleable where Self: Encodable { - public static func genericOpenAPINode(using encoder: JSONEncoder) throws -> JSONNode { - - // short circuit for dates - if let dateType = self as? Date.Type, - let node = try dateType.dateOpenAPINodeGuess(using: encoder) ?? primitiveGuess(using: encoder) { - return node - } - - let mirror = Mirror(reflecting: Self.sample) - let properties: [(String, JSONNode)] = try mirror.children.compactMap { child in - - // see if we can enumerate the possible values - let maybeAllCases: [AnyCodable]? = { - switch type(of: child.value) { - case let valType as AnyJSONCaseIterable.Type: - return valType.allCases(using: encoder) - case let valType as AnyWrappedJSONCaseIterable.Type: - return valType.allCases(using: encoder) - default: - return nil - } - }() - - // try to snag an OpenAPI Node - let maybeOpenAPINode: JSONNode? = try { - switch type(of: child.value) { - case let valType as OpenAPINodeType.Type: - return try valType.openAPINode() - - case let valType as RawOpenAPINodeType.Type: - return try valType.rawOpenAPINode() - - case let valType as WrappedRawOpenAPIType.Type: - return try valType.wrappedOpenAPINode() - - case let valType as DoubleWrappedRawOpenAPIType.Type: - return try valType.wrappedOpenAPINode() - - case let valType as GenericOpenAPINodeType.Type: - return try valType.genericOpenAPINode(using: encoder) - - case let valType as DateOpenAPINodeType.Type: - return valType.dateOpenAPINodeGuess(using: encoder) - - default: - throw OpenAPITypeError.unknownNodeType(self) -// return nil - } - }() - - // put it all together - let newNode: JSONNode? - if let allCases = maybeAllCases, - let openAPINode = maybeOpenAPINode { - newNode = try openAPINode.with(allowedValues: allCases) - } else { - newNode = maybeOpenAPINode - } - - return zip(child.label, newNode) { ($0, $1) } - } - - // if there are no properties, let's see if we are dealing - // with a primitive. - if properties.count == 0, - let primitive = try primitiveGuess(using: encoder) { - return primitive - } - - // There should not be any duplication of keys since these are - // property names, but rather than risk runtime exception, we just - // fail to the newer value arbitrarily - let propertiesDict = Dictionary(properties) { _, value2 in value2 } - - return .object(.init(format: .generic, - required: true), - .init(properties: propertiesDict)) - } - - private static func primitiveGuess(using encoder: JSONEncoder) throws -> JSONNode? { - - let data = try encoder.encode(PrimitiveWrapper(primitive: Self.sample)) - let wrappedValue = try JSONSerialization.jsonObject(with: data, options: [.allowFragments]) - - guard let wrapperDict = wrappedValue as? [String: Any], - wrapperDict.contains(where: { $0.key == "primitive" }) else { - throw OpenAPICodableError.primitiveGuessFailed - } - - let value = (wrappedValue as! [String: Any])["primitive"]! - - return try { - switch type(of: value) { - case let valType as OpenAPINodeType.Type: - return try valType.openAPINode() - - case let valType as RawOpenAPINodeType.Type: - return try valType.rawOpenAPINode() - - case let valType as WrappedRawOpenAPIType.Type: - return try valType.wrappedOpenAPINode() - - case let valType as DoubleWrappedRawOpenAPIType.Type: - return try valType.wrappedOpenAPINode() - - case let valType as GenericOpenAPINodeType.Type: - return try valType.genericOpenAPINode(using: encoder) - - case let valType as DateOpenAPINodeType.Type: - return valType.dateOpenAPINodeGuess(using: encoder) - - default: - return nil - } - }() ?? { - switch value { - case is String: - return .string(.init(format: .generic, - required: true), - .init()) - - case is Int: - return .integer(.init(format: .generic, - required: true), - .init()) - - case is Double: - return .number(.init(format: .double, - required: true), - .init()) - - case is Bool: - return .boolean(.init(format: .generic, - required: true)) - - default: - return nil - } - }() - } -} - -// The following wrapper is only needed because JSONEncoder cannot yet encode -// JSON fragments. It is a very unfortunate limitation that requires silly -// workarounds in edge cases like this. -private struct PrimitiveWrapper: Encodable { - let primitive: Wrapped -} diff --git a/Tests/JSONAPIArbitraryTests/PlaceholderTests.swift b/Tests/JSONAPIArbitraryTests/PlaceholderTests.swift deleted file mode 100644 index 61ceaa0..0000000 --- a/Tests/JSONAPIArbitraryTests/PlaceholderTests.swift +++ /dev/null @@ -1,15 +0,0 @@ -// -// PlaceholderTests.swift -// JSONAPIArbitraryTests -// -// Created by Mathew Polzin on 12/7/18. -// - -import XCTest -import JSONAPIArbitrary - -class PlaceholderTests: XCTestCase { - func test_Placeholder() { - - } -} diff --git a/Tests/JSONAPIArbitraryTests/XCTestManifests.swift b/Tests/JSONAPIArbitraryTests/XCTestManifests.swift deleted file mode 100644 index acb4b36..0000000 --- a/Tests/JSONAPIArbitraryTests/XCTestManifests.swift +++ /dev/null @@ -1,15 +0,0 @@ -import XCTest - -extension PlaceholderTests { - static let __allTests = [ - ("test_Placeholder", test_Placeholder), - ] -} - -#if !os(macOS) -public func __allTests() -> [XCTestCaseEntry] { - return [ - testCase(PlaceholderTests.__allTests), - ] -} -#endif diff --git a/Tests/JSONAPIOpenAPITests/JSONAPIAttributeOpenAPITests.swift b/Tests/JSONAPIOpenAPITests/JSONAPIAttributeOpenAPITests.swift deleted file mode 100644 index 1cee8f8..0000000 --- a/Tests/JSONAPIOpenAPITests/JSONAPIAttributeOpenAPITests.swift +++ /dev/null @@ -1,893 +0,0 @@ -// -// JSONAPIAttributeOpenAPITests.swift -// JSONAPIOpenAPITests -// -// Created by Mathew Polzin on 1/20/19. -// - -import XCTest -import JSONAPI -import JSONAPIOpenAPI -import SwiftCheck -import AnyCodable - -class JSONAPIAttributeOpenAPITests: XCTestCase { -} - -// MARK: - Boolean -extension JSONAPIAttributeOpenAPITests { - func test_BooleanAttribute() { - let node = try! Attribute.openAPINode() - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .boolean(.generic)) - - guard case .boolean(let contextA) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: true, - nullable: false, - allowedValues: nil)) - } - - func test_NullableBooleanAttribute() { - let node = try! Attribute.openAPINode() - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .boolean(.generic)) - - guard case .boolean(let contextA) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: true, - nullable: true, - allowedValues: nil)) - } - - func test_OptionalBooleanAttribute() { - let node = try! Attribute?.openAPINode() - - XCTAssertFalse(node.required) - XCTAssertEqual(node.jsonTypeFormat, .boolean(.generic)) - - guard case .boolean(let contextA) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: false, - nullable: false, - allowedValues: nil)) - } - - func test_OptionalNullableBooleanAttribute() { - let node = try! Attribute?.openAPINode() - - XCTAssertFalse(node.required) - XCTAssertEqual(node.jsonTypeFormat, .boolean(.generic)) - - guard case .boolean(let contextA) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: false, - nullable: true, - allowedValues: nil)) - } -} - -// MARK: - Array of Strings -extension JSONAPIAttributeOpenAPITests { - func test_Arrayttribute() { - let node = try! Attribute<[String]>.openAPINode() - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .array(.generic)) - - guard case .array(let contextA, let arrayContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: true, - nullable: false, - allowedValues: nil)) - - let stringNode = JSONNode.string(.init(format: .generic, - required: true), - .init()) - - XCTAssertEqual(arrayContext, .init(items: stringNode)) - } - - func test_NullableArrayAttribute() { - let node = try! Attribute<[String]?>.openAPINode() - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .array(.generic)) - - guard case .array(let contextA, let arrayContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: true, - nullable: true, - allowedValues: nil)) - - let stringNode = JSONNode.string(.init(format: .generic, - required: true), - .init()) - - XCTAssertEqual(arrayContext, .init(items: stringNode)) - } - - func test_OptionalArrayAttribute() { - let node = try! Attribute<[String]>?.openAPINode() - - XCTAssertFalse(node.required) - XCTAssertEqual(node.jsonTypeFormat, .array(.generic)) - - guard case .array(let contextA, let arrayContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: false, - nullable: false, - allowedValues: nil)) - - let stringNode = JSONNode.string(.init(format: .generic, - required: true), - .init()) - - XCTAssertEqual(arrayContext, .init(items: stringNode)) - } - - func test_OptionalNullableArrayAttribute() { - let node = try! Attribute<[String]?>?.openAPINode() - - XCTAssertFalse(node.required) - XCTAssertEqual(node.jsonTypeFormat, .array(.generic)) - - guard case .array(let contextA, let arrayContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: false, - nullable: true, - allowedValues: nil)) - - let stringNode = JSONNode.string(.init(format: .generic, - required: true), - .init()) - - XCTAssertEqual(arrayContext, .init(items: stringNode)) - } -} - -// MARK: - Number -extension JSONAPIAttributeOpenAPITests { - func test_NumberAttribute() { - let node = try! Attribute.openAPINode() - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .number(.double)) - - guard case .number(let contextA, let numberContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .double, - required: true, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(numberContext, .init()) - } - - func test_NullableNumberAttribute() { - let node = try! Attribute.openAPINode() - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .number(.double)) - - guard case .number(let contextA, let numberContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .double, - required: true, - nullable: true, - allowedValues: nil)) - - XCTAssertEqual(numberContext, .init()) - } - - func test_OptionalNumberAttribute() { - let node = try! Attribute?.openAPINode() - - XCTAssertFalse(node.required) - XCTAssertEqual(node.jsonTypeFormat, .number(.double)) - - guard case .number(let contextA, let numberContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .double, - required: false, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(numberContext, .init()) - } - - func test_OptionalNullableNumberAttribute() { - let node = try! Attribute?.openAPINode() - - XCTAssertFalse(node.required) - XCTAssertEqual(node.jsonTypeFormat, .number(.double)) - - guard case .number(let contextA, let numberContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .double, - required: false, - nullable: true, - allowedValues: nil)) - - XCTAssertEqual(numberContext, .init()) - } - - func test_FloatNumberAttribute() { - let node = try! Attribute.openAPINode() - - XCTAssertEqual(node.jsonTypeFormat, .number(.float)) - } -} - -// MARK: - Integer -extension JSONAPIAttributeOpenAPITests { - func test_IntegerAttribute() { - let node = try! Attribute.openAPINode() - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .integer(.generic)) - - guard case .integer(let contextA, let intContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: true, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(intContext, .init()) - } - - func test_NullableIntegerAttribute() { - let node = try! Attribute.openAPINode() - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .integer(.generic)) - - guard case .integer(let contextA, let intContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: true, - nullable: true, - allowedValues: nil)) - - XCTAssertEqual(intContext, .init()) - } - - func test_OptionalIntegerAttribute() { - let node = try! Attribute?.openAPINode() - - XCTAssertFalse(node.required) - XCTAssertEqual(node.jsonTypeFormat, .integer(.generic)) - - guard case .integer(let contextA, let intContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: false, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(intContext, .init()) - } - - func test_OptionalNullableIntegerAttribute() { - let node = try! Attribute?.openAPINode() - - XCTAssertFalse(node.required) - XCTAssertEqual(node.jsonTypeFormat, .integer(.generic)) - - guard case .integer(let contextA, let intContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: false, - nullable: true, - allowedValues: nil)) - - XCTAssertEqual(intContext, .init()) - } -} - -// MARK: - String -extension JSONAPIAttributeOpenAPITests { - func test_StringAttribute() { - let node = try! Attribute.openAPINode() - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .string(.generic)) - - guard case .string(let contextA, let stringContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: true, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(stringContext, .init()) - } - - func test_NullableStringAttribute() { - let node = try! Attribute.openAPINode() - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .string(.generic)) - - guard case .string(let contextA, let stringContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: true, - nullable: true, - allowedValues: nil)) - - XCTAssertEqual(stringContext, .init()) - } - - func test_OptionalStringAttribute() { - let node = try! Attribute?.openAPINode() - - XCTAssertFalse(node.required) - XCTAssertEqual(node.jsonTypeFormat, .string(.generic)) - - guard case .string(let contextA, let stringContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: false, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(stringContext, .init()) - } - - func test_OptionalNullableStringAttribute() { - let node = try! Attribute?.openAPINode() - - XCTAssertFalse(node.required) - XCTAssertEqual(node.jsonTypeFormat, .string(.generic)) - - guard case .string(let contextA, let stringContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: false, - nullable: true, - allowedValues: nil)) - - XCTAssertEqual(stringContext, .init()) - } -} - -// MARK: - Enum -// NOTE: `enum` Attributes only gain the automatic support for allowed values -// (`enum` property in the OpenAPI Spec) at the Entity scope. These attributes -// will all still have `allowedValues: nil` at the attribute scope. -extension JSONAPIAttributeOpenAPITests { - func test_EnumAttribute() { - let node = try! Attribute.rawOpenAPINode() - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .string(.generic)) - - guard case .string(let contextA, let stringContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: true, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(stringContext, .init()) - } - - func test_NullableEnumAttribute() { - let node = try! Attribute.wrappedOpenAPINode() - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .string(.generic)) - - guard case .string(let contextA, let stringContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: true, - nullable: true, - allowedValues: nil)) - - XCTAssertEqual(stringContext, .init()) - } - - func test_OptionalEnumAttribute() { - let node = try! Attribute?.wrappedOpenAPINode() - - XCTAssertFalse(node.required) - XCTAssertEqual(node.jsonTypeFormat, .string(.generic)) - - guard case .string(let contextA, let stringContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: false, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(stringContext, .init()) - } - - func test_OptionalNullableEnumAttribute() { - let node = try! Attribute?.wrappedOpenAPINode() - - XCTAssertFalse(node.required) - XCTAssertEqual(node.jsonTypeFormat, .string(.generic)) - - guard case .string(let contextA, let stringContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: false, - nullable: true, - allowedValues: nil)) - - XCTAssertEqual(stringContext, .init()) - } -} - -// MARK: - Date -extension JSONAPIAttributeOpenAPITests { - func test_DateStringAttribute() { - // TEST: - // Encoder is set to use - // formatter with date - // with no time. - - let dateFormatter = DateFormatter() - dateFormatter.dateStyle = .medium - dateFormatter.timeStyle = .none - dateFormatter.locale = Locale(identifier: "en_US") - - let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted - encoder.dateEncodingStrategy = .formatted(dateFormatter) - - let node = Attribute.dateOpenAPINodeGuess(using: encoder) - - XCTAssertNotNil(node) - - XCTAssertTrue(node?.required ?? false) - XCTAssertEqual(node?.jsonTypeFormat, .string(.date)) - - guard case .string(let contextA, let stringContext)? = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .date, - required: true, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(stringContext, .init()) - } - - func test_DateStringAttribute_Sampleable() { - // TEST: - // Encoder is set to use - // formatter with date - // with no time. - - let dateFormatter = DateFormatter() - dateFormatter.dateStyle = .medium - dateFormatter.timeStyle = .none - dateFormatter.locale = Locale(identifier: "en_US") - - let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted - encoder.dateEncodingStrategy = .formatted(dateFormatter) - - let node = try! Attribute.genericOpenAPINode(using: encoder) - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .string(.date)) - - guard case .string(let contextA, let stringContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .date, - required: true, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(stringContext, .init()) - } - - func test_DateTimeStringAttribute() { - // TEST: - // Encoder is set to use - // formatter with date - // with time. - - let dateFormatter = DateFormatter() - dateFormatter.dateStyle = .medium - dateFormatter.timeStyle = .short - dateFormatter.locale = Locale(identifier: "en_US") - - let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted - encoder.dateEncodingStrategy = .formatted(dateFormatter) - - let node = Attribute.dateOpenAPINodeGuess(using: encoder) - - XCTAssertNotNil(node) - - XCTAssertTrue(node?.required ?? false) - XCTAssertEqual(node?.jsonTypeFormat, .string(.dateTime)) - - guard case .string(let contextA, let stringContext)? = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .dateTime, - required: true, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(stringContext, .init()) - } - - func test_DateTimeStringAttribute_Sampleable() { - // TEST: - // Encoder is set to use - // formatter with date - // with time. - - let dateFormatter = DateFormatter() - dateFormatter.dateStyle = .medium - dateFormatter.timeStyle = .short - dateFormatter.locale = Locale(identifier: "en_US") - - let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted - encoder.dateEncodingStrategy = .formatted(dateFormatter) - - let node = try! Attribute.genericOpenAPINode(using: encoder) - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .string(.dateTime)) - - guard case .string(let contextA, let stringContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .dateTime, - required: true, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(stringContext, .init()) - } - - func test_8601DateStringAttribute() { - if #available(OSX 10.12, *) { - // TEST: - // Encoder is set to use - // iso8601 date format - - let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted - encoder.dateEncodingStrategy = .iso8601 - - let node = Attribute.dateOpenAPINodeGuess(using: encoder) - - XCTAssertNotNil(node) - - XCTAssertTrue(node?.required ?? false) - XCTAssertEqual(node?.jsonTypeFormat, .string(.dateTime)) - - guard case .string(let contextA, let stringContext)? = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .dateTime, - required: true, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(stringContext, .init()) - } - } - - func test_8601DateStringAttribute_Sampleable() { - if #available(OSX 10.12, *) { - // TEST: - // Encoder is set to use - // iso8601 date format - - let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted - encoder.dateEncodingStrategy = .iso8601 - - let node = try! Attribute.genericOpenAPINode(using: encoder) - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .string(.dateTime)) - - guard case .string(let contextA, let stringContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .dateTime, - required: true, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(stringContext, .init()) - } - } - - func test_DateNumberAttribute() { - // TEST: - // Encoder is set to use - // seconds since 1970 as - // date format - - let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted - encoder.dateEncodingStrategy = .secondsSince1970 - - let node = Attribute.dateOpenAPINodeGuess(using: encoder) - - XCTAssertNotNil(node) - - XCTAssertTrue(node?.required ?? false) - XCTAssertEqual(node?.jsonTypeFormat, .number(.double)) - - guard case .number(let contextA, let numberContext)? = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .double, - required: true, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(numberContext, .init()) - } - - func test_DateNumberAttribute_Sampleable() { - // TEST: - // Encoder is set to use - // seconds since 1970 as - // date format - - let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted - encoder.dateEncodingStrategy = .secondsSince1970 - - let node = try! Attribute.genericOpenAPINode(using: encoder) - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .number(.double)) - - guard case .number(let contextA, let numberContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .double, - required: true, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(numberContext, .init()) - } - - func test_DateDeferredAttribute() { - // TEST: - // Encoder is set to use - // Date default encoding - - let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted - encoder.dateEncodingStrategy = .deferredToDate - - let node = Attribute.dateOpenAPINodeGuess(using: encoder) - - XCTAssertNil(node) - } - - func test_DateDeferredAttribute_Sampleable() { - // TEST: - // Encoder is set to use - // Date default encoding - - let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted - encoder.dateEncodingStrategy = .deferredToDate - - let node = try! Attribute.genericOpenAPINode(using: encoder) - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .number(.double)) - - guard case .number(let contextA, let numberContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .double, - required: true, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(numberContext, .init()) - } - - func test_NullableDateAttribute() { - let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted - encoder.dateEncodingStrategy = .secondsSince1970 - - let node = Attribute.dateOpenAPINodeGuess(using: encoder) - - XCTAssertNotNil(node) - - XCTAssertTrue(node?.required ?? false) - XCTAssertEqual(node?.jsonTypeFormat, .number(.double)) - - guard case .number(let contextA, let numberContext)? = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .double, - required: true, - nullable: true, - allowedValues: nil)) - - XCTAssertEqual(numberContext, .init()) - } - - func test_OptionalDateAttribute() { - let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted - encoder.dateEncodingStrategy = .secondsSince1970 - - let node = Attribute?.dateOpenAPINodeGuess(using: encoder) - - XCTAssertNotNil(node) - - XCTAssertFalse(node?.required ?? true) - XCTAssertEqual(node?.jsonTypeFormat, .number(.double)) - - guard case .number(let contextA, let numberContext)? = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .double, - required: false, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(numberContext, .init()) - } - - func test_OptionalNullableDateAttribute() { - let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted - encoder.dateEncodingStrategy = .secondsSince1970 - - let node = Attribute?.dateOpenAPINodeGuess(using: encoder) - - XCTAssertNotNil(node) - - XCTAssertFalse(node?.required ?? true) - XCTAssertEqual(node?.jsonTypeFormat, .number(.double)) - - guard case .number(let contextA, let numberContext)? = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .double, - required: false, - nullable: true, - allowedValues: nil)) - - XCTAssertEqual(numberContext, .init()) - } -} - -// MARK: - Test Types -extension JSONAPIAttributeOpenAPITests { - enum EnumAttribute: String, Codable, CaseIterable { - case one - case two - } -} - -extension Date: SampleableOpenAPIType { - public static var sample: Date { - return TimeInterval.arbitrary.map { Date(timeIntervalSince1970: $0) }.generate - } -} diff --git a/Tests/JSONAPIOpenAPITests/JSONAPIDocumentOpenAPITests.swift b/Tests/JSONAPIOpenAPITests/JSONAPIDocumentOpenAPITests.swift deleted file mode 100644 index c2dad8c..0000000 --- a/Tests/JSONAPIOpenAPITests/JSONAPIDocumentOpenAPITests.swift +++ /dev/null @@ -1,377 +0,0 @@ -// -// JSONAPIDocumentOpenAPITests.swift -// JSONAPIOpenAPITests -// -// Created by Mathew Polzin on 1/21/19. -// - -import XCTest -import SwiftCheck -import JSONAPI -import JSONAPIOpenAPI -import Sampleable - -class JSONAPIDocumentOpenAPITests: XCTestCase { - func test_SingleResourceDocument() { - - let dateFormatter = DateFormatter() - dateFormatter.dateStyle = .medium - dateFormatter.timeStyle = .none - dateFormatter.locale = Locale(identifier: "en_US") - - let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted - encoder.dateEncodingStrategy = .formatted(dateFormatter) - - let node = try! SingleEntityDocument.openAPINodeWithExample(using: encoder) - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .object(.generic)) - - guard case let .object(contextA, objectContext1) = node else { - XCTFail("Expected JSON Document to be an Object Node") - return - } - - XCTAssertNotNil(contextA.example) - XCTAssertFalse(contextA.nullable) - XCTAssertEqual(contextA.format, .generic) - XCTAssertTrue(contextA.required) - - XCTAssertEqual(objectContext1.minProperties, 1) - XCTAssertEqual(Set(objectContext1.requiredProperties), Set(["data"])) - XCTAssertEqual(Set(objectContext1.properties.keys), Set(["data"])) - - guard case let .object(contextB, objectContext2)? = objectContext1.properties["data"] else { - XCTFail("Expected Data field of JSON Document to be an Object Node") - return - } - - XCTAssertFalse(contextB.nullable) - XCTAssertEqual(contextB.format, .generic) - XCTAssertTrue(contextB.required) - - XCTAssertEqual(objectContext2.minProperties, 3) - XCTAssertEqual(Set(objectContext2.requiredProperties), Set(["id", "attributes", "type"])) - XCTAssertEqual(Set(objectContext2.properties.keys), Set(["id", "attributes", "type"])) - - XCTAssertEqual(objectContext2.properties["type"], - JSONNode.string(.init(format: .generic, - required: true, - allowedValues: [.init("test")]), - .init())) - } - - func test_ManyResourceDocument() { - - let dateFormatter = DateFormatter() - dateFormatter.dateStyle = .medium - dateFormatter.timeStyle = .none - dateFormatter.locale = Locale(identifier: "en_US") - - let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted - encoder.dateEncodingStrategy = .formatted(dateFormatter) - - let node = try! ManyEntityDocument.openAPINodeWithExample(using: encoder) - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .object(.generic)) - - guard case let .object(contextA, objectContext1) = node else { - XCTFail("Expected JSON Document to be an Object Node") - return - } - - XCTAssertNotNil(contextA.example) - XCTAssertFalse(contextA.nullable) - XCTAssertEqual(contextA.format, .generic) - XCTAssertTrue(contextA.required) - - XCTAssertEqual(objectContext1.minProperties, 1) - XCTAssertEqual(Set(objectContext1.requiredProperties), Set(["data"])) - XCTAssertEqual(Set(objectContext1.properties.keys), Set(["data"])) - - guard case let .array(contextB, arrayContext)? = objectContext1.properties["data"] else { - XCTFail("Expected Data field of JSON Document to be an Array Node") - return - } - - XCTAssertFalse(contextB.nullable) - XCTAssertEqual(contextB.format, .generic) - XCTAssertTrue(contextB.required) - - XCTAssertFalse(arrayContext.uniqueItems) - XCTAssertEqual(arrayContext.minItems, 0) - - guard case let .object(contextC, objectContext2) = arrayContext.items else { - XCTFail("Expected Items of Array under Data to be an Object Node") - return - } - - XCTAssertFalse(contextC.nullable) - XCTAssertEqual(contextC.format, .generic) - XCTAssertTrue(contextC.required) - - XCTAssertEqual(objectContext2.minProperties, 3) - XCTAssertEqual(Set(objectContext2.requiredProperties), Set(["id", "attributes", "type"])) - XCTAssertEqual(Set(objectContext2.properties.keys), Set(["id", "attributes", "type"])) - - XCTAssertEqual(objectContext2.properties["type"], - JSONNode.string(.init(format: .generic, - required: true, - allowedValues: [.init("test")]), - .init())) - } - - func test_DocumentWithOneIncludeType() { - let dateFormatter = DateFormatter() - dateFormatter.dateStyle = .medium - dateFormatter.timeStyle = .none - dateFormatter.locale = Locale(identifier: "en_US") - - let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted - encoder.dateEncodingStrategy = .formatted(dateFormatter) - - let node = try! DocumentWithIncludes.openAPINodeWithExample(using: encoder) - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .object(.generic)) - - guard case let .object(contextA, objectContext1) = node else { - XCTFail("Expected JSON Document to be an Object Node") - return - } - - XCTAssertNotNil(contextA.example) - XCTAssertFalse(contextA.nullable) - XCTAssertEqual(contextA.format, .generic) - XCTAssertTrue(contextA.required) - - XCTAssertEqual(objectContext1.minProperties, 2) - XCTAssertEqual(Set(objectContext1.requiredProperties), Set(["data", "included"])) - XCTAssertEqual(Set(objectContext1.properties.keys), Set(["data", "included"])) - - guard case let .object(contextB, objectContext2)? = objectContext1.properties["data"] else { - XCTFail("Expected Data field of JSON Document to be an Object Node") - return - } - - XCTAssertFalse(contextB.nullable) - XCTAssertEqual(contextB.format, .generic) - XCTAssertTrue(contextB.required) - - XCTAssertEqual(objectContext2.minProperties, 3) - XCTAssertEqual(Set(objectContext2.requiredProperties), Set(["id", "attributes", "type"])) - XCTAssertEqual(Set(objectContext2.properties.keys), Set(["id", "attributes", "type"])) - - XCTAssertEqual(objectContext2.properties["type"], - JSONNode.string(.init(format: .generic, - required: true, - allowedValues: [.init("test")]), - .init())) - - guard case let .array(contextC, arrayContext)? = objectContext1.properties["included"] else { - XCTFail("Expected Includes field of JSON Document to be an Array Node") - return - } - - XCTAssertFalse(contextC.nullable) - XCTAssertEqual(contextC.format, .generic) - XCTAssertTrue(contextC.required) - - XCTAssertTrue(arrayContext.uniqueItems) - XCTAssertEqual(arrayContext.minItems, 0) - - guard case let .object(contextD, objectContext3) = arrayContext.items else { - XCTFail("Expected Items of Array under Data to be an Object Node") - return - } - - XCTAssertFalse(contextD.nullable) - XCTAssertEqual(contextD.format, .generic) - XCTAssertTrue(contextD.required) - - XCTAssertEqual(objectContext3.minProperties, 3) - XCTAssertEqual(Set(objectContext3.requiredProperties), Set(["id", "attributes", "type"])) - XCTAssertEqual(Set(objectContext3.properties.keys), Set(["id", "attributes", "type"])) - - XCTAssertEqual(objectContext3.properties["type"], - JSONNode.string(.init(format: .generic, - required: true, - allowedValues: [.init("test")]), - .init())) - } - - func test_DocumentWithTwoIncludeTypes() { - let dateFormatter = DateFormatter() - dateFormatter.dateStyle = .medium - dateFormatter.timeStyle = .none - dateFormatter.locale = Locale(identifier: "en_US") - - let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted - encoder.dateEncodingStrategy = .formatted(dateFormatter) - - let node = try! DocumentWithMultipleTypesOfIncludes.openAPINodeWithExample(using: encoder) - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .object(.generic)) - - guard case let .object(contextA, objectContext1) = node else { - XCTFail("Expected JSON Document to be an Object Node") - return - } - - XCTAssertNotNil(contextA.example) - XCTAssertFalse(contextA.nullable) - XCTAssertEqual(contextA.format, .generic) - XCTAssertTrue(contextA.required) - - XCTAssertEqual(objectContext1.minProperties, 2) - XCTAssertEqual(Set(objectContext1.requiredProperties), Set(["data", "included"])) - XCTAssertEqual(Set(objectContext1.properties.keys), Set(["data", "included"])) - - guard case let .object(contextB, objectContext2)? = objectContext1.properties["data"] else { - XCTFail("Expected Data field of JSON Document to be an Object Node") - return - } - - XCTAssertFalse(contextB.nullable) - XCTAssertEqual(contextB.format, .generic) - XCTAssertTrue(contextB.required) - - XCTAssertEqual(objectContext2.minProperties, 3) - XCTAssertEqual(Set(objectContext2.requiredProperties), Set(["id", "attributes", "type"])) - XCTAssertEqual(Set(objectContext2.properties.keys), Set(["id", "attributes", "type"])) - - XCTAssertEqual(objectContext2.properties["type"], - JSONNode.string(.init(format: .generic, - required: true, - allowedValues: [.init("test")]), - .init())) - - guard case let .array(contextC, arrayContext)? = objectContext1.properties["included"] else { - XCTFail("Expected Includes field of JSON Document to be an Array Node") - return - } - - XCTAssertFalse(contextC.nullable) - XCTAssertEqual(contextC.format, .generic) - XCTAssertTrue(contextC.required) - - XCTAssertTrue(arrayContext.uniqueItems) - XCTAssertEqual(arrayContext.minItems, 0) - - guard case let .one(of: includeNodes) = arrayContext.items else { - XCTFail("Expected Included to contain multiple types of items.") - return - } - - XCTAssertEqual(includeNodes.count, 2) - - guard case let .object(contextD, objectContext3) = includeNodes[0] else { - XCTFail("Expected Items of OneOf under Array under Data to be an Object Node") - return - } - - XCTAssertFalse(contextD.nullable) - XCTAssertEqual(contextD.format, .generic) - XCTAssertTrue(contextD.required) - - XCTAssertEqual(objectContext3.minProperties, 3) - XCTAssertEqual(Set(objectContext3.requiredProperties), Set(["id", "attributes", "type"])) - XCTAssertEqual(Set(objectContext3.properties.keys), Set(["id", "attributes", "type"])) - - XCTAssertEqual(objectContext3.properties["type"], - JSONNode.string(.init(format: .generic, - required: true, - allowedValues: [.init("test")]), - .init())) - - guard case let .object(contextE, objectContext4) = includeNodes[1] else { - XCTFail("Expected Items of OneOf under Array under Data to be an Object Node") - return - } - - XCTAssertFalse(contextE.nullable) - XCTAssertEqual(contextE.format, .generic) - XCTAssertTrue(contextE.required) - - XCTAssertEqual(objectContext4.minProperties, 2) - XCTAssertEqual(Set(objectContext4.requiredProperties), Set(["id", "type"])) - XCTAssertEqual(Set(objectContext4.properties.keys), Set(["id", "type"])) - - XCTAssertEqual(objectContext4.properties["type"], - JSONNode.string(.init(format: .generic, - required: true, - allowedValues: [.init("test2")]), - .init())) - } -} - -// MARK: - Test Types -extension JSONAPIDocumentOpenAPITests { - enum TestEntityDescription: EntityDescription { - static var jsonType: String { return "test" } - - struct Attributes: JSONAPI.Attributes, Sampleable { - let name: Attribute - let date: Attribute - - static var sample: Attributes { - return .init(name: "hello world", - date: .init(value: Date())) - } - } - - typealias Relationships = NoRelationships - } - - typealias TestEntity = BasicEntity - - typealias SingleEntityDocument = Document, NoMetadata, NoLinks, NoIncludes, NoAPIDescription, UnknownJSONAPIError> - - typealias ManyEntityDocument = Document, NoMetadata, NoLinks, NoIncludes, NoAPIDescription, UnknownJSONAPIError> - - typealias DocumentWithIncludes = Document, NoMetadata, NoLinks, Include1, NoAPIDescription, UnknownJSONAPIError> - - enum TestEntityDescription2: EntityDescription { - static var jsonType: String { return "test2" } - - typealias Attributes = NoAttributes - - typealias Relationships = NoRelationships - } - - typealias TestEntity2 = BasicEntity - - typealias DocumentWithMultipleTypesOfIncludes = Document, NoMetadata, NoLinks, Include2, NoAPIDescription, UnknownJSONAPIError> -} - -extension Id: Sampleable where RawType == String { - public static var sample: Id { - return .init(rawValue: String.arbitrary.generate) - } -} - -extension JSONAPI.Entity: Sampleable where Description.Attributes: Sampleable, Description.Relationships: Sampleable, MetaType: Sampleable, LinksType: Sampleable, EntityRawIdType == String { - public static var sample: JSONAPI.Entity { - return JSONAPI.Entity(id: .sample, - attributes: .sample, - relationships: .sample, - meta: .sample, - links: .sample) - } -} - -extension Document: Sampleable where PrimaryResourceBody: Sampleable, MetaType: Sampleable, LinksType: Sampleable, IncludeType: Sampleable, APIDescription: Sampleable, Error: Sampleable { - public static var sample: Document { - return Document(apiDescription: .sample, - body: .sample, - includes: .sample, - meta: .sample, - links: .sample) - } -} diff --git a/Tests/JSONAPIOpenAPITests/JSONAPIEntityOpenAPITests.swift b/Tests/JSONAPIOpenAPITests/JSONAPIEntityOpenAPITests.swift deleted file mode 100644 index dde8cbb..0000000 --- a/Tests/JSONAPIOpenAPITests/JSONAPIEntityOpenAPITests.swift +++ /dev/null @@ -1,365 +0,0 @@ -// -// JSONAPIEntityOpenAPITests.swift -// JSONAPIOpenAPITests -// -// Created by Mathew Polzin on 1/15/19. -// - -import XCTest -import JSONAPI -import JSONAPIOpenAPI -import AnyCodable -import Sampleable - -class JSONAPIEntityOpenAPITests: XCTestCase { - func test_EmptyEntity() { - let node = try! TestType1.openAPINode(using: JSONEncoder()) - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .object(.generic)) - - guard case let .object(contextA, objectContext1) = node else { - XCTFail("Expected Object node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: true, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(objectContext1.minProperties, 2) - XCTAssertEqual(Set(objectContext1.requiredProperties), Set(["id", "type"])) - XCTAssertEqual(Set(objectContext1.properties.keys), Set(["id", "type"])) - XCTAssertEqual(objectContext1.properties["id"], .string(.init(format: .generic, - required: true), - .init())) - XCTAssertEqual(objectContext1.properties["type"], .string(.init(format: .generic, - required: true, - allowedValues: [.init(TestType1.jsonType)]), - .init())) - } - - func test_UnidentifiedEmptyEntity() { - let node = try! UnidentifiedTestType1.openAPINode(using: JSONEncoder()) - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .object(.generic)) - - guard case let .object(contextA, objectContext1) = node else { - XCTFail("Expected Object node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: true, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(objectContext1.minProperties, 1) - XCTAssertEqual(Set(objectContext1.requiredProperties), Set(["type"])) - XCTAssertEqual(Set(objectContext1.properties.keys), Set(["type"])) - XCTAssertEqual(objectContext1.properties["type"], .string(.init(format: .generic, - required: true, - allowedValues: [.init(TestType1.jsonType)]), - .init())) - } - - func test_AttributesEntity() { - - let dateFormatter = DateFormatter() - dateFormatter.dateStyle = .medium - dateFormatter.timeStyle = .short - dateFormatter.locale = Locale(identifier: "en_US") - - let encoder = JSONEncoder() - encoder.dateEncodingStrategy = .formatted(dateFormatter) - - let node = try! TestType2.openAPINode(using: encoder) - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .object(.generic)) - - guard case let .object(contextA, objectContext1) = node else { - XCTFail("Expected Object node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: true, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(objectContext1.minProperties, 3) - XCTAssertEqual(Set(objectContext1.requiredProperties), Set(["id", "type", "attributes"])) - XCTAssertEqual(Set(objectContext1.properties.keys), Set(["id", "type", "attributes"])) - - XCTAssertEqual(objectContext1.properties["id"], .string(.init(format: .generic, - required: true), - .init())) - XCTAssertEqual(objectContext1.properties["type"], .string(.init(format: .generic, - required: true, - allowedValues: [.init(TestType2.jsonType)]), - .init())) - - let attributesNode = objectContext1.properties["attributes"] - - XCTAssertNotNil(attributesNode) - XCTAssertTrue(attributesNode?.required ?? false) - XCTAssertEqual(attributesNode?.jsonTypeFormat, .object(.generic)) - - guard case let .object(contextB, attributesContext)? = attributesNode else { - XCTFail("Expected Object node for attributes") - return - } - - XCTAssertEqual(contextB, .init(format: .generic, - required: true, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(attributesContext.minProperties, 4) - XCTAssertEqual(Set(attributesContext.requiredProperties), Set(["stringProperty", "enumProperty", "dateProperty", "nullableProperty"])) - XCTAssertEqual(Set(attributesContext.properties.keys), Set(["stringProperty", "enumProperty", "dateProperty", "optionalProperty", "nullableProperty", "nullableOptionalProperty"])) - - XCTAssertEqual(attributesContext.properties["stringProperty"], - .string(.init(format: .generic, - required: true), - .init())) - - XCTAssertEqual(attributesContext.properties["enumProperty"], - .string(.init(format: .generic, - required: true, - nullable: false, - allowedValues: ["one", "two"].map(AnyCodable.init)), - .init())) - - XCTAssertEqual(attributesContext.properties["dateProperty"], - .string(.init(format: .dateTime, - required: true, - nullable: false, - allowedValues: nil), - .init())) - - XCTAssertEqual(attributesContext.properties["optionalProperty"], - .string(.init(format: .generic, - required: false, - nullable: false, - allowedValues: nil), - .init())) - - XCTAssertEqual(attributesContext.properties["nullableProperty"], - .string(.init(format: .generic, - required: true, - nullable: true, - allowedValues: nil), - .init())) - - XCTAssertEqual(attributesContext.properties["nullableOptionalProperty"], - .string(.init(format: .generic, - required: false, - nullable: true, - allowedValues: nil), - .init())) - } - - func test_RelationshipsEntity() { - let node = try! TestType3.openAPINode(using: JSONEncoder()) - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .object(.generic)) - - guard case let .object(contextA, objectContext1) = node else { - XCTFail("Expected Object node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: true, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(objectContext1.minProperties, 3) - XCTAssertEqual(Set(objectContext1.requiredProperties), Set(["id", "type", "relationships"])) - XCTAssertEqual(Set(objectContext1.properties.keys), Set(["id", "type", "relationships"])) - - XCTAssertEqual(objectContext1.properties["id"], .string(.init(format: .generic, - required: true), - .init())) - XCTAssertEqual(objectContext1.properties["type"], .string(.init(format: .generic, - required: true, - allowedValues: [.init(TestType3.jsonType)]), - .init())) - - let relationshipsNode = objectContext1.properties["relationships"] - - XCTAssertNotNil(relationshipsNode) - XCTAssertTrue(relationshipsNode?.required ?? false) - XCTAssertEqual(relationshipsNode?.jsonTypeFormat, .object(.generic)) - - guard case let .object(contextB, relationshipsContext)? = relationshipsNode else { - XCTFail("Expected Object node for relationships") - return - } - - XCTAssertEqual(contextB, .init(format: .generic, - required: true, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(relationshipsContext.minProperties, 3) - XCTAssertEqual(Set(relationshipsContext.requiredProperties), Set(["toOne", "nullableToOne", "toMany"])) - XCTAssertEqual(Set(relationshipsContext.properties.keys), Set(["toOne", "optionalTooOne", "nullableToOne", "nullableOptionalToOne", "toMany", "optionalToMany"])) - - let pointerDataContext = JSONNode.ObjectContext(properties: ["id": .string(.init(format: .generic, - required: true), - .init()), - "type": .string(.init(format: .generic, - required: true, - allowedValues: [.init(TestType1.jsonType)]), - .init())]) - - let pointerContext = JSONNode.ObjectContext(properties: ["data": .object(.init(format: .generic, - required: true), - pointerDataContext)]) - - let nullablePointerContext = JSONNode.ObjectContext(properties: ["data": .object(.init(format: .generic, - required: true, - nullable: true), - pointerDataContext)]) - - let manyPointerContext = JSONNode.ObjectContext(properties: ["data": .array(.init(format: .generic, - required: true), - .init(items: .object(.init(format: .generic, - required: true), - pointerDataContext)))]) - - XCTAssertEqual(relationshipsContext.properties["toOne"], - .object(.init(format: .generic, - required: true), - pointerContext)) - - XCTAssertEqual(relationshipsContext.properties["optionalTooOne"], - .object(.init(format: .generic, - required: false, - nullable: false, - allowedValues: nil), - pointerContext)) - - XCTAssertEqual(relationshipsContext.properties["nullableToOne"], - .object(.init(format: .generic, - required: true, - nullable: false, - allowedValues: nil), - nullablePointerContext)) - - XCTAssertEqual(relationshipsContext.properties["nullableOptionalToOne"], - .object(.init(format: .generic, - required: false, - nullable: false, - allowedValues: nil), - nullablePointerContext)) - - XCTAssertEqual(relationshipsContext.properties["toMany"], - .object(.init(format: .generic, - required: true), - manyPointerContext)) - - XCTAssertEqual(relationshipsContext.properties["optionalToMany"], - .object(.init(format: .generic, - required: false, - nullable: false, - allowedValues: nil), - manyPointerContext)) - } - - func test_AttributesAndRelationshipsEntity() { - // TODO: write test - - /* - - let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted - let string = String(data: try! encoder.encode(node), encoding: .utf8)! - print(string) - - */ - } -} - -// MARK: Test Types -extension JSONAPIEntityOpenAPITests { - enum TestType1Description: EntityDescription { - public static var jsonType: String { return "test1" } - - public typealias Attributes = NoAttributes - - public typealias Relationships = NoRelationships - } - - typealias TestType1 = BasicEntity - typealias UnidentifiedTestType1 = JSONAPI.Entity - - enum TestType2Description: EntityDescription { - public static var jsonType: String { return "test2" } - - public enum EnumType: String, CaseIterable, Codable, Equatable { - case one - case two - } - - public struct Attributes: JSONAPI.Attributes, Sampleable { - let stringProperty: Attribute - let enumProperty: Attribute - let dateProperty: Attribute - let optionalProperty: Attribute? - let nullableProperty: Attribute - let nullableOptionalProperty: Attribute? - var computedProperty: Attribute { - return enumProperty - } - - public static var sample: Attributes { - return Attributes(stringProperty: .init(value: "hello"), - enumProperty: .init(value: .one), - dateProperty: .init(value: Date()), - optionalProperty: nil, - nullableProperty: .init(value: nil), - nullableOptionalProperty: nil) - } - } - - public typealias Relationships = NoRelationships - } - - typealias TestType2 = BasicEntity - - enum TestType3Description: EntityDescription { - public static var jsonType: String { return "test3" } - - public typealias Attributes = NoAttributes - - public struct Relationships: JSONAPI.Relationships, Sampleable { - public let toOne: ToOneRelationship - public let optionalTooOne: ToOneRelationship? - public let nullableToOne: ToOneRelationship - public let nullableOptionalToOne: ToOneRelationship? - - public let toMany: ToManyRelationship - public let optionalToMany: ToManyRelationship? - // Note there is no such thing as nullable to-many relationships (Just use - // an empty array) - - public static var sample: Relationships { - return Relationships(toOne: .init(id: .init(rawValue: "1")), - optionalTooOne: nil, - nullableToOne: .init(id: nil), - nullableOptionalToOne: nil, - toMany: .init(ids: [.init(rawValue: "1")]), - optionalToMany: nil) - } - } - } - - typealias TestType3 = BasicEntity -} diff --git a/Tests/JSONAPIOpenAPITests/JSONAPIRelationshipsOpenAPITests.swift b/Tests/JSONAPIOpenAPITests/JSONAPIRelationshipsOpenAPITests.swift deleted file mode 100644 index ca51f8d..0000000 --- a/Tests/JSONAPIOpenAPITests/JSONAPIRelationshipsOpenAPITests.swift +++ /dev/null @@ -1,257 +0,0 @@ -// -// JSONAPIRelationshipsOpenAPITests.swift -// JSONAPI -// -// Created by Mathew Polzin on 1/14/19. -// - -import Foundation -import XCTest -import JSONAPI -import JSONAPITesting -import JSONAPIOpenAPI - -class JSONAPIRelationshipsOpenAPITests: XCTestCase { - - func test_ToOne() { - let node = try! ToOneRelationship.openAPINode() - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .object(.generic)) - - guard case .object(let contextA, let objectContext1) = node else { - XCTFail("Expected object Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: true, - nullable: false, - allowedValues: nil)) - - XCTAssertNil(objectContext1.additionalProperties) - XCTAssertEqual(Array(objectContext1.properties.keys), ["data"]) - - guard case .object(let contextB, let objectContext2)? = objectContext1.properties["data"] else { - XCTFail("Expected object node within properties") - return - } - - XCTAssertEqual(contextB, .init(format: .generic, - required: true, - nullable: false, - allowedValues: nil)) - - XCTAssertNil(objectContext2.additionalProperties) - XCTAssertEqual(Set(objectContext2.properties.keys), Set(["id", "type"])) - } - - func test_OptionalToOne() { - let node = try! ToOneRelationship?.openAPINode() - - XCTAssertFalse(node.required) - XCTAssertEqual(node.jsonTypeFormat, .object(.generic)) - - guard case .object(let contextA, let objectContext1) = node else { - XCTFail("Expected object Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: false, - nullable: false, - allowedValues: nil)) - - XCTAssertNil(objectContext1.additionalProperties) - XCTAssertEqual(Array(objectContext1.properties.keys), ["data"]) - - guard case .object(let contextB, let objectContext2)? = objectContext1.properties["data"] else { - XCTFail("Expected object node within properties") - return - } - - XCTAssertEqual(contextB, .init(format: .generic, - required: true, - nullable: false, - allowedValues: nil)) - - XCTAssertNil(objectContext2.additionalProperties) - XCTAssertEqual(Set(objectContext2.properties.keys), Set(["id", "type"])) - } - - func test_NullableToOne() { - let node = try! ToOneRelationship.openAPINode() - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .object(.generic)) - - guard case .object(let contextA, let objectContext1) = node else { - XCTFail("Expected object Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: true, - nullable: false, - allowedValues: nil)) - - XCTAssertNil(objectContext1.additionalProperties) - XCTAssertEqual(Array(objectContext1.properties.keys), ["data"]) - - guard case .object(let contextB, let objectContext2)? = objectContext1.properties["data"] else { - XCTFail("Expected object node within properties") - return - } - - XCTAssertEqual(contextB, .init(format: .generic, - required: true, - nullable: true, - allowedValues: nil)) - - XCTAssertNil(objectContext2.additionalProperties) - XCTAssertEqual(Set(objectContext2.properties.keys), Set(["id", "type"])) - } - - func test_OptionalNullableToOne() { - let node = try! ToOneRelationship?.openAPINode() - - XCTAssertFalse(node.required) - XCTAssertEqual(node.jsonTypeFormat, .object(.generic)) - - guard case .object(let contextA, let objectContext1) = node else { - XCTFail("Expected object Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: false, - nullable: false, - allowedValues: nil)) - - XCTAssertNil(objectContext1.additionalProperties) - XCTAssertEqual(Array(objectContext1.properties.keys), ["data"]) - - guard case .object(let contextB, let objectContext2)? = objectContext1.properties["data"] else { - XCTFail("Expected object node within properties") - return - } - - XCTAssertEqual(contextB, .init(format: .generic, - required: true, - nullable: true, - allowedValues: nil)) - - XCTAssertNil(objectContext2.additionalProperties) - XCTAssertEqual(Set(objectContext2.properties.keys), Set(["id", "type"])) - } - - func test_ToMany() { - let node = try! ToManyRelationship.openAPINode() - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .object(.generic)) - - guard case .object(let contextA, let objectContext1) = node else { - XCTFail("Expected object Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: true, - nullable: false, - allowedValues: nil)) - - XCTAssertNil(objectContext1.additionalProperties) - XCTAssertEqual(Array(objectContext1.properties.keys), ["data"]) - - guard case .array(let contextB, let arrayContext)? = objectContext1.properties["data"] else { - XCTFail("Expected array node within properties") - return - } - - XCTAssertEqual(contextB, .init(format: .generic, - required: true, - nullable: false, - allowedValues: nil)) - - guard case .object(let contextC, let objectContext2) = arrayContext.items else { - XCTFail("Expected object node within items") - return - } - - XCTAssertEqual(contextC, .init(format: .generic, - required: true, - nullable: false, - allowedValues: nil)) - - XCTAssertNil(objectContext2.additionalProperties) - XCTAssertEqual(Set(objectContext2.properties.keys), Set(["id", "type"])) - } - - func test_OptionalToMany() { - let node = try! ToManyRelationship?.openAPINode() - - XCTAssertFalse(node.required) - XCTAssertEqual(node.jsonTypeFormat, .object(.generic)) - - guard case .object(let contextA, let objectContext1) = node else { - XCTFail("Expected object Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: false, - nullable: false, - allowedValues: nil)) - - XCTAssertNil(objectContext1.additionalProperties) - XCTAssertEqual(Array(objectContext1.properties.keys), ["data"]) - - guard case .array(let contextB, let arrayContext)? = objectContext1.properties["data"] else { - XCTFail("Expected array node within properties") - return - } - - XCTAssertEqual(contextB, .init(format: .generic, - required: true, - nullable: false, - allowedValues: nil)) - - guard case .object(let contextC, let objectContext2) = arrayContext.items else { - XCTFail("Expected object node within items") - return - } - - XCTAssertEqual(contextC, .init(format: .generic, - required: true, - nullable: false, - allowedValues: nil)) - - XCTAssertNil(objectContext2.additionalProperties) - XCTAssertEqual(Set(objectContext2.properties.keys), Set(["id", "type"])) - } -} - -// MARK: Test Types -extension JSONAPIRelationshipsOpenAPITests { - enum TestEntityType1: EntityDescription { - static var jsonType: String { return "test_entities"} - - typealias Attributes = NoAttributes - typealias Relationships = NoRelationships - } - - typealias TestEntity1 = BasicEntity - - enum TestEntityType2: EntityDescription { - static var jsonType: String { return "second_test_entities"} - - typealias Attributes = NoAttributes - - struct Relationships: JSONAPI.Relationships { - let other: ToOneRelationship - } - } - - typealias TestEntity2 = BasicEntity -} diff --git a/Tests/JSONAPIOpenAPITests/JSONAPITransformedAttributeOpenAPITests.swift b/Tests/JSONAPIOpenAPITests/JSONAPITransformedAttributeOpenAPITests.swift deleted file mode 100644 index b92890f..0000000 --- a/Tests/JSONAPIOpenAPITests/JSONAPITransformedAttributeOpenAPITests.swift +++ /dev/null @@ -1,889 +0,0 @@ -// -// JSONAPITransformedAttributeOpenAPITests.swift -// JSONAPIOpenAPITests -// -// Created by Mathew Polzin on 1/28/19. -// - -import XCTest -import JSONAPI -import JSONAPIOpenAPI -import SwiftCheck -import AnyCodable - -class JSONAPITransformedAttributeOpenAPITests: XCTestCase { -} - -private typealias IdentityAttribute = TransformedAttribute> - -// MARK: - Boolean -extension JSONAPITransformedAttributeOpenAPITests { - func test_BooleanAttribute() { - let node = try! IdentityAttribute.openAPINode() - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .boolean(.generic)) - - guard case .boolean(let contextA) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: true, - nullable: false, - allowedValues: nil)) - } - - func test_NullableBooleanAttribute() { - let node = try! IdentityAttribute.openAPINode() - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .boolean(.generic)) - - guard case .boolean(let contextA) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: true, - nullable: true, - allowedValues: nil)) - } - - func test_OptionalBooleanAttribute() { - let node = try! IdentityAttribute?.openAPINode() - - XCTAssertFalse(node.required) - XCTAssertEqual(node.jsonTypeFormat, .boolean(.generic)) - - guard case .boolean(let contextA) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: false, - nullable: false, - allowedValues: nil)) - } - - func test_OptionalNullableBooleanAttribute() { - let node = try! IdentityAttribute?.openAPINode() - - XCTAssertFalse(node.required) - XCTAssertEqual(node.jsonTypeFormat, .boolean(.generic)) - - guard case .boolean(let contextA) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: false, - nullable: true, - allowedValues: nil)) - } -} - -// MARK: - Array of Strings -extension JSONAPITransformedAttributeOpenAPITests { - func test_Arrayttribute() { - let node = try! IdentityAttribute<[String]>.openAPINode() - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .array(.generic)) - - guard case .array(let contextA, let arrayContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: true, - nullable: false, - allowedValues: nil)) - - let stringNode = JSONNode.string(.init(format: .generic, - required: true), - .init()) - - XCTAssertEqual(arrayContext, .init(items: stringNode)) - } - - func test_NullableArrayAttribute() { - let node = try! IdentityAttribute<[String]?>.openAPINode() - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .array(.generic)) - - guard case .array(let contextA, let arrayContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: true, - nullable: true, - allowedValues: nil)) - - let stringNode = JSONNode.string(.init(format: .generic, - required: true), - .init()) - - XCTAssertEqual(arrayContext, .init(items: stringNode)) - } - - func test_OptionalArrayAttribute() { - let node = try! IdentityAttribute<[String]>?.openAPINode() - - XCTAssertFalse(node.required) - XCTAssertEqual(node.jsonTypeFormat, .array(.generic)) - - guard case .array(let contextA, let arrayContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: false, - nullable: false, - allowedValues: nil)) - - let stringNode = JSONNode.string(.init(format: .generic, - required: true), - .init()) - - XCTAssertEqual(arrayContext, .init(items: stringNode)) - } - - func test_OptionalNullableArrayAttribute() { - let node = try! IdentityAttribute<[String]?>?.openAPINode() - - XCTAssertFalse(node.required) - XCTAssertEqual(node.jsonTypeFormat, .array(.generic)) - - guard case .array(let contextA, let arrayContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: false, - nullable: true, - allowedValues: nil)) - - let stringNode = JSONNode.string(.init(format: .generic, - required: true), - .init()) - - XCTAssertEqual(arrayContext, .init(items: stringNode)) - } -} - -// MARK: - Number -extension JSONAPITransformedAttributeOpenAPITests { - func test_NumberAttribute() { - let node = try! IdentityAttribute.openAPINode() - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .number(.double)) - - guard case .number(let contextA, let numberContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .double, - required: true, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(numberContext, .init()) - } - - func test_NullableNumberAttribute() { - let node = try! IdentityAttribute.openAPINode() - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .number(.double)) - - guard case .number(let contextA, let numberContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .double, - required: true, - nullable: true, - allowedValues: nil)) - - XCTAssertEqual(numberContext, .init()) - } - - func test_OptionalNumberAttribute() { - let node = try! IdentityAttribute?.openAPINode() - - XCTAssertFalse(node.required) - XCTAssertEqual(node.jsonTypeFormat, .number(.double)) - - guard case .number(let contextA, let numberContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .double, - required: false, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(numberContext, .init()) - } - - func test_OptionalNullableNumberAttribute() { - let node = try! IdentityAttribute?.openAPINode() - - XCTAssertFalse(node.required) - XCTAssertEqual(node.jsonTypeFormat, .number(.double)) - - guard case .number(let contextA, let numberContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .double, - required: false, - nullable: true, - allowedValues: nil)) - - XCTAssertEqual(numberContext, .init()) - } - - func test_FloatNumberAttribute() { - let node = try! IdentityAttribute.openAPINode() - - XCTAssertEqual(node.jsonTypeFormat, .number(.float)) - } -} - -// MARK: - Integer -extension JSONAPITransformedAttributeOpenAPITests { - func test_IntegerAttribute() { - let node = try! IdentityAttribute.openAPINode() - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .integer(.generic)) - - guard case .integer(let contextA, let intContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: true, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(intContext, .init()) - } - - func test_NullableIntegerAttribute() { - let node = try! IdentityAttribute.openAPINode() - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .integer(.generic)) - - guard case .integer(let contextA, let intContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: true, - nullable: true, - allowedValues: nil)) - - XCTAssertEqual(intContext, .init()) - } - - func test_OptionalIntegerAttribute() { - let node = try! IdentityAttribute?.openAPINode() - - XCTAssertFalse(node.required) - XCTAssertEqual(node.jsonTypeFormat, .integer(.generic)) - - guard case .integer(let contextA, let intContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: false, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(intContext, .init()) - } - - func test_OptionalNullableIntegerAttribute() { - let node = try! IdentityAttribute?.openAPINode() - - XCTAssertFalse(node.required) - XCTAssertEqual(node.jsonTypeFormat, .integer(.generic)) - - guard case .integer(let contextA, let intContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: false, - nullable: true, - allowedValues: nil)) - - XCTAssertEqual(intContext, .init()) - } -} - -// MARK: - String -extension JSONAPITransformedAttributeOpenAPITests { - func test_StringAttribute() { - let node = try! IdentityAttribute.openAPINode() - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .string(.generic)) - - guard case .string(let contextA, let stringContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: true, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(stringContext, .init()) - } - - func test_NullableStringAttribute() { - let node = try! IdentityAttribute.openAPINode() - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .string(.generic)) - - guard case .string(let contextA, let stringContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: true, - nullable: true, - allowedValues: nil)) - - XCTAssertEqual(stringContext, .init()) - } - - func test_OptionalStringAttribute() { - let node = try! IdentityAttribute?.openAPINode() - - XCTAssertFalse(node.required) - XCTAssertEqual(node.jsonTypeFormat, .string(.generic)) - - guard case .string(let contextA, let stringContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: false, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(stringContext, .init()) - } - - func test_OptionalNullableStringAttribute() { - let node = try! IdentityAttribute?.openAPINode() - - XCTAssertFalse(node.required) - XCTAssertEqual(node.jsonTypeFormat, .string(.generic)) - - guard case .string(let contextA, let stringContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: false, - nullable: true, - allowedValues: nil)) - - XCTAssertEqual(stringContext, .init()) - } -} - -// MARK: - Enum -// NOTE: `enum` Attributes only gain the automatic support for allowed values -// (`enum` property in the OpenAPI Spec) at the Entity scope. These attributes -// will all still have `allowedValues: nil` at the attribute scope. -extension JSONAPITransformedAttributeOpenAPITests { - func test_EnumAttribute() { - let node = try! IdentityAttribute.rawOpenAPINode() - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .string(.generic)) - - guard case .string(let contextA, let stringContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: true, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(stringContext, .init()) - } - - func test_NullableEnumAttribute() { - let node = try! IdentityAttribute.wrappedOpenAPINode() - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .string(.generic)) - - guard case .string(let contextA, let stringContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: true, - nullable: true, - allowedValues: nil)) - - XCTAssertEqual(stringContext, .init()) - } - - func test_OptionalEnumAttribute() { - let node = try! IdentityAttribute?.wrappedOpenAPINode() - - XCTAssertFalse(node.required) - XCTAssertEqual(node.jsonTypeFormat, .string(.generic)) - - guard case .string(let contextA, let stringContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: false, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(stringContext, .init()) - } - - func test_OptionalNullableEnumAttribute() { - let node = try! IdentityAttribute?.wrappedOpenAPINode() - - XCTAssertFalse(node.required) - XCTAssertEqual(node.jsonTypeFormat, .string(.generic)) - - guard case .string(let contextA, let stringContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .generic, - required: false, - nullable: true, - allowedValues: nil)) - - XCTAssertEqual(stringContext, .init()) - } -} - -// MARK: - Date -extension JSONAPITransformedAttributeOpenAPITests { - func test_DateStringAttribute() { - // TEST: - // Encoder is set to use - // formatter with date - // with no time. - - let dateFormatter = DateFormatter() - dateFormatter.dateStyle = .medium - dateFormatter.timeStyle = .none - dateFormatter.locale = Locale(identifier: "en_US") - - let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted - encoder.dateEncodingStrategy = .formatted(dateFormatter) - - let node = IdentityAttribute.dateOpenAPINodeGuess(using: encoder) - - XCTAssertNotNil(node) - - XCTAssertTrue(node?.required ?? false) - XCTAssertEqual(node?.jsonTypeFormat, .string(.date)) - - guard case .string(let contextA, let stringContext)? = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .date, - required: true, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(stringContext, .init()) - } - - func test_DateStringAttribute_Sampleable() { - // TEST: - // Encoder is set to use - // formatter with date - // with no time. - - let dateFormatter = DateFormatter() - dateFormatter.dateStyle = .medium - dateFormatter.timeStyle = .none - dateFormatter.locale = Locale(identifier: "en_US") - - let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted - encoder.dateEncodingStrategy = .formatted(dateFormatter) - - let node = try! IdentityAttribute.genericOpenAPINode(using: encoder) - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .string(.date)) - - guard case .string(let contextA, let stringContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .date, - required: true, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(stringContext, .init()) - } - - func test_DateTimeStringAttribute() { - // TEST: - // Encoder is set to use - // formatter with date - // with time. - - let dateFormatter = DateFormatter() - dateFormatter.dateStyle = .medium - dateFormatter.timeStyle = .short - dateFormatter.locale = Locale(identifier: "en_US") - - let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted - encoder.dateEncodingStrategy = .formatted(dateFormatter) - - let node = IdentityAttribute.dateOpenAPINodeGuess(using: encoder) - - XCTAssertNotNil(node) - - XCTAssertTrue(node?.required ?? false) - XCTAssertEqual(node?.jsonTypeFormat, .string(.dateTime)) - - guard case .string(let contextA, let stringContext)? = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .dateTime, - required: true, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(stringContext, .init()) - } - - func test_DateTimeStringAttribute_Sampleable() { - // TEST: - // Encoder is set to use - // formatter with date - // with time. - - let dateFormatter = DateFormatter() - dateFormatter.dateStyle = .medium - dateFormatter.timeStyle = .short - dateFormatter.locale = Locale(identifier: "en_US") - - let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted - encoder.dateEncodingStrategy = .formatted(dateFormatter) - - let node = try! IdentityAttribute.genericOpenAPINode(using: encoder) - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .string(.dateTime)) - - guard case .string(let contextA, let stringContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .dateTime, - required: true, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(stringContext, .init()) - } - - func test_8601DateStringAttribute() { - if #available(OSX 10.12, *) { - // TEST: - // Encoder is set to use - // iso8601 date format - - let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted - encoder.dateEncodingStrategy = .iso8601 - - let node = IdentityAttribute.dateOpenAPINodeGuess(using: encoder) - - XCTAssertNotNil(node) - - XCTAssertTrue(node?.required ?? false) - XCTAssertEqual(node?.jsonTypeFormat, .string(.dateTime)) - - guard case .string(let contextA, let stringContext)? = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .dateTime, - required: true, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(stringContext, .init()) - } - } - - func test_8601DateStringAttribute_Sampleable() { - if #available(OSX 10.12, *) { - // TEST: - // Encoder is set to use - // iso8601 date format - - let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted - encoder.dateEncodingStrategy = .iso8601 - - let node = try! IdentityAttribute.genericOpenAPINode(using: encoder) - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .string(.dateTime)) - - guard case .string(let contextA, let stringContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .dateTime, - required: true, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(stringContext, .init()) - } - } - - func test_DateNumberAttribute() { - // TEST: - // Encoder is set to use - // seconds since 1970 as - // date format - - let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted - encoder.dateEncodingStrategy = .secondsSince1970 - - let node = IdentityAttribute.dateOpenAPINodeGuess(using: encoder) - - XCTAssertNotNil(node) - - XCTAssertTrue(node?.required ?? false) - XCTAssertEqual(node?.jsonTypeFormat, .number(.double)) - - guard case .number(let contextA, let numberContext)? = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .double, - required: true, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(numberContext, .init()) - } - - func test_DateNumberAttribute_Sampleable() { - // TEST: - // Encoder is set to use - // seconds since 1970 as - // date format - - let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted - encoder.dateEncodingStrategy = .secondsSince1970 - - let node = try! IdentityAttribute.genericOpenAPINode(using: encoder) - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .number(.double)) - - guard case .number(let contextA, let numberContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .double, - required: true, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(numberContext, .init()) - } - - func test_DateDeferredAttribute() { - // TEST: - // Encoder is set to use - // Date default encoding - - let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted - encoder.dateEncodingStrategy = .deferredToDate - - let node = IdentityAttribute.dateOpenAPINodeGuess(using: encoder) - - XCTAssertNil(node) - } - - func test_DateDeferredAttribute_Sampleable() { - // TEST: - // Encoder is set to use - // Date default encoding - - let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted - encoder.dateEncodingStrategy = .deferredToDate - - let node = try! IdentityAttribute.genericOpenAPINode(using: encoder) - - XCTAssertTrue(node.required) - XCTAssertEqual(node.jsonTypeFormat, .number(.double)) - - guard case .number(let contextA, let numberContext) = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .double, - required: true, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(numberContext, .init()) - } - - func test_NullableDateAttribute() { - let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted - encoder.dateEncodingStrategy = .secondsSince1970 - - let node = IdentityAttribute.dateOpenAPINodeGuess(using: encoder) - - XCTAssertNotNil(node) - - XCTAssertTrue(node?.required ?? false) - XCTAssertEqual(node?.jsonTypeFormat, .number(.double)) - - guard case .number(let contextA, let numberContext)? = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .double, - required: true, - nullable: true, - allowedValues: nil)) - - XCTAssertEqual(numberContext, .init()) - } - - func test_OptionalDateAttribute() { - let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted - encoder.dateEncodingStrategy = .secondsSince1970 - - let node = IdentityAttribute?.dateOpenAPINodeGuess(using: encoder) - - XCTAssertNotNil(node) - - XCTAssertFalse(node?.required ?? true) - XCTAssertEqual(node?.jsonTypeFormat, .number(.double)) - - guard case .number(let contextA, let numberContext)? = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .double, - required: false, - nullable: false, - allowedValues: nil)) - - XCTAssertEqual(numberContext, .init()) - } - - func test_OptionalNullableDateAttribute() { - let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted - encoder.dateEncodingStrategy = .secondsSince1970 - - let node = IdentityAttribute?.dateOpenAPINodeGuess(using: encoder) - - XCTAssertNotNil(node) - - XCTAssertFalse(node?.required ?? true) - XCTAssertEqual(node?.jsonTypeFormat, .number(.double)) - - guard case .number(let contextA, let numberContext)? = node else { - XCTFail("Expected string Node") - return - } - - XCTAssertEqual(contextA, .init(format: .double, - required: false, - nullable: true, - allowedValues: nil)) - - XCTAssertEqual(numberContext, .init()) - } -} - -// MARK: - Test Types -extension JSONAPITransformedAttributeOpenAPITests { - enum EnumAttribute: String, Codable, CaseIterable { - case one - case two - } -} diff --git a/Tests/JSONAPIOpenAPITests/OpenAPI/OpenAPITests.swift b/Tests/JSONAPIOpenAPITests/OpenAPI/OpenAPITests.swift deleted file mode 100644 index 62faad8..0000000 --- a/Tests/JSONAPIOpenAPITests/OpenAPI/OpenAPITests.swift +++ /dev/null @@ -1,52 +0,0 @@ -// -// OpenAPITests.swift -// JSONAPIOpenAPITests -// -// Created by Mathew Polzin on 1/25/19. -// - -import XCTest -import JSONAPI -import JSONAPIOpenAPI - -class OpenAPITests: XCTestCase { - - func test_placeholder() { - - let schemaInfo = OpenAPISchema.Info(title: "Cool API", version: "0.1.0") - - let personResponse = OpenAPIResponse(description: "Successfully created a Person", - content: [ - .json: .init(schema: .init(JSONReference.node(.init(type: \.schemas, selector: "person")))) - ]) - - let schemaPaths: [OpenAPISchema.PathComponents: OpenAPIPathItem] = [ - .init(["api","people"]): - .operations( - .init(parameters: [], - post: OpenAPIPathItem.PathProperties.Operation( - summary: "", - operationId: "createPerson", - parameters: [], - responses: [ - .status(code: 200): .init(personResponse) - ] - ) - ) - ) - ] - - let schemaComponents = OpenAPIComponents(schemas: ["person": .reference(.file("person.json"))], - parameters: [:]) - - let openAPISchema = OpenAPISchema(info: schemaInfo, - paths: schemaPaths, - components: schemaComponents) - - let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted - - print(String(data: try! encoder.encode(openAPISchema), encoding: .utf8)!) - } - -} diff --git a/Tests/JSONAPIOpenAPITests/Test Helpers/EntityTestTypes.swift b/Tests/JSONAPIOpenAPITests/Test Helpers/EntityTestTypes.swift deleted file mode 100644 index 23e6b7d..0000000 --- a/Tests/JSONAPIOpenAPITests/Test Helpers/EntityTestTypes.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// EntityTestTypes.swift -// JSONAPITests -// -// Created by Mathew Polzin on 11/15/18. -// - -import JSONAPI - -public typealias Entity = JSONAPI.Entity - -public typealias BasicEntity = Entity - -public typealias NewEntity = JSONAPI.Entity diff --git a/Tests/JSONAPIOpenAPITests/Test Helpers/String+CreatableRawIdType.swift b/Tests/JSONAPIOpenAPITests/Test Helpers/String+CreatableRawIdType.swift deleted file mode 100644 index dd3c8f7..0000000 --- a/Tests/JSONAPIOpenAPITests/Test Helpers/String+CreatableRawIdType.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// String+CreatableRawIdType.swift -// JSONAPITests -// -// Created by Mathew Polzin on 11/12/18. -// - -import JSONAPI - -private var uniqueStringCounter = 0 - -extension String: CreatableRawIdType { - public static func unique() -> String { - uniqueStringCounter += 1 - return String(uniqueStringCounter) - } -} diff --git a/Tests/JSONAPIOpenAPITests/XCTestManifests.swift b/Tests/JSONAPIOpenAPITests/XCTestManifests.swift deleted file mode 100644 index b0e381a..0000000 --- a/Tests/JSONAPIOpenAPITests/XCTestManifests.swift +++ /dev/null @@ -1,136 +0,0 @@ -import XCTest - -extension JSONAPIAttributeOpenAPITests { - static let __allTests = [ - ("test_8601DateStringAttribute", test_8601DateStringAttribute), - ("test_8601DateStringAttribute_Sampleable", test_8601DateStringAttribute_Sampleable), - ("test_Arrayttribute", test_Arrayttribute), - ("test_BooleanAttribute", test_BooleanAttribute), - ("test_DateDeferredAttribute", test_DateDeferredAttribute), - ("test_DateDeferredAttribute_Sampleable", test_DateDeferredAttribute_Sampleable), - ("test_DateNumberAttribute", test_DateNumberAttribute), - ("test_DateNumberAttribute_Sampleable", test_DateNumberAttribute_Sampleable), - ("test_DateStringAttribute", test_DateStringAttribute), - ("test_DateStringAttribute_Sampleable", test_DateStringAttribute_Sampleable), - ("test_DateTimeStringAttribute", test_DateTimeStringAttribute), - ("test_DateTimeStringAttribute_Sampleable", test_DateTimeStringAttribute_Sampleable), - ("test_EnumAttribute", test_EnumAttribute), - ("test_FloatNumberAttribute", test_FloatNumberAttribute), - ("test_IntegerAttribute", test_IntegerAttribute), - ("test_NullableArrayAttribute", test_NullableArrayAttribute), - ("test_NullableBooleanAttribute", test_NullableBooleanAttribute), - ("test_NullableDateAttribute", test_NullableDateAttribute), - ("test_NullableEnumAttribute", test_NullableEnumAttribute), - ("test_NullableIntegerAttribute", test_NullableIntegerAttribute), - ("test_NullableNumberAttribute", test_NullableNumberAttribute), - ("test_NullableStringAttribute", test_NullableStringAttribute), - ("test_NumberAttribute", test_NumberAttribute), - ("test_OptionalArrayAttribute", test_OptionalArrayAttribute), - ("test_OptionalBooleanAttribute", test_OptionalBooleanAttribute), - ("test_OptionalDateAttribute", test_OptionalDateAttribute), - ("test_OptionalEnumAttribute", test_OptionalEnumAttribute), - ("test_OptionalIntegerAttribute", test_OptionalIntegerAttribute), - ("test_OptionalNullableArrayAttribute", test_OptionalNullableArrayAttribute), - ("test_OptionalNullableBooleanAttribute", test_OptionalNullableBooleanAttribute), - ("test_OptionalNullableDateAttribute", test_OptionalNullableDateAttribute), - ("test_OptionalNullableEnumAttribute", test_OptionalNullableEnumAttribute), - ("test_OptionalNullableIntegerAttribute", test_OptionalNullableIntegerAttribute), - ("test_OptionalNullableNumberAttribute", test_OptionalNullableNumberAttribute), - ("test_OptionalNullableStringAttribute", test_OptionalNullableStringAttribute), - ("test_OptionalNumberAttribute", test_OptionalNumberAttribute), - ("test_OptionalStringAttribute", test_OptionalStringAttribute), - ("test_StringAttribute", test_StringAttribute), - ] -} - -extension JSONAPIDocumentOpenAPITests { - static let __allTests = [ - ("test_DocumentWithOneIncludeType", test_DocumentWithOneIncludeType), - ("test_DocumentWithTwoIncludeTypes", test_DocumentWithTwoIncludeTypes), - ("test_ManyResourceDocument", test_ManyResourceDocument), - ("test_SingleResourceDocument", test_SingleResourceDocument), - ] -} - -extension JSONAPIEntityOpenAPITests { - static let __allTests = [ - ("test_AttributesAndRelationshipsEntity", test_AttributesAndRelationshipsEntity), - ("test_AttributesEntity", test_AttributesEntity), - ("test_EmptyEntity", test_EmptyEntity), - ("test_RelationshipsEntity", test_RelationshipsEntity), - ("test_UnidentifiedEmptyEntity", test_UnidentifiedEmptyEntity), - ] -} - -extension JSONAPIRelationshipsOpenAPITests { - static let __allTests = [ - ("test_NullableToOne", test_NullableToOne), - ("test_OptionalNullableToOne", test_OptionalNullableToOne), - ("test_OptionalToMany", test_OptionalToMany), - ("test_OptionalToOne", test_OptionalToOne), - ("test_ToMany", test_ToMany), - ("test_ToOne", test_ToOne), - ] -} - -extension JSONAPITransformedAttributeOpenAPITests { - static let __allTests = [ - ("test_8601DateStringAttribute", test_8601DateStringAttribute), - ("test_8601DateStringAttribute_Sampleable", test_8601DateStringAttribute_Sampleable), - ("test_Arrayttribute", test_Arrayttribute), - ("test_BooleanAttribute", test_BooleanAttribute), - ("test_DateDeferredAttribute", test_DateDeferredAttribute), - ("test_DateDeferredAttribute_Sampleable", test_DateDeferredAttribute_Sampleable), - ("test_DateNumberAttribute", test_DateNumberAttribute), - ("test_DateNumberAttribute_Sampleable", test_DateNumberAttribute_Sampleable), - ("test_DateStringAttribute", test_DateStringAttribute), - ("test_DateStringAttribute_Sampleable", test_DateStringAttribute_Sampleable), - ("test_DateTimeStringAttribute", test_DateTimeStringAttribute), - ("test_DateTimeStringAttribute_Sampleable", test_DateTimeStringAttribute_Sampleable), - ("test_EnumAttribute", test_EnumAttribute), - ("test_FloatNumberAttribute", test_FloatNumberAttribute), - ("test_IntegerAttribute", test_IntegerAttribute), - ("test_NullableArrayAttribute", test_NullableArrayAttribute), - ("test_NullableBooleanAttribute", test_NullableBooleanAttribute), - ("test_NullableDateAttribute", test_NullableDateAttribute), - ("test_NullableEnumAttribute", test_NullableEnumAttribute), - ("test_NullableIntegerAttribute", test_NullableIntegerAttribute), - ("test_NullableNumberAttribute", test_NullableNumberAttribute), - ("test_NullableStringAttribute", test_NullableStringAttribute), - ("test_NumberAttribute", test_NumberAttribute), - ("test_OptionalArrayAttribute", test_OptionalArrayAttribute), - ("test_OptionalBooleanAttribute", test_OptionalBooleanAttribute), - ("test_OptionalDateAttribute", test_OptionalDateAttribute), - ("test_OptionalEnumAttribute", test_OptionalEnumAttribute), - ("test_OptionalIntegerAttribute", test_OptionalIntegerAttribute), - ("test_OptionalNullableArrayAttribute", test_OptionalNullableArrayAttribute), - ("test_OptionalNullableBooleanAttribute", test_OptionalNullableBooleanAttribute), - ("test_OptionalNullableDateAttribute", test_OptionalNullableDateAttribute), - ("test_OptionalNullableEnumAttribute", test_OptionalNullableEnumAttribute), - ("test_OptionalNullableIntegerAttribute", test_OptionalNullableIntegerAttribute), - ("test_OptionalNullableNumberAttribute", test_OptionalNullableNumberAttribute), - ("test_OptionalNullableStringAttribute", test_OptionalNullableStringAttribute), - ("test_OptionalNumberAttribute", test_OptionalNumberAttribute), - ("test_OptionalStringAttribute", test_OptionalStringAttribute), - ("test_StringAttribute", test_StringAttribute), - ] -} - -extension OpenAPITests { - static let __allTests = [ - ("test_placeholder", test_placeholder), - ] -} - -#if !os(macOS) -public func __allTests() -> [XCTestCaseEntry] { - return [ - testCase(JSONAPIAttributeOpenAPITests.__allTests), - testCase(JSONAPIDocumentOpenAPITests.__allTests), - testCase(JSONAPIEntityOpenAPITests.__allTests), - testCase(JSONAPIRelationshipsOpenAPITests.__allTests), - testCase(JSONAPITransformedAttributeOpenAPITests.__allTests), - testCase(OpenAPITests.__allTests), - ] -} -#endif From 247848e788333b39ad25ef6bac166a38da5dbf65 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Wed, 30 Jan 2019 18:00:27 -0800 Subject: [PATCH 2/7] Update README --- Package.resolved | 36 ------------------------------------ README.md | 8 ++------ 2 files changed, 2 insertions(+), 42 deletions(-) diff --git a/Package.resolved b/Package.resolved index c0de976..920f7b5 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,24 +1,6 @@ { "object": { "pins": [ - { - "package": "AnyCodable", - "repositoryURL": "https://github.com/Flight-School/AnyCodable.git", - "state": { - "branch": null, - "revision": "396ccc3dba5bdee04c1e742e7fab40582861401e", - "version": "0.1.0" - } - }, - { - "package": "FileCheck", - "repositoryURL": "https://github.com/llvm-swift/FileCheck.git", - "state": { - "branch": null, - "revision": "89b8480055f9adf8ce2f9ad5e2fac7ac1076242e", - "version": "0.0.8" - } - }, { "package": "Poly", "repositoryURL": "https://github.com/mattpolzin/Poly.git", @@ -27,24 +9,6 @@ "revision": "77f45b8963a51c02d71fc4075eba5cff47ff0d07", "version": "1.0.0" } - }, - { - "package": "Sampleable", - "repositoryURL": "https://github.com/mattpolzin/Sampleable.git", - "state": { - "branch": null, - "revision": "6572998ac30685d8bb73e6b725596d07d891a75a", - "version": "1.0.0" - } - }, - { - "package": "SwiftCheck", - "repositoryURL": "https://github.com/typelift/SwiftCheck.git", - "state": { - "branch": null, - "revision": "cf9958085b2ee1643e541e407c3233d1b76c18ff", - "version": "0.11.0" - } } ] }, diff --git a/README.md b/README.md index a861608..1906073 100644 --- a/README.md +++ b/README.md @@ -836,11 +836,7 @@ print(response.author) The `JSONAPI` framework is packaged with a test library to help you test your `JSONAPI` integration. The test library is called `JSONAPITesting`. It provides literal expressibility for `Attribute`, `ToOneRelationship`, and `Id` in many situations so that you can easily write test `Entity` values into your unit tests. It also provides a `check()` function for each `Entity` type that can be used to catch problems with your `JSONAPI` structures that are not caught by Swift's type system. You can see the `JSONAPITesting` in action in the Playground included with the `JSONAPI` repository. # JSONAPI+Arbitrary -The `JSONAPIArbitrary` framework adds `Arbitrary` support via `SwiftCheck`. With a little extra work on your part, this framework will allow you to create "arbitrary" (i.e. randomly generated) instances of your JSONAPI entities, includes, documents, etc. - -This Framework is currently undocumented, but familiarity with `SwiftCheck` will likely afford the user enough information to use this library to aid in the generation of arbitrary JSONAPI Documents for testing purposes. +This library has moved into its own Package. See https://github.com/mattpolzin/JSONAPI-Arbitrary # JSONAPI+OpenAPI -The `JSONAPIOpenAPI` framework adds the ability to generate OpenAPI compliant JSON documentation of a JSONAPI Document. - -*This library is in its infancy. The documentation will grow as the framework becomes more complete.* +This library has moved into its own Package. See https://github.com/mattpolzin/JSONAPI-OpenAPI From a88bb77bd0d2cf9bc493bc21839c98fd5962f772 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Wed, 30 Jan 2019 18:00:51 -0800 Subject: [PATCH 3/7] update linuxmain --- Tests/LinuxMain.swift | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift index 791d328..18367a8 100644 --- a/Tests/LinuxMain.swift +++ b/Tests/LinuxMain.swift @@ -2,13 +2,9 @@ import XCTest import JSONAPITests import JSONAPITestingTests -import JSONAPIOpenAPITests -import JSONAPIArbitraryTests var tests = [XCTestCaseEntry]() tests += JSONAPITests.__allTests() tests += JSONAPITestingTests.__allTests() -tests += JSONAPIOpenAPITests.__allTests() -tests += JSONAPIArbitraryTests.__allTests() XCTMain(tests) From c8fbc09582a4a2374c5d71b0cff6a2ef0851d998 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Wed, 30 Jan 2019 18:34:52 -0800 Subject: [PATCH 4/7] Remove Playground page that used the OpenAPI support that has now moved into its own repository. --- .../Contents.swift | 46 ------------ .../Sources/OpenAPISupport.swift | 71 ------------------- 2 files changed, 117 deletions(-) delete mode 100644 JSONAPI.playground/Pages/OpenAPI Documentation.xcplaygroundpage/Contents.swift delete mode 100644 JSONAPI.playground/Sources/OpenAPISupport.swift diff --git a/JSONAPI.playground/Pages/OpenAPI Documentation.xcplaygroundpage/Contents.swift b/JSONAPI.playground/Pages/OpenAPI Documentation.xcplaygroundpage/Contents.swift deleted file mode 100644 index 597375b..0000000 --- a/JSONAPI.playground/Pages/OpenAPI Documentation.xcplaygroundpage/Contents.swift +++ /dev/null @@ -1,46 +0,0 @@ -//: [Previous](@previous) - -import Foundation -import JSONAPI -import JSONAPIOpenAPI -import Poly - -// print Entity Schema -let encoder = JSONEncoder() -encoder.outputFormatting = .prettyPrinted - -let personSchemaData = try? encoder.encode(Person.openAPINode(using: encoder)) - -print("Person Schema") -print("====") -print(personSchemaData.map { String(data: $0, encoding: .utf8)! } ?? "Schema Construction Failed") -print("====") - -let dogDocumentSchemaData = try? encoder.encode(SingleDogDocument.openAPINodeWithExample(using: encoder)) - -print("Dog Document Schema") -print("====") -print(dogDocumentSchemaData.map { String(data: $0, encoding: .utf8)! } ?? "Schema Construction Failed") -print("====") - -let batchPersonSchemaData = try? encoder.encode(BatchPeopleDocument.openAPINodeWithExample(using: encoder)) - -print("Batch Person Document Schema") -print("====") -print(batchPersonSchemaData.map { String(data: $0, encoding: .utf8)! } ?? "Schema Construction Failed") -print("====") - -let tmp: [String: JSONNode] = [ - "BatchPerson": try! BatchPeopleDocument.openAPINodeWithExample(using: encoder) -] - -let components = OpenAPIComponents(schemas: tmp, parameters: [:]) - -let batchPeopleRef = JSONReference.node(.init(type: \OpenAPIComponents.schemas, selector: "BatchPerson")) - -let tmp2 = JSONNode.reference(batchPeopleRef) - -print("====") -print("====") -//print(String(data: try! encoder.encode(components), encoding: .utf8)!) -print(String(data: try! encoder.encode(tmp2), encoding: .utf8)!) diff --git a/JSONAPI.playground/Sources/OpenAPISupport.swift b/JSONAPI.playground/Sources/OpenAPISupport.swift deleted file mode 100644 index ff2471a..0000000 --- a/JSONAPI.playground/Sources/OpenAPISupport.swift +++ /dev/null @@ -1,71 +0,0 @@ -import Foundation -import JSONAPI -import JSONAPITesting // for the convenience of literal initialization -import JSONAPIOpenAPI -import SwiftCheck -import JSONAPIArbitrary - -extension PersonDescription.Attributes: Arbitrary, Sampleable { - public static var arbitrary: Gen { - return Gen.compose { c in - return PersonDescription.Attributes(name: c.generate(), - favoriteColor: c.generate()) - } - } - - public static var sample: PersonDescription.Attributes { - return .init(name: ["Abbie", "Eibba"], favoriteColor: "Blue") - } -} - -extension PersonDescription.Relationships: Arbitrary, Sampleable { - public static var arbitrary: Gen { - return Gen.compose { c in - return PersonDescription.Relationships(friends: c.generate(), - dogs: c.generate(), - home: c.generate()) - } - } - - public static var sample: PersonDescription.Relationships { - return .init(friends: ["1", "2"], dogs: ["2"], home: "1") - } -} - -extension DogDescription.Attributes: Arbitrary, Sampleable { - public static var arbitrary: Gen { - return Gen.compose { c in - return DogDescription.Attributes(name: c.generate()) - } - } - - public static var sample: DogDescription.Attributes { - return DogDescription.Attributes.arbitrary.generate - } -} - -extension DogDescription.Relationships: Arbitrary, Sampleable { - public static var arbitrary: Gen { - return Gen.compose { c in - return DogDescription.Relationships(owner: c.generate()) - } - } - - public static var sample: DogDescription.Relationships { - return DogDescription.Relationships.arbitrary.generate - } -} - -extension Document: Sampleable where PrimaryResourceBody: Arbitrary, IncludeType: Arbitrary, MetaType: Arbitrary, LinksType: Arbitrary, Error: Arbitrary, APIDescription: Arbitrary { - public static var sample: Document { - return Document.arbitrary.generate - } - - public static var successSample: Document? { - return Document.arbitraryData.generate - } - - public static var failureSample: Document? { - return Document.arbitraryErrors.generate - } -} From f7a81df4511b2e10927af4b393fd26a98f46f60b Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Thu, 7 Feb 2019 18:43:57 -0800 Subject: [PATCH 5/7] Fix inaccuracy and add a bit of clarity to README section on Entity Ids. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1906073..0a6073c 100644 --- a/README.md +++ b/README.md @@ -266,9 +266,9 @@ The third generic specialization on `Entity` is `Links`. This is described in it #### `IdType` - The second is the raw type of `Id` to use for the `Entity`. The actual `Id` of the `Entity` will not be a `RawIdType`, though. The `Id` will package a value of `RawIdType` with a specialized reference back to the `Entity` type it identifies. This just looks like `Id>`. + The last generic specialization on `Entity` is the raw type of `Id` to use for the `Entity`. The actual `Id` of the `Entity` will not be a `RawIdType`, though. The `Id` will package a value of `RawIdType` with a specialized reference back to the `Entity` type it identifies. This just looks like `Id>`. -Having the `Entity` type associated with the `Id` makes it easy to store all of your entities in a hash broken out by `Entity` type; You can pass `Ids` around and always know where to look for the `Entity` to which the `Id` refers. +Having the `Entity` type associated with the `Id` makes it easy to store all of your entities in a hash broken out by `Entity` type; You can pass `Ids` around and always know where to look for the `Entity` to which the `Id` refers. This encapsulation provides some type safety because the Ids of two `Entities` with the "raw ID" of `"1"` but different types will not compare as equal. A `RawIdType` is the underlying type that uniquely identifies an `Entity`. This is often a `String` or a `UUID`. From 5257fd79fab0e13ba60a064d8aca79ce57aad036 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Thu, 7 Feb 2019 18:49:58 -0800 Subject: [PATCH 6/7] Remove redundant public scoping on functions within public extensions. This becomes a warning with the Swift 5 compiler. --- JSONAPI.playground/Sources/Entities.swift | 6 ++-- .../JSONAPI/Resource/Attribute+Functor.swift | 4 +-- Sources/JSONAPI/Resource/Entity.swift | 32 +++++++++---------- Sources/JSONAPITesting/EntityCheck.swift | 2 +- Tests/JSONAPITests/Poly/PolyProxyTests.swift | 4 +-- 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/JSONAPI.playground/Sources/Entities.swift b/JSONAPI.playground/Sources/Entities.swift index c340862..58345c9 100644 --- a/JSONAPI.playground/Sources/Entities.swift +++ b/JSONAPI.playground/Sources/Entities.swift @@ -63,7 +63,7 @@ public enum PersonDescription: EntityDescription { public typealias Person = ExampleEntity public extension Entity where Description == PersonDescription, MetaType == NoMetadata, LinksType == NoLinks, EntityRawIdType == String { - public init(id: Person.Id? = nil,name: [String], favoriteColor: String, friends: [Person], dogs: [Dog], home: House) throws { + init(id: Person.Id? = nil,name: [String], favoriteColor: String, friends: [Person], dogs: [Dog], home: House) throws { self = Person(id: id ?? Person.Id(), attributes: .init(name: .init(value: name), favoriteColor: .init(value: favoriteColor)), relationships: .init(friends: .init(entities: friends), dogs: .init(entities: dogs), home: .init(entity: home)), meta: .none, links: .none) } } @@ -120,11 +120,11 @@ public enum AlternativeDogDescription: EntityDescription { public typealias AlternativeDog = ExampleEntity public extension Entity where Description == DogDescription, MetaType == NoMetadata, LinksType == NoLinks, EntityRawIdType == String { - public init(name: String, owner: Person?) throws { + init(name: String, owner: Person?) throws { self = Dog(attributes: .init(name: .init(value: name)), relationships: DogDescription.Relationships(owner: .init(entity: owner)), meta: .none, links: .none) } - public init(name: String, owner: Person.Id) throws { + init(name: String, owner: Person.Id) throws { self = Dog(attributes: .init(name: .init(value: name)), relationships: .init(owner: .init(id: owner)), meta: .none, links: .none) } } diff --git a/Sources/JSONAPI/Resource/Attribute+Functor.swift b/Sources/JSONAPI/Resource/Attribute+Functor.swift index e914e2e..1da6ff0 100644 --- a/Sources/JSONAPI/Resource/Attribute+Functor.swift +++ b/Sources/JSONAPI/Resource/Attribute+Functor.swift @@ -15,7 +15,7 @@ public extension TransformedAttribute { /// Generally, this is the most useful operation. The transformer gives you /// control over the decoding of the Attribute, but once the Attribute exists, /// mapping on it is most useful for creating computed Attribute properties. - public func map(_ transform: (Transformer.To) throws -> T) rethrows -> Attribute { + func map(_ transform: (Transformer.To) throws -> T) rethrows -> Attribute { return Attribute(value: try transform(value)) } } @@ -30,7 +30,7 @@ public extension Attribute { /// Generally, this is the most useful operation. The transformer gives you /// control over the decoding of the Attribute, but once the Attribute exists, /// mapping on it is most useful for creating computed Attribute properties. - public func map(_ transform: (ValueType) throws -> T) rethrows -> Attribute { + func map(_ transform: (ValueType) throws -> T) rethrows -> Attribute { return Attribute(value: try transform(value)) } } diff --git a/Sources/JSONAPI/Resource/Entity.swift b/Sources/JSONAPI/Resource/Entity.swift index 49f332d..f596612 100644 --- a/Sources/JSONAPI/Resource/Entity.swift +++ b/Sources/JSONAPI/Resource/Entity.swift @@ -397,20 +397,20 @@ public extension Entity where EntityRawIdType: JSONAPI.RawIdType { /// An Entity.Pointer is a `ToOneRelationship` with no metadata or links. /// This is just a convenient way to reference an Entity so that /// other Entities' Relationships can be built up from it. - public typealias Pointer = ToOneRelationship + typealias Pointer = ToOneRelationship /// Entity.Pointers is a `ToManyRelationship` with no metadata or links. /// This is just a convenient way to reference a bunch of Entities so /// that other Entities' Relationships can be built up from them. - public typealias Pointers = ToManyRelationship + typealias Pointers = ToManyRelationship /// Get a pointer to this entity that can be used as a /// relationship to another entity. - public var pointer: Pointer { + var pointer: Pointer { return Pointer(entity: self) } - public func pointer(withMeta meta: MType, links: LType) -> ToOneRelationship { + func pointer(withMeta meta: MType, links: LType) -> ToOneRelationship { return ToOneRelationship(entity: self, meta: meta, links: links) } } @@ -419,19 +419,19 @@ public extension Entity where EntityRawIdType: JSONAPI.RawIdType { public extension Entity where EntityRawIdType == Unidentified { /// Create a new Entity from this one with a newly created /// unique Id of the given type. - public func identified(byType: RawIdType.Type) -> Entity { + func identified(byType: RawIdType.Type) -> Entity { return .init(attributes: attributes, relationships: relationships, meta: meta, links: links) } /// Create a new Entity from this one with the given Id. - public func identified(by id: RawIdType) -> Entity { + func identified(by id: RawIdType) -> Entity { return .init(id: Entity.Identifier(rawValue: id), attributes: attributes, relationships: relationships, meta: meta, links: links) } } public extension Entity where EntityRawIdType: CreatableRawIdType { /// Create a copy of this Entity with a new unique Id. - public func withNewIdentifier() -> Entity { + func withNewIdentifier() -> Entity { return Entity(attributes: attributes, relationships: relationships, meta: meta, links: links) } } @@ -485,14 +485,14 @@ public extension EntityProxy { /// Access to an Id of a `ToOneRelationship`. /// This allows you to write `entity ~> \.other` instead /// of `entity.relationships.other.id`. - public static func ~>(entity: Self, path: KeyPath>) -> OtherEntity.Identifier { + static func ~>(entity: Self, path: KeyPath>) -> OtherEntity.Identifier { return entity.relationships[keyPath: path].id } /// Access to an Id of an optional `ToOneRelationship`. /// This allows you to write `entity ~> \.other` instead /// of `entity.relationships.other?.id`. - public static func ~>(entity: Self, path: KeyPath?>) -> OtherEntity.Identifier { + static func ~>(entity: Self, path: KeyPath?>) -> OtherEntity.Identifier { // Implementation Note: This signature applies to `ToOneRelationship?` // whereas the one below applies to `ToOneRelationship?` return entity.relationships[keyPath: path]?.id @@ -501,7 +501,7 @@ public extension EntityProxy { /// Access to an Id of an optional `ToOneRelationship`. /// This allows you to write `entity ~> \.other` instead /// of `entity.relationships.other?.id`. - public static func ~>(entity: Self, path: KeyPath?>) -> OtherEntity.Identifier? { + static func ~>(entity: Self, path: KeyPath?>) -> OtherEntity.Identifier? { // Implementation Note: This signature applies to `ToOneRelationship?` // whereas the one above applies to `ToOneRelationship?` return entity.relationships[keyPath: path]?.id @@ -510,14 +510,14 @@ public extension EntityProxy { /// Access to all Ids of a `ToManyRelationship`. /// This allows you to write `entity ~> \.others` instead /// of `entity.relationships.others.ids`. - public static func ~>(entity: Self, path: KeyPath>) -> [OtherEntity.Identifier] { + static func ~>(entity: Self, path: KeyPath>) -> [OtherEntity.Identifier] { return entity.relationships[keyPath: path].ids } /// Access to all Ids of an optional `ToManyRelationship`. /// This allows you to write `entity ~> \.others` instead /// of `entity.relationships.others?.ids`. - public static func ~>(entity: Self, path: KeyPath?>) -> [OtherEntity.Identifier]? { + static func ~>(entity: Self, path: KeyPath?>) -> [OtherEntity.Identifier]? { return entity.relationships[keyPath: path]?.ids } } @@ -527,14 +527,14 @@ public extension EntityProxy { /// Access to an Id of a `ToOneRelationship`. /// This allows you to write `entity ~> \.other` instead /// of `entity.relationships.other.id`. - public static func ~>(entity: Self, path: KeyPath Identifier>) -> Identifier { + static func ~>(entity: Self, path: KeyPath Identifier>) -> Identifier { return entity.relationships[keyPath: path](entity) } /// Access to all Ids of a `ToManyRelationship`. /// This allows you to write `entity ~> \.others` instead /// of `entity.relationships.others.ids`. - public static func ~>(entity: Self, path: KeyPath [Identifier]>) -> [Identifier] { + static func ~>(entity: Self, path: KeyPath [Identifier]>) -> [Identifier] { return entity.relationships[keyPath: path](entity) } } @@ -552,7 +552,7 @@ private enum ResourceObjectCodingKeys: String, CodingKey { } public extension Entity { - public func encode(to encoder: Encoder) throws { + func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: ResourceObjectCodingKeys.self) try container.encode(Entity.jsonType, forKey: .type) @@ -578,7 +578,7 @@ public extension Entity { } } - public init(from decoder: Decoder) throws { + init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: ResourceObjectCodingKeys.self) diff --git a/Sources/JSONAPITesting/EntityCheck.swift b/Sources/JSONAPITesting/EntityCheck.swift index 1b7bd1f..abac22b 100644 --- a/Sources/JSONAPITesting/EntityCheck.swift +++ b/Sources/JSONAPITesting/EntityCheck.swift @@ -55,7 +55,7 @@ extension TransformedAttribute: _AttributeType {} extension Attribute: _AttributeType {} public extension Entity { - public static func check(_ entity: Entity) throws { + static func check(_ entity: Entity) throws { var problems = [EntityCheckError]() let attributesMirror = Mirror(reflecting: entity.attributes) diff --git a/Tests/JSONAPITests/Poly/PolyProxyTests.swift b/Tests/JSONAPITests/Poly/PolyProxyTests.swift index 5c6cd87..01dcfb6 100644 --- a/Tests/JSONAPITests/Poly/PolyProxyTests.swift +++ b/Tests/JSONAPITests/Poly/PolyProxyTests.swift @@ -65,7 +65,7 @@ public class PolyProxyTests: XCTestCase { // MARK: - Test types public extension PolyProxyTests { - public enum UserDescription1: EntityDescription { + enum UserDescription1: EntityDescription { public static var jsonType: String { return "users" } public struct Attributes: JSONAPI.Attributes { @@ -76,7 +76,7 @@ public extension PolyProxyTests { public typealias Relationships = NoRelationships } - public enum UserDescription2: EntityDescription { + enum UserDescription2: EntityDescription { public static var jsonType: String { return "users" } public struct Attributes: JSONAPI.Attributes { From abe16dda0d57cf915febce02c07a6f765eb5d42d Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Thu, 7 Feb 2019 18:56:22 -0800 Subject: [PATCH 7/7] A bit more clarification/correction --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 0a6073c..1313936 100644 --- a/README.md +++ b/README.md @@ -258,24 +258,24 @@ An `Entity` needs to be specialized on four generic types. The first is the `Ent #### `Meta` -The second generic specialization on `Entity` is `Meta`. This is described in its own section [below](#jsonapimeta). All `Meta` at any level of a JSON API Document follow the same rules. +The second generic specialization on `Entity` is `Meta`. This is described in its own section [below](#jsonapimeta). All `Meta` at any level of a JSON API Document follow the same rules. You can use `NoMetadata` if you do not need to package any metadata with the `Entity`. #### `Links` -The third generic specialization on `Entity` is `Links`. This is described in its own section [below](#jsonnapilinks). All `Links` at any level of a JSON API Document follow the same rules, although the **SPEC** makes different suggestions as to what types of links might live on which parts of the Document. +The third generic specialization on `Entity` is `Links`. This is described in its own section [below](#jsonnapilinks). All `Links` at any level of a JSON API Document follow the same rules, although the **SPEC** makes different suggestions as to what types of links might live on which parts of the Document. You can use `NoLinks` if you do not need to package any links with the `Entity`. -#### `IdType` +#### `MaybeRawId` - The last generic specialization on `Entity` is the raw type of `Id` to use for the `Entity`. The actual `Id` of the `Entity` will not be a `RawIdType`, though. The `Id` will package a value of `RawIdType` with a specialized reference back to the `Entity` type it identifies. This just looks like `Id>`. +The last generic specialization on `Entity` is `MaybeRawId`. This is either a `RawIdType` that can be used to uniquely identify `Entities` or it is `Unidentified` which is used to indicate an `Entity` does not have an `Id` (which is useful when a client is requesting that the server create an `Entity` and assign it a new `Id`). + +##### `RawIdType` + +The raw type of `Id` to use for the `Entity`. The actual `Id` of the `Entity` will not be a `RawIdType`, though. The `Id` will package a value of `RawIdType` with a specialized reference back to the `Entity` type it identifies. This just looks like `Id>`. Having the `Entity` type associated with the `Id` makes it easy to store all of your entities in a hash broken out by `Entity` type; You can pass `Ids` around and always know where to look for the `Entity` to which the `Id` refers. This encapsulation provides some type safety because the Ids of two `Entities` with the "raw ID" of `"1"` but different types will not compare as equal. A `RawIdType` is the underlying type that uniquely identifies an `Entity`. This is often a `String` or a `UUID`. -#### `MaybeRawId` - -`MaybeRawId` is either a `RawIdType` that can be used to uniquely identify `Entities` or it is `Unidentified` which is used to indicate an `Entity` does not have an `Id` (which is useful when a client is requesting that the server create an `Entity` and assign it a new `Id`). - #### Convenient `typealiases` Often you can use one `RawIdType` for many if not all of your `Entities`. That means you can save yourself some boilerplate by using `typealias`es like the following: