mirror of
https://github.com/encounter/JSONAPI.git
synced 2026-03-30 11:18:38 -07:00
Merge remote-tracking branch 'origin/master' into swift-5
This commit is contained in:
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user