Compare commits

...

345 Commits

Author SHA1 Message Date
Mathew Polzin a6b7d7a94a woops, abstract wrapper protocol landed in wrong module by accident 2019-11-15 17:05:45 -08:00
Mathew Polzin 1010489a02 compare(to:) bug fixes and test additions 2019-11-15 16:59:00 -08:00
Mathew Polzin ae855c85ee going through and fleshing out tests. minor adjustments and bug fixes. 2019-11-15 08:30:17 -08:00
Mathew Polzin 440b649577 Merge pull request #49 from mattpolzin/alpha/3x
Alpha/3x
2019-11-12 18:38:31 -08:00
Mathew Polzin 54551617b4 Add more errors for the resource object type property 2019-11-12 18:34:34 -08:00
Mathew Polzin 8c3a82ec23 Merge pull request #53 from mattpolzin/feature/better-error-messages
Feature/better error messages
2019-11-10 23:47:05 -08:00
Mathew Polzin e9a3b35dc7 improve error messages for poly include types 2019-11-10 23:43:35 -08:00
Mathew Polzin 2eecf95995 Adding Document Decoding Errors for some common problems 2019-11-10 23:02:26 -08:00
Mathew Polzin 4dc30ddc1c Rounding out the Resource Object errors 2019-11-10 20:46:35 -08:00
Mathew Polzin 455ff64326 update linuxmain 2019-11-09 00:34:39 -08:00
Mathew Polzin 0b4baf35d5 got some attribute cases added and tested. added some descriptions (custom string convertible) 2019-11-09 00:33:42 -08:00
Mathew Polzin 11ef050d58 most common relationship errors tested. 2019-11-08 18:47:28 -08:00
Mathew Polzin 86344ef93f trivial refactor in sparse fieldset file 2019-11-07 08:08:45 -08:00
Mathew Polzin 7fabe2574e rename some of the new public protocols in JSONAPITesting 2019-11-07 07:56:58 -08:00
Mathew Polzin 19636a47f0 Merge pull request #51 from mattpolzin/feature/primary-resource-body-improvements
Feature/primary resource body improvements
2019-11-06 23:46:13 -08:00
Mathew Polzin 0538de48cb rename PolyWrapped to CodablePolyWrapped (missed when I did other similar renaming 2019-11-06 23:41:02 -08:00
Mathew Polzin 024fe2d452 Down to one sequence of compare functions for all documents 2019-11-06 23:21:22 -08:00
Mathew Polzin 832161628b go from 3 specializations of all document related compare functions down to 2. 2019-11-06 23:05:21 -08:00
Mathew Polzin f37f44cfda add comparable protocol 2019-11-06 21:42:13 -08:00
Mathew Polzin ae7e0f528a abstract away document comparison 2019-11-06 00:19:27 -08:00
Mathew Polzin 9f42e5fa3f Merge pull request #48 from mattpolzin/feature/indentation
Feature/indentation
2019-11-05 23:03:03 -08:00
Mathew Polzin 3e96adf775 merge and fix conflicts 2019-11-05 23:00:18 -08:00
Mathew Polzin 83233a7e1a Merge pull request #46 from mattpolzin/feature/testing-comparisons
Feature/testing comparisons
2019-11-05 22:47:47 -08:00
Mathew Polzin adcc6bfb10 fix error after merge 2019-11-05 22:43:40 -08:00
Mathew Polzin 6f9a2df605 Merge remote-tracking branch 'origin/alpha/3x' into feature/testing-comparisons 2019-11-05 22:41:53 -08:00
Mathew Polzin c24b70b49f Merge pull request #45 from mattpolzin/feature/deprecate-subscript
Feature/deprecate subscript
2019-11-05 22:40:37 -08:00
Mathew Polzin 7b56e35a91 Merge pull request #47 from mattpolzin/feature/document-improvements
Feature/document improvements
2019-11-05 22:38:42 -08:00
Mathew Polzin e6f82c6052 indentation 2019-11-05 22:37:48 -08:00
Mathew Polzin d6f01d6c1d indentation 2019-11-05 22:27:59 -08:00
Mathew Polzin a7f6ed5845 update README 2019-11-05 21:03:51 -08:00
Mathew Polzin 87271b93f9 deprecate subscript attribute accessor in favor of key path dynamic member lookup 2019-11-05 20:48:04 -08:00
Mathew Polzin 706346e3a6 just reformatting and rearranging 2019-11-05 20:04:47 -08:00
Mathew Polzin 33a5ff41a0 beginning to do some breaking change cleanup 2019-11-05 19:50:52 -08:00
Mathew Polzin 0fe5c53ada generate linuxmain 2019-11-05 19:04:47 -08:00
Mathew Polzin f7bfa91ccc lots of comparison code, a few small breaking changes 2019-11-05 19:03:51 -08:00
Mathew Polzin 1a15ab6f9d Fix return type of success document after adding includes 2019-10-27 16:21:41 -07:00
Mathew Polzin eb85620379 regenerate swift test manifest 2019-10-20 22:30:15 -07:00
Mathew Polzin 57f85476c0 Bump podspec version 2019-10-20 22:29:22 -07:00
Mathew Polzin ea5c0b8601 Add Document.ErrorDocument and Document.SuccessDocument types 2019-10-20 22:23:52 -07:00
Mathew Polzin b46429a0ad Update README.md 2019-10-15 23:37:02 -07:00
Mathew Polzin dbc6ecc6d8 Update README.md 2019-10-12 21:39:29 -07:00
Mathew Polzin dbbacab105 Bump podspec version 2019-10-12 19:30:24 -07:00
Mathew Polzin 4eb36d4594 Merge pull request #41 from mattpolzin/feature/resource-object-replacing
Resource Object replacing and tapping
2019-10-12 19:27:29 -07:00
Mathew Polzin 43e02351de Add documentation on replacing and tapping attributes and relationships 2019-10-12 18:22:48 -07:00
Mathew Polzin 0b307bd3bc Add replacement and tapping functions for attributes and relationships. 2019-10-12 17:54:28 -07:00
Mathew Polzin 662a84ccf0 Add examples around modifying attributes/relationships on resource object and then preparing a new request (i.e. for PATCHing) 2019-10-12 09:35:01 -07:00
Mathew Polzin 44f9bca7dc Merge pull request #39 from mattpolzin/feature/add-poly-11
Add Include11 Type
2019-10-02 20:10:56 -07:00
Mathew Polzin 774b53b9b6 bump podspec version and bump podspec Poly dependency. 2019-10-02 20:05:28 -07:00
Mathew Polzin 654d3bfd2b Update Poly, add Include11 type. 2019-10-02 20:02:11 -07:00
Mathew Polzin c26d6d99c0 Update README.md 2019-09-29 17:21:43 -07:00
Mathew Polzin 43dcc4fb12 Update README.md 2019-09-29 17:21:08 -07:00
Mathew Polzin abae975f59 Update README.md 2019-09-29 17:20:42 -07:00
Mathew Polzin 0e6b2a7771 Update README.md 2019-09-29 17:14:25 -07:00
Mathew Polzin 194a58ae56 Merge pull request #37 from mattpolzin/feature/basic-error
Feature/basic error
2019-09-29 17:01:25 -07:00
Mathew Polzin f1d6b22f61 Add playground example, add/update documentation, correct visibility of new error payload properties to public. 2019-09-29 16:49:38 -07:00
Mathew Polzin 3057992348 bump podspec version, update Playground examples 2019-09-29 15:57:54 -07:00
Mathew Polzin d4806ff557 Add a few decode examples 2019-09-29 15:36:16 -07:00
Mathew Polzin b0801f7cee Add tests for BasicJSONAPIError and tweak documentation 2019-09-29 15:20:08 -07:00
Mathew Polzin 88c5d400aa Add generic and basic error types. add tests for generic type. 2019-09-29 14:56:04 -07:00
Mathew Polzin 6cd5aeaba6 Update README.md
Update max number of `Include` types in documentation.
2019-09-16 17:16:42 -07:00
Mathew Polzin 7e28cd2606 Merge pull request #35 from mattpolzin/add-include-10
Add include10 Type
2019-09-16 17:13:49 -07:00
Mathew Polzin cf6fa39548 bump Podspec version 2019-09-16 09:58:57 -07:00
Mathew Polzin a24f15dc4e regenerate linuxmain 2019-09-16 09:38:36 -07:00
Mathew Polzin 87e9ee0606 remove tests that are duplicates of those in the Poly package. I just never deleted this file when the Poly stuff moved into its own package. Add support for Include10. 2019-09-16 09:37:34 -07:00
Mathew Polzin 5ed45078a1 Update README.md
Fix incorrect Swift version requirement and suggested SPM dependency version.
2019-09-14 16:44:46 -07:00
Mathew Polzin 34dc351810 Merge pull request #34 from mattpolzin/swift-5.1
Swift 5.1 tools dependency, access to attributes via dynamicMemberLookup
2019-09-14 16:28:24 -07:00
Mathew Polzin 3ff1b867ca Bump spec version to account for breaking changes with configs and swift tools version 2019-09-14 15:53:48 -07:00
Mathew Polzin ae51be20d8 Merge remote-tracking branch 'origin/master' into swift-5.1 2019-09-14 15:51:05 -07:00
Mathew Polzin c142e93fec Merge pull request #33 from chrisballinger/patch-1
Change Pod name to avoid conflict. Resolves #32
2019-09-14 15:49:37 -07:00
Chris Ballinger e99c6625f8 Update README.md 2019-09-14 10:07:53 -07:00
Chris Ballinger e9048a4861 Change Pod name to avoid conflict. Resolves #32 2019-09-13 15:07:37 -07:00
Mathew Polzin ff06c36b36 uncomment out language version declaration in package manifest, 'fix' indentation. 2019-09-05 22:22:55 -07:00
Mathew Polzin 8eb4c14bb3 Merge remote-tracking branch 'origin/master' into swift-5.1 2019-09-05 21:39:27 -07:00
Mathew Polzin baee85fb1b Update README.md 2019-09-02 16:07:10 -07:00
Mathew Polzin 44f21b4430 Update README.md 2019-09-02 16:04:06 -07:00
Mathew Polzin f7f36c6e7a Update README.md 2019-09-02 15:59:24 -07:00
Mathew Polzin af7eaeb684 Update README.md 2019-09-02 15:10:43 -07:00
Mathew Polzin 0cc2368331 Update README.md
Add instructions for adding as Swift Package Manager dependency.
2019-09-02 15:05:23 -07:00
Mathew Polzin 6c54845525 Update README.md 2019-09-02 15:00:07 -07:00
Mathew Polzin 51b981c108 Update README.md 2019-09-02 13:25:57 -07:00
Mathew Polzin b33c7f4ab5 Update README.md 2019-08-29 09:33:03 -07:00
Mathew Polzin bf8245c7b8 Update README.md 2019-08-29 09:30:59 -07:00
Mathew Polzin 7bd4021790 Update README.md 2019-08-28 20:21:50 -07:00
Mathew Polzin 771641b82c Update README.md 2019-08-28 18:55:42 -07:00
Mathew Polzin 198e5a9800 Update README.md 2019-08-28 18:55:11 -07:00
Mathew Polzin 9db534dea0 Update README.md
Add basic example Google Colab.
2019-08-27 00:48:11 -07:00
Mathew Polzin 2b409e464e merge w/ master 2019-08-23 17:53:33 -07:00
Mathew Polzin 7824bd8f68 Merge pull request #30 from mattpolzin/release/1.0
Release/1.0
2019-08-14 17:39:42 -07:00
Mathew Polzin 89217f7187 bump podspec version 2019-08-14 17:31:43 -07:00
Mathew Polzin 86a9345fdd Adding some documentation and making SparseFieldEncoder internal because it does not need to be public. 2019-08-14 09:22:06 -07:00
Mathew Polzin 8f9ec11f27 Housekeeping in the README 2019-08-14 08:57:57 -07:00
Mathew Polzin 9a9403bd98 Merge pull request #28 from mattpolzin/sparse-encoding
Sparse encoding
2019-08-12 07:35:49 -07:00
Mathew Polzin 453ce4b3a8 whitespace changes -- trying to switch from one whitespace standard to another in cohesive chunks to fit Xcode 11 default. 2019-08-06 09:19:06 -07:00
Mathew Polzin 9a0bba8d07 Rename AppendableResourceBody to Appendable. It is used in conjunction with ResourceBody but does not conform to it anymore. 2019-08-06 09:12:02 -07:00
Mathew Polzin d5b4aa70c7 Update linuxmain 2019-08-06 09:04:15 -07:00
Mathew Polzin 1df891ce06 Add test that uses JSONAPIDocument protocol in generic context 2019-08-06 08:55:49 -07:00
Mathew Polzin 6ba217f553 More sparse field encoder tests 2019-08-06 08:27:22 -07:00
Mathew Polzin e23ec090ed Starting to add SparseFieldEncoder tests 2019-08-05 19:44:09 -07:00
Mathew Polzin ed23349351 Update README 2019-08-05 18:45:13 -07:00
Mathew Polzin 6c8646a1b4 whitespace 2019-08-05 18:09:29 -07:00
Mathew Polzin 3a60ac5fe2 Add playground page showing off sparse fieldset encoding 2019-08-05 17:59:38 -07:00
Mathew Polzin 045e88f4d4 More code documentation 2019-08-05 16:28:02 -07:00
Mathew Polzin fe1f4c6c19 A bit of code documentation 2019-08-05 16:15:56 -07:00
Mathew Polzin 83f7a7b60e Add sparse document tests 2019-08-05 16:02:40 -07:00
Mathew Polzin 61f2edb59a Add tests for sparse fieldset includes 2019-08-05 15:21:59 -07:00
Mathew Polzin 32d584099f Update linuxmain 2019-08-05 14:51:48 -07:00
Mathew Polzin 9a07cf7066 Add test class placeholder 2019-08-05 14:51:18 -07:00
Mathew Polzin 0c7c7edcab Add sparse fieldset resource body tests 2019-08-05 14:48:08 -07:00
Mathew Polzin a596ecaecc A stab at separating out decoding enough to make it possible to use encode-only sparse fieldsets with JSONDocument 2019-08-05 09:23:44 -07:00
Mathew Polzin 265cffe8f0 Rename MaybePrimaryResource to OptionalPrimaryResource because I use 'Maybe' elsewhere to indicate a type-level distinction whereas this is a value-level distinction that really is just 'Optional' at play. 2019-08-05 08:17:38 -07:00
Mathew Polzin b98fb08353 Adding a bit of code doc 2019-08-05 08:13:18 -07:00
Mathew Polzin 61e00c2de5 small rename 2019-08-05 08:01:42 -07:00
Mathew Polzin e0b53236bb Add sparse fields method to ResourceObject and test it. 2019-08-04 23:03:56 -07:00
Mathew Polzin 2f3a619284 Spacing difference 2019-08-04 18:47:26 -07:00
Mathew Polzin 5aef44c3b3 Add Sparse Fieldset support for Attributes 2019-08-04 18:44:28 -07:00
Mathew Polzin 99e97aadcc Merge branch 'master' into swift-5.1 2019-08-04 10:34:32 -07:00
Mathew Polzin 17e2ce3138 Merge pull request #27 from mattpolzin/round-off-empty-decoder-tests
Cover the EmptyObjectDecoder unkeyed container with tests
2019-08-04 10:30:57 -07:00
Mathew Polzin 049dc1f7b5 Cover the EmptyObjectDecoder unkeyed container with tests 2019-08-04 10:28:48 -07:00
Mathew Polzin cf7b68c026 Merge w/ master 2019-08-04 09:02:04 -07:00
Mathew Polzin 223c39cec0 Merge pull request #26 from mattpolzin/testcov1
Test coverage PR
2019-08-02 09:34:36 -07:00
Mathew Polzin 5335f24e9b Add test coverage for EmptyObjectDecoder's keyed container 2019-08-02 09:30:30 -07:00
Mathew Polzin 7ec185bfe8 Add a couple tests and a bit of code doc. 2019-08-02 08:45:38 -07:00
Mathew Polzin e0e6723222 Make Xcode Beta happy by explicitly creating optionals where they used to be implied. 2019-08-02 07:27:17 -07:00
Mathew Polzin 4b4ebb4214 merge w/ master 2019-07-30 17:52:42 -07:00
Mathew Polzin 60cd515fd6 just filling in a couple small holes in test coverage 2019-07-30 17:44:06 -07:00
Mathew Polzin 396f8453d1 swap out broken nil checks (not harming anything, but not working as evidenced by not getting hit by any test cases) 2019-07-29 21:00:44 -07:00
Mathew Polzin f5eb343bd4 Complete test coverage of APIDescription 2019-07-29 20:41:22 -07:00
Mathew Polzin c4f96c0376 Updated example in playground/documentation after realizing I was not using a variable I had created. 2019-07-28 13:10:42 -07:00
Mathew Polzin 4d3597ef0e Update README.md 2019-07-27 21:30:14 -07:00
Mathew Polzin fb72817de6 bump swift version badge to swift 5.1 2019-07-25 09:36:35 -07:00
Mathew Polzin 569cec05cf ditch language version argument in package file that is not accepted by swift test. update linuxmain. 2019-07-25 09:27:56 -07:00
Mathew Polzin 78b9b12592 Update README 2019-07-25 09:23:01 -07:00
Mathew Polzin 9143281290 Introduce dynamic member keypath lookup as alternative to existing subscript keypath lookup for attributes. also did a bit of tidying up. 2019-07-25 09:12:17 -07:00
Mathew Polzin badfa30668 Merge remote-tracking branch 'origin/master' into swift-5.1 2019-07-25 07:49:22 -07:00
Mathew Polzin 0144a2ee80 Remove prior experimentation with property wrappers -- feature was not baked when that experimentation was done. 2019-07-25 07:49:17 -07:00
Mathew Polzin 880894d026 small fix for example in README 2019-07-25 07:38:21 -07:00
Mathew Polzin 7b5b17918c Update linuxmain, slightly update wording and indentation on example at bottom of README. 2019-07-24 19:43:11 -07:00
Mathew Polzin c996e7447c Update README.md 2019-07-23 21:11:59 -07:00
Mathew Polzin 61fa56e84b bump podspec version after two cocoapods fixes. 2019-07-16 22:19:50 -07:00
Mathew Polzin a6bf3e0ae0 Merge pull request #22 from chrisballinger/fix-podspec
Remove Poly from podspec frameworks
2019-07-16 22:15:04 -07:00
Mathew Polzin 620f6989d2 Merge pull request #23 from chrisballinger/fix-issue-20
Do not include testing source files in podspec
2019-07-16 22:01:29 -07:00
Chris Ballinger 715db33138 Do not include testing source files in podspec 2019-07-16 17:11:36 -07:00
Chris Ballinger e99abd26b3 Remove Poly from podspec frameworks 2019-07-16 16:16:32 -07:00
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 c7b97567a9 update swift tools version in package file, add some property wrappers, add some tests for wrappers. its all broken but worth holding onto for now. 2019-06-20 22:35:55 -07: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
Mathew Polzin 4cd697aae4 Merge pull request #17 from mattpolzin/swift-5
Default to Swift version 5.0 and Swift Tools version 5.0
2019-04-16 22:35:11 -07:00
Mathew Polzin db0f7329e4 Comment out playground code that is failing in Xcode 10.2 2019-04-16 22:11:09 -07:00
Mathew Polzin fb710c397b Entirely remove Result type that not only is unneeded with Swift 5 but also was not being used by this library (it was left over from before Poly became its own library). Fix a few redundent public warnings. 2019-04-16 21:56:49 -07:00
Mathew Polzin b1adc60719 Update package and podspec files 2019-04-16 21:52:50 -07:00
Mathew Polzin 423e61b285 Merge branch 'master' into swift-5 2019-04-16 21:42:17 -07:00
Mathew Polzin 895d575cdb Fix indentation in README 2019-04-14 21:03:14 -07:00
Mathew Polzin cd75e9649b Update cocoapods version, update readme to mention cocoapods support. 2019-04-14 21:01:20 -07:00
Mathew Polzin c60dfc1584 Testing out podspec 2019-04-14 20:49:59 -07:00
Mathew Polzin 16004051e9 Update language version to 5.0 and specify deployment targets for iOS and Mac OS. 2019-02-17 15:13:02 -08:00
Mathew Polzin 864a66f1ea Merge remote-tracking branch 'origin/master' into swift-5 2019-02-17 14:43:19 -08:00
Mathew Polzin abe16dda0d A bit more clarification/correction 2019-02-07 18:56:22 -08:00
Mathew Polzin 5257fd79fa Remove redundant public scoping on functions within public extensions. This becomes a warning with the Swift 5 compiler. 2019-02-07 18:49:58 -08:00
Mathew Polzin f7a81df451 Fix inaccuracy and add a bit of clarity to README section on Entity Ids. 2019-02-07 18:43:57 -08:00
Mathew Polzin c8fbc09582 Remove Playground page that used the OpenAPI support that has now moved into its own repository. 2019-01-30 18:34:52 -08:00
Mathew Polzin a88bb77bd0 update linuxmain 2019-01-30 18:00:51 -08:00
Mathew Polzin 247848e788 Update README 2019-01-30 18:00:27 -08:00
Mathew Polzin 7adbdb4505 Move JSONAPIArbitrary and JSONAPIOpenAPI into their own packages. 2019-01-30 17:54:26 -08:00
Mathew Polzin 0c7bf9a92d Merge branch 'master' into swift-5 2019-01-29 16:42:59 -08:00
Mathew Polzin ea663bb229 Move Sampleable into its own Package. 2019-01-29 00:44:31 -08:00
Mathew Polzin f48c385f99 super minor code doc tweak 2019-01-28 23:51:29 -08:00
Mathew Polzin c96bfadd82 Had not updated linuxmain in a while. pretty sure I'll have problems building on Linux at least with the stuff going on in JSONAPI+OpenAPI. 2019-01-28 23:46:56 -08:00
Mathew Polzin 88be701062 Add OpenAPI support for TransformedAttributes 2019-01-28 23:45:28 -08:00
Mathew Polzin 1e85e1d2a3 fix open API playground. 2019-01-27 22:55:32 -08:00
Mathew Polzin 11a7727ac9 Fix Include OpenAPI support. Fix bug with single include being considered 'OneOf' even though that is an overcomplication of the schema. Add tests for single include type and two include types on document 2019-01-27 22:47:01 -08:00
Mathew Polzin e8bfbc881b Added a couple of test cases for JSONDocuments 2019-01-27 22:24:13 -08:00
Mathew Polzin af757a2fac Add emphasis to README line. 2019-01-27 21:46:22 -08:00
Mathew Polzin 84955872f8 Finish writing Date Attribute test cases and add support for Optional Date OpenAPI Node guesses. 2019-01-27 21:39:18 -08:00
Mathew Polzin cb2800abd4 Add support for RequestBody on an OpenAPI Operation. 2019-01-27 13:57:04 -08:00
Mathew Polzin 85d5fef3c8 Fix bug where some references were correctly encoded as objects and others were just encoded as strings 2019-01-27 13:22:18 -08:00
Mathew Polzin e4ef61fd56 bugfix: OpenAPI path components should begin with a slash. 2019-01-27 12:46:13 -08:00
Mathew Polzin 75ec4f156e Fix a third dictionary-as-array bug 2019-01-26 20:04:11 -08:00
Mathew Polzin b45fc73188 Merge pull request #15 from mattpolzin/feature/OpenAPISchema
Feature/open api schema
2019-01-26 19:52:15 -08:00
Mathew Polzin 3b73dc9989 Fix super shitty bug caused by Apples implementation of Dictionary's conformance to Encodable sometimes encoding the dictionary as an array. 2019-01-26 19:50:20 -08:00
Mathew Polzin b2c81026f4 remove 'id' from Unidentified Entity OpenAPI node. 2019-01-26 18:55:47 -08:00
Mathew Polzin 8d057b4398 Merge pull request #14 from mattpolzin/feature/OpenAPISchema
Just enough OpenAPI Schema stuff to be dangerous
2019-01-25 18:25:19 -08:00
Mathew Polzin c8421cdd58 just throw that json extension on the filename to make everything super real 2019-01-25 18:20:04 -08:00
Mathew Polzin cde10a8491 Finish implementing remaining first wave of encodable conformances for OpenAPI 2019-01-25 18:17:58 -08:00
Mathew Polzin ad05d3908a Add a simple test OpenAPISchema and start to tweak things to get it workable. 2019-01-25 12:49:59 -08:00
Mathew Polzin 23b2b2e04f merge w/ master 2019-01-25 12:01:01 -08:00
Mathew Polzin 2988503d7d Add Sampleable conformance to Unidentified. Rename file slightly. 2019-01-25 11:59:05 -08:00
Mathew Polzin 5ea83b07c1 Hopefully remove some ambiguity. 2019-01-24 19:09:46 -08:00
Mathew Polzin 2b59f54067 Fix bug causing a supplied encoder to be used for generating an example but not for helping determine the correct Date formatting. 2019-01-24 17:47:44 -08:00
Mathew Polzin 58a7c82436 Restructure files a bit. Make Date handling relatively robust compared to my first pass at it. Make the failure to construct a generic open API node type throw an error rather than silently omit the node. 2019-01-24 17:25:34 -08:00
Mathew Polzin dc30cb3b9e Make Attribute Sampleable where its RawValue is Sampleable. Allow Sampleable things to provide a best guess for their node type based on the result of encoding and then deserializing them. 2019-01-24 00:47:24 -08:00
Mathew Polzin 7045373708 update and make sure all Playground pages run 2019-01-23 23:24:53 -08:00
Mathew Polzin 952fe8ba7e Remove encoder requirement to almost all Open API Node constructors. Made a new protocol for the few places where an encoder did need to be passed in. 2019-01-23 23:21:16 -08:00
Mathew Polzin 951c04ad44 Add Sampleable conformances. Make blanket JSONEncoder change I am not happy with; will try to walk back requirement that encoder gets passed to all functions creating OpenAPI Nodes 2019-01-23 22:21:27 -08:00
Mathew Polzin 57df6b147e Add Sampleable conformance to UnknownJSONAPIError. Add Sampleable conformance to all Include types and the Includes type. 2019-01-23 11:52:16 -08:00
Mathew Polzin a88844fe5e Switch to Poly 1.0.0 (same commit as previously used but by version rather than branch) 2019-01-23 00:34:14 -08:00
Mathew Polzin d1cf19f9fe Neck deep in stubbing out OpenAPI types 2019-01-22 22:14:36 -08:00
Mathew Polzin 59835fbe11 Add JSON Reference and OpenAPIComponents (initially just with schemas) 2019-01-22 16:08:47 -08:00
Mathew Polzin 850a713dcb Merge pull request #13 from mattpolzin/feature/OpenAPISchema
Add Include support to OpenAPI schema of JSONAPI Document.
2019-01-22 12:01:40 -08:00
Mathew Polzin e3c637a41e update README and add associated type labels to patterns for clarity 2019-01-22 11:57:18 -08:00
Mathew Polzin 9cbc626410 Add Include support to OpenAPI schema of JSONAPI Document. 2019-01-22 11:53:32 -08:00
Mathew Polzin 6dd14daf53 Merge pull request #12 from mattpolzin/feature/OpenAPISchema
Testing, Documentation, more Arbitrary conformance, "example" support.
2019-01-22 08:21:22 -08:00
Mathew Polzin 744e08acf4 Update linuxmain 2019-01-21 23:00:13 -08:00
Mathew Polzin 5da4a963d4 Update README and code documentation slightly 2019-01-21 22:50:19 -08:00
Mathew Polzin 5433dddc81 Add support for 'const' via OpenAPI 3.0 workaround suggested by others: Use 'enum' with one value. Add Sampleable support for requesting samples representing 'success' and 'failure' if available. 2019-01-21 22:47:03 -08:00
Mathew Polzin 95f9d8084d Add Arbitrary conformance for UnknownJSONAPIError. Fix weird encoding exception that I cannot quite figure out or explain at the moment. 2019-01-21 21:56:33 -08:00
Mathew Polzin d6911f170c Add example to JSONNode 2019-01-21 20:57:54 -08:00
Mathew Polzin 9972d13a4e Document zip(with:) on optionals 2019-01-21 15:37:41 -08:00
Mathew Polzin 4dc63167d6 Rename a couple of files 2019-01-21 15:35:54 -08:00
Mathew Polzin 845d085455 Fill out a fair bit of OpenAPI Entity testing 2019-01-21 15:32:38 -08:00
Mathew Polzin 45eb7d06bd Merge branch 'master' into feature/OpenAPISchema 2019-01-21 13:18:59 -08:00
Mathew Polzin 6ce962cec9 Merge pull request #11 from mattpolzin/feature/Arbitrary
Add Arbitrary support for the rest of JSONAPI's key types.
2019-01-21 13:16:46 -08:00
Mathew Polzin 477dae0a73 Add Arbitrary support for the rest of JSONAPI's key types. 2019-01-21 13:13:09 -08:00
Mathew Polzin 4afe4cfb31 Rename first JSONAPI+Testing header to Testing to remove conflict in GitHub Markdown links. 2019-01-21 11:52:02 -08:00
Mathew Polzin 65144d27ae Merge pull request #10 from mattpolzin/feature/OpenAPISchema
Feature/open api schema
2019-01-21 11:50:02 -08:00
Mathew Polzin 1ec4b21c34 Add missing case in Sampleable generic OpenAPINode implementation. Add info on new auxiliary frameworks to the README. 2019-01-20 19:18:10 -08:00
Mathew Polzin 15e13e2cc7 Remove unused error and add a bit of code documentation 2019-01-20 18:54:37 -08:00
Mathew Polzin 1d8ceef317 Update README, add a bit more code documentation 2019-01-20 18:52:21 -08:00
Mathew Polzin e546614c15 merge with Arbitrary branch 2019-01-20 18:41:54 -08:00
Mathew Polzin 8939d637d4 Add placeholder test to arbitrary test target. 2019-01-20 18:34:24 -08:00
Mathew Polzin e3b005b89a Update README 2019-01-20 18:25:28 -08:00
Mathew Polzin 2de6580519 Add some tests around Attribute OpenAPI descriptions 2019-01-20 18:18:35 -08:00
Mathew Polzin 875d938b95 Add id and type properties to Entity OpenAPI Schema 2019-01-20 16:19:26 -08:00
Mathew Polzin 1ed34b00dc Add todos for Entity Id and type. Add example usage of OpenAPI entity schema support in Playground 2019-01-20 16:10:31 -08:00
Mathew Polzin 531a7f35bd Cleanup and a bit of documentation 2019-01-20 15:50:46 -08:00
Mathew Polzin dc42ec27fc Not crazy about how I got there, but now it is relatively easy to print arbitrary enum's allCases as the list of possible values in the format specced out by OpenAPI. 2019-01-20 15:39:54 -08:00
Mathew Polzin cf746e182f currently in a pretty broken state with support for enumerations being turned into allowed values via reflection. I think I am going to have to give up type safety if I want to use reflection and keep things open ended 2019-01-19 15:30:09 -08:00
Mathew Polzin 52d2e9819d Update README project status to include OpenAPI and Arbitrary work. 2019-01-15 09:40:49 -08:00
Mathew Polzin 81b78cd749 Add more Arbitrary conformances 2019-01-15 09:27:09 -08:00
Mathew Polzin 3a0ede2372 Get rid of OpenAPI namespace because a compiler bug was making it more hassle than it was worth. 2019-01-14 23:46:39 -08:00
Mathew Polzin cb04a01e71 Add some todos and delete an old file 2019-01-14 23:29:49 -08:00
Mathew Polzin 3c29244540 fill out some of the relationship tests 2019-01-14 23:14:25 -08:00
Mathew Polzin a9e1e49a79 Add test helpers for Open API test target 2019-01-14 21:18:49 -08:00
Mathew Polzin 9e6e713ad2 Add Codable for the parts of OpenAPI that are built so far. 2019-01-14 21:17:07 -08:00
Mathew Polzin 308f168a8c Trying to map out the Open API Schema Object using Swift enums 2019-01-13 23:24:02 -08:00
Mathew Polzin 8726bcc9fa Update all references to JSONAPITestLib to reflect renaming 2019-01-13 18:35:20 -08:00
Mathew Polzin 25bda795a7 Rename JSONAPITestLib to JSONAPITesting to fit with naming convention I am adopting going forward. Added OpenAPI JSON schema types. Added default schema types for a handful of Swift primitives. 2019-01-13 18:28:41 -08:00
Mathew Polzin 14a0256885 Add new targets 2019-01-13 17:27:10 -08:00
Mathew Polzin b2041e9bc8 Update to bleeding edge Poly library 2019-01-13 07:47:20 -08:00
Mathew Polzin fd9a626551 Ditch unused test constant. 2019-01-13 07:42:13 -08:00
Mathew Polzin 69bb40aa00 Move JSONAPITestLib tests into their own test target. 2019-01-13 07:16:46 -08:00
Mathew Polzin 060e0e3898 Move PolyWrapped out of Poly and into JSONAPI. 2019-01-12 21:51:51 -08:00
Mathew Polzin b374367234 Move Poly into its own library. 2019-01-12 21:30:20 -08:00
Mathew Polzin b363db30cd Add MIT license docuemnt 2019-01-12 18:41:04 -08:00
Mathew Polzin 6904416623 Begin making Poly more useful in a broader context than JSONAPI. 2019-01-12 17:08:46 -08:00
Mathew Polzin b294cec542 Merge remote-tracking branch 'origin/master' into swift-5 2019-01-08 21:27:39 -08:00
Mathew Polzin c04d3301b6 Add Meta-Relationship access. 2019-01-08 21:23:17 -08:00
Mathew Polzin 11e8d13f02 Switch package to swift 5, fix warnings. 2019-01-08 20:47:12 -08:00
Mathew Polzin 5a64bebc99 Merge branch 'master' of github.com:mattpolzin/JSONAPI 2019-01-08 20:13:11 -08:00
Mathew Polzin 790b0fbf52 Change capitalization 2019-01-06 18:20:18 -08:00
Mathew Polzin 539ecc451a Remove swift syntax highlighting from JSON snippet. 2019-01-03 22:26:25 -08:00
Mathew Polzin 7eb1b05eae Add swift syntax highlighting to README. 2019-01-03 22:23:17 -08:00
Mathew Polzin 3408263c2a Add warning note to README 2019-01-03 22:09:43 -08:00
Mathew Polzin 1d6e5d3810 Added Meta-Attribute support and documentation 2019-01-02 22:49:38 -08:00
Mathew Polzin d68404db36 update README to reflect change made in v0.12.0 2019-01-02 19:46:30 -08:00
Mathew Polzin 4f7db98a87 Update playground files to work with v0.12.0 changes 2019-01-02 19:42:32 -08:00
Mathew Polzin 072b081ac3 Breaking - Rename static var type: String to static var jsonType:String to avoid unnecessary conflict with Swift.type(of:) 2019-01-02 19:35:50 -08:00
Mathew Polzin 897410492d Add much more substantial example to README and mirror it in the included playground. Add convenient methods for adding includes to a Document. Make Poly less picky about what type of things it contains (don't need to be entities anymore). Typealias Either to Poly2 because they are isomorphic. 2019-01-02 17:14:58 -08:00
Mathew Polzin b46f5166ea APIDescription gets a public initializer. A new playground page explores the full potential of the Framework, in a sense. 2018-12-31 17:19:51 -08:00
Mathew Polzin d5a24c4adb Split Attribute out into its own Type (no longer just a type alias to TransformedAttribute) 2018-12-29 23:07:14 -08:00
Mathew Polzin 669d5d1342 Add ability to merge Document.Body.Data values 2018-12-28 19:11:24 -08:00
Mathew Polzin 923ab7d9f4 Add support for Poly8/Poly9 and Include8/Include9 2018-12-28 11:05:18 -08:00
Mathew Polzin 72769b6107 Fix small problem with sample code in README 2018-12-27 18:22:31 -08:00
Mathew Polzin 109e15d741 Add convenience method for default decoding of attributes. add tests for custom decoding and encoding as well as custom coding keys. add documentation. 2018-12-27 18:18:34 -08:00
Mathew Polzin 72180f64ef clarify some things about computed attribute properties. 2018-12-27 16:27:10 -08:00
Mathew Polzin fc962f9a0d Lift the constraint that Attributes and Relationships are Codable for EntityProxies. 2018-12-24 07:05:35 -08:00
Mathew Polzin 52eb123166 update documentation 2018-12-22 13:57:38 -08:00
Mathew Polzin 4ef147ec45 Update linuxmain 2018-12-22 13:49:10 -08:00
Mathew Polzin 61074ecc69 Add methods that make it easy to copy an entity with a new ID or copy an unidentified entity and give it an ID 2018-12-22 13:41:34 -08:00
Mathew Polzin 4dbcff6023 Add another way to initialize a nullable attribute to the tests 2018-12-22 13:10:05 -08:00
Mathew Polzin d6e82fab55 get id and type tests into the encoded entity test function 2018-12-21 08:53:55 -08:00
Mathew Polzin 5fa9848f02 test some attribute encoding a bit further 2018-12-21 08:45:51 -08:00
Mathew Polzin f38399a1d6 Add a bit of entity structure testing 2018-12-21 07:50:32 -08:00
Mathew Polzin 28f664326d Added a couple of tests 2018-12-12 21:05:07 -08:00
Mathew Polzin 3b7ef4aeb9 Add access to easy optional access to entire data on Document.Body 2018-12-12 20:05:29 -08:00
Mathew Polzin bb1ed30e89 Rename Document.Body.primaryData to primaryResource 2018-12-12 20:04:14 -08:00
Mathew Polzin dfdc266645 remove a bunch of convenience initializers that appeared to be giving the compiler some strife 2018-12-10 22:45:28 -08:00
Mathew Polzin 585cad0a83 Added literal initialization for nullable Attributes (TestLib) 2018-12-10 21:50:30 -08:00
Mathew Polzin 89abdd4cca fix example code in Playground 2018-12-10 21:11:48 -08:00
Mathew Polzin 67a43be26c update linuxmain 2018-12-08 20:05:00 -08:00
Mathew Polzin 8f279ce191 Add tests around nullable attributes and remove unnecessary encoding code 2018-12-08 20:04:11 -08:00
Mathew Polzin c9d388579f Made it much more convenient to work with Non-EntityType relationships. Discovered and fixed a bug where nullable relationships were encoded incorrectly. 2018-12-08 19:48:10 -08:00
Mathew Polzin 1061283905 loosen the grip entity had on Ids 2018-12-08 01:17:47 -08:00
Mathew Polzin 08949d0a93 Move encoding error type to its own file. Restructure Relatable and OptionalRelatable to not be dependent upon EntityDescription 2018-12-08 00:36:05 -08:00
Mathew Polzin 3047e2d23a Add access to computed attributes that are not AttributeType 2018-12-07 21:19:08 -08:00
Mathew Polzin 41a2a01788 Added support for relationship operator ~> to optional relationships 2018-12-07 20:59:39 -08:00
Mathew Polzin 53f7f55e07 Add Poly7 and Include7 because why not 2018-12-06 18:38:20 -08:00
Mathew Polzin d8d030286d Add a little bit of code doc 2018-12-06 18:12:56 -08:00
Mathew Polzin 005a981bf7 Add a couple of missing initializers to Entity 2018-12-05 22:51:12 -08:00
Mathew Polzin fad45203dd small refactor to consider ToOneRelationship with no meta or links to be even more synonymous with 'pointer' 2018-12-05 22:42:34 -08:00
Mathew Polzin 3468cb555a Remove Result dependency in favor of a super stripped down internally scoped Result enum. This won't interfere with other Result libraries and I was not exposing the Result anyway. 2018-12-05 22:02:59 -08:00
Mathew Polzin 8defe82536 Update README and linuxmain 2018-12-05 18:33:07 -08:00
Mathew Polzin 1f83216b03 Adding a few tests here and there 2018-12-05 18:31:53 -08:00
Mathew Polzin 6e6973c170 Adding test coverage for Relationship Meta and Links 2018-12-05 18:12:00 -08:00
Mathew Polzin 4f52cc2fd1 update README 2018-12-05 17:30:48 -08:00
Mathew Polzin 35d6cbb440 Add support for Relationship Meta and Links (untested) 2018-12-05 09:14:38 -08:00
Mathew Polzin d667e91a6a Add some missing initializers for Entity and fix Playground sources. 2018-12-03 19:08:09 -08:00
Mathew Polzin 75c8ce7405 Update linuxmain 2018-12-03 18:46:03 -08:00
Mathew Polzin 3a8237ec0f Add test coverage for Meta and Links on Entity 2018-12-03 18:45:07 -08:00
Mathew Polzin 49e33baa96 give all top-level-ish headers the JSONAPI prefix 2018-12-03 18:14:05 -08:00
Mathew Polzin f2a9b5a7b9 That should do the trick 2018-12-03 18:10:36 -08:00
Mathew Polzin 24baadd7a8 try to make headers unique 2018-12-03 18:09:40 -08:00
Mathew Polzin 8d18be6154 Update README after adding Meta and Links to Entity. 2018-12-03 18:04:53 -08:00
Mathew Polzin 8aa887b527 Add metadata and links to resource objects (i.e. Entity). untested. 2018-12-02 22:57:59 -08:00
Mathew Polzin 7d0a686dd9 update linuxmain 2018-12-02 21:39:45 -08:00
Mathew Polzin e4eb7816d7 Added an APIDescription type to Document that supports the JSON API Spec's JSON:API Object. 2018-12-02 21:35:09 -08:00
Mathew Polzin 482174f5b4 update linuxmain 2018-12-02 01:44:02 -08:00
Mathew Polzin 339480264e Add JSON:API Object to Document as the APIDescription type. Begin to add testing around this new type. 2018-12-02 01:43:27 -08:00
Mathew Polzin 5d666ec998 move entity validator (i.e. check function) section of project status into a TestLib subsection. 2018-12-01 09:58:07 -08:00
Mathew Polzin 9cacbe9b8b Revamp project status section and remove old todo list items. 2018-12-01 09:55:31 -08:00
Mathew Polzin 6663ad042e A cleanup and clarification pass at the README 2018-12-01 01:13:05 -08:00
Mathew Polzin 07402259c4 Add ability to specify that a SingleResourceBody should be optional or not (or specifically that its PrimaryResource is nullable or not). add tests. update documentation. 2018-12-01 00:36:39 -08:00
Mathew Polzin 8274ecb523 Add tests confirming that initializers work as expected without using any TestLib conveniences 2018-11-30 23:56:18 -08:00
Mathew Polzin 853c7e892a Fix playground bug caused by not running after removing a typealias 2018-11-30 20:18:08 -08:00
Mathew Polzin 04bf96a0cf update linuxmain 2018-11-29 20:13:21 -08:00
Mathew Polzin 8668311307 better poly proxy tests 2018-11-29 20:10:56 -08:00
Mathew Polzin 689517c633 Add ability to differentiate between things that _act_ like Entities and things that _are_ entities in the sense that the latter are persisted between client and server but the former is just a clientside convenience. 2018-11-29 20:03:44 -08:00
Mathew Polzin 65b80ee0eb Add ReversibleTransformer and tests. remove unneeded check in Entity.check(). 2018-11-29 18:39:59 -08:00
Mathew Polzin e91a03b396 Expose initializer for Document.Body.Data 2018-11-29 13:37:06 -08:00
Mathew Polzin 82088c7852 Remove BodyData which just aliased Body.Data 2018-11-29 13:15:07 -08:00
Mathew Polzin 34a4c8e7fc Add TestLib ability to represent to-one relationships with the values of their IDs. 2018-11-29 11:31:17 -08:00
Mathew Polzin 6aeb859c24 Remove now deleted test from linuxmain 2018-11-28 21:13:47 -08:00
Mathew Polzin 163ac94c51 I did some more type wrangling to finally get the Id type to specialize on Entity rather than EntityDescription. The compiler gets into trouble depending on which of a few semantically identical routes are taken, but I finally stumbled upon the correct combination of protocols and extensions to get the job done. this was always the ideal outcome, but I was not sure the Swift compiler would allow it. 2018-11-28 21:13:07 -08:00
Mathew Polzin e67b9fc142 Update playground and README for computed attributes. 2018-11-28 09:20:50 -08:00
Mathew Polzin a628992fcb Make Attribute a Functor to make computed attributes easier to write. 2018-11-28 09:09:23 -08:00
Mathew Polzin cf47f88a61 Add tests to confirm that Entities can safely have computed attributes or relationships. 2018-11-28 08:15:57 -08:00
Mathew Polzin 0425e2adcb rename folder in tests 2018-11-28 07:50:47 -08:00
Mathew Polzin d3763ba713 Add array literal expressibility for ToManyRelationship 2018-11-27 23:59:12 -08:00
121 changed files with 16135 additions and 3638 deletions
@@ -0,0 +1,175 @@
import Foundation
import JSONAPI
import Poly
// MARK: - Preamble (setup)
// Make String a CreatableRawIdType. This is actually done in
// this Playground's Entities.swift file, so it is commented out here.
/*
var globalStringId: Int = 0
extension String: CreatableRawIdType {
public static func unique() -> String {
globalStringId += 1
return String(globalStringId)
}
}
*/
// Create a typealias because 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: ResourceObjectDescription> = JSONAPI.ResourceObject<Description, NoMetadata, NoLinks, String>
// Similarly, 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: ResourceObjectDescription> = JSONAPI.ResourceObject<Description, NoMetadata, NoLinks, Unidentified>
// Create relationship typealiases because we do not expect
// JSON:API Relationships for this particular API to have
// Metadata or Links associated with them.
typealias ToOneRelationship<Entity: Identifiable> = JSONAPI.ToOneRelationship<Entity, NoMetadata, NoLinks>
typealias ToManyRelationship<Entity: Relatable> = JSONAPI.ToManyRelationship<Entity, NoMetadata, NoLinks>
// Create a typealias for a Document because we do not expect
// JSON:API Documents for this particular API to have Metadata, Links,
// useful Errors, or an APIDescription (The *SPEC* calls this
// "API Description" the "JSON:API Object").
typealias Document<PrimaryResourceBody: JSONAPI.ResourceBody, IncludeType: JSONAPI.Include> = JSONAPI.Document<PrimaryResourceBody, NoMetadata, NoLinks, IncludeType, NoAPIDescription, BasicJSONAPIError<String>>
// MARK: Entity Definitions
enum AuthorDescription: ResourceObjectDescription {
public static var jsonType: String { return "authors" }
public struct Attributes: JSONAPI.Attributes {
public let name: Attribute<String>
}
public typealias Relationships = NoRelationships
}
typealias Author = JSONEntity<AuthorDescription>
enum ArticleDescription: ResourceObjectDescription {
public static var jsonType: String { return "articles" }
public struct Attributes: JSONAPI.Attributes {
public let title: Attribute<String>
public let abstract: Attribute<String>
}
public struct Relationships: JSONAPI.Relationships {
public let author: ToOneRelationship<Author>
}
}
typealias Article = JSONEntity<ArticleDescription>
// MARK: Document Definitions
// We create a typealias to represent a document containing one Article
// and including its Author
typealias SingleArticleDocumentWithIncludes = Document<SingleResourceBody<Article>, Include1<Author>>
// ... and a typealias to represent a document containing one Article and
// not including any related entities.
typealias SingleArticleDocument = Document<SingleResourceBody<Article>, NoIncludes>
// MARK: - Server Pseudo-example
// Skipping over all the API and database stuff, here's a chunk of code
// that creates a document. Note that this document is the entirety
// of a JSON:API response body.
func articleDocument(includeAuthor: Bool) -> Either<SingleArticleDocument, SingleArticleDocumentWithIncludes> {
// Let's pretend all of this is coming from a database:
let authorId = Author.Identifier(rawValue: "1234")
let article = Article(id: .init(rawValue: "5678"),
attributes: .init(title: .init(value: "JSON:API in Swift"),
abstract: .init(value: "Not yet written")),
relationships: .init(author: .init(id: authorId)),
meta: .none,
links: .none)
let document = SingleArticleDocument(apiDescription: .none,
body: .init(resourceObject: article),
includes: .none,
meta: .none,
links: .none)
switch includeAuthor {
case false:
return .init(document)
case true:
let author = Author(id: authorId,
attributes: .init(name: .init(value: "Janice Bluff")),
relationships: .none,
meta: .none,
links: .none)
let includes: Includes<SingleArticleDocumentWithIncludes.Include> = .init(values: [.init(author)])
return .init(document.including(includes))
}
}
let encoder = JSONEncoder()
encoder.keyEncodingStrategy = .convertToSnakeCase
encoder.outputFormatting = .prettyPrinted
let responseBody = articleDocument(includeAuthor: true)
let responseData = try! encoder.encode(responseBody)
// Next step would be setting the HTTP body of a response.
// We will just print it out instead:
print("-----")
print(String(data: responseData, encoding: .utf8)!)
// ... and if we had received a request for an article without
// including the author:
let otherResponseBody = articleDocument(includeAuthor: false)
let otherResponseData = try! encoder.encode(otherResponseBody)
print("-----")
print(String(data: otherResponseData, encoding: .utf8)!)
// MARK: - Client Pseudo-example
enum NetworkError: Swift.Error {
case serverError
case quantityMismatch
}
// Skipping over all the API stuff, here's a chunk of code that will
// decode a document. We will assume we have made a request for a
// single article including the author.
func docode(articleResponseData: Data) throws -> (article: Article, author: Author) {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let articleDocument = try decoder.decode(SingleArticleDocumentWithIncludes.self, from: articleResponseData)
switch articleDocument.body {
case .data(let data):
let authors = data.includes[Author.self]
guard authors.count == 1 else {
throw NetworkError.quantityMismatch
}
return (article: data.primary.value, author: authors[0])
case .errors(let errors, meta: _, links: _):
throw NetworkError.serverError
}
}
let response = try! docode(articleResponseData: responseData)
// Next step would be to do something useful with the article and author but we will print them instead.
print("-----")
print(response.article)
print(response.author)
@@ -0,0 +1,213 @@
import Foundation
import JSONAPI
/*
This is not realistically what JSONAPI code will look like. It makes
every bit of sense to typealias the long-winded generics of this
framework to make working with them more concise and readable.
The purpose of this example, then, is not to show "good" JSONAPI
code, but rather to generate a JSONAPI example document that uses
all the various (often optional) parts of a JSONAPI document.
The JSON example produced in this manner can be used to compare the
nomenclature of this library with the nomenclature of the JSON:API Spec.
*/
// MARK: - Extensions
extension Foundation.URL: JSONAPIURL {}
// extension String: CreatableRawIdType {} found in playground Entities.swift file.
// MARK: - Definitions
/// Metadata associated with entities
struct EntityMetadata: JSONAPI.Meta {
let creationDate: Date
let updatedDate: Date
}
/// Metadata associated with links
struct LinkMeta: JSONAPI.Meta {
/// Don't trust this link past the expiry date.
let expiry: Date
}
/// Links associated with entities
struct EntityLinks: JSONAPI.Links {
let `self`: Link<Foundation.URL, LinkMeta>
init(selfLink: Link<Foundation.URL, LinkMeta>) {
self.`self` = selfLink
}
}
/// Metadata associated with relationships
struct ToManyRelationshipMetadata: JSONAPI.Meta {
/// Pagination for the relationship (to support really large
/// to-many relationships)
let pagination: Pagination
struct Pagination: Codable, Equatable {
let total: Int
let limit: Int
let offset: Int
}
}
/// Links associated with relationships
struct ToManyRelationshipLinks: JSONAPI.Links {
/// next page of relationships.
let next: Link<Foundation.URL, LinkMeta>
}
/// Description of an Author entity.
enum AuthorDescription: ResourceObjectDescription {
static var jsonType: String { return "authors" }
struct Attributes: JSONAPI.Attributes {
let name: Attribute<String>
}
struct Relationships: JSONAPI.Relationships {
let articles: ToManyRelationship<Article, ToManyRelationshipMetadata, ToManyRelationshipLinks>
}
}
typealias Author = JSONAPI.ResourceObject<AuthorDescription, EntityMetadata, EntityLinks, String>
/// Description of an Article entity.
enum ArticleDescription: ResourceObjectDescription {
static var jsonType: String { return "articles" }
struct Attributes: JSONAPI.Attributes {
let title: Attribute<String>
}
struct Relationships: JSONAPI.Relationships {
/// The primary attributed author of the article.
let primaryAuthor: ToOneRelationship<Author, NoMetadata, NoLinks>
/// All authors excluding the primary author.
/// It is customary to print the primary author's
/// name first, followed by the other authors.
let otherAuthors: ToManyRelationship<Author, ToManyRelationshipMetadata, ToManyRelationshipLinks>
}
}
typealias Article = JSONAPI.ResourceObject<ArticleDescription, EntityMetadata, EntityLinks, String>
/// Metadata associated with the API Description
struct APIDescriptionMetadata: JSONAPI.Meta {
/// A clever codename for this API version.
let codename: String
}
/// Metadata associated with any Document
struct DocumentMetadata: JSONAPI.Meta {
/// Assuming caching is in play, this is when
/// the cached document being returned was created.
let updatedDate: Date
}
/// Links associated with an Article Document
struct SingleArticleDocumentLinks: JSONAPI.Links {
let otherArticlesByPrimaryAuthor: Link<Foundation.URL, LinkMeta>
}
/// Error that can occur when retrieving a Single Article Document
enum ArticleDocumentError: String, JSONAPIError, Codable {
case unknownError = "unknown"
case missing = "missing"
static var unknown: ArticleDocumentError {
return .unknownError
}
}
typealias SingleArticleDocument = JSONAPI.Document<SingleResourceBody<Article>, DocumentMetadata, SingleArticleDocumentLinks, Include1<Author>, APIDescription<APIDescriptionMetadata>, ArticleDocumentError>
// MARK: - Instantiations
let authorId1 = Author.Identifier()
let authorId2 = Author.Identifier()
let authorId3 = Author.Identifier()
let now = Date()
let tomorrow = Calendar.current.date(byAdding: .day, value: 1, to: now)!
let tomorrowEntityMeta = EntityMetadata(creationDate: Calendar.current.date(byAdding: .day, value: -10, to: now)!,
updatedDate: Calendar.current.date(byAdding: .day, value: -2, to: now)!)
let articleLinks = EntityLinks(selfLink: .init(url: URL(string: "https://articles.com/article/1234")!,
meta: .init(expiry: tomorrow)))
let article = Article(attributes: .init(title: .init(value: "How to read JSONAPI")),
relationships: .init(primaryAuthor: .init(id: authorId1),
otherAuthors: .init(ids: [authorId2, authorId3],
meta: .init(pagination: .init(total: 2,
limit: 50,
offset: 0)),
links: .init(next: .init(url: URL(string: "https://articles.com/article/1234?otherAuthors[offset]=50")!,
meta: .init(expiry: tomorrow))))),
meta: tomorrowEntityMeta,
links: articleLinks)
let author1Links = EntityLinks(selfLink: .init(url: URL(string: "https://articles.com/author/\(authorId1.rawValue)")!,
meta: .init(expiry: tomorrow)))
let author1 = Author(id: authorId1,
attributes: .init(name: .init(value: "James Kinney")),
relationships: .init(articles: .init(ids: [article.id, Article.Identifier(), Article.Identifier()],
meta: .init(pagination: .init(total: 3,
limit: 50,
offset: 0)),
links: .init(next: .init(url: URL(string: "https://articles.com/author/\(authorId1.rawValue)?articles[offset]=50")!, meta: .init(expiry: tomorrow))))),
meta: tomorrowEntityMeta,
links: author1Links)
let author2Links = EntityLinks(selfLink: .init(url: URL(string: "https://articles.com/author/\(authorId2.rawValue)")!,
meta: .init(expiry: tomorrow)))
let author2 = Author(id: authorId2,
attributes: .init(name: .init(value: "James Kinney")),
relationships: .init(articles: .init(ids: [article.id, Article.Identifier()],
meta: .init(pagination: .init(total: 2,
limit: 50,
offset: 0)),
links: .init(next: .init(url: URL(string: "https://articles.com/author/\(authorId2.rawValue)?articles[offset]=50")!, meta: .init(expiry: tomorrow))))),
meta: tomorrowEntityMeta,
links: author2Links)
let author3Links = EntityLinks(selfLink: .init(url: URL(string: "https://articles.com/author/\(authorId3.rawValue)")!,
meta: .init(expiry: tomorrow)))
let author3 = Author(id: authorId3,
attributes: .init(name: .init(value: "James Kinney")),
relationships: .init(articles: .init(ids: [article.id],
meta: .init(pagination: .init(total: 1,
limit: 50,
offset: 0)),
links: .init(next: .init(url: URL(string: "https://articles.com/author/\(authorId3.rawValue)?articles[offset]=50")!, meta: .init(expiry: tomorrow))))),
meta: tomorrowEntityMeta,
links: author3Links)
let apiDescription = APIDescription<APIDescriptionMetadata>(version: "1.2",
meta: APIDescriptionMetadata(codename: "cobra"))
let documentMetadata = DocumentMetadata(updatedDate: Date())
let documentLinks = SingleArticleDocumentLinks(otherArticlesByPrimaryAuthor: .init(url: URL(string: "https://articles.com/articles?author=\(authorId1.rawValue)")!,
meta: .init(expiry: tomorrow)))
let singleArticleDocument = SingleArticleDocument(apiDescription: apiDescription,
body: .init(resourceObject: article),
includes: .init(values: [.init(author1), .init(author2), .init(author3)]),
meta: documentMetadata,
links: documentLinks)
// MARK: - Encoding
let encoder = JSONEncoder()
encoder.keyEncodingStrategy = .convertToSnakeCase
encoder.dateEncodingStrategy = .iso8601
encoder.outputFormatting = .prettyPrinted
let jsonData = try! encoder.encode(singleArticleDocument)
// MARK: - Printing JSON
print(String(data: jsonData, encoding: .utf8)!)
@@ -0,0 +1,124 @@
//: [Previous](@previous)
import Foundation
import JSONAPI
/*******
Please enjoy these examples, but allow me the forced casting and the lack of error checking for the sake of brevity.
This playground focuses on receiving a resource, making some changes, and then creating a request body for a PATCH request.
As with all examples in these playround pages, no actual networking code will be provided.
********/
// Mock up a server response
let mockDogData = """
{
"data": {
"id": "1234",
"type": "dogs",
"attributes": {
"name": "Sparky"
},
"relationships": {
"owner": {
"data": null
}
}
}
}
""".data(using: .utf8)!
//
// MARK: - EXAMPLE 1 (Mutable Attributes)
//
// pretend to have requested a Dog and received the mock data
// now parse it.
let parsedResponse = try! JSONDecoder().decode(MutableDogDocument.self, from: mockDogData)
// extract our Dog (skipping over any robustness to handle errors)
var dog = parsedResponse.body.primaryResource!.value
print("Received dog named: \(dog.name)")
// change the dog's name
let changedDog = dog.tappingAttributes { $0.name = .init(value: "Julia") }
// create a document to be used as a request body for a PATCH request
let patchRequest = MutableDogDocument(apiDescription: .none,
body: .init(resourceObject: changedDog),
includes: .none,
meta: .none,
links: .none)
// encode and send off to server
let encodedPatchRequest = try! JSONEncoder().encode(patchRequest)
print("----")
print(String(data: encodedPatchRequest, encoding:.utf8)!)
//
// MARK: - EXAMPLE 2 (Immutable Attributes)
//
print()
print("####")
print()
// pretend to have requested a Dog and received the mock data
// now parse it.
let parsedResponse2 = try! JSONDecoder().decode(SingleDogDocument.self, from: mockDogData)
// extract our Dog (skipping over any robustness to handle errors)
var dog2 = parsedResponse2.body.primaryResource!.value
print("Received dog named: \(dog2.name)")
// change the dog's name
let changedDog2 = dog2.replacingAttributes { _ in
return .init(name: .init(value: "Nigel"))
}
// create a document to be used as a request body for a PATCH request
let patchRequest2 = SingleDogDocument(apiDescription: .none,
body: .init(resourceObject: changedDog2),
includes: .none,
meta: .none,
links: .none)
// encode and send off to server
let encodedPatchRequest2 = try! JSONEncoder().encode(patchRequest2)
print("----")
print(String(data: encodedPatchRequest2, encoding:.utf8)!)
//
// MARK: - EXAMPLE 3 (Change relationship)
//
print()
print("####")
print()
// pretend to have requested a Dog and received the mock data
// now parse it.
let parsedResponse3 = try! JSONDecoder().decode(SingleDogDocument.self, from: mockDogData)
// extract our Dog (skipping over any robustness to handle errors)
var dog3 = parsedResponse2.body.primaryResource!.value
print("Received dog with owner: \(dog3 ~> \.owner)")
// give the dog an owner
let changedDog3 = dog3.replacingRelationships { _ in
return .init(owner: .init(id: Id(rawValue: "1")))
}
// create a document to be used as a request body for a PATCH request
let patchRequest3 = SingleDogDocument(apiDescription: .none,
body: .init(resourceObject: changedDog3),
includes: .none,
meta: .none,
links: .none)
// encode and send off to server
let encodedPatchRequest3 = try! JSONEncoder().encode(patchRequest3)
print("----")
print(String(data: encodedPatchRequest3, encoding:.utf8)!)
@@ -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<String>
let numberThing: Attribute<Double>
let boolThing: Attribute<Bool?>
//
// NOTE: Special implementation of `CodingKeys`
//
enum CodingKeys: String, JSONAPI.SparsableCodingKey {
case stringThing
case numberThing
case boolThing
}
}
typealias Relationships = NoRelationships
}
typealias ThingWithProperties = JSONAPI.ResourceObject<ThingWithPropertiesDescription, NoMetadata, NoLinks, String>
// MARK: - Document
//
// NOTE: Using `JSONAPI.EncodableResourceBody` which means the document type will be `Encodable` but not `Decodable`.
//
typealias Document<PrimaryResourceBody: JSONAPI.EncodableResourceBody, IncludeType: JSONAPI.Include> = JSONAPI.Document<PrimaryResourceBody, NoMetadata, NoLinks, IncludeType, NoAPIDescription, BasicJSONAPIError<String>>
//
// NOTE: Using `JSONAPI.EncodablePrimaryResource` which means the `ResourceBody` will be `Encodable` but not `Decodable.
//
typealias SingleDocument<T: JSONAPI.EncodablePrimaryResource> = Document<SingleResourceBody<T>, 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)!)
@@ -2,7 +2,7 @@
import Foundation
import JSONAPI
import JSONAPITestLib
import JSONAPITesting
/*******
@@ -11,12 +11,12 @@ Please enjoy these examples, but allow me the forced casting and the lack of err
********/
// MARK: - Literal Expressibility
// The JSONAPITestLib provides literal expressibility for key types to
// The JSONAPITesting framework provides literal expressibility for key types to
// make creating tests easier
let dog = Dog(id: "1234", attributes: Dog.Attributes(name: "Buddy"), relationships: Dog.Relationships(owner: nil))
let dog = Dog(id: "1234", attributes: Dog.Attributes(name: "Buddy"), relationships: Dog.Relationships(owner: nil), meta: .none, links: .none)
// MARK: - JSON API structure checking
// The JSONAPITestLib provides a `check` function for each Entity type
// The JSONAPITesting framework provides a `check` function for each Entity type
// that uses reflection to catch mistakes that are not forbidden by
// Swift's type system but will result in unexpected results when
// encoding/decoding. It is a good idea to add a `check` to each of
@@ -8,52 +8,92 @@ 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)
typealias SingleDogDocument = JSONAPI.Document<SingleResourceBody<Dog>, NoMetadata, NoLinks, NoIncludes, UnknownJSONAPIError>
let singleDogDocument = SingleDogDocument(body: SingleResourceBody(entity: dogFromCode))
let singleDogDocument = SingleDogDocument(apiDescription: .none, body: .init(resourceObject: dogFromCode), includes: .none, meta: .none, links: .none)
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.primaryData?.value
let dogFromData = dogResponse.body.primaryResource?.value
let dogOwner: Person.Identifier? = dogFromData.flatMap { $0 ~> \.owner }
// MARK: - Parse a request or response body with one Dog in it using an alternative model
typealias AltSingleDogDocument = JSONAPI.Document<SingleResourceBody<AlternativeDog>, NoMetadata, NoLinks, NoIncludes, NoAPIDescription, BasicJSONAPIError<String>>
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])]
let houses = [House(), House()]
let houses = [House(attributes: .none, relationships: .none, meta: .none, links: .none), House(attributes: .none, relationships: .none, meta: .none, links: .none)]
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])]
typealias BatchPeopleDocument = JSONAPI.Document<ManyResourceBody<Person>, NoMetadata, NoLinks, Include2<Dog, House>, UnknownJSONAPIError>
let includes = dogs.map { BatchPeopleDocument.Include($0) } + houses.map { BatchPeopleDocument.Include($0) }
let batchPeopleDocument = BatchPeopleDocument(body: .init(entities: people), includes: .init(values: includes))
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)
let peopleFromData = peopleResponse.body.primaryData?.values
let peopleFromData = peopleResponse.body.primaryResource?.values
let dogsFromData = peopleResponse.body.includes?[Dog.self]
let housesFromData = peopleResponse.body.includes?[House.self]
print("-----")
print(peopleResponse)
print("-----")
// MARK: - Pass successfully parsed body to other parts of the code
if case let .data(bodyData) = peopleResponse.body {
print(bodyData)
print("first person's name: \(bodyData.primary.values[0].fullName)")
} else {
print("no body data")
}
// MARK: - Work in the abstract
// MARK: - Work in the abstract
print("-----")
func process<T: JSONAPIDocument>(document: T) {
guard case let .data(body) = document.body else {
return
}
let x: T.BodyData = body
let x: T.Body.Data = body
}
process(document: peopleResponse)
// MARK: - Work with errors
typealias ErrorDoc = JSONAPI.Document<NoResourceBody, NoMetadata, NoLinks, NoIncludes, NoAPIDescription, BasicJSONAPIError<String>>
let mockErrorData =
"""
{
"errors": [
{
"status": "500",
"title": "Internal Server Error",
"detail": "Server fell over while parsing your request."
}
]
}
""".data(using: .utf8)!
let errorResponse = try! JSONDecoder().decode(ErrorDoc.self, from: mockErrorData)
switch errorResponse.body {
case .data:
print("cool, data!")
case .errors(let errors, let meta, let links):
let errorDetails = errors.compactMap { $0.payload?.detail }
print("error details: \(errorDetails)")
}
+86 -25
View File
@@ -15,26 +15,32 @@ Please enjoy these examples, but allow me the forced casting and the lack of err
********/
// MARK: - String as CreatableRawIdType
var GlobalStringId: Int = 0
var globalStringId: Int = 0
extension String: CreatableRawIdType {
public static func unique() -> String {
GlobalStringId += 1
return String(GlobalStringId)
globalStringId += 1
return String(globalStringId)
}
}
// MARK: - Entity typealias for convenience
public typealias ExampleEntity<Description: EntityDescription> = Entity<Description, Id<String, Description>>
// MARK: - typealiases for convenience
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 type: String { return "people" }
public static var jsonType: String { return "people" }
public struct Attributes: JSONAPI.Attributes {
public let name: Attribute<[String]>
public let favoriteColor: Attribute<String>
public var fullName: Attribute<String> {
return name.map { $0.joined(separator: " ") }
}
public init(name: Attribute<[String]>, favoriteColor: Attribute<String>) {
self.name = name
self.favoriteColor = favoriteColor
@@ -42,11 +48,11 @@ public enum PersonDescription: EntityDescription {
}
public struct Relationships: JSONAPI.Relationships {
public let friends: ToManyRelationship<Person>
public let dogs: ToManyRelationship<Dog>
public let home: ToOneRelationship<House>
public let friends: ToMany<Person>
public let dogs: ToMany<Dog>
public let home: ToOne<House>
public init(friends: ToManyRelationship<Person>, dogs: ToManyRelationship<Dog>, home: ToOneRelationship<House>) {
public init(friends: ToMany<Person>, dogs: ToMany<Dog>, home: ToOne<House>) {
self.friends = friends
self.dogs = dogs
self.home = home
@@ -56,15 +62,15 @@ public enum PersonDescription: EntityDescription {
public typealias Person = ExampleEntity<PersonDescription>
public extension Entity where Description == PersonDescription, Identifier == Id<String, PersonDescription> {
public init(id: Person.Identifier? = nil,name: [String], favoriteColor: String, friends: [Person], dogs: [Dog], home: House) throws {
self = try Person(id: id ?? Person.Identifier(), attributes: .init(name: .init(rawValue: name), favoriteColor: .init(rawValue: favoriteColor)), relationships: .init(friends: .init(entities: friends), dogs: .init(entities: dogs), home: .init(entity: home)))
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(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 type: String { return "dogs" }
public static var jsonType: String { return "dogs" }
public struct Attributes: JSONAPI.Attributes {
public let name: Attribute<String>
@@ -75,9 +81,9 @@ public enum DogDescription: EntityDescription {
}
public struct Relationships: JSONAPI.Relationships {
public let owner: ToOneRelationship<Person?>
public let owner: ToOne<Person?>
public init(owner: ToOneRelationship<Person?>) {
public init(owner: ToOne<Person?>) {
self.owner = owner
}
}
@@ -85,19 +91,70 @@ public enum DogDescription: EntityDescription {
public typealias Dog = ExampleEntity<DogDescription>
public extension Entity where Description == DogDescription, Identifier == Id<String, DogDescription> {
public init(name: String, owner: Person?) throws {
self = try Dog(attributes: .init(name: .init(rawValue: name)), relationships: DogDescription.Relationships(owner: .init(entity: owner)))
public enum AlternativeDogDescription: ResourceObjectDescription {
public static var jsonType: String { return "dogs" }
public struct Attributes: JSONAPI.Attributes {
public let name: Attribute<String>
public init(name: Attribute<String>) {
self.name = name
}
}
public init(name: String, owner: Person.Identifier) throws {
self = try Dog(attributes: .init(name: .init(rawValue: name)), relationships: .init(owner: .init(id: owner)))
public struct Relationships: JSONAPI.Relationships {
public let human: ToOne<Person?>
public init(human: ToOne<Person?>) {
self.human = human
}
// define custom key mapping:
enum CodingKeys: String, CodingKey {
case human = "owner"
}
}
}
public enum HouseDescription: EntityDescription {
public typealias AlternativeDog = ExampleEntity<AlternativeDogDescription>
public static var type: String { return "houses" }
public enum MutableDogDescription: ResourceObjectDescription {
public static var jsonType: String { return "dogs" }
public struct Attributes: JSONAPI.Attributes {
public var name: Attribute<String>
public init(name: Attribute<String>) {
self.name = name
}
}
public struct Relationships: JSONAPI.Relationships {
public var owner: ToOne<Person?>
public init(owner: ToOne<Person?>) {
self.owner = owner
}
}
}
public typealias MutableDog = ExampleEntity<MutableDogDescription>
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(resourceObject: owner)), meta: .none, links: .none)
}
init(name: String, owner: Person.Id) throws {
self = Dog(attributes: .init(name: .init(value: name)), relationships: .init(owner: .init(id: owner)), meta: .none, links: .none)
}
}
public enum HouseDescription: ResourceObjectDescription {
public static var jsonType: String { return "houses" }
public typealias Attributes = NoAttributes
public typealias Relationships = NoRelationships
@@ -105,4 +162,8 @@ public enum HouseDescription: EntityDescription {
public typealias House = ExampleEntity<HouseDescription>
public typealias SingleDogDocument = JSONAPI.Document<SingleResourceBody<Dog>, NoMetadata, NoLinks, NoIncludes, NoAPIDescription, BasicJSONAPIError<String>>
public typealias MutableDogDocument = JSONAPI.Document<SingleResourceBody<MutableDog>, NoMetadata, NoLinks, NoIncludes, NoAPIDescription, BasicJSONAPIError<String>>
public typealias BatchPeopleDocument = JSONAPI.Document<ManyResourceBody<Person>, NoMetadata, NoLinks, Include2<Dog, House>, NoAPIDescription, BasicJSONAPIError<String>>
+5 -1
View File
@@ -1,7 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<playground version='6.0' target-platform='macos' executeOnSourceChanges='false'>
<playground version='6.0' target-platform='macos'>
<pages>
<page name='Test Library'/>
<page name='Usage'/>
<page name='Full Client &amp; Server Example'/>
<page name='Full Document Verbose Generation'/>
<page name='Sparse Fieldsets Example'/>
<page name='PATCHing'/>
</pages>
</playground>
+141
View File
@@ -0,0 +1,141 @@
#
# Be sure to run `pod spec lint JSONAPI.podspec' to ensure this is a
# valid spec and to remove all comments including this before submitting the spec.
#
# To learn more about Podspec attributes see https://docs.cocoapods.org/specification.html
# To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/
#
Pod::Spec.new do |spec|
# ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# These will help people to find your library, and whilst it
# can feel like a chore to fill in it's definitely to your advantage. The
# summary should be tweet-length, and the description more in depth.
#
spec.name = "MP-JSONAPI"
spec.version = "2.5.0"
spec.summary = "Swift Codable JSON API framework."
# This description is used to generate tags and improve search results.
# * Think: What does it do? Why did you write it? What is the focus?
# * Try to keep it short, snappy and to the point.
# * Write the description between the DESC delimiters below.
# * Finally, don't worry about the indent, CocoaPods strips it!
spec.description = <<-DESC
A Swift package for encoding to- and decoding from JSON API compliant requests and responses.
See the JSON API Spec here: https://jsonapi.org/format/
DESC
spec.homepage = "https://github.com/mattpolzin/JSONAPI"
# spec.screenshots = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif"
# ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# Licensing your code is important. See https://choosealicense.com for more info.
# CocoaPods will detect a license file if there is a named LICENSE*
# Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'.
#
# spec.license = "MIT"
spec.license = { :type => "MIT", :file => "LICENSE.txt" }
# ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# Specify the authors of the library, with email addresses. Email addresses
# of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also
# accepts just a name if you'd rather not provide an email address.
#
# Specify a social_media_url where others can refer to, for example a twitter
# profile URL.
#
spec.author = { "Mathew Polzin" => "matt.polzin@gmail.com" }
# Or just: spec.author = "Mathew Polzin"
# spec.authors = { "Mathew Polzin" => "matt.polzin@gmail.com" }
# spec.social_media_url = "https://twitter.com/Mathew Polzin"
# ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# If this Pod runs only on iOS or OS X, then specify the platform and
# the deployment target. You can optionally include the target after the platform.
#
# spec.platform = :ios
# spec.platform = :ios, "5.0"
# When using multiple platforms
spec.ios.deployment_target = "8.0"
spec.osx.deployment_target = "10.9"
# spec.watchos.deployment_target = "2.0"
# spec.tvos.deployment_target = "9.0"
# ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# Specify the location from where the source should be retrieved.
# Supports git, hg, bzr, svn and HTTP.
#
spec.source = { :git => "https://github.com/mattpolzin/JSONAPI.git", :tag => "#{spec.version}" }
# ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# CocoaPods is smart about how it includes source code. For source files
# giving a folder will include any swift, h, m, mm, c & cpp files.
# For header files it will include any header in the folder.
# Not including the public_header_files will make all headers public.
#
spec.source_files = "Sources/JSONAPI/**/*.{swift}"
# spec.exclude_files = "Classes/Exclude"
# spec.public_header_files = "Classes/**/*.h"
# ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# A list of resources included with the Pod. These are copied into the
# target bundle with a build phase script. Anything else will be cleaned.
# You can preserve files from being cleaned, please don't preserve
# non-essential files like tests, examples and documentation.
#
# spec.resource = "icon.png"
# spec.resources = "Resources/*.png"
# spec.preserve_paths = "FilesToSave", "MoreFilesToSave"
# ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# Link your library with frameworks, or libraries. Libraries do not include
# the lib prefix of their name.
#
# spec.frameworks = "SomeFramework", "AnotherFramework"
# spec.library = "iconv"
# spec.libraries = "iconv", "xml2"
# ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# If your library depends on compiler flags you can set them in the xcconfig hash
# where they will only apply to your library. If you depend on other Podspecs
# you can include multiple dependencies to ensure it works.
spec.swift_version = "5.1"
spec.module_name = "JSONAPI"
# spec.requires_arc = true
# spec.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" }
spec.dependency "Poly", "~> 2.2"
end
+7
View File
@@ -0,0 +1,7 @@
Copyright 2018 Mathew Polzin
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+5 -5
View File
@@ -2,12 +2,12 @@
"object": {
"pins": [
{
"package": "Result",
"repositoryURL": "https://github.com/mattpolzin/Result",
"package": "Poly",
"repositoryURL": "https://github.com/mattpolzin/Poly.git",
"state": {
"branch": "master",
"revision": "b98e238da6ea030fa7862ae6fd6500552370019c",
"version": null
"branch": null,
"revision": "0c9c08204142babc480938d704a23513d11420e5",
"version": "2.3.1"
}
}
]
+18 -12
View File
@@ -1,32 +1,38 @@
// swift-tools-version:4.2
// swift-tools-version:5.1
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "JSONAPI",
platforms: [
.macOS(.v10_10),
.iOS(.v10)
],
products: [
.library(
name: "JSONAPI",
targets: ["JSONAPI"]),
.library(
name: "JSONAPITestLib",
targets: ["JSONAPITestLib"])
.library(
name: "JSONAPITesting",
targets: ["JSONAPITesting"])
],
dependencies: [
// antitypical/Result without the Foundation requirement:
.package(url: "https://github.com/mattpolzin/Result", .branch("master"))
.package(url: "https://github.com/mattpolzin/Poly.git", .upToNextMajor(from: "2.3.1")),
],
targets: [
.target(
name: "JSONAPI",
dependencies: ["Result"]),
.target(
name: "JSONAPITestLib",
dependencies: ["JSONAPI"]),
dependencies: ["Poly"]),
.target(
name: "JSONAPITesting",
dependencies: ["JSONAPI"]),
.testTarget(
name: "JSONAPITests",
dependencies: ["JSONAPITestLib"])
dependencies: ["JSONAPI", "JSONAPITesting"]),
.testTarget(
name: "JSONAPITestingTests",
dependencies: ["JSONAPI", "JSONAPITesting"])
],
swiftLanguageVersions: [.v4_2]
swiftLanguageVersions: [.v5]
)
+723 -118
View File
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,64 @@
//
// APIDescription.swift
// JSONAPI
//
// Created by Mathew Polzin on 12/1/18.
//
/// This is what the JSON API Spec calls the "JSON:API Object"
public protocol APIDescriptionType: Codable, Equatable {
associatedtype Meta
}
/// This is what the JSON API Spec calls the "JSON:API Object"
public struct APIDescription<Meta: JSONAPI.Meta>: APIDescriptionType {
public let version: String
public let meta: Meta
public init(version: String, meta: Meta) {
self.version = version
self.meta = meta
}
}
/// Can be used as `APIDescriptionType` for Documents that do not
/// have any API Description (a.k.a. "JSON:API Object").
public struct NoAPIDescription: APIDescriptionType, CustomStringConvertible {
public typealias Meta = NoMetadata
public init() {}
public static var none: NoAPIDescription { return .init() }
public var description: String { return "No JSON:API Object" }
}
extension APIDescription {
private enum CodingKeys: String, CodingKey {
case version
case meta
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
// The spec says that if a version is not specified, it should be assumed to be at least 1.0
version = (try? container.decode(String.self, forKey: .version)) ?? "1.0"
if let metaVal = NoMetadata() as? Meta {
meta = metaVal
} else {
meta = try container.decode(Meta.self, forKey: .meta)
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(version, forKey: .version)
if Meta.self != NoMetadata.self {
try container.encode(meta, forKey: .meta)
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,73 @@
//
// DocumentDecodingError.swift
//
//
// Created by Mathew Polzin on 10/20/19.
//
public enum DocumentDecodingError: Swift.Error, Equatable {
case primaryResource(error: ResourceObjectDecodingError, idx: Int?)
case primaryResourceMissing
case primaryResourcesMissing
case includes(error: IncludesDecodingError)
case foundErrorDocumentWhenExpectingSuccess
case foundSuccessDocumentWhenExpectingError
init(_ decodingError: ResourceObjectDecodingError) {
self = .primaryResource(error: decodingError, idx: nil)
}
init(_ decodingError: ManyResourceBodyDecodingError) {
self = .primaryResource(error: decodingError.error, idx: decodingError.idx)
}
init(_ decodingError: IncludesDecodingError) {
self = .includes(error: decodingError)
}
init?(_ decodingError: DecodingError) {
switch decodingError {
case .valueNotFound(let type, let context) where Location(context) == .data && type is AbstractResourceObject.Type:
self = .primaryResourceMissing
case .valueNotFound(let type, let context) where Location(context) == .data && type == UnkeyedDecodingContainer.self:
self = .primaryResourcesMissing
default:
return nil
}
}
private enum Location: Equatable {
case data
init?(_ context: DecodingError.Context) {
guard context.codingPath.contains(where: { $0.stringValue == "data" }) else {
return nil
}
self = .data
}
}
}
extension DocumentDecodingError: CustomStringConvertible {
public var description: String {
switch self {
case .primaryResource(error: let error, idx: let idx):
let idxString = idx.map { " \($0 + 1)" } ?? ""
return "Primary Resource\(idxString) failed to parse because \(error)"
case .primaryResourceMissing:
return "Primary Resource missing."
case .primaryResourcesMissing:
return "Primary Resources array missing."
case .includes(error: let error):
return "\(error)"
case .foundErrorDocumentWhenExpectingSuccess:
return "Expected a success document with a 'data' property but found an error document."
case .foundSuccessDocumentWhenExpectingError:
return "Expected an error document but found a success document with a 'data' property."
}
}
}
-27
View File
@@ -1,27 +0,0 @@
//
// Error.swift
// JSONAPI
//
// Created by Mathew Polzin on 11/10/18.
//
public protocol JSONAPIError: Swift.Error, Equatable, Codable {
static var unknown: Self { get }
}
public enum UnknownJSONAPIError: JSONAPIError {
case unknownError
public init(from decoder: Decoder) throws {
self = .unknown
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode("unknown")
}
public static var unknown: UnknownJSONAPIError {
return .unknownError
}
}
+180 -63
View File
@@ -5,61 +5,112 @@
// Created by Mathew Polzin on 11/10/18.
//
public typealias Include = Poly
import Poly
public struct Includes<I: Include>: Codable, Equatable {
public static var none: Includes { return .init(values: []) }
let values: [I]
public init(values: [I]) {
self.values = values
}
public init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
// If not parsing includes, no need to loop over them.
guard I.self != NoIncludes.self else {
values = []
return
}
var valueAggregator = [I]()
while !container.isAtEnd {
valueAggregator.append(try container.decode(I.self))
}
values = valueAggregator
}
public typealias Include = EncodableJSONPoly
public func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
/// A structure holding zero or more included Resource Objects.
/// The resources are accessed by their type using a subscript.
///
/// If you have
///
/// let includes: Includes<Include2<Thing1, Thing2>> = ...
///
/// then you can access all `Thing1` included resources with
///
/// let includedThings = includes[Thing1.self]
public struct Includes<I: Include>: Encodable, Equatable {
public static var none: Includes { return .init(values: []) }
guard I.self != NoIncludes.self else {
throw JSONAPIEncodingError.illegalEncoding("Attempting to encode Include0, which should be represented by the absense of an 'included' entry altogether.")
}
public let values: [I]
for value in values {
try container.encode(value)
}
}
public var count: Int {
return values.count
}
public init(values: [I]) {
self.values = values
}
public func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
guard I.self != NoIncludes.self else {
throw JSONAPICodingError.illegalEncoding("Attempting to encode Include0, which should be represented by the absense of an 'included' entry altogether.", path: encoder.codingPath)
}
for value in values {
try container.encode(value)
}
}
public var count: Int {
return values.count
}
}
extension Includes: Decodable where I: Decodable {
public init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
// If not parsing includes, no need to loop over them.
guard I.self != NoIncludes.self else {
values = []
return
}
var valueAggregator = [I]()
var idx = 0
while !container.isAtEnd {
do {
valueAggregator.append(try container.decode(I.self))
idx = idx + 1
} catch let error as PolyDecodeNoTypesMatchedError {
let errors: [ResourceObjectDecodingError] = error
.individualTypeFailures
.compactMap { decodingError in
switch decodingError.error {
case .typeMismatch(_, let context),
.valueNotFound(_, let context),
.keyNotFound(_, let context),
.dataCorrupted(let context):
return context.underlyingError as? ResourceObjectDecodingError
@unknown default:
return nil
}
}
guard errors.count == error.individualTypeFailures.count else {
throw IncludesDecodingError(error: error, idx: idx)
}
throw IncludesDecodingError(
error: IncludeDecodingError(failures: errors),
idx: idx
)
} catch let error {
throw IncludesDecodingError(error: error, idx: idx)
}
}
values = valueAggregator
}
}
extension Includes {
public func appending(_ other: Includes<I>) -> Includes {
return Includes(values: values + other.values)
}
}
public func +<I: Include>(_ left: Includes<I>, _ right: Includes<I>) -> Includes<I> {
return left.appending(right)
}
extension Includes: CustomStringConvertible {
public var description: String {
return "Includes(\(String(describing: values))"
}
public var description: String {
return "Includes(\(String(describing: values))"
}
}
extension Includes where I == NoIncludes {
public init() {
values = []
}
public init() {
values = []
}
}
// MARK: - 0 includes
@@ -69,50 +120,116 @@ public typealias NoIncludes = Include0
// MARK: - 1 include
public typealias Include1 = Poly1
extension Includes where I: _Poly1 {
public subscript(_ lookup: I.A.Type) -> [I.A] {
return values.compactMap { $0.a }
}
public subscript(_ lookup: I.A.Type) -> [I.A] {
return values.compactMap { $0.a }
}
}
// MARK: - 2 includes
public typealias Include2 = Poly2
extension Includes where I: _Poly2 {
public subscript(_ lookup: I.B.Type) -> [I.B] {
return values.compactMap { $0.b }
}
public subscript(_ lookup: I.B.Type) -> [I.B] {
return values.compactMap { $0.b }
}
}
// MARK: - 3 includes
public typealias Include3 = Poly3
extension Includes where I: _Poly3 {
public subscript(_ lookup: I.C.Type) -> [I.C] {
return values.compactMap { $0.c }
}
public subscript(_ lookup: I.C.Type) -> [I.C] {
return values.compactMap { $0.c }
}
}
// MARK: - 4 includes
public typealias Include4 = Poly4
extension Includes where I: _Poly4 {
public subscript(_ lookup: I.D.Type) -> [I.D] {
return values.compactMap { $0.d }
}
public subscript(_ lookup: I.D.Type) -> [I.D] {
return values.compactMap { $0.d }
}
}
// MARK: - 5 includes
public typealias Include5 = Poly5
extension Includes where I: _Poly5 {
public subscript(_ lookup: I.E.Type) -> [I.E] {
return values.compactMap { $0.e }
}
public subscript(_ lookup: I.E.Type) -> [I.E] {
return values.compactMap { $0.e }
}
}
// MARK: - 6 includes
public typealias Include6 = Poly6
extension Includes where I: _Poly6 {
public subscript(_ lookup: I.F.Type) -> [I.F] {
return values.compactMap { $0.f }
}
public subscript(_ lookup: I.F.Type) -> [I.F] {
return values.compactMap { $0.f }
}
}
// MARK: - 7 includes
public typealias Include7 = Poly7
extension Includes where I: _Poly7 {
public subscript(_ lookup: I.G.Type) -> [I.G] {
return values.compactMap { $0.g }
}
}
// MARK: - 8 includes
public typealias Include8 = Poly8
extension Includes where I: _Poly8 {
public subscript(_ lookup: I.H.Type) -> [I.H] {
return values.compactMap { $0.h }
}
}
// MARK: - 9 includes
public typealias Include9 = Poly9
extension Includes where I: _Poly9 {
public subscript(_ lookup: I.I.Type) -> [I.I] {
return values.compactMap { $0.i }
}
}
// MARK: - 10 includes
public typealias Include10 = Poly10
extension Includes where I: _Poly10 {
public subscript(_ lookup: I.J.Type) -> [I.J] {
return values.compactMap { $0.j }
}
}
// MARK: - 11 includes
public typealias Include11 = Poly11
extension Includes where I: _Poly11 {
public subscript(_ lookup: I.K.Type) -> [I.K] {
return values.compactMap { $0.k }
}
}
// MARK: - DecodingError
public struct IncludesDecodingError: Swift.Error, Equatable {
public let error: Swift.Error
public let idx: Int
public static func ==(lhs: Self, rhs: Self) -> Bool {
return lhs.idx == rhs.idx
&& String(describing: lhs) == String(describing: rhs)
}
}
extension IncludesDecodingError: CustomStringConvertible {
public var description: String {
return "Include \(idx + 1) failed to parse: \(error)"
}
}
public struct IncludeDecodingError: Swift.Error, Equatable, CustomStringConvertible {
public let failures: [ResourceObjectDecodingError]
public var description: String {
return failures
.enumerated()
.map {
"\nCould not have been Include Type \($0.offset + 1) because:\n\($0.element)"
}.joined(separator: "\n")
}
}
+122 -51
View File
@@ -5,87 +5,158 @@
// Created by Mathew Polzin on 11/10/18.
//
public protocol PrimaryResource: Equatable, Codable {}
/// This protocol allows for a `SingleResourceBody` to contain a `null`
/// data object where `ManyResourceBody` cannot (because an empty
/// array should be used for no results).
public protocol OptionalEncodablePrimaryResource: Equatable, Encodable {}
public protocol ResourceBody: Codable, Equatable {
/// An `EncodablePrimaryResource` is a `CodablePrimaryResource` that only supports encoding.
public protocol EncodablePrimaryResource: OptionalEncodablePrimaryResource {}
/// This protocol allows for `SingleResourceBody` to contain a `null`
/// data object where `ManyResourceBody` cannot (because an empty
/// array should be used for no results).
public protocol OptionalCodablePrimaryResource: OptionalEncodablePrimaryResource, Decodable {}
/// A `CodablePrimaryResource` is a type that can be used in the body of a JSON API
/// document as the primary resource.
public protocol CodablePrimaryResource: EncodablePrimaryResource, OptionalCodablePrimaryResource {}
extension Optional: OptionalEncodablePrimaryResource where Wrapped: EncodablePrimaryResource {}
extension Optional: OptionalCodablePrimaryResource where Wrapped: CodablePrimaryResource {}
/// An `EncodableResourceBody` is a `ResourceBody` that only supports being
/// encoded. It is actually weaker than `ResourceBody`, which supports both encoding
/// and decoding.
public protocol EncodableResourceBody: Equatable, Encodable {
associatedtype PrimaryResource
}
public struct SingleResourceBody<Entity: JSONAPI.PrimaryResource>: ResourceBody {
public let value: Entity?
/// A `CodableResourceBody` is a representation of the body of the JSON:API Document.
/// It can either be one resource (which can be specified as optional or not)
/// or it can contain many resources (and array with zero or more entries).
public protocol CodableResourceBody: Decodable, EncodableResourceBody {}
public init(entity: Entity?) {
self.value = entity
}
/// A `ResourceBody` that has the ability to take on more primary
/// resources by appending another similarly typed `ResourceBody`.
public protocol ResourceBodyAppendable {
func appending(_ other: Self) -> Self
}
public struct ManyResourceBody<Entity: JSONAPI.PrimaryResource>: ResourceBody {
public let values: [Entity]
public func +<R: ResourceBodyAppendable>(_ left: R, right: R) -> R {
return left.appending(right)
}
public init(entities: [Entity]) {
values = entities
}
/// A type allowing for a document body containing 1 primary resource.
/// If the `Entity` specialization is an `Optional` type, the body can contain
/// 0 or 1 primary resources.
public struct SingleResourceBody<PrimaryResource: JSONAPI.OptionalEncodablePrimaryResource>: EncodableResourceBody {
public let value: PrimaryResource
public init(resourceObject: PrimaryResource) {
self.value = resourceObject
}
}
/// A type allowing for a document body containing 0 or more primary resources.
public struct ManyResourceBody<PrimaryResource: JSONAPI.EncodablePrimaryResource>: EncodableResourceBody, ResourceBodyAppendable {
public let values: [PrimaryResource]
public init(resourceObjects: [PrimaryResource]) {
values = resourceObjects
}
public func appending(_ other: ManyResourceBody) -> ManyResourceBody {
return ManyResourceBody(resourceObjects: values + other.values)
}
}
/// Use NoResourceBody to indicate you expect a JSON API document to not
/// contain a "data" top-level key.
public struct NoResourceBody: ResourceBody {
public static var none: NoResourceBody { return NoResourceBody() }
public struct NoResourceBody: CodableResourceBody {
public typealias PrimaryResource = Void
public static var none: NoResourceBody { return NoResourceBody() }
}
// MARK: Decodable
// MARK: Codable
extension SingleResourceBody {
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
if container.decodeNil() {
value = nil
return
}
let anyNil: Any? = nil
let nilValue = anyNil as? PrimaryResource
guard value != nilValue else {
try container.encodeNil()
return
}
value = try container.decode(Entity.self)
}
try container.encode(value)
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
extension SingleResourceBody: Decodable, CodableResourceBody where PrimaryResource: OptionalCodablePrimaryResource {
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if value == nil {
try container.encodeNil()
return
}
let anyNil: Any? = nil
if container.decodeNil(),
let val = anyNil as? PrimaryResource {
value = val
return
}
try container.encode(value)
}
value = try container.decode(PrimaryResource.self)
}
}
extension ManyResourceBody {
public init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
var valueAggregator = [Entity]()
while !container.isAtEnd {
valueAggregator.append(try container.decode(Entity.self))
}
values = valueAggregator
}
public func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
public func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
for value in values {
try container.encode(value)
}
}
}
for value in values {
try container.encode(value)
}
}
extension ManyResourceBody: Decodable, CodableResourceBody where PrimaryResource: CodablePrimaryResource {
public init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
var valueAggregator = [PrimaryResource]()
var idx = 0
while !container.isAtEnd {
do {
valueAggregator.append(try container.decode(PrimaryResource.self))
} catch let error as ResourceObjectDecodingError {
throw ManyResourceBodyDecodingError(
error: error,
idx: idx
)
}
idx = idx + 1
}
values = valueAggregator
}
}
// MARK: CustomStringConvertible
extension SingleResourceBody: CustomStringConvertible {
public var description: String {
return "PrimaryResourceBody(\(String(describing: value)))"
}
public var description: String {
return "PrimaryResourceBody(\(String(describing: value)))"
}
}
extension ManyResourceBody: CustomStringConvertible {
public var description: String {
return "PrimaryResourceBody(\(String(describing: values)))"
}
public var description: String {
return "PrimaryResourceBody(\(String(describing: values)))"
}
}
// MARK: - DecodingError
public struct ManyResourceBodyDecodingError: Swift.Error, Equatable {
public let error: ResourceObjectDecodingError
public let idx: Int
}
+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
}
}

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