Compare commits

...

34 Commits

Author SHA1 Message Date
Mathew Polzin abe16dda0d A bit more clarification/correction 2019-02-07 18:56:22 -08:00
Mathew Polzin 5257fd79fa Remove redundant public scoping on functions within public extensions. This becomes a warning with the Swift 5 compiler. 2019-02-07 18:49:58 -08:00
Mathew Polzin f7a81df451 Fix inaccuracy and add a bit of clarity to README section on Entity Ids. 2019-02-07 18:43:57 -08:00
Mathew Polzin c8fbc09582 Remove Playground page that used the OpenAPI support that has now moved into its own repository. 2019-01-30 18:34:52 -08:00
Mathew Polzin a88bb77bd0 update linuxmain 2019-01-30 18:00:51 -08:00
Mathew Polzin 247848e788 Update README 2019-01-30 18:00:27 -08:00
Mathew Polzin 7adbdb4505 Move JSONAPIArbitrary and JSONAPIOpenAPI into their own packages. 2019-01-30 17:54:26 -08:00
Mathew Polzin ea663bb229 Move Sampleable into its own Package. 2019-01-29 00:44:31 -08:00
Mathew Polzin f48c385f99 super minor code doc tweak 2019-01-28 23:51:29 -08:00
Mathew Polzin c96bfadd82 Had not updated linuxmain in a while. pretty sure I'll have problems building on Linux at least with the stuff going on in JSONAPI+OpenAPI. 2019-01-28 23:46:56 -08:00
Mathew Polzin 88be701062 Add OpenAPI support for TransformedAttributes 2019-01-28 23:45:28 -08:00
Mathew Polzin 1e85e1d2a3 fix open API playground. 2019-01-27 22:55:32 -08:00
Mathew Polzin 11a7727ac9 Fix Include OpenAPI support. Fix bug with single include being considered 'OneOf' even though that is an overcomplication of the schema. Add tests for single include type and two include types on document 2019-01-27 22:47:01 -08:00
Mathew Polzin e8bfbc881b Added a couple of test cases for JSONDocuments 2019-01-27 22:24:13 -08:00
Mathew Polzin af757a2fac Add emphasis to README line. 2019-01-27 21:46:22 -08:00
Mathew Polzin 84955872f8 Finish writing Date Attribute test cases and add support for Optional Date OpenAPI Node guesses. 2019-01-27 21:39:18 -08:00
Mathew Polzin cb2800abd4 Add support for RequestBody on an OpenAPI Operation. 2019-01-27 13:57:04 -08:00
Mathew Polzin 85d5fef3c8 Fix bug where some references were correctly encoded as objects and others were just encoded as strings 2019-01-27 13:22:18 -08:00
Mathew Polzin e4ef61fd56 bugfix: OpenAPI path components should begin with a slash. 2019-01-27 12:46:13 -08:00
Mathew Polzin 75ec4f156e Fix a third dictionary-as-array bug 2019-01-26 20:04:11 -08:00
Mathew Polzin b45fc73188 Merge pull request #15 from mattpolzin/feature/OpenAPISchema
Feature/open api schema
2019-01-26 19:52:15 -08:00
Mathew Polzin 3b73dc9989 Fix super shitty bug caused by Apples implementation of Dictionary's conformance to Encodable sometimes encoding the dictionary as an array. 2019-01-26 19:50:20 -08:00
Mathew Polzin b2c81026f4 remove 'id' from Unidentified Entity OpenAPI node. 2019-01-26 18:55:47 -08:00
Mathew Polzin 8d057b4398 Merge pull request #14 from mattpolzin/feature/OpenAPISchema
Just enough OpenAPI Schema stuff to be dangerous
2019-01-25 18:25:19 -08:00
Mathew Polzin c8421cdd58 just throw that json extension on the filename to make everything super real 2019-01-25 18:20:04 -08:00
Mathew Polzin cde10a8491 Finish implementing remaining first wave of encodable conformances for OpenAPI 2019-01-25 18:17:58 -08:00
Mathew Polzin ad05d3908a Add a simple test OpenAPISchema and start to tweak things to get it workable. 2019-01-25 12:49:59 -08:00
Mathew Polzin 23b2b2e04f merge w/ master 2019-01-25 12:01:01 -08:00
Mathew Polzin 2988503d7d Add Sampleable conformance to Unidentified. Rename file slightly. 2019-01-25 11:59:05 -08:00
Mathew Polzin 5ea83b07c1 Hopefully remove some ambiguity. 2019-01-24 19:09:46 -08:00
Mathew Polzin 2b59f54067 Fix bug causing a supplied encoder to be used for generating an example but not for helping determine the correct Date formatting. 2019-01-24 17:47:44 -08:00
Mathew Polzin 58a7c82436 Restructure files a bit. Make Date handling relatively robust compared to my first pass at it. Make the failure to construct a generic open API node type throw an error rather than silently omit the node. 2019-01-24 17:25:34 -08:00
Mathew Polzin d1cf19f9fe Neck deep in stubbing out OpenAPI types 2019-01-22 22:14:36 -08:00
Mathew Polzin 59835fbe11 Add JSON Reference and OpenAPIComponents (initially just with schemas) 2019-01-22 16:08:47 -08:00
37 changed files with 39 additions and 3881 deletions
@@ -1,30 +0,0 @@
//: [Previous](@previous)
import Foundation
import JSONAPI
import JSONAPIOpenAPI
// print Entity Schema
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let personSchemaData = try? encoder.encode(Person.openAPINode())
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("====")
+3 -3
View File
@@ -63,7 +63,7 @@ public enum PersonDescription: EntityDescription {
public typealias Person = ExampleEntity<PersonDescription>
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<AlternativeDogDescription>
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)
}
}
@@ -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<PersonDescription.Attributes> {
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<PersonDescription.Relationships> {
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<DogDescription.Attributes> {
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<DogDescription.Relationships> {
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
}
}
-27
View File
@@ -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,15 +9,6 @@
"revision": "77f45b8963a51c02d71fc4075eba5cff47ff0d07",
"version": "1.0.0"
}
},
{
"package": "SwiftCheck",
"repositoryURL": "https://github.com/typelift/SwiftCheck.git",
"state": {
"branch": null,
"revision": "cf9958085b2ee1643e541e407c3233d1b76c18ff",
"version": "0.11.0"
}
}
]
},
+2 -22
View File
@@ -11,18 +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/Flight-School/AnyCodable.git", from: "0.1.0"),
.package(url: "https://github.com/typelift/SwiftCheck.git", from: "0.11.0")
],
targets: [
.target(
@@ -31,24 +23,12 @@ let package = Package(
.target(
name: "JSONAPITesting",
dependencies: ["JSONAPI"]),
.target(
name: "JSONAPIArbitrary",
dependencies: ["JSONAPI", "SwiftCheck"]),
.target(
name: "JSONAPIOpenAPI",
dependencies: ["JSONAPI", "AnyCodable", "JSONAPIArbitrary"]),
.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]
)
+13 -17
View File
@@ -258,23 +258,23 @@ 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.
#### `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<RawIdType, Entity<EntityDescription, RawIdType>>`.
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.
A `RawIdType` is the underlying type that uniquely identifies an `Entity`. This is often a `String` or a `UUID`.
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`.
#### `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`).
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<RawIdType, Entity<EntityDescription, Meta, Links, RawIdType>>`.
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`.
#### Convenient `typealiases`
@@ -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
@@ -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<T: Codable>(_ transform: (Transformer.To) throws -> T) rethrows -> Attribute<T> {
func map<T: Codable>(_ transform: (Transformer.To) throws -> T) rethrows -> Attribute<T> {
return Attribute<T>(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<T: Codable>(_ transform: (ValueType) throws -> T) rethrows -> Attribute<T> {
func map<T: Codable>(_ transform: (ValueType) throws -> T) rethrows -> Attribute<T> {
return Attribute<T>(value: try transform(value))
}
}
+16 -16
View File
@@ -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<Entity, NoMetadata, NoLinks>
typealias Pointer = ToOneRelationship<Entity, NoMetadata, NoLinks>
/// 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<Entity, NoMetadata, NoLinks>
typealias Pointers = ToManyRelationship<Entity, NoMetadata, NoLinks>
/// 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<MType: JSONAPI.Meta, LType: JSONAPI.Links>(withMeta meta: MType, links: LType) -> ToOneRelationship<Entity, MType, LType> {
func pointer<MType: JSONAPI.Meta, LType: JSONAPI.Links>(withMeta meta: MType, links: LType) -> ToOneRelationship<Entity, MType, LType> {
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<RawIdType: CreatableRawIdType>(byType: RawIdType.Type) -> Entity<Description, MetaType, LinksType, RawIdType> {
func identified<RawIdType: CreatableRawIdType>(byType: RawIdType.Type) -> Entity<Description, MetaType, LinksType, RawIdType> {
return .init(attributes: attributes, relationships: relationships, meta: meta, links: links)
}
/// Create a new Entity from this one with the given Id.
public func identified<RawIdType: JSONAPI.RawIdType>(by id: RawIdType) -> Entity<Description, MetaType, LinksType, RawIdType> {
func identified<RawIdType: JSONAPI.RawIdType>(by id: RawIdType) -> Entity<Description, MetaType, LinksType, RawIdType> {
return .init(id: Entity<Description, MetaType, LinksType, RawIdType>.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 ~><OtherEntity: Identifiable, MType: JSONAPI.Meta, LType: JSONAPI.Links>(entity: Self, path: KeyPath<Description.Relationships, ToOneRelationship<OtherEntity, MType, LType>>) -> OtherEntity.Identifier {
static func ~><OtherEntity: Identifiable, MType: JSONAPI.Meta, LType: JSONAPI.Links>(entity: Self, path: KeyPath<Description.Relationships, ToOneRelationship<OtherEntity, MType, LType>>) -> 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 ~><OtherEntity: OptionalRelatable, MType: JSONAPI.Meta, LType: JSONAPI.Links>(entity: Self, path: KeyPath<Description.Relationships, ToOneRelationship<OtherEntity, MType, LType>?>) -> OtherEntity.Identifier {
static func ~><OtherEntity: OptionalRelatable, MType: JSONAPI.Meta, LType: JSONAPI.Links>(entity: Self, path: KeyPath<Description.Relationships, ToOneRelationship<OtherEntity, MType, LType>?>) -> OtherEntity.Identifier {
// Implementation Note: This signature applies to `ToOneRelationship<E?, _, _>?`
// whereas the one below applies to `ToOneRelationship<E, _, _>?`
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 ~><OtherEntity: Relatable, MType: JSONAPI.Meta, LType: JSONAPI.Links>(entity: Self, path: KeyPath<Description.Relationships, ToOneRelationship<OtherEntity, MType, LType>?>) -> OtherEntity.Identifier? {
static func ~><OtherEntity: Relatable, MType: JSONAPI.Meta, LType: JSONAPI.Links>(entity: Self, path: KeyPath<Description.Relationships, ToOneRelationship<OtherEntity, MType, LType>?>) -> OtherEntity.Identifier? {
// Implementation Note: This signature applies to `ToOneRelationship<E, _, _>?`
// whereas the one above applies to `ToOneRelationship<E?, _, _>?`
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 ~><OtherEntity: Relatable, MType: JSONAPI.Meta, LType: JSONAPI.Links>(entity: Self, path: KeyPath<Description.Relationships, ToManyRelationship<OtherEntity, MType, LType>>) -> [OtherEntity.Identifier] {
static func ~><OtherEntity: Relatable, MType: JSONAPI.Meta, LType: JSONAPI.Links>(entity: Self, path: KeyPath<Description.Relationships, ToManyRelationship<OtherEntity, MType, LType>>) -> [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 ~><OtherEntity: Relatable, MType: JSONAPI.Meta, LType: JSONAPI.Links>(entity: Self, path: KeyPath<Description.Relationships, ToManyRelationship<OtherEntity, MType, LType>?>) -> [OtherEntity.Identifier]? {
static func ~><OtherEntity: Relatable, MType: JSONAPI.Meta, LType: JSONAPI.Links>(entity: Self, path: KeyPath<Description.Relationships, ToManyRelationship<OtherEntity, MType, LType>?>) -> [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 ~><Identifier: IdType>(entity: Self, path: KeyPath<Description.Relationships, (Self) -> Identifier>) -> Identifier {
static func ~><Identifier: IdType>(entity: Self, path: KeyPath<Description.Relationships, (Self) -> 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 ~><Identifier: IdType>(entity: Self, path: KeyPath<Description.Relationships, (Self) -> [Identifier]>) -> [Identifier] {
static func ~><Identifier: IdType>(entity: Self, path: KeyPath<Description.Relationships, (Self) -> [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)
@@ -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<APIDescription<Meta>> {
return Gen.compose { c in
APIDescription(version: c.generate(),
meta: c.generate())
}
}
}
extension NoAPIDescription: Arbitrary {
public static var arbitrary: Gen<NoAPIDescription> {
return Gen.pure(.none)
}
}
@@ -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<Attribute<RawValue>> {
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.
@@ -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<Document<PrimaryResourceBody, MetaType, LinksType, IncludeType, APIDescription, Error>.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<Document<PrimaryResourceBody, MetaType, LinksType, IncludeType, APIDescription, Error>.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<Document<PrimaryResourceBody, MetaType, LinksType, IncludeType, APIDescription, Error>.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<Document<PrimaryResourceBody, MetaType, LinksType, IncludeType, APIDescription, Error>.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<Document<PrimaryResourceBody, MetaType, LinksType, IncludeType, APIDescription, Error>.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<Document<PrimaryResourceBody, MetaType, LinksType, IncludeType, APIDescription, Error>> {
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<Document<PrimaryResourceBody, MetaType, LinksType, IncludeType, APIDescription, Error>> {
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<Document<PrimaryResourceBody, MetaType, LinksType, IncludeType, APIDescription, Error>> {
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<Document<PrimaryResourceBody, MetaType, LinksType, IncludeType, APIDescription, Error>> {
return Gen.compose { c in
Document(apiDescription: c.generate(),
errors: c.generate())
}
}
}
@@ -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<NoMetadata> {
return Gen.pure(.none)
}
}
extension NoLinks: Arbitrary {
public static var arbitrary: Gen<NoLinks> {
return Gen.pure(.none)
}
}
extension NoAttributes: Arbitrary {
public static var arbitrary: Gen<NoAttributes> {
return Gen.pure(.none)
}
}
extension NoRelationships: Arbitrary {
public static var arbitrary: Gen<NoRelationships> {
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<Entity<Description, MetaType, LinksType, EntityRawIdType>> {
return Gen.compose { c in
Entity(id: c.generate(),
attributes: c.generate(),
relationships: c.generate(),
meta: c.generate(),
links: c.generate())
}
}
}
@@ -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<UnknownJSONAPIError> {
return Gen.pure(.unknownError)
}
}
@@ -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<Unidentified> {
return Gen.pure(.init())
}
}
extension Id: Arbitrary where RawType: Arbitrary {
public static var arbitrary: Gen<Id<RawType, IdentifiableType>> {
return RawType.arbitrary.map { Id(rawValue: $0) }
}
}
@@ -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<Includes<I>> {
return I
.arbitrary
.proliferate
.map(Includes.init(values:))
}
}
extension NoIncludes: Arbitrary {
public static var arbitrary: Gen<NoIncludes> {
return Gen.pure(NoIncludes())
}
}
extension Include1: Arbitrary where A: Arbitrary {
public static var arbitrary: Gen<Include1<A>> {
return Gen.one(of: [
A.arbitrary.map(Include1.init)
])
}
}
extension Include2: Arbitrary where A: Arbitrary, B: Arbitrary {
public static var arbitrary: Gen<Include2<A, B>> {
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<Include3<A, B, C>> {
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<Include4<A, B, C, D>> {
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<Include5<A, B, C, D, E>> {
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<Include6<A, B, C, D, E, F>> {
// Note broken up because compiler cannot typecheck entire array
// before it times out
let set1: [Gen<Include6<A, B, C, D, E, F>>] = [
A.arbitrary.map(Include6.init),
B.arbitrary.map(Include6.init),
C.arbitrary.map(Include6.init)
]
let set2: [Gen<Include6<A, B, C, D, E, F>>] = [
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<Include7<A, B, C, D, E, F, G>> {
// Note broken up because compiler cannot typecheck entire array
// before it times out
let set1: [Gen<Include7<A, B, C, D, E, F, G>>] = [
A.arbitrary.map(Include7.init),
B.arbitrary.map(Include7.init),
C.arbitrary.map(Include7.init)
]
let set2: [Gen<Include7<A, B, C, D, E, F, G>>] = [
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<Include8<A, B, C, D, E, F, G, H>> {
// Note broken up because compiler cannot typecheck entire array
// before it times out
let set1: [Gen<Include8<A, B, C, D, E, F, G, H>>] = [
A.arbitrary.map(Include8.init),
B.arbitrary.map(Include8.init),
C.arbitrary.map(Include8.init)
]
let set2: [Gen<Include8<A, B, C, D, E, F, G, H>>] = [
D.arbitrary.map(Include8.init),
E.arbitrary.map(Include8.init),
F.arbitrary.map(Include8.init)
]
let set3: [Gen<Include8<A, B, C, D, E, F, G, H>>] = [
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<Include9<A, B, C, D, E, F, G, H, I>> {
// Note broken up because compiler cannot typecheck entire array
// before it times out
let set1: [Gen<Include9<A, B, C, D, E, F, G, H, I>>] = [
A.arbitrary.map(Include9.init),
B.arbitrary.map(Include9.init),
C.arbitrary.map(Include9.init)
]
let set2: [Gen<Include9<A, B, C, D, E, F, G, H, I>>] = [
D.arbitrary.map(Include9.init),
E.arbitrary.map(Include9.init),
F.arbitrary.map(Include9.init)
]
let set3: [Gen<Include9<A, B, C, D, E, F, G, H, I>>] = [
G.arbitrary.map(Include9.init),
H.arbitrary.map(Include9.init),
I.arbitrary.map(Include9.init)
]
return Gen.one(of: set1 + set2 + set3)
}
}
@@ -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<ToOneRelationship<Identifiable, MetaType, LinksType>> {
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<E: EntityType>(givenEntities: [E]) -> Gen<ToOneRelationship<Identifiable, MetaType, LinksType>> 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<ToManyRelationship<Relatable, MetaType, LinksType>> {
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<E: EntityType>(givenEntities: [E]) -> Gen<ToManyRelationship<Relatable, MetaType, LinksType>> 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())
}
}
}
@@ -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<SingleResourceBody<Entity>> {
return Entity.arbitrary.map(SingleResourceBody.init(entity:))
}
}
extension ManyResourceBody: Arbitrary where Entity: Arbitrary {
public static var arbitrary: Gen<ManyResourceBody<Entity>> {
return Entity.arbitrary.proliferate.map(ManyResourceBody.init(entities:))
}
}
extension NoResourceBody: Arbitrary {
public static var arbitrary: Gen<NoResourceBody> {
return Gen.pure(.none)
}
}
@@ -1,132 +0,0 @@
//
// JSONAPIInclude+OpenAPI.swift
// JSONAPIOpenAPI
//
// Created by Mathew Polzin on 1/22/19.
//
import JSONAPI
import Foundation
extension Includes: OpenAPINodeType where I: OpenAPINodeType {
public static func openAPINode() throws -> JSONNode {
let includeNode = try I.openAPINode()
return .array(.init(format: .generic,
required: true),
.init(items: includeNode,
uniqueItems: true))
}
}
extension Include0: OpenAPINodeType {
public static func openAPINode() throws -> JSONNode {
throw OpenAPITypeError.invalidNode
}
}
extension Include1: OpenAPINodeType where A: OpenAPINodeType {
public static func openAPINode() throws -> JSONNode {
return try .one(of: [A.openAPINode()])
}
}
extension Include2: OpenAPINodeType where A: OpenAPINodeType, B: OpenAPINodeType {
public static func openAPINode() throws -> JSONNode {
return try .one(of: [
A.openAPINode(),
B.openAPINode()
])
}
}
extension Include3: OpenAPINodeType where A: OpenAPINodeType, B: OpenAPINodeType, C: OpenAPINodeType {
public static func openAPINode() throws -> JSONNode {
return try .one(of: [
A.openAPINode(),
B.openAPINode(),
C.openAPINode()
])
}
}
extension Include4: OpenAPINodeType where A: OpenAPINodeType, B: OpenAPINodeType, C: OpenAPINodeType, D: OpenAPINodeType {
public static func openAPINode() throws -> JSONNode {
return try .one(of: [
A.openAPINode(),
B.openAPINode(),
C.openAPINode(),
D.openAPINode()
])
}
}
extension Include5: OpenAPINodeType where A: OpenAPINodeType, B: OpenAPINodeType, C: OpenAPINodeType, D: OpenAPINodeType, E: OpenAPINodeType {
public static func openAPINode() throws -> JSONNode {
return try .one(of: [
A.openAPINode(),
B.openAPINode(),
C.openAPINode(),
D.openAPINode(),
E.openAPINode()
])
}
}
extension Include6: OpenAPINodeType where A: OpenAPINodeType, B: OpenAPINodeType, C: OpenAPINodeType, D: OpenAPINodeType, E: OpenAPINodeType, F: OpenAPINodeType {
public static func openAPINode() throws -> JSONNode {
return try .one(of: [
A.openAPINode(),
B.openAPINode(),
C.openAPINode(),
D.openAPINode(),
E.openAPINode(),
F.openAPINode()
])
}
}
extension Include7: OpenAPINodeType where A: OpenAPINodeType, B: OpenAPINodeType, C: OpenAPINodeType, D: OpenAPINodeType, E: OpenAPINodeType, F: OpenAPINodeType, G: OpenAPINodeType {
public static func openAPINode() throws -> JSONNode {
return try .one(of: [
A.openAPINode(),
B.openAPINode(),
C.openAPINode(),
D.openAPINode(),
E.openAPINode(),
F.openAPINode(),
G.openAPINode()
])
}
}
extension Include8: OpenAPINodeType where A: OpenAPINodeType, B: OpenAPINodeType, C: OpenAPINodeType, D: OpenAPINodeType, E: OpenAPINodeType, F: OpenAPINodeType, G: OpenAPINodeType, H: OpenAPINodeType {
public static func openAPINode() throws -> JSONNode {
return try .one(of: [
A.openAPINode(),
B.openAPINode(),
C.openAPINode(),
D.openAPINode(),
E.openAPINode(),
F.openAPINode(),
G.openAPINode(),
H.openAPINode()
])
}
}
extension Include9: OpenAPINodeType where A: OpenAPINodeType, B: OpenAPINodeType, C: OpenAPINodeType, D: OpenAPINodeType, E: OpenAPINodeType, F: OpenAPINodeType, G: OpenAPINodeType, H: OpenAPINodeType, I: OpenAPINodeType {
public static func openAPINode() throws -> JSONNode {
return try .one(of: [
A.openAPINode(),
B.openAPINode(),
C.openAPINode(),
D.openAPINode(),
E.openAPINode(),
F.openAPINode(),
G.openAPINode(),
H.openAPINode(),
I.openAPINode()
])
}
}
@@ -1,219 +0,0 @@
//
// JSONAPIOpenAPITypes.swift
// JSONAPIOpenAPI
//
// Created by Mathew Polzin on 1/13/19.
//
import JSONAPI
import Foundation
import AnyCodable
private protocol _Optional {}
extension Optional: _Optional {}
private protocol Wrapper {
associatedtype Wrapped
}
extension Optional: Wrapper {}
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: 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)
}
}
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()
}
}
// TODO: conform TransformedAttribute to all of the above protocols that Attribute conforms to.
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, OpenAPINodeType 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.string(.init(format: .generic,
required: true),
.init())
let idProperty = ("id", idNode)
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, OpenAPINodeType where Entity: OpenAPIEncodedNodeType {
public static func openAPINode(using encoder: JSONEncoder) throws -> JSONNode {
return try Entity.openAPINode(using: encoder)
}
}
extension ManyResourceBody: OpenAPIEncodedNodeType, OpenAPINodeType 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, OpenAPINodeType where PrimaryResourceBody: OpenAPIEncodedNodeType, IncludeType: OpenAPINodeType {
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<Include>.openAPINode()
} catch let err as OpenAPITypeError {
guard err == .invalidNode 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))
}
}
@@ -1,194 +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)
}
}
}

Some files were not shown because too many files have changed in this diff Show More