2019-01-13 23:24:02 -08:00
|
|
|
//
|
|
|
|
|
// JSONAPIOpenAPITypes.swift
|
|
|
|
|
// JSONAPIOpenAPI
|
|
|
|
|
//
|
|
|
|
|
// Created by Mathew Polzin on 1/13/19.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
import JSONAPI
|
2019-01-20 15:39:54 -08:00
|
|
|
import AnyCodable
|
2019-01-13 23:24:02 -08:00
|
|
|
|
2019-01-14 23:14:25 -08:00
|
|
|
private protocol _Optional {}
|
|
|
|
|
extension Optional: _Optional {}
|
|
|
|
|
|
2019-01-14 21:17:07 -08:00
|
|
|
extension Attribute: OpenAPINodeType where RawValue: OpenAPINodeType {
|
2019-01-19 15:30:09 -08:00
|
|
|
static public func openAPINode() throws -> JSONNode {
|
2019-01-14 23:14:25 -08:00
|
|
|
// If the RawValue is not required, we actually consider it
|
|
|
|
|
// nullable. To be not required is for the Attribute itself
|
|
|
|
|
// to be optional.
|
2019-01-19 15:30:09 -08:00
|
|
|
if try !RawValue.openAPINode().required {
|
|
|
|
|
return try RawValue.openAPINode().requiredNode().nullableNode()
|
2019-01-14 23:14:25 -08:00
|
|
|
}
|
2019-01-19 15:30:09 -08:00
|
|
|
return try RawValue.openAPINode()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
extension Attribute: RawOpenAPINodeType where RawValue: RawRepresentable, RawValue.RawValue: OpenAPINodeType {
|
2019-01-20 18:18:35 -08:00
|
|
|
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.
|
2019-01-19 15:30:09 -08:00
|
|
|
if try !RawValue.RawValue.openAPINode().required {
|
|
|
|
|
return try RawValue.RawValue.openAPINode().requiredNode().nullableNode()
|
|
|
|
|
}
|
|
|
|
|
return try RawValue.RawValue.openAPINode()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-20 18:18:35 -08:00
|
|
|
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()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-20 15:39:54 -08:00
|
|
|
extension Attribute: AnyJSONCaseIterable where RawValue: CaseIterable, RawValue: Codable {
|
|
|
|
|
public static var allCases: [AnyCodable] {
|
|
|
|
|
return (try? allCases(from: Array(RawValue.allCases))) ?? []
|
2019-01-19 15:30:09 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
extension Attribute: AnyWrappedJSONCaseIterable where RawValue: AnyJSONCaseIterable {
|
2019-01-20 15:39:54 -08:00
|
|
|
public static var allCases: [AnyCodable] {
|
2019-01-19 15:30:09 -08:00
|
|
|
return RawValue.allCases
|
2019-01-13 23:24:02 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-14 21:17:07 -08:00
|
|
|
extension TransformedAttribute: OpenAPINodeType where RawValue: OpenAPINodeType {
|
2019-01-19 15:30:09 -08:00
|
|
|
static public func openAPINode() throws -> JSONNode {
|
2019-01-14 23:14:25 -08:00
|
|
|
// If the RawValue is not required, we actually consider it
|
|
|
|
|
// nullable. To be not required is for the Attribute itself
|
|
|
|
|
// to be optional.
|
2019-01-19 15:30:09 -08:00
|
|
|
if try !RawValue.openAPINode().required {
|
|
|
|
|
return try RawValue.openAPINode().requiredNode().nullableNode()
|
2019-01-14 23:14:25 -08:00
|
|
|
}
|
2019-01-19 15:30:09 -08:00
|
|
|
return try RawValue.openAPINode()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
extension RelationshipType {
|
|
|
|
|
static func relationshipNode(nullable: Bool) -> JSONNode {
|
|
|
|
|
let propertiesDict: [String: JSONNode] = [
|
|
|
|
|
"id": .string(.init(format: .generic,
|
|
|
|
|
required: true),
|
|
|
|
|
.init()),
|
|
|
|
|
"type": .string(.init(format: .generic,
|
|
|
|
|
required: true),
|
|
|
|
|
.init())
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
return .object(.init(format: .generic,
|
|
|
|
|
required: true,
|
|
|
|
|
nullable: nullable),
|
|
|
|
|
.init(properties: propertiesDict))
|
2019-01-13 23:24:02 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-14 21:17:07 -08:00
|
|
|
extension ToOneRelationship: OpenAPINodeType {
|
|
|
|
|
// TODO: const for json `type`
|
2019-01-14 23:29:49 -08:00
|
|
|
// TODO: metadata & links
|
2019-01-19 15:30:09 -08:00
|
|
|
static public func openAPINode() throws -> JSONNode {
|
2019-01-14 21:17:07 -08:00
|
|
|
let nullable = Identifiable.self is _Optional.Type
|
|
|
|
|
return .object(.init(format: .generic,
|
|
|
|
|
required: true),
|
|
|
|
|
.init(properties: [
|
2019-01-19 15:30:09 -08:00
|
|
|
"data": ToOneRelationship.relationshipNode(nullable: nullable)
|
2019-01-14 21:17:07 -08:00
|
|
|
]))
|
2019-01-13 23:24:02 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-14 21:17:07 -08:00
|
|
|
extension ToManyRelationship: OpenAPINodeType {
|
|
|
|
|
// TODO: const for json `type`
|
2019-01-14 23:29:49 -08:00
|
|
|
// TODO: metadata & links
|
2019-01-19 15:30:09 -08:00
|
|
|
static public func openAPINode() throws -> JSONNode {
|
2019-01-14 21:17:07 -08:00
|
|
|
return .object(.init(format: .generic,
|
|
|
|
|
required: true),
|
|
|
|
|
.init(properties: [
|
|
|
|
|
"data": .array(.init(format: .generic,
|
|
|
|
|
required: true),
|
2019-01-19 15:30:09 -08:00
|
|
|
.init(items: ToManyRelationship.relationshipNode(nullable: false)))
|
2019-01-14 21:17:07 -08:00
|
|
|
]))
|
2019-01-13 23:24:02 -08:00
|
|
|
}
|
|
|
|
|
}
|
2019-01-19 15:30:09 -08:00
|
|
|
|
|
|
|
|
extension Entity: OpenAPINodeType where Description.Attributes: Sampleable, Description.Relationships: Sampleable {
|
|
|
|
|
public static func openAPINode() throws -> JSONNode {
|
2019-01-20 16:19:26 -08:00
|
|
|
// 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),
|
|
|
|
|
.init())
|
|
|
|
|
let typeProperty = ("type", typeNode)
|
|
|
|
|
|
2019-01-19 15:30:09 -08:00
|
|
|
let attributesNode: JSONNode? = Description.Attributes.self == NoAttributes.self
|
|
|
|
|
? nil
|
|
|
|
|
: try Description.Attributes.genericObjectOpenAPINode()
|
|
|
|
|
|
|
|
|
|
let attributesProperty = attributesNode.map { ("attributes", $0) }
|
|
|
|
|
|
|
|
|
|
let relationshipsNode: JSONNode? = Description.Relationships.self == NoRelationships.self
|
|
|
|
|
? nil
|
|
|
|
|
: try Description.Relationships.genericObjectOpenAPINode()
|
|
|
|
|
|
|
|
|
|
let relationshipsProperty = relationshipsNode.map { ("relationships", $0) }
|
|
|
|
|
|
|
|
|
|
let propertiesDict = Dictionary([
|
2019-01-20 16:19:26 -08:00
|
|
|
idProperty,
|
|
|
|
|
typeProperty,
|
2019-01-19 15:30:09 -08:00
|
|
|
attributesProperty,
|
|
|
|
|
relationshipsProperty
|
|
|
|
|
].compactMap { $0 }) { _, value in value }
|
|
|
|
|
|
|
|
|
|
return .object(.init(format: .generic,
|
|
|
|
|
required: true),
|
|
|
|
|
.init(properties: propertiesDict))
|
|
|
|
|
}
|
|
|
|
|
}
|