mirror of
https://github.com/encounter/JSONAPI.git
synced 2026-03-30 11:18:38 -07:00
merge w/ master
This commit is contained in:
+1
-1
@@ -114,7 +114,7 @@ func articleDocument(includeAuthor: Bool) -> Either<SingleArticleDocument, Singl
|
||||
|
||||
let includes: Includes<SingleArticleDocumentWithIncludes.Include> = .init(values: [.init(author)])
|
||||
|
||||
return .init(document.including(.init(values: [.init(author)])))
|
||||
return .init(document.including(includes))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -757,7 +757,7 @@ func articleDocument(includeAuthor: Bool) -> Either<SingleArticleDocument, Singl
|
||||
|
||||
let includes: Includes<SingleArticleDocumentWithIncludes.Include> = .init(values: [.init(author)])
|
||||
|
||||
return .init(document.including(.init(values: [.init(author)])))
|
||||
return .init(document.including(includes))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -820,10 +820,18 @@ print(response.author)
|
||||
```
|
||||
|
||||
# JSONAPI+Testing
|
||||
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 `ResourceObject` values into your unit tests. It also provides a `check()` function for each `ResourceObject` 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.
|
||||
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 `ResourceObject` values into your unit tests. It also provides a `check()` function for each `ResourceObject` 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
|
||||
This library has moved into its own Package. See https://github.com/mattpolzin/JSONAPI-Arbitrary
|
||||
This library has moved into its own Package. See https://github.com/mattpolzin/JSONAPI-Arbitrary for more information.
|
||||
|
||||
# JSONAPI+OpenAPI
|
||||
This library has moved into its own Package. See https://github.com/mattpolzin/JSONAPI-OpenAPI
|
||||
The `JSONAPI+OpenAPI` library generates OpenAPI compliant JSON Schema for models built with the `JSONAPI` library. If your Swift code is your preferred source of truth for API information, this is an easy way to document the response schemas of your API.
|
||||
|
||||
`JSONAPI+OpenAPI` also has experimental support for generating `JSONAPI` Swift code from Open API documentation (this currently lives on the `feature/gen-swift` branch).
|
||||
|
||||
See https://github.com/mattpolzin/JSONAPI-OpenAPI for more information.
|
||||
|
||||
@@ -71,10 +71,12 @@ extension SingleResourceBody {
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.singleValueContainer()
|
||||
|
||||
if (value as Any?) == nil {
|
||||
try container.encodeNil()
|
||||
return
|
||||
}
|
||||
let anyNil: Any? = nil
|
||||
let nilValue = anyNil as? Entity
|
||||
guard value != nilValue else {
|
||||
try container.encodeNil()
|
||||
return
|
||||
}
|
||||
|
||||
try container.encode(value)
|
||||
}
|
||||
|
||||
@@ -190,10 +190,6 @@ extension ToOneRelationship: Codable where Identifiable.Identifier: OptionalId {
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: ResourceLinkageCodingKeys.self)
|
||||
|
||||
if (id as Any?) == nil {
|
||||
try container.encodeNil(forKey: .data)
|
||||
}
|
||||
|
||||
if MetaType.self != NoMetadata.self {
|
||||
try container.encode(meta, forKey: .meta)
|
||||
}
|
||||
|
||||
@@ -10,6 +10,17 @@ import JSONAPI
|
||||
|
||||
class APIDescriptionTests: XCTestCase {
|
||||
|
||||
func test_init() {
|
||||
let _ = APIDescription<NoMetadata>(version: "hello",
|
||||
meta: .none)
|
||||
let _ = APIDescription<TestMetadata>(version: "world",
|
||||
meta: .init(hello: "there",
|
||||
number: 2))
|
||||
let _ = NoAPIDescription()
|
||||
|
||||
XCTAssertEqual(NoAPIDescription(), NoAPIDescription.none)
|
||||
}
|
||||
|
||||
func test_NoDescriptionString() {
|
||||
XCTAssertEqual(String(describing: NoAPIDescription()), "No JSON:API Object")
|
||||
}
|
||||
@@ -18,12 +29,18 @@ class APIDescriptionTests: XCTestCase {
|
||||
let description = decoded(type: APIDescription<NoMetadata>.self, data: api_description_empty)
|
||||
|
||||
XCTAssertEqual(description.version, "1.0")
|
||||
|
||||
test_DecodeEncodeEquality(type: APIDescription<NoMetadata>.self,
|
||||
data: api_description_empty)
|
||||
}
|
||||
|
||||
func test_WithVersion() {
|
||||
let description = decoded(type: APIDescription<NoMetadata>.self, data: api_description_with_version)
|
||||
|
||||
XCTAssertEqual(description.version, "1.5")
|
||||
|
||||
test_DecodeEncodeEquality(type: APIDescription<NoMetadata>.self,
|
||||
data: api_description_with_version)
|
||||
}
|
||||
|
||||
func test_WithMeta() {
|
||||
@@ -32,6 +49,9 @@ class APIDescriptionTests: XCTestCase {
|
||||
XCTAssertEqual(description.version, "1.0")
|
||||
XCTAssertEqual(description.meta.hello, "world")
|
||||
XCTAssertEqual(description.meta.number, 10)
|
||||
|
||||
test_DecodeEncodeEquality(type: APIDescription<TestMetadata>.self,
|
||||
data: api_description_with_meta)
|
||||
}
|
||||
|
||||
func test_WithVersionAndMeta() {
|
||||
@@ -40,6 +60,9 @@ class APIDescriptionTests: XCTestCase {
|
||||
XCTAssertEqual(description.version, "2.0")
|
||||
XCTAssertEqual(description.meta.hello, "world")
|
||||
XCTAssertEqual(description.meta.number, 10)
|
||||
|
||||
test_DecodeEncodeEquality(type: APIDescription<TestMetadata>.self,
|
||||
data: api_description_with_version_and_meta)
|
||||
}
|
||||
|
||||
func test_failsMissingMeta() {
|
||||
|
||||
@@ -100,6 +100,9 @@ class ResourceBodyTests: XCTestCase {
|
||||
XCTAssertEqual(combined.values.count, 3)
|
||||
XCTAssertEqual(combined.values, body1.values + body2.values)
|
||||
}
|
||||
}
|
||||
|
||||
extension ResourceBodyTests {
|
||||
|
||||
enum ArticleType: ResourceObjectDescription {
|
||||
public static var jsonType: String { return "articles" }
|
||||
|
||||
@@ -10,14 +10,14 @@ import JSONAPI
|
||||
import JSONAPITesting
|
||||
|
||||
class ResourceObjectTests: XCTestCase {
|
||||
|
||||
|
||||
func test_relationship_access() {
|
||||
let entity1 = TestEntity1(attributes: .none, relationships: .none, meta: .none, links: .none)
|
||||
let entity2 = TestEntity2(attributes: .none, relationships: .init(other: entity1.pointer), meta: .none, links: .none)
|
||||
|
||||
|
||||
XCTAssertEqual(entity2.relationships.other, entity1.pointer)
|
||||
}
|
||||
|
||||
|
||||
func test_relationship_operator_access() {
|
||||
let entity1 = TestEntity1(attributes: .none, relationships: .none, meta: .none, links: .none)
|
||||
let entity2 = TestEntity2(attributes: .none, relationships: .init(other: entity1.pointer), meta: .none, links: .none)
|
||||
@@ -30,6 +30,7 @@ class ResourceObjectTests: XCTestCase {
|
||||
let entity = TestEntity9(attributes: .none, relationships: .init(one: entity1.pointer, nullableOne: .init(resourceObject: entity1, meta: .none, links: .none), optionalOne: .init(resourceObject: entity1, meta: .none, links: .none), optionalNullableOne: nil, optionalMany: .init(resourceObjects: [entity1, entity1], meta: .none, links: .none)), meta: .none, links: .none)
|
||||
|
||||
XCTAssertEqual(entity ~> \.optionalOne, Optional(entity1.id))
|
||||
XCTAssertEqual((entity ~> \.optionalOne).rawValue, Optional(entity1.id.rawValue))
|
||||
}
|
||||
|
||||
func test_toMany_relationship_operator_access() {
|
||||
@@ -47,11 +48,11 @@ class ResourceObjectTests: XCTestCase {
|
||||
|
||||
XCTAssertEqual(entity ~> \.optionalMany, [entity1.id, entity1.id])
|
||||
}
|
||||
|
||||
|
||||
func test_relationshipIds() {
|
||||
let entity1 = TestEntity1(attributes: .none, relationships: .none, meta: .none, links: .none)
|
||||
let entity2 = TestEntity2(attributes: .none, relationships: .init(other: entity1.pointer), meta: .none, links: .none)
|
||||
|
||||
|
||||
XCTAssertEqual(entity2.relationships.other.id, entity1.id)
|
||||
}
|
||||
|
||||
@@ -174,7 +175,7 @@ extension ResourceObjectTests {
|
||||
data: entity_some_relationships_no_attributes)
|
||||
|
||||
XCTAssert(type(of: entity.attributes) == NoAttributes.self)
|
||||
|
||||
|
||||
XCTAssertEqual((entity ~> \.others).map { $0.rawValue }, ["364B3B69-4DF1-467F-B52E-B0C9E44F666E"])
|
||||
XCTAssertNoThrow(try TestEntity3.check(entity))
|
||||
|
||||
@@ -185,11 +186,11 @@ extension ResourceObjectTests {
|
||||
test_DecodeEncodeEquality(type: TestEntity3.self,
|
||||
data: entity_some_relationships_no_attributes)
|
||||
}
|
||||
|
||||
|
||||
func test_EntitySomeRelationshipsSomeAttributes() {
|
||||
let entity = decoded(type: TestEntity4.self,
|
||||
data: entity_some_relationships_some_attributes)
|
||||
|
||||
|
||||
XCTAssertEqual(entity[\.word], "coolio")
|
||||
XCTAssertEqual(entity.word, "coolio")
|
||||
XCTAssertEqual(entity[\.number], 992299)
|
||||
@@ -208,11 +209,11 @@ extension ResourceObjectTests {
|
||||
|
||||
// MARK: Attribute omission and nullification
|
||||
extension ResourceObjectTests {
|
||||
|
||||
|
||||
func test_entityOneOmittedAttribute() {
|
||||
let entity = decoded(type: TestEntity6.self,
|
||||
data: entity_one_omitted_attribute)
|
||||
|
||||
|
||||
XCTAssertEqual(entity[\.here], "Hello")
|
||||
XCTAssertEqual(entity.here, "Hello")
|
||||
XCTAssertNil(entity[\.maybeHere])
|
||||
@@ -228,11 +229,11 @@ extension ResourceObjectTests {
|
||||
test_DecodeEncodeEquality(type: TestEntity6.self,
|
||||
data: entity_one_omitted_attribute)
|
||||
}
|
||||
|
||||
|
||||
func test_entityOneNullAttribute() {
|
||||
let entity = decoded(type: TestEntity6.self,
|
||||
data: entity_one_null_attribute)
|
||||
|
||||
|
||||
XCTAssertEqual(entity[\.here], "Hello")
|
||||
XCTAssertEqual(entity.here, "Hello")
|
||||
XCTAssertEqual(entity[\.maybeHere], "World")
|
||||
@@ -248,11 +249,11 @@ extension ResourceObjectTests {
|
||||
test_DecodeEncodeEquality(type: TestEntity6.self,
|
||||
data: entity_one_null_attribute)
|
||||
}
|
||||
|
||||
|
||||
func test_entityAllAttribute() {
|
||||
let entity = decoded(type: TestEntity6.self,
|
||||
data: entity_all_attributes)
|
||||
|
||||
|
||||
XCTAssertEqual(entity[\.here], "Hello")
|
||||
XCTAssertEqual(entity.here, "Hello")
|
||||
XCTAssertEqual(entity[\.maybeHere], "World")
|
||||
@@ -268,11 +269,11 @@ extension ResourceObjectTests {
|
||||
test_DecodeEncodeEquality(type: TestEntity6.self,
|
||||
data: entity_all_attributes)
|
||||
}
|
||||
|
||||
|
||||
func test_entityOneNullAndOneOmittedAttribute() {
|
||||
let entity = decoded(type: TestEntity6.self,
|
||||
data: entity_one_null_and_one_missing_attribute)
|
||||
|
||||
|
||||
XCTAssertEqual(entity[\.here], "Hello")
|
||||
XCTAssertEqual(entity.here, "Hello")
|
||||
XCTAssertNil(entity[\.maybeHere])
|
||||
@@ -288,16 +289,16 @@ extension ResourceObjectTests {
|
||||
test_DecodeEncodeEquality(type: TestEntity6.self,
|
||||
data: entity_one_null_and_one_missing_attribute)
|
||||
}
|
||||
|
||||
|
||||
func test_entityBrokenNullableOmittedAttribute() {
|
||||
XCTAssertThrowsError(try JSONDecoder().decode(TestEntity6.self,
|
||||
from: entity_broken_missing_nullable_attribute))
|
||||
}
|
||||
|
||||
|
||||
func test_NullOptionalNullableAttribute() {
|
||||
let entity = decoded(type: TestEntity7.self,
|
||||
data: entity_null_optional_nullable_attribute)
|
||||
|
||||
|
||||
XCTAssertEqual(entity[\.here], "Hello")
|
||||
XCTAssertEqual(entity.here, "Hello")
|
||||
XCTAssertNil(entity[\.maybeHereMaybeNull])
|
||||
@@ -311,7 +312,7 @@ extension ResourceObjectTests {
|
||||
test_DecodeEncodeEquality(type: TestEntity7.self,
|
||||
data: entity_null_optional_nullable_attribute)
|
||||
}
|
||||
|
||||
|
||||
func test_NonNullOptionalNullableAttribute() {
|
||||
let entity = decoded(type: TestEntity7.self,
|
||||
data: entity_non_null_optional_nullable_attribute)
|
||||
@@ -336,7 +337,7 @@ extension ResourceObjectTests {
|
||||
func test_IntToString() {
|
||||
let entity = decoded(type: TestEntity8.self,
|
||||
data: entity_int_to_string_attribute)
|
||||
|
||||
|
||||
XCTAssertEqual(entity[\.string], "22")
|
||||
XCTAssertEqual(entity.string, "22")
|
||||
XCTAssertEqual(entity[\.int], 22)
|
||||
@@ -419,6 +420,7 @@ extension ResourceObjectTests {
|
||||
XCTAssertEqual((entity ~> \.nullableOne)?.rawValue, "3323")
|
||||
XCTAssertEqual((entity ~> \.one).rawValue, "4459")
|
||||
XCTAssertNil(entity ~> \.optionalNullableOne)
|
||||
XCTAssertNil((entity ~> \.optionalNullableOne).rawValue)
|
||||
XCTAssertNoThrow(try TestEntity9.check(entity))
|
||||
|
||||
testEncoded(entity: entity)
|
||||
@@ -690,6 +692,16 @@ extension ResourceObjectTests {
|
||||
|
||||
XCTAssertEqual(entity1 ~> \.metaRelationship, "hello")
|
||||
}
|
||||
|
||||
func test_toManyMetaRelationshipAccessWorks() {
|
||||
let entity1 = TestEntityWithMetaRelationship(id: "even",
|
||||
attributes: .none,
|
||||
relationships: .init(),
|
||||
meta: .none,
|
||||
links: .none)
|
||||
|
||||
XCTAssertEqual(entity1 ~> \.toManyMetaRelationship, ["hello"])
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Test Types
|
||||
@@ -708,7 +720,7 @@ extension ResourceObjectTests {
|
||||
static var jsonType: String { return "second_test_entities"}
|
||||
|
||||
typealias Attributes = NoAttributes
|
||||
|
||||
|
||||
struct Relationships: JSONAPI.Relationships {
|
||||
let other: ToOneRelationship<TestEntity1, NoMetadata, NoLinks>
|
||||
}
|
||||
@@ -720,7 +732,7 @@ extension ResourceObjectTests {
|
||||
static var jsonType: String { return "third_test_entities"}
|
||||
|
||||
typealias Attributes = NoAttributes
|
||||
|
||||
|
||||
struct Relationships: JSONAPI.Relationships {
|
||||
let others: ToManyRelationship<TestEntity1, NoMetadata, NoLinks>
|
||||
}
|
||||
@@ -793,7 +805,7 @@ extension ResourceObjectTests {
|
||||
static var jsonType: String { return "eighth_test_entities" }
|
||||
|
||||
typealias Relationships = NoRelationships
|
||||
|
||||
|
||||
struct Attributes: JSONAPI.Attributes {
|
||||
let string: Attribute<String>
|
||||
let int: Attribute<Int>
|
||||
@@ -804,7 +816,7 @@ extension ResourceObjectTests {
|
||||
let nullToString: TransformedAttribute<Int?, OptionalToString<Int>>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
typealias TestEntity8 = BasicEntity<TestEntityType8>
|
||||
|
||||
enum TestEntityType9: ResourceObjectDescription {
|
||||
@@ -922,6 +934,12 @@ extension ResourceObjectTests {
|
||||
return TestEntity1.Identifier(rawValue: "hello")
|
||||
}
|
||||
}
|
||||
|
||||
var toManyMetaRelationship: (TestEntityWithMetaRelationship) -> [TestEntity1.Identifier] {
|
||||
return { entity in
|
||||
return [TestEntity1.Identifier.id(from: "hello")]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -932,19 +950,19 @@ extension ResourceObjectTests {
|
||||
return String(from)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
enum IntPlusOneHundred: Transformer {
|
||||
public static func transform(_ from: Int) -> Int {
|
||||
return from + 100
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
enum IntToDouble: Transformer {
|
||||
public static func transform(_ from: Int) -> Double {
|
||||
return Double(from)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
enum OptionalToString<T>: Transformer {
|
||||
public static func transform(_ from: T?) -> String {
|
||||
return String(describing: from)
|
||||
|
||||
Reference in New Issue
Block a user