Merge remote-tracking branch 'origin/master' into swift-5

This commit is contained in:
Mathew Polzin
2019-01-08 21:27:39 -08:00
4 changed files with 84 additions and 2 deletions
+34
View File
@@ -55,6 +55,7 @@ See the JSON API Spec here: https://jsonapi.org/format/
- [Custom Attribute or Relationship Key Mapping](#custom-attribute-or-relationship-key-mapping)
- [Custom Attribute Encode/Decode](#custom-attribute-encodedecode)
- [Meta-Attributes](#meta-attributes)
- [Meta-Relationships](#meta-relationships)
- [Example](#example)
- [Preamble (Setup shared by server and client)](#preamble-setup-shared-by-server-and-client)
- [Server Pseudo-example](#server-pseudo-example)
@@ -574,6 +575,39 @@ let createdAt = user[\.createdAt]
This works because `createdAt` is defined in the form: `var {name}: ({Entity}) -> {Value}` where `{Entity}` is the `JSONAPI.Entity` described by the `EntityDescription` containing the meta-attribute.
### Meta-Relationships
This advanced feature may not ever be useful, but if you find yourself in the situation of dealing with an API that does not 100% follow the **SPEC** then you might find meta-relationships are just the thing to make your entities more natural to work with.
Similarly to Meta-Attributes, Meta-Relationships allow you to represent non-compliant relationships as computed relationship properties. In the following example, a relationship is created from some attributes on the JSON model.
```swift
enum UserDescription: EntityDescription {
public static var jsonType: String { return "users" }
struct Attributes: JSONAPI.Attributes {
let friend_id: Attribute<String>
}
struct Relationships: JSONAPI.Relationships {
public var friend: (User) -> User.Identifier {
return { user in
return User.Identifier(rawValue: user[\.friend_id])
}
}
}
}
typealias User = JSONAPI.Entity<UserDescription, NoMetadata, NoLinks, String>
```
Given a value `user` of the above entity type, you can access the `friend` relationship just like you would any other:
```swift
let friendId = user ~> \.friend
```
This works because `friend` is defined in the form: `var {name}: ({Entity}) -> {Identifier}` where `{Entity}` is the `JSONAPI.Entity` described by the `EntityDescription` containing the meta-relationship.
## Example
The following serves as a sort of pseudo-example. It skips server/client implementation details not related to JSON:API but still gives a more complete picture of what an implementation using this framework might look like. You can play with this example code in the Playground provided with this repo.
+17
View File
@@ -522,6 +522,23 @@ public extension EntityProxy {
}
}
// MARK: Meta-Relationship Access
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 {
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] {
return entity.relationships[keyPath: path](entity)
}
}
infix operator ~>
// MARK: - Codable
+31 -1
View File
@@ -612,7 +612,7 @@ extension EntityTests {
// MARK: With a Meta Attribute
extension EntityTests {
func test_MetaEntityAccessWorks() {
func test_MetaEntityAttributeAccessWorks() {
let entity1 = TestEntityWithMetaAttribute(id: "even",
attributes: .init(),
relationships: .none,
@@ -629,6 +629,20 @@ extension EntityTests {
}
}
// MARK: With a Meta Relationship
extension EntityTests {
func test_MetaEntityRelationshipAccessWorks() {
let entity1 = TestEntityWithMetaRelationship(id: "even",
attributes: .none,
relationships: .init(),
meta: .none,
links: .none)
XCTAssertEqual(entity1 ~> \.metaRelationship, "hello")
}
}
// MARK: - Test Types
extension EntityTests {
@@ -826,6 +840,22 @@ extension EntityTests {
typealias TestEntityWithMetaAttribute = BasicEntity<TestEntityWithMetaAttributeDescription>
enum TestEntityWithMetaRelationshipDescription: EntityDescription {
public static var jsonType: String { return "meta_relationship_entity" }
typealias Attributes = NoAttributes
struct Relationships: JSONAPI.Relationships {
var metaRelationship: (TestEntityWithMetaRelationship) -> TestEntity1.Identifier {
return { entity in
return TestEntity1.Identifier(rawValue: "hello")
}
}
}
}
typealias TestEntityWithMetaRelationship = BasicEntity<TestEntityWithMetaRelationshipDescription>
enum IntToString: Transformer {
public static func transform(_ from: Int) -> String {
return String(from)
+2 -1
View File
@@ -228,7 +228,8 @@ extension EntityTests {
("test_IntOver10_success", test_IntOver10_success),
("test_IntToString", test_IntToString),
("test_IntToString_encode", test_IntToString_encode),
("test_MetaEntityAccessWorks", test_MetaEntityAccessWorks),
("test_MetaEntityAttributeAccessWorks", test_MetaEntityAttributeAccessWorks),
("test_MetaEntityRelationshipAccessWorks", test_MetaEntityRelationshipAccessWorks),
("test_NonNullOptionalNullableAttribute", test_NonNullOptionalNullableAttribute),
("test_NonNullOptionalNullableAttribute_encode", test_NonNullOptionalNullableAttribute_encode),
("test_nullableRelationshipIsNull", test_nullableRelationshipIsNull),