Compare commits

..

20 Commits

Author SHA1 Message Date
Mathew Polzin 099d17aa58 Update README.md
Removing check boxes for `Arbitrary` conformance and `OpenAPI` integration. Both of these "features" are maintained in separate libraries.
2019-07-03 07:28:31 -07:00
Mathew Polzin 92d1239a98 Merge pull request #19 from gufo/optional-relationships
Allow omitting `relationships` if all are optional
2019-07-03 07:19:14 -07:00
Janko Luin b7ce3d226e Apply EmptyObjectDecoder rather than empty JSON
Co-Authored-By: Mathew Polzin <matt.polzin@gmail.com>
2019-07-03 08:32:44 +02:00
Janko Luin 5b56d596e2 Merge remote-tracking branch 'upstream/master' into optional-relationships 2019-07-03 08:30:17 +02:00
Mathew Polzin e820f34253 change podspec version in anticipation of next release 2019-07-02 18:12:29 -07:00
Mathew Polzin c75912ab79 switch Poly versioning to 'upToNextMajor' and updated the resolved version. 2019-07-02 18:09:06 -07:00
Mathew Polzin e4481c9e4f noticed the README had some typos I missed when I changed Entity to ResourceObject. 2019-07-02 18:05:22 -07:00
Mathew Polzin e9b9a2bd78 Add Empty Object Decoder to be used in upcoming release. 2019-07-02 17:36:54 -07:00
Janko Luin fc78958f76 Allow omitting relationships if all are optional
When all relationships are optional, the `relationships` key is also
optional and not required in the structure.

I'm not super happy with importing Foundation and creating new objects
any time a key is missing, but ultimately none of my attempts at
conditional generics worked out for me.
2019-07-02 17:02:55 +02:00
Mathew Polzin f73ca8bc2c Merge pull request #18 from mattpolzin/feature/match-jsonapi-naming
Feature/match jsonapi naming
2019-06-12 20:59:50 -07:00
Mathew Polzin 176eca3dce Bump podspec version to 0.30.0 2019-06-12 20:57:14 -07:00
Mathew Polzin 4e0e44c45d Update README after renaming. Rename test lib entity checker. 2019-06-12 20:15:07 -07:00
Mathew Polzin e09e3cd8ac Finish renaming all things 'Entity' to 'ResourceObject' 2019-06-12 19:56:33 -07:00
Mathew Polzin 502f82ad14 Rename EntityProxy to ResourceObjectProxy and EntityType to ResourceObjectType. 2019-06-12 19:11:43 -07:00
Mathew Polzin 2bb238b092 Renamed EntityProxyDescription to ResourceObjectProxyDescription and EntityDescription to ResourceObjectDescription. 2019-06-12 19:07:21 -07:00
Mathew Polzin 491fe1fbea clarifying name change in tests. update documentation around entity direct subscript accessor 2019-04-18 22:54:08 -07:00
Mathew Polzin 6b6f40c968 breaking change: change spelling of Entity subscript accessor that returns values of properties within Attributes without digging into the AttributeType. This change will make it possible for the compiler to unambiguously determine the type of subscript access being made before the value is stored, cast, or compared. 2019-04-18 22:40:12 -07:00
Mathew Polzin 14bd29bf42 Update podspec in expectance of breaking change. 2019-04-18 22:37:58 -07:00
Mathew Polzin cf4d8fc378 Update README.md 2019-04-16 22:39:33 -07:00
Mathew Polzin 10b756f6db Update README.md 2019-04-16 22:37:35 -07:00
32 changed files with 629 additions and 402 deletions
@@ -19,13 +19,13 @@ extension String: CreatableRawIdType {
// We create a typealias given that we do not expect JSON:API Resource
// Objects for this particular API to have Metadata or Links associated
// with them. We also expect them to have String Identifiers.
typealias JSONEntity<Description: EntityDescription> = JSONAPI.Entity<Description, NoMetadata, NoLinks, String>
typealias JSONEntity<Description: ResourceObjectDescription> = JSONAPI.ResourceObject<Description, NoMetadata, NoLinks, String>
// Similarly, we create a typealias for unidentified entities. JSON:API
// only allows unidentified entities (i.e. no "id" field) for client
// requests that create new entities. In these situations, the server
// is expected to assign the new entity a unique ID.
typealias UnidentifiedJSONEntity<Description: EntityDescription> = JSONAPI.Entity<Description, NoMetadata, NoLinks, Unidentified>
typealias UnidentifiedJSONEntity<Description: ResourceObjectDescription> = JSONAPI.ResourceObject<Description, NoMetadata, NoLinks, Unidentified>
// We create typealiases given that we do not expect JSON:API Relationships
// for this particular API to have Metadata or Links associated
@@ -40,7 +40,7 @@ typealias Document<PrimaryResourceBody: JSONAPI.ResourceBody, IncludeType: JSONA
// MARK: Entity Definitions
enum AuthorDescription: EntityDescription {
enum AuthorDescription: ResourceObjectDescription {
public static var jsonType: String { return "authors" }
public struct Attributes: JSONAPI.Attributes {
@@ -52,7 +52,7 @@ enum AuthorDescription: EntityDescription {
typealias Author = JSONEntity<AuthorDescription>
enum ArticleDescription: EntityDescription {
enum ArticleDescription: ResourceObjectDescription {
public static var jsonType: String { return "articles" }
public struct Attributes: JSONAPI.Attributes {
@@ -95,7 +95,7 @@ func articleDocument(includeAuthor: Bool) -> Either<SingleArticleDocument, Singl
links: .none)
let document = SingleArticleDocument(apiDescription: .none,
body: .init(entity: article),
body: .init(resourceObject: article),
includes: .none,
meta: .none,
links: .none)
@@ -64,7 +64,7 @@ struct ToManyRelationshipLinks: JSONAPI.Links {
}
/// Description of an Author entity.
enum AuthorDescription: EntityDescription {
enum AuthorDescription: ResourceObjectDescription {
static var jsonType: String { return "authors" }
struct Attributes: JSONAPI.Attributes {
@@ -76,10 +76,10 @@ enum AuthorDescription: EntityDescription {
}
}
typealias Author = JSONAPI.Entity<AuthorDescription, EntityMetadata, EntityLinks, String>
typealias Author = JSONAPI.ResourceObject<AuthorDescription, EntityMetadata, EntityLinks, String>
/// Description of an Article entity.
enum ArticleDescription: EntityDescription {
enum ArticleDescription: ResourceObjectDescription {
static var jsonType: String { return "articles" }
struct Attributes: JSONAPI.Attributes {
@@ -96,7 +96,7 @@ enum ArticleDescription: EntityDescription {
}
}
typealias Article = JSONAPI.Entity<ArticleDescription, EntityMetadata, EntityLinks, String>
typealias Article = JSONAPI.ResourceObject<ArticleDescription, EntityMetadata, EntityLinks, String>
/// Metadata associated with the API Description
struct APIDescriptionMetadata: JSONAPI.Meta {
@@ -196,7 +196,7 @@ let documentLinks = SingleArticleDocumentLinks(otherArticlesByPrimaryAuthor: .in
meta: .init(expiry: tomorrow)))
let singleArticleDocument = SingleArticleDocument(apiDescription: apiDescription,
body: .init(entity: article),
body: .init(resourceObject: article),
includes: .init(values: [.init(author1), .init(author2), .init(author3)]),
meta: documentMetadata,
links: documentLinks)
@@ -11,7 +11,7 @@ Please enjoy these examples, but allow me the forced casting and the lack of err
// MARK: - Create a request or response body with one Dog in it
let dogFromCode = try! Dog(name: "Buddy", owner: nil)
let singleDogDocument = SingleDogDocument(apiDescription: .none, body: .init(entity: dogFromCode), includes: .none, meta: .none, links: .none)
let singleDogDocument = SingleDogDocument(apiDescription: .none, body: .init(resourceObject: dogFromCode), includes: .none, meta: .none, links: .none)
let singleDogData = try! JSONEncoder().encode(singleDogDocument)
@@ -33,7 +33,7 @@ let houses = [House(attributes: .none, relationships: .none, meta: .none, links:
let people = try! [Person(id: personIds[0], name: ["Gary", "Doe"], favoriteColor: "Orange-Red", friends: [], dogs: [dogs[0], dogs[1]], home: houses[0]), Person(id: personIds[1], name: ["Elise", "Joy"], favoriteColor: "Red", friends: [], dogs: [dogs[2]], home: houses[1])]
let includes = dogs.map { BatchPeopleDocument.Include($0) } + houses.map { BatchPeopleDocument.Include($0) }
let batchPeopleDocument = BatchPeopleDocument(apiDescription: .none, body: .init(entities: people), includes: .init(values: includes), meta: .none, links: .none)
let batchPeopleDocument = BatchPeopleDocument(apiDescription: .none, body: .init(resourceObjects: people), includes: .init(values: includes), meta: .none, links: .none)
let batchPeopleData = try! JSONEncoder().encode(batchPeopleDocument)
// MARK: - Parse a request or response body with multiple people in it and dogs and houses included
+9 -9
View File
@@ -24,12 +24,12 @@ extension String: CreatableRawIdType {
}
// MARK: - typealiases for convenience
public typealias ExampleEntity<Description: EntityDescription> = Entity<Description, NoMetadata, NoLinks, String>
public typealias ExampleEntity<Description: ResourceObjectDescription> = ResourceObject<Description, NoMetadata, NoLinks, String>
public typealias ToOne<E: Identifiable> = ToOneRelationship<E, NoMetadata, NoLinks>
public typealias ToMany<E: Relatable> = ToManyRelationship<E, NoMetadata, NoLinks>
// MARK: - A few resource objects (entities)
public enum PersonDescription: EntityDescription {
public enum PersonDescription: ResourceObjectDescription {
public static var jsonType: String { return "people" }
@@ -62,13 +62,13 @@ public enum PersonDescription: EntityDescription {
public typealias Person = ExampleEntity<PersonDescription>
public extension Entity where Description == PersonDescription, MetaType == NoMetadata, LinksType == NoLinks, EntityRawIdType == String {
public extension ResourceObject where Description == PersonDescription, MetaType == NoMetadata, LinksType == NoLinks, EntityRawIdType == String {
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)
self = Person(id: id ?? Person.Id(), attributes: .init(name: .init(value: name), favoriteColor: .init(value: favoriteColor)), relationships: .init(friends: .init(resourceObjects: friends), dogs: .init(resourceObjects: dogs), home: .init(resourceObject: home)), meta: .none, links: .none)
}
}
public enum DogDescription: EntityDescription {
public enum DogDescription: ResourceObjectDescription {
public static var jsonType: String { return "dogs" }
@@ -91,7 +91,7 @@ public enum DogDescription: EntityDescription {
public typealias Dog = ExampleEntity<DogDescription>
public enum AlternativeDogDescription: EntityDescription {
public enum AlternativeDogDescription: ResourceObjectDescription {
public static var jsonType: String { return "dogs" }
@@ -119,9 +119,9 @@ public enum AlternativeDogDescription: EntityDescription {
public typealias AlternativeDog = ExampleEntity<AlternativeDogDescription>
public extension Entity where Description == DogDescription, MetaType == NoMetadata, LinksType == NoLinks, EntityRawIdType == String {
public extension ResourceObject where Description == DogDescription, MetaType == NoMetadata, LinksType == NoLinks, EntityRawIdType == String {
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)
self = Dog(attributes: .init(name: .init(value: name)), relationships: DogDescription.Relationships(owner: .init(resourceObject: owner)), meta: .none, links: .none)
}
init(name: String, owner: Person.Id) throws {
@@ -129,7 +129,7 @@ public extension Entity where Description == DogDescription, MetaType == NoMetad
}
}
public enum HouseDescription: EntityDescription {
public enum HouseDescription: ResourceObjectDescription {
public static var jsonType: String { return "houses" }
+1 -1
View File
@@ -16,7 +16,7 @@ Pod::Spec.new do |spec|
#
spec.name = "JSONAPI"
spec.version = "0.20.0"
spec.version = "0.31.0"
spec.summary = "Swift Codable JSON API framework."
# This description is used to generate tags and improve search results.
+2 -2
View File
@@ -6,8 +6,8 @@
"repositoryURL": "https://github.com/mattpolzin/Poly.git",
"state": {
"branch": null,
"revision": "d24d4c1214dd05f89eb1182a46592856dd0a0645",
"version": "2.0.0"
"revision": "38051821d7ef49e590e26e819a2fe447e50be9ff",
"version": "2.0.1"
}
}
]
+1 -1
View File
@@ -18,7 +18,7 @@ let package = Package(
targets: ["JSONAPITesting"])
],
dependencies: [
.package(url: "https://github.com/mattpolzin/Poly.git", from: "2.0.0"),
.package(url: "https://github.com/mattpolzin/Poly.git", .upToNextMajor(from: "2.0.0")),
],
targets: [
.target(
+81 -116
View File
File diff suppressed because it is too large Load Diff
+5 -5
View File
@@ -30,20 +30,20 @@ public func +<R: AppendableResourceBody>(_ left: R, right: R) -> R {
public struct SingleResourceBody<Entity: JSONAPI.MaybePrimaryResource>: ResourceBody {
public let value: Entity
public init(entity: Entity) {
self.value = entity
public init(resourceObject: Entity) {
self.value = resourceObject
}
}
public struct ManyResourceBody<Entity: JSONAPI.PrimaryResource>: AppendableResourceBody {
public let values: [Entity]
public init(entities: [Entity]) {
values = entities
public init(resourceObjects: [Entity]) {
values = resourceObjects
}
public func appending(_ other: ManyResourceBody) -> ManyResourceBody {
return ManyResourceBody(entities: values + other.values)
return ManyResourceBody(resourceObjects: values + other.values)
}
}
+215
View File
@@ -0,0 +1,215 @@
//
// EmptyObjectDecoder.swift
// JSONAPI
//
// Created by Mathew Polzin on 7/2/19.
//
/// `EmptyObjectDecoder` exists internally for the sole purpose of
/// allowing certain fallback logic paths to attempt to create `Decodable`
/// types from empty containers (specifically in a way that is agnostic
/// of any given encoding). In other words, this serves the same purpose
/// as `JSONDecoder().decode(Thing.self, from: "{}".data(using: .utf8)!)`
/// without needing to use a third party or `Foundation` library decoder.
struct EmptyObjectDecoder: Decoder {
var codingPath: [CodingKey] = []
var userInfo: [CodingUserInfoKey : Any] = [:]
func container<Key>(keyedBy type: Key.Type) throws -> KeyedDecodingContainer<Key> where Key : CodingKey {
return KeyedDecodingContainer(EmptyKeyedContainer())
}
func unkeyedContainer() throws -> UnkeyedDecodingContainer {
return EmptyUnkeyedContainer()
}
func singleValueContainer() throws -> SingleValueDecodingContainer {
throw EmptyObjectDecodingError.emptyObjectCannotBeSingleValue
}
}
enum EmptyObjectDecodingError: Swift.Error {
case emptyObjectCannotBeSingleValue
case emptyObjectCannotBeUnkeyedValues
case emptyObjectCannotHaveKeyedValues
case emptyObjectCannotHaveNestedContainers
case emptyObjectCannotHaveSuper
}
struct EmptyUnkeyedContainer: UnkeyedDecodingContainer {
var codingPath: [CodingKey] { return [] }
var count: Int? { return 0 }
var isAtEnd: Bool { return true }
var currentIndex: Int { return 0 }
mutating func decodeNil() throws -> Bool {
throw EmptyObjectDecodingError.emptyObjectCannotBeUnkeyedValues
}
mutating func decode(_ type: Bool.Type) throws -> Bool {
throw EmptyObjectDecodingError.emptyObjectCannotBeUnkeyedValues
}
mutating func decode(_ type: String.Type) throws -> String {
throw EmptyObjectDecodingError.emptyObjectCannotBeUnkeyedValues
}
mutating func decode(_ type: Double.Type) throws -> Double {
throw EmptyObjectDecodingError.emptyObjectCannotBeUnkeyedValues
}
mutating func decode(_ type: Float.Type) throws -> Float {
throw EmptyObjectDecodingError.emptyObjectCannotBeUnkeyedValues
}
mutating func decode(_ type: Int.Type) throws -> Int {
throw EmptyObjectDecodingError.emptyObjectCannotBeUnkeyedValues
}
mutating func decode(_ type: Int8.Type) throws -> Int8 {
throw EmptyObjectDecodingError.emptyObjectCannotBeUnkeyedValues
}
mutating func decode(_ type: Int16.Type) throws -> Int16 {
throw EmptyObjectDecodingError.emptyObjectCannotBeUnkeyedValues
}
mutating func decode(_ type: Int32.Type) throws -> Int32 {
throw EmptyObjectDecodingError.emptyObjectCannotBeUnkeyedValues
}
mutating func decode(_ type: Int64.Type) throws -> Int64 {
throw EmptyObjectDecodingError.emptyObjectCannotBeUnkeyedValues
}
mutating func decode(_ type: UInt.Type) throws -> UInt {
throw EmptyObjectDecodingError.emptyObjectCannotBeUnkeyedValues
}
mutating func decode(_ type: UInt8.Type) throws -> UInt8 {
throw EmptyObjectDecodingError.emptyObjectCannotBeUnkeyedValues
}
mutating func decode(_ type: UInt16.Type) throws -> UInt16 {
throw EmptyObjectDecodingError.emptyObjectCannotBeUnkeyedValues
}
mutating func decode(_ type: UInt32.Type) throws -> UInt32 {
throw EmptyObjectDecodingError.emptyObjectCannotBeUnkeyedValues
}
mutating func decode(_ type: UInt64.Type) throws -> UInt64 {
throw EmptyObjectDecodingError.emptyObjectCannotBeUnkeyedValues
}
mutating func decode<T>(_ type: T.Type) throws -> T where T : Decodable {
throw EmptyObjectDecodingError.emptyObjectCannotBeUnkeyedValues
}
mutating func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer<NestedKey> where NestedKey : CodingKey {
throw EmptyObjectDecodingError.emptyObjectCannotHaveNestedContainers
}
mutating func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer {
throw EmptyObjectDecodingError.emptyObjectCannotHaveNestedContainers
}
mutating func superDecoder() throws -> Decoder {
throw EmptyObjectDecodingError.emptyObjectCannotHaveSuper
}
}
struct EmptyKeyedContainer<Key: CodingKey>: KeyedDecodingContainerProtocol {
var codingPath: [CodingKey] { return [] }
var allKeys: [Key] { return [] }
func contains(_ key: Key) -> Bool {
return false
}
func decodeNil(forKey key: Key) throws -> Bool {
throw EmptyObjectDecodingError.emptyObjectCannotHaveKeyedValues
}
func decode(_ type: Bool.Type, forKey key: Key) throws -> Bool {
throw EmptyObjectDecodingError.emptyObjectCannotHaveKeyedValues
}
func decode(_ type: String.Type, forKey key: Key) throws -> String {
throw EmptyObjectDecodingError.emptyObjectCannotHaveKeyedValues
}
func decode(_ type: Double.Type, forKey key: Key) throws -> Double {
throw EmptyObjectDecodingError.emptyObjectCannotHaveKeyedValues
}
func decode(_ type: Float.Type, forKey key: Key) throws -> Float {
throw EmptyObjectDecodingError.emptyObjectCannotHaveKeyedValues
}
func decode(_ type: Int.Type, forKey key: Key) throws -> Int {
throw EmptyObjectDecodingError.emptyObjectCannotHaveKeyedValues
}
func decode(_ type: Int8.Type, forKey key: Key) throws -> Int8 {
throw EmptyObjectDecodingError.emptyObjectCannotHaveKeyedValues
}
func decode(_ type: Int16.Type, forKey key: Key) throws -> Int16 {
throw EmptyObjectDecodingError.emptyObjectCannotHaveKeyedValues
}
func decode(_ type: Int32.Type, forKey key: Key) throws -> Int32 {
throw EmptyObjectDecodingError.emptyObjectCannotHaveKeyedValues
}
func decode(_ type: Int64.Type, forKey key: Key) throws -> Int64 {
throw EmptyObjectDecodingError.emptyObjectCannotHaveKeyedValues
}
func decode(_ type: UInt.Type, forKey key: Key) throws -> UInt {
throw EmptyObjectDecodingError.emptyObjectCannotHaveKeyedValues
}
func decode(_ type: UInt8.Type, forKey key: Key) throws -> UInt8 {
throw EmptyObjectDecodingError.emptyObjectCannotHaveKeyedValues
}
func decode(_ type: UInt16.Type, forKey key: Key) throws -> UInt16 {
throw EmptyObjectDecodingError.emptyObjectCannotHaveKeyedValues
}
func decode(_ type: UInt32.Type, forKey key: Key) throws -> UInt32 {
throw EmptyObjectDecodingError.emptyObjectCannotHaveKeyedValues
}
func decode(_ type: UInt64.Type, forKey key: Key) throws -> UInt64 {
throw EmptyObjectDecodingError.emptyObjectCannotHaveKeyedValues
}
func decode<T>(_ type: T.Type, forKey key: Key) throws -> T where T : Decodable {
throw EmptyObjectDecodingError.emptyObjectCannotHaveKeyedValues
}
func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer<NestedKey> where NestedKey : CodingKey {
throw EmptyObjectDecodingError.emptyObjectCannotHaveNestedContainers
}
func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer {
throw EmptyObjectDecodingError.emptyObjectCannotHaveNestedContainers
}
func superDecoder() throws -> Decoder {
throw EmptyObjectDecodingError.emptyObjectCannotHaveSuper
}
func superDecoder(forKey key: Key) throws -> Decoder {
throw EmptyObjectDecodingError.emptyObjectCannotHaveSuper
}
}
+1 -1
View File
@@ -69,7 +69,7 @@ public protocol CreatableIdType: IdType {
init()
}
/// An Entity ID. These IDs can be encoded to or decoded from
/// An ResourceObject ID. These IDs can be encoded to or decoded from
/// JSON API IDs.
public struct Id<RawType: MaybeRawId, IdentifiableType: JSONAPI.JSONTyped>: Equatable, OptionalId {
+16 -16
View File
@@ -13,10 +13,10 @@ public protocol RelationshipType {
var meta: MetaType { get }
}
/// An Entity relationship that can be encoded to or decoded from
/// An ResourceObject relationship that can be encoded to or decoded from
/// a JSON API "Resource Linkage."
/// See https://jsonapi.org/format/#document-resource-object-linkage
/// A convenient typealias might make your code much more legible: `One<EntityDescription>`
/// A convenient typealias might make your code much more legible: `One<ResourceObjectDescription>`
public struct ToOneRelationship<Identifiable: JSONAPI.Identifiable, MetaType: JSONAPI.Meta, LinksType: JSONAPI.Links>: RelationshipType, Equatable {
public let id: Identifiable.Identifier
@@ -38,33 +38,33 @@ extension ToOneRelationship where MetaType == NoMetadata, LinksType == NoLinks {
}
extension ToOneRelationship {
public init<E: EntityType>(entity: E, meta: MetaType, links: LinksType) where E.Id == Identifiable.Identifier {
self.init(id: entity.id, meta: meta, links: links)
public init<T: ResourceObjectType>(resourceObject: T, meta: MetaType, links: LinksType) where T.Id == Identifiable.Identifier {
self.init(id: resourceObject.id, meta: meta, links: links)
}
}
extension ToOneRelationship where MetaType == NoMetadata, LinksType == NoLinks {
public init<E: EntityType>(entity: E) where E.Id == Identifiable.Identifier {
self.init(id: entity.id, meta: .none, links: .none)
public init<T: ResourceObjectType>(resourceObject: T) where T.Id == Identifiable.Identifier {
self.init(id: resourceObject.id, meta: .none, links: .none)
}
}
extension ToOneRelationship where Identifiable: OptionalRelatable {
public init<E: EntityType>(entity: E?, meta: MetaType, links: LinksType) where E.Id == Identifiable.Wrapped.Identifier {
self.init(id: entity?.id, meta: meta, links: links)
public init<T: ResourceObjectType>(resourceObject: T?, meta: MetaType, links: LinksType) where T.Id == Identifiable.Wrapped.Identifier {
self.init(id: resourceObject?.id, meta: meta, links: links)
}
}
extension ToOneRelationship where Identifiable: OptionalRelatable, MetaType == NoMetadata, LinksType == NoLinks {
public init<E: EntityType>(entity: E?) where E.Id == Identifiable.Wrapped.Identifier {
self.init(id: entity?.id, meta: .none, links: .none)
public init<T: ResourceObjectType>(resourceObject: T?) where T.Id == Identifiable.Wrapped.Identifier {
self.init(id: resourceObject?.id, meta: .none, links: .none)
}
}
/// An Entity relationship that can be encoded to or decoded from
/// An ResourceObject relationship that can be encoded to or decoded from
/// a JSON API "Resource Linkage."
/// See https://jsonapi.org/format/#document-resource-object-linkage
/// A convenient typealias might make your code much more legible: `Many<EntityDescription>`
/// A convenient typealias might make your code much more legible: `Many<ResourceObjectDescription>`
public struct ToManyRelationship<Relatable: JSONAPI.Relatable, MetaType: JSONAPI.Meta, LinksType: JSONAPI.Links>: RelationshipType, Equatable {
public let ids: [Relatable.Identifier]
@@ -84,8 +84,8 @@ public struct ToManyRelationship<Relatable: JSONAPI.Relatable, MetaType: JSONAPI
self.links = links
}
public init<E: EntityType>(entities: [E], meta: MetaType, links: LinksType) where E.Id == Relatable.Identifier {
self.init(ids: entities.map { $0.id }, meta: meta, links: links)
public init<T: ResourceObjectType>(resourceObjects: [T], meta: MetaType, links: LinksType) where T.Id == Relatable.Identifier {
self.init(ids: resourceObjects.map { $0.id }, meta: meta, links: links)
}
private init(meta: MetaType, links: LinksType) {
@@ -111,8 +111,8 @@ extension ToManyRelationship where MetaType == NoMetadata, LinksType == NoLinks
return .none(withMeta: .none, links: .none)
}
public init<E: EntityType>(entities: [E]) where E.Id == Relatable.Identifier {
self.init(entities: entities, meta: .none, links: .none)
public init<T: ResourceObjectType>(resourceObjects: [T]) where T.Id == Relatable.Identifier {
self.init(resourceObjects: resourceObjects, meta: .none, links: .none)
}
}
@@ -1,5 +1,5 @@
//
// EntityCheck.swift
// ResourceObjectCheck.swift
// JSONAPITesting
//
// Created by Mathew Polzin on 11/27/18.
@@ -7,7 +7,7 @@
import JSONAPI
public enum EntityCheckError: Swift.Error {
public enum ResourceObjectCheckError: Swift.Error {
/// The attributes should live in a struct, not
/// another type class.
case attributesNotStruct
@@ -29,8 +29,8 @@ public enum EntityCheckError: Swift.Error {
case nullArray(named: String)
}
public struct EntityCheckErrors: Swift.Error {
let problems: [EntityCheckError]
public struct ResourceObjectCheckErrors: Swift.Error {
let problems: [ResourceObjectCheckError]
}
private protocol OptionalAttributeType {}
@@ -54,9 +54,9 @@ private protocol _AttributeType {}
extension TransformedAttribute: _AttributeType {}
extension Attribute: _AttributeType {}
public extension Entity {
static func check(_ entity: Entity) throws {
var problems = [EntityCheckError]()
public extension ResourceObject {
static func check(_ entity: ResourceObject) throws {
var problems = [ResourceObjectCheckError]()
let attributesMirror = Mirror(reflecting: entity.attributes)
@@ -88,7 +88,7 @@ public extension Entity {
}
guard problems.count == 0 else {
throw EntityCheckErrors(problems: problems)
throw ResourceObjectCheckErrors(problems: problems)
}
}
}
@@ -40,7 +40,7 @@ class EntityCheckTests: XCTestCase {
// MARK: - Test types
extension EntityCheckTests {
enum OkDescription: EntityDescription {
enum OkDescription: ResourceObjectDescription {
public static var jsonType: String { return "hello" }
public typealias Attributes = NoAttributes
@@ -49,7 +49,7 @@ extension EntityCheckTests {
public typealias OkEntity = BasicEntity<OkDescription>
enum OtherOkDescription: EntityDescription {
enum OtherOkDescription: ResourceObjectDescription {
public static var jsonType: String { return "hmm" }
public typealias Attributes = NoAttributes
@@ -58,7 +58,7 @@ extension EntityCheckTests {
public typealias OtherOkEntity = BasicEntity<OtherOkDescription>
enum EnumAttributesDescription: EntityDescription {
enum EnumAttributesDescription: ResourceObjectDescription {
public static var jsonType: String { return "hello" }
public enum Attributes: JSONAPI.Attributes {
@@ -77,7 +77,7 @@ extension EntityCheckTests {
public typealias EnumAttributesEntity = BasicEntity<EnumAttributesDescription>
enum EnumRelationshipsDescription: EntityDescription {
enum EnumRelationshipsDescription: ResourceObjectDescription {
public static var jsonType: String { return "hello" }
public typealias Attributes = NoAttributes
@@ -96,7 +96,7 @@ extension EntityCheckTests {
public typealias EnumRelationshipsEntity = BasicEntity<EnumRelationshipsDescription>
enum BadAttributeDescription: EntityDescription {
enum BadAttributeDescription: ResourceObjectDescription {
public static var jsonType: String { return "hello" }
public struct Attributes: JSONAPI.Attributes {
@@ -109,7 +109,7 @@ extension EntityCheckTests {
public typealias BadAttributeEntity = BasicEntity<BadAttributeDescription>
enum BadRelationshipDescription: EntityDescription {
enum BadRelationshipDescription: ResourceObjectDescription {
public static var jsonType: String { return "hello" }
public typealias Attributes = NoAttributes
@@ -122,7 +122,7 @@ extension EntityCheckTests {
public typealias BadRelationshipEntity = BasicEntity<BadRelationshipDescription>
enum OptionalArrayAttributeDescription: EntityDescription {
enum OptionalArrayAttributeDescription: ResourceObjectDescription {
public static var jsonType: String { return "hello" }
public struct Attributes: JSONAPI.Attributes {
@@ -24,7 +24,7 @@ class Id_LiteralTests: XCTestCase {
// MARK: - Test types
extension Id_LiteralTests {
enum TestDescription: EntityDescription {
enum TestDescription: ResourceObjectDescription {
public static var jsonType: String { return "test" }
public typealias Attributes = NoAttributes
@@ -27,7 +27,7 @@ class Relationship_LiteralTests: XCTestCase {
// MARK: - Test types
extension Relationship_LiteralTests {
enum TestDescription: EntityDescription {
enum TestDescription: ResourceObjectDescription {
public static var jsonType: String { return "test" }
public typealias Attributes = NoAttributes
@@ -7,8 +7,8 @@
import JSONAPI
public typealias Entity<Description: JSONAPI.EntityDescription, Meta: JSONAPI.Meta, Links: JSONAPI.Links> = JSONAPI.Entity<Description, Meta, Links, String>
public typealias Entity<Description: JSONAPI.ResourceObjectDescription, Meta: JSONAPI.Meta, Links: JSONAPI.Links> = JSONAPI.ResourceObject<Description, Meta, Links, String>
public typealias BasicEntity<Description: JSONAPI.EntityDescription> = Entity<Description, NoMetadata, NoLinks>
public typealias BasicEntity<Description: JSONAPI.ResourceObjectDescription> = Entity<Description, NoMetadata, NoLinks>
public typealias NewEntity<Description: JSONAPI.EntityDescription, Meta: JSONAPI.Meta, Links: JSONAPI.Links> = JSONAPI.Entity<Description, Meta, Links, Unidentified>
public typealias NewEntity<Description: JSONAPI.ResourceObjectDescription, Meta: JSONAPI.Meta, Links: JSONAPI.Links> = JSONAPI.ResourceObject<Description, Meta, Links, Unidentified>
@@ -37,7 +37,7 @@ class Attribute_FunctorTests: XCTestCase {
// MARK: Test types
extension Attribute_FunctorTests {
enum TestTypeDescription: EntityDescription {
enum TestTypeDescription: ResourceObjectDescription {
public static var jsonType: String { return "test" }
public struct Attributes: JSONAPI.Attributes {
@@ -27,13 +27,13 @@ class ComputedPropertiesTests: XCTestCase {
let entity = decoded(type: TestType.self, data: computed_property_attribute)
XCTAssertEqual(entity[\.computed], "Sarah2")
XCTAssertEqual(entity[\.secretsOut], "shhhh")
XCTAssertEqual(entity[direct: \.directSecretsOut], "shhhh")
}
func test_ComputedNonAttributeAccess() {
let entity = decoded(type: TestType.self, data: computed_property_attribute)
XCTAssertEqual(entity[\.computed2], "Sarah2")
XCTAssertEqual(entity[direct: \.directComputed], "Sarah2")
}
func test_ComputedRelationshipAccess() {
@@ -45,7 +45,7 @@ class ComputedPropertiesTests: XCTestCase {
// MARK: Test types
extension ComputedPropertiesTests {
public enum TestTypeDescription: EntityDescription {
public enum TestTypeDescription: ResourceObjectDescription {
public static var jsonType: String { return "test" }
public struct Attributes: JSONAPI.Attributes {
@@ -56,11 +56,11 @@ extension ComputedPropertiesTests {
return name.map { $0 + "2" }
}
public var computed2: String {
public var directComputed: String {
return computed.value
}
public var secretsOut: String {
public var directSecretsOut: String {
return secret.value
}
}

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