From 3a60ac5fe22940e3f25ac64fc24c2c7d081496b7 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Mon, 5 Aug 2019 17:59:38 -0700 Subject: [PATCH] Add playground page showing off sparse fieldset encoding --- .../Contents.swift | 72 +++++++++++++++++++ .../Usage.xcplaygroundpage/Contents.swift | 9 ++- JSONAPI.playground/contents.xcplayground | 1 + Tests/JSONAPITests/XCTestManifests.swift | 7 ++ 4 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 JSONAPI.playground/Pages/Sparse Fieldsets Example.xcplaygroundpage/Contents.swift diff --git a/JSONAPI.playground/Pages/Sparse Fieldsets Example.xcplaygroundpage/Contents.swift b/JSONAPI.playground/Pages/Sparse Fieldsets Example.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000..e34667b --- /dev/null +++ b/JSONAPI.playground/Pages/Sparse Fieldsets Example.xcplaygroundpage/Contents.swift @@ -0,0 +1,72 @@ + +import JSONAPI +import Foundation + +// MARK: - Resource Object + +enum ThingWithPropertiesDescription: JSONAPI.ResourceObjectDescription { + static let jsonType: String = "thing" + + // + // NOTE: `JSONAPI.SparsableAttributes` as opposed to `JSONAPI.Attributes` + // + struct Attributes: JSONAPI.SparsableAttributes { + let stringThing: Attribute + let numberThing: Attribute + let boolThing: Attribute + + // + // NOTE: Special implementation of `CodingKeys` + // + enum CodingKeys: String, JSONAPI.SparsableCodingKey { + case stringThing + case numberThing + case boolThing + } + } + + typealias Relationships = NoRelationships +} + +typealias ThingWithProperties = JSONAPI.ResourceObject + +// MARK: - Document + +// +// NOTE: Using `JSONAPI.EncodableResourceBody` which means the document type will be `Encodable` but not `Decodable`. +// +typealias Document = JSONAPI.Document + +// +// NOTE: Using `JSONAPI.EncodablePrimaryResource` which means the `ResourceBody` will be `Encodable` but not `Decodable. +// +typealias SingleDocument = Document, NoIncludes> + +// MARK: - Resource Initialization + +let resource = ThingWithProperties(id: .init(rawValue: "1234"), + attributes: .init(stringThing: .init(value: "hello world"), + numberThing: .init(value: 10), + boolThing: .init(value: nil)), + relationships: .none, + meta: .none, + links: .none) +// +// NOTE: Creating a sparse resource that will only encode +// the attribute named "stringThing" +// +let sparseResource = resource.sparse(with: [.stringThing]) + +// MARK: - Encoding + +let encoder = JSONEncoder() + +let sparseResourceDoc = SingleDocument(apiDescription: .none, + body: .init(resourceObject: sparseResource), + includes: .none, + meta: .none, + links: .none) + +let data = try! encoder.encode(sparseResourceDoc) + +print(String(data: data, encoding: .utf8)!) diff --git a/JSONAPI.playground/Pages/Usage.xcplaygroundpage/Contents.swift b/JSONAPI.playground/Pages/Usage.xcplaygroundpage/Contents.swift index 22bb3c0..00b5172 100644 --- a/JSONAPI.playground/Pages/Usage.xcplaygroundpage/Contents.swift +++ b/JSONAPI.playground/Pages/Usage.xcplaygroundpage/Contents.swift @@ -8,6 +8,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) @@ -15,17 +16,20 @@ let singleDogDocument = SingleDogDocument(apiDescription: .none, body: .init(res let singleDogData = try! JSONEncoder().encode(singleDogDocument) + // MARK: - Parse a request or response body with one Dog in it let dogResponse = try! JSONDecoder().decode(SingleDogDocument.self, from: singleDogData) let dogFromData = dogResponse.body.primaryResource?.value let dogOwner: Person.Identifier? = dogFromData.flatMap { $0 ~> \.owner } -// MARKL - Parse a request or response body with one Dog in it using an alternative model + +// MARK: - Parse a request or response body with one Dog in it using an alternative model typealias AltSingleDogDocument = JSONAPI.Document, NoMetadata, NoLinks, NoIncludes, NoAPIDescription, UnknownJSONAPIError> let altDogResponse = try! JSONDecoder().decode(AltSingleDogDocument.self, from: singleDogData) let altDogFromData = altDogResponse.body.primaryResource?.value let altDogHuman: Person.Identifier? = altDogFromData.flatMap { $0 ~> \.human } + // MARK: - Create a request or response with multiple people and dogs and houses included let personIds = [Person.Identifier(), Person.Identifier()] let dogs = try! [Dog(name: "Buddy", owner: personIds[0]), Dog(name: "Joy", owner: personIds[0]), Dog(name: "Travis", owner: personIds[1])] @@ -36,6 +40,7 @@ let includes = dogs.map { BatchPeopleDocument.Include($0) } + houses.map { Batch 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 let peopleResponse = try! JSONDecoder().decode(BatchPeopleDocument.self, from: batchPeopleData) @@ -47,6 +52,7 @@ print("-----") print(peopleResponse) print("-----") + // MARK: - Pass successfully parsed body to other parts of the code /* @@ -59,6 +65,7 @@ if case let .data(bodyData) = peopleResponse.body { } */ + // MARK: - Work in the abstract func process(document: T) { diff --git a/JSONAPI.playground/contents.xcplayground b/JSONAPI.playground/contents.xcplayground index 3da156e..21e919c 100644 --- a/JSONAPI.playground/contents.xcplayground +++ b/JSONAPI.playground/contents.xcplayground @@ -5,5 +5,6 @@ + \ No newline at end of file diff --git a/Tests/JSONAPITests/XCTestManifests.swift b/Tests/JSONAPITests/XCTestManifests.swift index 3011985..dc100bd 100644 --- a/Tests/JSONAPITests/XCTestManifests.swift +++ b/Tests/JSONAPITests/XCTestManifests.swift @@ -144,6 +144,10 @@ extension DocumentTests { ("test_singleDocumentSomeIncludesWithMetadata_encode", test_singleDocumentSomeIncludesWithMetadata_encode), ("test_singleDocumentSomeIncludesWithMetadataWithAPIDescription", test_singleDocumentSomeIncludesWithMetadataWithAPIDescription), ("test_singleDocumentSomeIncludesWithMetadataWithAPIDescription_encode", test_singleDocumentSomeIncludesWithMetadataWithAPIDescription_encode), + ("test_sparseIncludeFullPrimaryResource", test_sparseIncludeFullPrimaryResource), + ("test_sparseIncludeSparsePrimaryResource", test_sparseIncludeSparsePrimaryResource), + ("test_sparsePrimaryResource", test_sparsePrimaryResource), + ("test_sparsePrimaryResourceOptionalAndNil", test_sparsePrimaryResourceOptionalAndNil), ("test_unknownErrorDocumentAddIncludes", test_unknownErrorDocumentAddIncludes), ("test_unknownErrorDocumentAddIncludingType", test_unknownErrorDocumentAddIncludingType), ("test_unknownErrorDocumentMissingLinks", test_unknownErrorDocumentMissingLinks), @@ -275,6 +279,7 @@ extension IncludedTests { // to regenerate. static let __allTests__IncludedTests = [ ("test_appending", test_appending), + ("test_ComboSparseAndFullIncludeTypes", test_ComboSparseAndFullIncludeTypes), ("test_EightDifferentIncludes", test_EightDifferentIncludes), ("test_EightDifferentIncludes_encode", test_EightDifferentIncludes_encode), ("test_FiveDifferentIncludes", test_FiveDifferentIncludes), @@ -285,6 +290,7 @@ extension IncludedTests { ("test_NineDifferentIncludes_encode", test_NineDifferentIncludes_encode), ("test_OneInclude", test_OneInclude), ("test_OneInclude_encode", test_OneInclude_encode), + ("test_OneSparseIncludeType", test_OneSparseIncludeType), ("test_SevenDifferentIncludes", test_SevenDifferentIncludes), ("test_SevenDifferentIncludes_encode", test_SevenDifferentIncludes_encode), ("test_SixDifferentIncludes", test_SixDifferentIncludes), @@ -295,6 +301,7 @@ extension IncludedTests { ("test_TwoDifferentIncludes_encode", test_TwoDifferentIncludes_encode), ("test_TwoSameIncludes", test_TwoSameIncludes), ("test_TwoSameIncludes_encode", test_TwoSameIncludes_encode), + ("test_TwoSparseIncludeTypes", test_TwoSparseIncludeTypes), ("test_zeroIncludes", test_zeroIncludes), ("test_zeroIncludes_encode", test_zeroIncludes_encode), ("test_zeroIncludes_init", test_zeroIncludes_init),