mirror of
https://github.com/encounter/JSONAPI.git
synced 2026-07-10 12:18:40 -07:00
Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 491fe1fbea | |||
| 6b6f40c968 | |||
| 14bd29bf42 | |||
| cf4d8fc378 | |||
| 10b756f6db | |||
| 4cd697aae4 | |||
| db0f7329e4 | |||
| fb710c397b | |||
| b1adc60719 | |||
| 423e61b285 | |||
| 895d575cdb | |||
| cd75e9649b | |||
| c60dfc1584 | |||
| 16004051e9 | |||
| 864a66f1ea | |||
| abe16dda0d | |||
| 5257fd79fa | |||
| f7a81df451 | |||
| c8fbc09582 | |||
| a88bb77bd0 | |||
| 247848e788 | |||
| 7adbdb4505 | |||
| 0c7bf9a92d | |||
| ea663bb229 | |||
| f48c385f99 | |||
| c96bfadd82 | |||
| 88be701062 | |||
| b294cec542 | |||
| 11e8d13f02 |
@@ -1,46 +0,0 @@
|
||||
//: [Previous](@previous)
|
||||
|
||||
import Foundation
|
||||
import JSONAPI
|
||||
import JSONAPIOpenAPI
|
||||
import Poly
|
||||
|
||||
// print Entity Schema
|
||||
let encoder = JSONEncoder()
|
||||
encoder.outputFormatting = .prettyPrinted
|
||||
|
||||
let personSchemaData = try? encoder.encode(Person.openAPINode(using: encoder))
|
||||
|
||||
print("Person Schema")
|
||||
print("====")
|
||||
print(personSchemaData.map { String(data: $0, encoding: .utf8)! } ?? "Schema Construction Failed")
|
||||
print("====")
|
||||
|
||||
let dogDocumentSchemaData = try? encoder.encode(SingleDogDocument.openAPINodeWithExample(using: encoder))
|
||||
|
||||
print("Dog Document Schema")
|
||||
print("====")
|
||||
print(dogDocumentSchemaData.map { String(data: $0, encoding: .utf8)! } ?? "Schema Construction Failed")
|
||||
print("====")
|
||||
|
||||
let batchPersonSchemaData = try? encoder.encode(BatchPeopleDocument.openAPINodeWithExample(using: encoder))
|
||||
|
||||
print("Batch Person Document Schema")
|
||||
print("====")
|
||||
print(batchPersonSchemaData.map { String(data: $0, encoding: .utf8)! } ?? "Schema Construction Failed")
|
||||
print("====")
|
||||
|
||||
let tmp: [String: JSONNode] = [
|
||||
"BatchPerson": try! BatchPeopleDocument.openAPINodeWithExample(using: encoder)
|
||||
]
|
||||
|
||||
let components = OpenAPIComponents(schemas: tmp, parameters: [:])
|
||||
|
||||
let batchPeopleRef = JSONReference.node(.init(type: \OpenAPIComponents.schemas, selector: "BatchPerson"))
|
||||
|
||||
let tmp2 = JSONNode.reference(batchPeopleRef)
|
||||
|
||||
print("====")
|
||||
print("====")
|
||||
//print(String(data: try! encoder.encode(components), encoding: .utf8)!)
|
||||
print(String(data: try! encoder.encode(tmp2), encoding: .utf8)!)
|
||||
@@ -49,11 +49,15 @@ print("-----")
|
||||
|
||||
// MARK: - Pass successfully parsed body to other parts of the code
|
||||
|
||||
/*
|
||||
---- CRASHING IN XCODE 10.2 PLAYGROUND ----
|
||||
|
||||
if case let .data(bodyData) = peopleResponse.body {
|
||||
print("first person's name: \(bodyData.primary.values[0][\.fullName])")
|
||||
} else {
|
||||
print("no body data")
|
||||
}
|
||||
*/
|
||||
|
||||
// MARK: - Work in the abstract
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ public enum PersonDescription: EntityDescription {
|
||||
public typealias Person = ExampleEntity<PersonDescription>
|
||||
|
||||
public extension Entity where Description == PersonDescription, MetaType == NoMetadata, LinksType == NoLinks, EntityRawIdType == String {
|
||||
public init(id: Person.Id? = nil,name: [String], favoriteColor: String, friends: [Person], dogs: [Dog], home: House) throws {
|
||||
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(entities: friends), dogs: .init(entities: dogs), home: .init(entity: home)), meta: .none, links: .none)
|
||||
}
|
||||
}
|
||||
@@ -120,11 +120,11 @@ public enum AlternativeDogDescription: EntityDescription {
|
||||
public typealias AlternativeDog = ExampleEntity<AlternativeDogDescription>
|
||||
|
||||
public extension Entity where Description == DogDescription, MetaType == NoMetadata, LinksType == NoLinks, EntityRawIdType == String {
|
||||
public init(name: String, owner: Person?) throws {
|
||||
init(name: String, owner: Person?) throws {
|
||||
self = Dog(attributes: .init(name: .init(value: name)), relationships: DogDescription.Relationships(owner: .init(entity: owner)), meta: .none, links: .none)
|
||||
}
|
||||
|
||||
public init(name: String, owner: Person.Id) throws {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
import Foundation
|
||||
import JSONAPI
|
||||
import JSONAPITesting // for the convenience of literal initialization
|
||||
import JSONAPIOpenAPI
|
||||
import SwiftCheck
|
||||
import JSONAPIArbitrary
|
||||
|
||||
extension PersonDescription.Attributes: Arbitrary, Sampleable {
|
||||
public static var arbitrary: Gen<PersonDescription.Attributes> {
|
||||
return Gen.compose { c in
|
||||
return PersonDescription.Attributes(name: c.generate(),
|
||||
favoriteColor: c.generate())
|
||||
}
|
||||
}
|
||||
|
||||
public static var sample: PersonDescription.Attributes {
|
||||
return .init(name: ["Abbie", "Eibba"], favoriteColor: "Blue")
|
||||
}
|
||||
}
|
||||
|
||||
extension PersonDescription.Relationships: Arbitrary, Sampleable {
|
||||
public static var arbitrary: Gen<PersonDescription.Relationships> {
|
||||
return Gen.compose { c in
|
||||
return PersonDescription.Relationships(friends: c.generate(),
|
||||
dogs: c.generate(),
|
||||
home: c.generate())
|
||||
}
|
||||
}
|
||||
|
||||
public static var sample: PersonDescription.Relationships {
|
||||
return .init(friends: ["1", "2"], dogs: ["2"], home: "1")
|
||||
}
|
||||
}
|
||||
|
||||
extension DogDescription.Attributes: Arbitrary, Sampleable {
|
||||
public static var arbitrary: Gen<DogDescription.Attributes> {
|
||||
return Gen.compose { c in
|
||||
return DogDescription.Attributes(name: c.generate())
|
||||
}
|
||||
}
|
||||
|
||||
public static var sample: DogDescription.Attributes {
|
||||
return DogDescription.Attributes.arbitrary.generate
|
||||
}
|
||||
}
|
||||
|
||||
extension DogDescription.Relationships: Arbitrary, Sampleable {
|
||||
public static var arbitrary: Gen<DogDescription.Relationships> {
|
||||
return Gen.compose { c in
|
||||
return DogDescription.Relationships(owner: c.generate())
|
||||
}
|
||||
}
|
||||
|
||||
public static var sample: DogDescription.Relationships {
|
||||
return DogDescription.Relationships.arbitrary.generate
|
||||
}
|
||||
}
|
||||
|
||||
extension Document: Sampleable where PrimaryResourceBody: Arbitrary, IncludeType: Arbitrary, MetaType: Arbitrary, LinksType: Arbitrary, Error: Arbitrary, APIDescription: Arbitrary {
|
||||
public static var sample: Document {
|
||||
return Document.arbitrary.generate
|
||||
}
|
||||
|
||||
public static var successSample: Document? {
|
||||
return Document.arbitraryData.generate
|
||||
}
|
||||
|
||||
public static var failureSample: Document? {
|
||||
return Document.arbitraryErrors.generate
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
<?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'/>
|
||||
|
||||
+141
@@ -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 = "JSONAPI"
|
||||
spec.version = "0.21.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", "Sources/**/*.{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.framework = "Poly"
|
||||
# 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.0"
|
||||
# spec.requires_arc = true
|
||||
|
||||
# spec.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" }
|
||||
spec.dependency "Poly", "~> 2.0"
|
||||
|
||||
end
|
||||
+2
-29
@@ -1,40 +1,13 @@
|
||||
{
|
||||
"object": {
|
||||
"pins": [
|
||||
{
|
||||
"package": "AnyCodable",
|
||||
"repositoryURL": "https://github.com/Flight-School/AnyCodable.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "396ccc3dba5bdee04c1e742e7fab40582861401e",
|
||||
"version": "0.1.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "FileCheck",
|
||||
"repositoryURL": "https://github.com/llvm-swift/FileCheck.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "89b8480055f9adf8ce2f9ad5e2fac7ac1076242e",
|
||||
"version": "0.0.8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "Poly",
|
||||
"repositoryURL": "https://github.com/mattpolzin/Poly.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "77f45b8963a51c02d71fc4075eba5cff47ff0d07",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "SwiftCheck",
|
||||
"repositoryURL": "https://github.com/typelift/SwiftCheck.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "cf9958085b2ee1643e541e407c3233d1b76c18ff",
|
||||
"version": "0.11.0"
|
||||
"revision": "d24d4c1214dd05f89eb1182a46592856dd0a0645",
|
||||
"version": "2.0.0"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
+9
-25
@@ -1,28 +1,24 @@
|
||||
// swift-tools-version:4.2
|
||||
// swift-tools-version:5.0
|
||||
// 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: "JSONAPITesting",
|
||||
targets: ["JSONAPITesting"]),
|
||||
.library(
|
||||
name: "JSONAPIArbitrary",
|
||||
targets: ["JSONAPIArbitrary"]),
|
||||
.library(
|
||||
name: "JSONAPIOpenAPI",
|
||||
targets: ["JSONAPIOpenAPI"])
|
||||
targets: ["JSONAPITesting"])
|
||||
],
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/mattpolzin/Poly.git", from: "1.0.0"),
|
||||
.package(url: "https://github.com/Flight-School/AnyCodable.git", from: "0.1.0"),
|
||||
.package(url: "https://github.com/typelift/SwiftCheck.git", from: "0.11.0")
|
||||
.package(url: "https://github.com/mattpolzin/Poly.git", from: "2.0.0"),
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
@@ -31,24 +27,12 @@ let package = Package(
|
||||
.target(
|
||||
name: "JSONAPITesting",
|
||||
dependencies: ["JSONAPI"]),
|
||||
.target(
|
||||
name: "JSONAPIArbitrary",
|
||||
dependencies: ["JSONAPI", "SwiftCheck"]),
|
||||
.target(
|
||||
name: "JSONAPIOpenAPI",
|
||||
dependencies: ["JSONAPI", "AnyCodable", "JSONAPIArbitrary"]),
|
||||
.testTarget(
|
||||
name: "JSONAPITests",
|
||||
dependencies: ["JSONAPI", "JSONAPITesting"]),
|
||||
.testTarget(
|
||||
name: "JSONAPITestingTests",
|
||||
dependencies: ["JSONAPI", "JSONAPITesting"]),
|
||||
.testTarget(
|
||||
name: "JSONAPIArbitraryTests",
|
||||
dependencies: ["JSONAPI", "SwiftCheck", "JSONAPIArbitrary"]),
|
||||
.testTarget(
|
||||
name: "JSONAPIOpenAPITests",
|
||||
dependencies: ["JSONAPI", "JSONAPIOpenAPI"])
|
||||
dependencies: ["JSONAPI", "JSONAPITesting"])
|
||||
],
|
||||
swiftLanguageVersions: [.v4_2]
|
||||
swiftLanguageVersions: [.v5]
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# JSONAPI
|
||||
[](http://opensource.org/licenses/MIT) [](https://swift.org) [](https://app.bitrise.io/app/c8295b9589aa401e)
|
||||
[](http://opensource.org/licenses/MIT) [](https://swift.org) [](https://app.bitrise.io/app/c8295b9589aa401e)
|
||||
|
||||
A Swift package for encoding to- and decoding from **JSON API** compliant requests and responses.
|
||||
|
||||
@@ -16,6 +16,7 @@ See the JSON API Spec here: https://jsonapi.org/format/
|
||||
- [Caveat](#caveat)
|
||||
- [Dev Environment](#dev-environment)
|
||||
- [Prerequisites](#prerequisites)
|
||||
- [CocoaPods](#cocoapods)
|
||||
- [Xcode project](#xcode-project)
|
||||
- [Running the Playground](#running-the-playground)
|
||||
- [Project Status](#project-status)
|
||||
@@ -33,8 +34,8 @@ See the JSON API Spec here: https://jsonapi.org/format/
|
||||
- [`JSONAPI.Entity`](#jsonapientity)
|
||||
- [`Meta`](#meta)
|
||||
- [`Links`](#links)
|
||||
- [`IdType`](#idtype)
|
||||
- [`MaybeRawId`](#mayberawid)
|
||||
- [`RawIdType`](#rawidtype)
|
||||
- [Convenient `typealiases`](#convenient-typealiases)
|
||||
- [`JSONAPI.Relationships`](#jsonapirelationships)
|
||||
- [`JSONAPI.Attributes`](#jsonapiattributes)
|
||||
@@ -82,7 +83,15 @@ If you find something wrong with this library and it isn't already mentioned und
|
||||
|
||||
## Dev Environment
|
||||
### Prerequisites
|
||||
1. Swift 4.2+ and Swift Package Manager
|
||||
1. Swift 4.2+
|
||||
2. Swift Package Manager *OR* Cocoapods
|
||||
|
||||
### CocoaPods
|
||||
To use this framework in your project via Cocoapods instead of Swift Package Manager, add the following dependencies to your Podfile.
|
||||
```
|
||||
pod 'Poly', :git => 'https://github.com/mattpolzin/Poly.git'
|
||||
pod 'JSONAPI', :git => 'https://github.com/mattpolzin/JSONAPI.git'
|
||||
```
|
||||
|
||||
### Xcode project
|
||||
To create an Xcode project for JSONAPI, run
|
||||
@@ -258,23 +267,23 @@ An `Entity` needs to be specialized on four generic types. The first is the `Ent
|
||||
|
||||
#### `Meta`
|
||||
|
||||
The second generic specialization on `Entity` is `Meta`. This is described in its own section [below](#jsonapimeta). All `Meta` at any level of a JSON API Document follow the same rules.
|
||||
The second generic specialization on `Entity` is `Meta`. This is described in its own section [below](#jsonapimeta). All `Meta` at any level of a JSON API Document follow the same rules. You can use `NoMetadata` if you do not need to package any metadata with the `Entity`.
|
||||
|
||||
#### `Links`
|
||||
|
||||
The third generic specialization on `Entity` is `Links`. This is described in its own section [below](#jsonnapilinks). All `Links` at any level of a JSON API Document follow the same rules, although the **SPEC** makes different suggestions as to what types of links might live on which parts of the Document.
|
||||
|
||||
#### `IdType`
|
||||
|
||||
The second is the raw type of `Id` to use for the `Entity`. The actual `Id` of the `Entity` will not be a `RawIdType`, though. The `Id` will package a value of `RawIdType` with a specialized reference back to the `Entity` type it identifies. This just looks like `Id<RawIdType, Entity<EntityDescription, RawIdType>>`.
|
||||
|
||||
Having the `Entity` type associated with the `Id` makes it easy to store all of your entities in a hash broken out by `Entity` type; You can pass `Ids` around and always know where to look for the `Entity` to which the `Id` refers.
|
||||
|
||||
A `RawIdType` is the underlying type that uniquely identifies an `Entity`. This is often a `String` or a `UUID`.
|
||||
The third generic specialization on `Entity` is `Links`. This is described in its own section [below](#jsonnapilinks). All `Links` at any level of a JSON API Document follow the same rules, although the **SPEC** makes different suggestions as to what types of links might live on which parts of the Document. You can use `NoLinks` if you do not need to package any links with the `Entity`.
|
||||
|
||||
#### `MaybeRawId`
|
||||
|
||||
`MaybeRawId` is either a `RawIdType` that can be used to uniquely identify `Entities` or it is `Unidentified` which is used to indicate an `Entity` does not have an `Id` (which is useful when a client is requesting that the server create an `Entity` and assign it a new `Id`).
|
||||
The last generic specialization on `Entity` is `MaybeRawId`. This is either a `RawIdType` that can be used to uniquely identify `Entities` or it is `Unidentified` which is used to indicate an `Entity` does not have an `Id` (which is useful when a client is requesting that the server create an `Entity` and assign it a new `Id`).
|
||||
|
||||
##### `RawIdType`
|
||||
|
||||
The raw type of `Id` to use for the `Entity`. The actual `Id` of the `Entity` will not be a `RawIdType`, though. The `Id` will package a value of `RawIdType` with a specialized reference back to the `Entity` type it identifies. This just looks like `Id<RawIdType, Entity<EntityDescription, Meta, Links, RawIdType>>`.
|
||||
|
||||
Having the `Entity` type associated with the `Id` makes it easy to store all of your entities in a hash broken out by `Entity` type; You can pass `Ids` around and always know where to look for the `Entity` to which the `Id` refers. This encapsulation provides some type safety because the Ids of two `Entities` with the "raw ID" of `"1"` but different types will not compare as equal.
|
||||
|
||||
A `RawIdType` is the underlying type that uniquely identifies an `Entity`. This is often a `String` or a `UUID`.
|
||||
|
||||
#### Convenient `typealiases`
|
||||
|
||||
@@ -339,11 +348,6 @@ typealias Attributes = NoAttributes
|
||||
let favoriteColor: String = person[\.favoriteColor]
|
||||
```
|
||||
|
||||
NOTE: Because of support for computed properties that are not wrapped in `Attribute`, `TransformedAttribute`, or `ValidatedAttribute`, the compiler cannot always infer the type of thing you want back when using subscript attribute access. The following code is ambiguous about whether it should return a `String` or an `Attribute<String>`:
|
||||
```swift
|
||||
let favoriteColor = person[\.favoriteColor]
|
||||
```
|
||||
|
||||
#### `Transformer`
|
||||
|
||||
Sometimes you need to use a type that does not encode or decode itself in the way you need to represent it as a serialized JSON object. For example, the Swift `Foundation` type `Date` can encode/decode itself to `Double` out of the box, but you might want to represent dates as ISO 8601 compliant `String`s instead. The Foundation library `JSONDecoder` has a setting to make this adjustment, but for the sake of an example, you could create a `Transformer`.
|
||||
@@ -382,7 +386,7 @@ You can also creator `Validators` and `ValidatedAttribute`s. A `Validator` is ju
|
||||
|
||||
#### Computed `Attribute`
|
||||
|
||||
You can add computed properties to your `EntityDescription.Attributes` struct if you would like to expose attributes that are not explicitly represented by the JSON. These computed properties do not have to be wrapped in `Attribute`, `ValidatedAttribute`, or `TransformedAttribute`. This allows computed attributes to be of types that are not `Codable`. Here's an example of how you might take the `Person[\.name]` attribute from the example above and create a `fullName` computed property.
|
||||
You can add computed properties to your `EntityDescription.Attributes` struct if you would like to expose attributes that are not explicitly represented by the JSON. These computed properties do not have to be wrapped in `Attribute`, `ValidatedAttribute`, or `TransformedAttribute`. This allows computed attributes to be of types that are not `Codable`. Here's an example of how you might take the `person[\.name]` attribute from the example above and create a `fullName` computed property.
|
||||
|
||||
```swift
|
||||
public var fullName: Attribute<String> {
|
||||
@@ -390,6 +394,8 @@ public var fullName: Attribute<String> {
|
||||
}
|
||||
```
|
||||
|
||||
If your computed property is wrapped in a `AttributeType` then you can still use the default subscript operator to access it (as would be the case with the `person[\.fullName]` example above). However, if you add a property to the `Attributes` `struct` that is not wrapped in an `AttributeType`, you must either access it from its full path (`person.attributes.newThing`) or with the "direct" subscript accessor (`person[direct: \.newThing]`). This keeps the subscript access unambiguous enough for the compiler to be helpful prior to explicitly casting, comparing, or storing the result.
|
||||
|
||||
### Copying `Entities`
|
||||
`Entity` is a value type, so copying is its default behavior. There are two common mutations you might want to make when copying an `Entity`:
|
||||
1. Assigning a new `Identifier` to the copy of an identified `Entity`.
|
||||
@@ -584,7 +590,7 @@ extension EntityDescription1.Attributes {
|
||||
### Meta-Attributes
|
||||
This advanced feature may not ever be useful, but if you find yourself in the situation of dealing with an API that does not 100% follow the **SPEC** then you might find meta-attributes are just the thing to make your entities more natural to work with.
|
||||
|
||||
Suppose, for example, you are presented with the unfortunate situation where a piece of information you need is only available as part of the `Id` of an entity. Perhaps a user's `Id` is formatted "{integer}-{createdAt}" where "createdAt" is the unix timestamp when the user account was created. The following `UserDescription` will expose what you need as an attribute. Realistically, this code is still terrible for its error handling. Using a `Result` type and/or invariants would clean things up substantially.
|
||||
Suppose, for example, you are presented with the unfortunate situation where a piece of information you need is only available as part of the `Id` of an entity. Perhaps a user's `Id` is formatted "{integer}-{createdAt}" where "createdAt" is the unix timestamp when the user account was created. The following `UserDescription` will expose what you need as an attribute. Realistically, the following example code is still terrible for its error handling. Using a `Result` type and/or invariants would clean things up substantially.
|
||||
|
||||
```swift
|
||||
enum UserDescription: EntityDescription {
|
||||
@@ -836,11 +842,7 @@ print(response.author)
|
||||
The `JSONAPI` framework is packaged with a test library to help you test your `JSONAPI` integration. The test library is called `JSONAPITesting`. It provides literal expressibility for `Attribute`, `ToOneRelationship`, and `Id` in many situations so that you can easily write test `Entity` values into your unit tests. It also provides a `check()` function for each `Entity` type that can be used to catch problems with your `JSONAPI` structures that are not caught by Swift's type system. You can see the `JSONAPITesting` in action in the Playground included with the `JSONAPI` repository.
|
||||
|
||||
# JSONAPI+Arbitrary
|
||||
The `JSONAPIArbitrary` framework adds `Arbitrary` support via `SwiftCheck`. With a little extra work on your part, this framework will allow you to create "arbitrary" (i.e. randomly generated) instances of your JSONAPI entities, includes, documents, etc.
|
||||
|
||||
This Framework is currently undocumented, but familiarity with `SwiftCheck` will likely afford the user enough information to use this library to aid in the generation of arbitrary JSONAPI Documents for testing purposes.
|
||||
This library has moved into its own Package. See https://github.com/mattpolzin/JSONAPI-Arbitrary
|
||||
|
||||
# JSONAPI+OpenAPI
|
||||
The `JSONAPIOpenAPI` framework adds the ability to generate OpenAPI compliant JSON documentation of a JSONAPI Document.
|
||||
|
||||
*This library is in its infancy. The documentation will grow as the framework becomes more complete.*
|
||||
This library has moved into its own Package. See https://github.com/mattpolzin/JSONAPI-OpenAPI
|
||||
|
||||
@@ -15,7 +15,7 @@ public extension TransformedAttribute {
|
||||
/// Generally, this is the most useful operation. The transformer gives you
|
||||
/// control over the decoding of the Attribute, but once the Attribute exists,
|
||||
/// mapping on it is most useful for creating computed Attribute properties.
|
||||
public func map<T: Codable>(_ transform: (Transformer.To) throws -> T) rethrows -> Attribute<T> {
|
||||
func map<T: Codable>(_ transform: (Transformer.To) throws -> T) rethrows -> Attribute<T> {
|
||||
return Attribute<T>(value: try transform(value))
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ public extension Attribute {
|
||||
/// Generally, this is the most useful operation. The transformer gives you
|
||||
/// control over the decoding of the Attribute, but once the Attribute exists,
|
||||
/// mapping on it is most useful for creating computed Attribute properties.
|
||||
public func map<T: Codable>(_ transform: (ValueType) throws -> T) rethrows -> Attribute<T> {
|
||||
func map<T: Codable>(_ transform: (ValueType) throws -> T) rethrows -> Attribute<T> {
|
||||
return Attribute<T>(value: try transform(value))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -397,20 +397,20 @@ public extension Entity where EntityRawIdType: JSONAPI.RawIdType {
|
||||
/// An Entity.Pointer is a `ToOneRelationship` with no metadata or links.
|
||||
/// This is just a convenient way to reference an Entity so that
|
||||
/// other Entities' Relationships can be built up from it.
|
||||
public typealias Pointer = ToOneRelationship<Entity, NoMetadata, NoLinks>
|
||||
typealias Pointer = ToOneRelationship<Entity, NoMetadata, NoLinks>
|
||||
|
||||
/// Entity.Pointers is a `ToManyRelationship` with no metadata or links.
|
||||
/// This is just a convenient way to reference a bunch of Entities so
|
||||
/// that other Entities' Relationships can be built up from them.
|
||||
public typealias Pointers = ToManyRelationship<Entity, NoMetadata, NoLinks>
|
||||
typealias Pointers = ToManyRelationship<Entity, NoMetadata, NoLinks>
|
||||
|
||||
/// Get a pointer to this entity that can be used as a
|
||||
/// relationship to another entity.
|
||||
public var pointer: Pointer {
|
||||
var pointer: Pointer {
|
||||
return Pointer(entity: self)
|
||||
}
|
||||
|
||||
public func pointer<MType: JSONAPI.Meta, LType: JSONAPI.Links>(withMeta meta: MType, links: LType) -> ToOneRelationship<Entity, MType, LType> {
|
||||
func pointer<MType: JSONAPI.Meta, LType: JSONAPI.Links>(withMeta meta: MType, links: LType) -> ToOneRelationship<Entity, MType, LType> {
|
||||
return ToOneRelationship(entity: self, meta: meta, links: links)
|
||||
}
|
||||
}
|
||||
@@ -419,19 +419,19 @@ public extension Entity where EntityRawIdType: JSONAPI.RawIdType {
|
||||
public extension Entity where EntityRawIdType == Unidentified {
|
||||
/// Create a new Entity from this one with a newly created
|
||||
/// unique Id of the given type.
|
||||
public func identified<RawIdType: CreatableRawIdType>(byType: RawIdType.Type) -> Entity<Description, MetaType, LinksType, RawIdType> {
|
||||
func identified<RawIdType: CreatableRawIdType>(byType: RawIdType.Type) -> Entity<Description, MetaType, LinksType, RawIdType> {
|
||||
return .init(attributes: attributes, relationships: relationships, meta: meta, links: links)
|
||||
}
|
||||
|
||||
/// Create a new Entity from this one with the given Id.
|
||||
public func identified<RawIdType: JSONAPI.RawIdType>(by id: RawIdType) -> Entity<Description, MetaType, LinksType, RawIdType> {
|
||||
func identified<RawIdType: JSONAPI.RawIdType>(by id: RawIdType) -> Entity<Description, MetaType, LinksType, RawIdType> {
|
||||
return .init(id: Entity<Description, MetaType, LinksType, RawIdType>.Identifier(rawValue: id), attributes: attributes, relationships: relationships, meta: meta, links: links)
|
||||
}
|
||||
}
|
||||
|
||||
public extension Entity where EntityRawIdType: CreatableRawIdType {
|
||||
/// Create a copy of this Entity with a new unique Id.
|
||||
public func withNewIdentifier() -> Entity {
|
||||
func withNewIdentifier() -> Entity {
|
||||
return Entity(attributes: attributes, relationships: relationships, meta: meta, links: links)
|
||||
}
|
||||
}
|
||||
@@ -440,31 +440,34 @@ public extension Entity where EntityRawIdType: CreatableRawIdType {
|
||||
public extension EntityProxy {
|
||||
/// Access the attribute at the given keypath. This just
|
||||
/// allows you to write `entity[\.propertyName]` instead
|
||||
/// of `entity.relationships.propertyName`.
|
||||
/// of `entity.attributes.propertyName`.
|
||||
subscript<T: AttributeType>(_ path: KeyPath<Description.Attributes, T>) -> T.ValueType {
|
||||
return attributes[keyPath: path].value
|
||||
}
|
||||
|
||||
/// Access the attribute at the given keypath. This just
|
||||
/// allows you to write `entity[\.propertyName]` instead
|
||||
/// of `entity.relationships.propertyName`.
|
||||
/// of `entity.attributes.propertyName`.
|
||||
subscript<T: AttributeType>(_ path: KeyPath<Description.Attributes, T?>) -> T.ValueType? {
|
||||
return attributes[keyPath: path]?.value
|
||||
}
|
||||
|
||||
/// Access the attribute at the given keypath. This just
|
||||
/// allows you to write `entity[\.propertyName]` instead
|
||||
/// of `entity.relationships.propertyName`.
|
||||
/// of `entity.attributes.propertyName`.
|
||||
subscript<T: AttributeType, U>(_ path: KeyPath<Description.Attributes, T?>) -> U? where T.ValueType == U? {
|
||||
// Implementation Note: Handles Transform that returns optional
|
||||
// type.
|
||||
return attributes[keyPath: path].flatMap { $0.value }
|
||||
}
|
||||
|
||||
/// Access the computed attribute at the given keypath. This just
|
||||
/// Access the storage of the attribute at the given keypath. This just
|
||||
/// allows you to write `entity[\.propertyName]` instead
|
||||
/// of `entity.relationships.propertyName`.
|
||||
subscript<T>(_ path: KeyPath<Description.Attributes, T>) -> T {
|
||||
/// of `entity.attributes.propertyName`.
|
||||
/// Most of the subscripts dig into an `AttributeType`. This subscript
|
||||
/// returns the `AttributeType` (or another type, if you are accessing
|
||||
/// an attribute that is not stored in an `AttributeType`).
|
||||
subscript<T>(direct path: KeyPath<Description.Attributes, T>) -> T {
|
||||
// Implementation Note: Handles attributes that are not
|
||||
// AttributeType. These should only exist as computed properties.
|
||||
return attributes[keyPath: path]
|
||||
@@ -485,14 +488,14 @@ public extension EntityProxy {
|
||||
/// Access to an Id of a `ToOneRelationship`.
|
||||
/// This allows you to write `entity ~> \.other` instead
|
||||
/// of `entity.relationships.other.id`.
|
||||
public static func ~><OtherEntity: Identifiable, MType: JSONAPI.Meta, LType: JSONAPI.Links>(entity: Self, path: KeyPath<Description.Relationships, ToOneRelationship<OtherEntity, MType, LType>>) -> OtherEntity.Identifier {
|
||||
static func ~><OtherEntity: Identifiable, MType: JSONAPI.Meta, LType: JSONAPI.Links>(entity: Self, path: KeyPath<Description.Relationships, ToOneRelationship<OtherEntity, MType, LType>>) -> OtherEntity.Identifier {
|
||||
return entity.relationships[keyPath: path].id
|
||||
}
|
||||
|
||||
/// Access to an Id of an optional `ToOneRelationship`.
|
||||
/// This allows you to write `entity ~> \.other` instead
|
||||
/// of `entity.relationships.other?.id`.
|
||||
public static func ~><OtherEntity: OptionalRelatable, MType: JSONAPI.Meta, LType: JSONAPI.Links>(entity: Self, path: KeyPath<Description.Relationships, ToOneRelationship<OtherEntity, MType, LType>?>) -> OtherEntity.Identifier {
|
||||
static func ~><OtherEntity: OptionalRelatable, MType: JSONAPI.Meta, LType: JSONAPI.Links>(entity: Self, path: KeyPath<Description.Relationships, ToOneRelationship<OtherEntity, MType, LType>?>) -> OtherEntity.Identifier {
|
||||
// Implementation Note: This signature applies to `ToOneRelationship<E?, _, _>?`
|
||||
// whereas the one below applies to `ToOneRelationship<E, _, _>?`
|
||||
return entity.relationships[keyPath: path]?.id
|
||||
@@ -501,7 +504,7 @@ public extension EntityProxy {
|
||||
/// Access to an Id of an optional `ToOneRelationship`.
|
||||
/// This allows you to write `entity ~> \.other` instead
|
||||
/// of `entity.relationships.other?.id`.
|
||||
public static func ~><OtherEntity: Relatable, MType: JSONAPI.Meta, LType: JSONAPI.Links>(entity: Self, path: KeyPath<Description.Relationships, ToOneRelationship<OtherEntity, MType, LType>?>) -> OtherEntity.Identifier? {
|
||||
static func ~><OtherEntity: Relatable, MType: JSONAPI.Meta, LType: JSONAPI.Links>(entity: Self, path: KeyPath<Description.Relationships, ToOneRelationship<OtherEntity, MType, LType>?>) -> OtherEntity.Identifier? {
|
||||
// Implementation Note: This signature applies to `ToOneRelationship<E, _, _>?`
|
||||
// whereas the one above applies to `ToOneRelationship<E?, _, _>?`
|
||||
return entity.relationships[keyPath: path]?.id
|
||||
@@ -510,14 +513,14 @@ public extension EntityProxy {
|
||||
/// Access to all Ids of a `ToManyRelationship`.
|
||||
/// This allows you to write `entity ~> \.others` instead
|
||||
/// of `entity.relationships.others.ids`.
|
||||
public static func ~><OtherEntity: Relatable, MType: JSONAPI.Meta, LType: JSONAPI.Links>(entity: Self, path: KeyPath<Description.Relationships, ToManyRelationship<OtherEntity, MType, LType>>) -> [OtherEntity.Identifier] {
|
||||
static func ~><OtherEntity: Relatable, MType: JSONAPI.Meta, LType: JSONAPI.Links>(entity: Self, path: KeyPath<Description.Relationships, ToManyRelationship<OtherEntity, MType, LType>>) -> [OtherEntity.Identifier] {
|
||||
return entity.relationships[keyPath: path].ids
|
||||
}
|
||||
|
||||
/// Access to all Ids of an optional `ToManyRelationship`.
|
||||
/// This allows you to write `entity ~> \.others` instead
|
||||
/// of `entity.relationships.others?.ids`.
|
||||
public static func ~><OtherEntity: Relatable, MType: JSONAPI.Meta, LType: JSONAPI.Links>(entity: Self, path: KeyPath<Description.Relationships, ToManyRelationship<OtherEntity, MType, LType>?>) -> [OtherEntity.Identifier]? {
|
||||
static func ~><OtherEntity: Relatable, MType: JSONAPI.Meta, LType: JSONAPI.Links>(entity: Self, path: KeyPath<Description.Relationships, ToManyRelationship<OtherEntity, MType, LType>?>) -> [OtherEntity.Identifier]? {
|
||||
return entity.relationships[keyPath: path]?.ids
|
||||
}
|
||||
}
|
||||
@@ -527,14 +530,14 @@ public extension EntityProxy {
|
||||
/// Access to an Id of a `ToOneRelationship`.
|
||||
/// This allows you to write `entity ~> \.other` instead
|
||||
/// of `entity.relationships.other.id`.
|
||||
public static func ~><Identifier: IdType>(entity: Self, path: KeyPath<Description.Relationships, (Self) -> Identifier>) -> Identifier {
|
||||
static func ~><Identifier: IdType>(entity: Self, path: KeyPath<Description.Relationships, (Self) -> Identifier>) -> Identifier {
|
||||
return entity.relationships[keyPath: path](entity)
|
||||
}
|
||||
|
||||
/// Access to all Ids of a `ToManyRelationship`.
|
||||
/// This allows you to write `entity ~> \.others` instead
|
||||
/// of `entity.relationships.others.ids`.
|
||||
public static func ~><Identifier: IdType>(entity: Self, path: KeyPath<Description.Relationships, (Self) -> [Identifier]>) -> [Identifier] {
|
||||
static func ~><Identifier: IdType>(entity: Self, path: KeyPath<Description.Relationships, (Self) -> [Identifier]>) -> [Identifier] {
|
||||
return entity.relationships[keyPath: path](entity)
|
||||
}
|
||||
}
|
||||
@@ -552,7 +555,7 @@ private enum ResourceObjectCodingKeys: String, CodingKey {
|
||||
}
|
||||
|
||||
public extension Entity {
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: ResourceObjectCodingKeys.self)
|
||||
|
||||
try container.encode(Entity.jsonType, forKey: .type)
|
||||
@@ -578,7 +581,7 @@ public extension Entity {
|
||||
}
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
init(from decoder: Decoder) throws {
|
||||
|
||||
let container = try decoder.container(keyedBy: ResourceObjectCodingKeys.self)
|
||||
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
//
|
||||
// Result.swift
|
||||
// JSONAPI
|
||||
//
|
||||
// Created by Mathew Polzin on 12/5/18.
|
||||
//
|
||||
|
||||
enum Result<T, E: Swift.Error> {
|
||||
case success(T)
|
||||
case failure(E)
|
||||
|
||||
var value: T? {
|
||||
guard case .success(let val) = self else {
|
||||
return nil
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
var error: E? {
|
||||
guard case .failure(let err) = self else {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func map<U>(_ transform: (T) -> U) -> Result<U, E> {
|
||||
switch self {
|
||||
case .failure(let err):
|
||||
return .failure(err)
|
||||
case .success(let val):
|
||||
return .success(transform(val))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Result: CustomStringConvertible where T: CustomStringConvertible, E: CustomStringConvertible {
|
||||
var description: String {
|
||||
switch self {
|
||||
case .success(let val):
|
||||
return String(describing: val)
|
||||
case .failure(let err):
|
||||
return String(describing: err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
//
|
||||
// APIDescription+Arbitrary.swift
|
||||
// JSONAPIArbitrary
|
||||
//
|
||||
// Created by Mathew Polzin on 1/21/19.
|
||||
//
|
||||
|
||||
import SwiftCheck
|
||||
import JSONAPI
|
||||
|
||||
extension APIDescription: Arbitrary where Meta: Arbitrary {
|
||||
public static var arbitrary: Gen<APIDescription<Meta>> {
|
||||
return Gen.compose { c in
|
||||
APIDescription(version: c.generate(),
|
||||
meta: c.generate())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension NoAPIDescription: Arbitrary {
|
||||
public static var arbitrary: Gen<NoAPIDescription> {
|
||||
return Gen.pure(.none)
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
//
|
||||
// Attribute+Arbitrary.swift
|
||||
// JSONAPIArbitrary
|
||||
//
|
||||
// Created by Mathew Polzin on 1/15/19.
|
||||
//
|
||||
|
||||
import SwiftCheck
|
||||
import JSONAPI
|
||||
|
||||
extension Attribute: Arbitrary where RawValue: Arbitrary {
|
||||
public static var arbitrary: Gen<Attribute<RawValue>> {
|
||||
return RawValue.arbitrary.map { .init(value: $0) }
|
||||
}
|
||||
}
|
||||
|
||||
// Cannot conform TransformedAttribute to Arbitrary here
|
||||
// because there is no way to guarantee that an arbitrary
|
||||
// RawValue will successfully transform or that an
|
||||
// arbitrary Value will successfully reverse-transform.
|
||||
@@ -1,110 +0,0 @@
|
||||
//
|
||||
// Document+Arbitrary.swift
|
||||
// JSONAPIArbitrary
|
||||
//
|
||||
// Created by Mathew Polzin on 1/21/19.
|
||||
//
|
||||
|
||||
import SwiftCheck
|
||||
import JSONAPI
|
||||
|
||||
extension Document.Body.Data: Arbitrary where PrimaryResourceBody: Arbitrary, IncludeType: Arbitrary, MetaType: Arbitrary, LinksType: Arbitrary {
|
||||
public static var arbitrary: Gen<Document<PrimaryResourceBody, MetaType, LinksType, IncludeType, APIDescription, Error>.Body.Data> {
|
||||
return Gen.compose { c in
|
||||
Document.Body.Data(primary: c.generate(),
|
||||
includes: c.generate(),
|
||||
meta: c.generate(),
|
||||
links: c.generate())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Document.Body: Arbitrary where PrimaryResourceBody: Arbitrary, IncludeType: Arbitrary, MetaType: Arbitrary, LinksType: Arbitrary, Error: Arbitrary {
|
||||
public static var arbitrary: Gen<Document<PrimaryResourceBody, MetaType, LinksType, IncludeType, APIDescription, Error>.Body> {
|
||||
return Gen.one(of: [
|
||||
arbitraryData,
|
||||
arbitraryErrors
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
extension Document.Body where PrimaryResourceBody: Arbitrary, IncludeType: Arbitrary, MetaType: Arbitrary, LinksType: Arbitrary {
|
||||
/// Arbitrary Document.Body with data (guaranteed to not
|
||||
/// be an error body).
|
||||
public static var arbitraryData: Gen<Document<PrimaryResourceBody, MetaType, LinksType, IncludeType, APIDescription, Error>.Body> {
|
||||
return Document.Body.Data.arbitrary.map(Document.Body.data)
|
||||
}
|
||||
}
|
||||
|
||||
extension Document.Body where MetaType: Arbitrary, LinksType: Arbitrary, Error: Arbitrary {
|
||||
/// Arbitrary Document.Body with errors (guaranteed to not
|
||||
/// be a data body).
|
||||
public static var arbitraryErrors: Gen<Document<PrimaryResourceBody, MetaType, LinksType, IncludeType, APIDescription, Error>.Body> {
|
||||
return Gen.compose { c in
|
||||
Document.Body.errors(c.generate(),
|
||||
meta: c.generate(),
|
||||
links: c.generate())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Document.Body where Error: Arbitrary {
|
||||
/// Arbitrary Document.Body with errors but no
|
||||
/// metadata or links (also guaranteed to not
|
||||
/// be a data body).
|
||||
public static var arbitraryErrorsWithoutMetaOrLinks: Gen<Document<PrimaryResourceBody, MetaType, LinksType, IncludeType, APIDescription, Error>.Body> {
|
||||
return Gen.compose { c in
|
||||
Document.Body.errors(c.generate(),
|
||||
meta: nil,
|
||||
links: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Document: Arbitrary where PrimaryResourceBody: Arbitrary, IncludeType: Arbitrary, MetaType: Arbitrary, LinksType: Arbitrary, Error: Arbitrary, APIDescription: Arbitrary {
|
||||
public static var arbitrary: Gen<Document<PrimaryResourceBody, MetaType, LinksType, IncludeType, APIDescription, Error>> {
|
||||
return Gen.one(of: [
|
||||
arbitraryData,
|
||||
arbitraryErrors
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
extension Document where PrimaryResourceBody: Arbitrary, IncludeType: Arbitrary, MetaType: Arbitrary, LinksType: Arbitrary, APIDescription: Arbitrary {
|
||||
/// Arbitrary Document with data (guaranteed to not
|
||||
/// be an error body).
|
||||
public static var arbitraryData: Gen<Document<PrimaryResourceBody, MetaType, LinksType, IncludeType, APIDescription, Error>> {
|
||||
return Gen.compose { c in
|
||||
Document(apiDescription: c.generate(),
|
||||
body: c.generate(),
|
||||
includes: c.generate(),
|
||||
meta: c.generate(),
|
||||
links: c.generate())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Document where MetaType: Arbitrary, LinksType: Arbitrary, Error: Arbitrary, APIDescription: Arbitrary {
|
||||
/// Arbitrary Document with errors (guaranteed to not
|
||||
/// be a data body).
|
||||
public static var arbitraryErrors: Gen<Document<PrimaryResourceBody, MetaType, LinksType, IncludeType, APIDescription, Error>> {
|
||||
return Gen.compose { c in
|
||||
Document(apiDescription: c.generate(),
|
||||
errors: c.generate(),
|
||||
meta: c.generate(),
|
||||
links: c.generate())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Document where Error: Arbitrary, APIDescription: Arbitrary {
|
||||
/// Arbitrary Document with errors but no
|
||||
/// metadata or links (also guaranteed to not
|
||||
/// be a data body).
|
||||
public static var arbitraryErrors: Gen<Document<PrimaryResourceBody, MetaType, LinksType, IncludeType, APIDescription, Error>> {
|
||||
return Gen.compose { c in
|
||||
Document(apiDescription: c.generate(),
|
||||
errors: c.generate())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
//
|
||||
// Entity+Arbitrary.swift
|
||||
// JSONAPIArbitrary
|
||||
//
|
||||
// Created by Mathew Polzin on 1/14/19.
|
||||
//
|
||||
|
||||
import SwiftCheck
|
||||
import JSONAPI
|
||||
|
||||
extension NoMetadata: Arbitrary {
|
||||
public static var arbitrary: Gen<NoMetadata> {
|
||||
return Gen.pure(.none)
|
||||
}
|
||||
}
|
||||
|
||||
extension NoLinks: Arbitrary {
|
||||
public static var arbitrary: Gen<NoLinks> {
|
||||
return Gen.pure(.none)
|
||||
}
|
||||
}
|
||||
|
||||
extension NoAttributes: Arbitrary {
|
||||
public static var arbitrary: Gen<NoAttributes> {
|
||||
return Gen.pure(.none)
|
||||
}
|
||||
}
|
||||
|
||||
extension NoRelationships: Arbitrary {
|
||||
public static var arbitrary: Gen<NoRelationships> {
|
||||
return Gen.pure(.none)
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Arbitrary conformance for MetaType, LinksType, Description.Attributes,
|
||||
// and Description.Relationships must all be provided BY YOU for Entity to
|
||||
// gain Arbitrary conformance (with the exception of NoMetadata, NoLinks,
|
||||
// NoAttributes, and NoRelationships which all have Arbitrary conformance
|
||||
// out of the box).
|
||||
extension Entity: Arbitrary where MetaType: Arbitrary, LinksType: Arbitrary, Description.Attributes: Arbitrary, Description.Relationships: Arbitrary, EntityRawIdType: Arbitrary {
|
||||
public static var arbitrary: Gen<Entity<Description, MetaType, LinksType, EntityRawIdType>> {
|
||||
return Gen.compose { c in
|
||||
Entity(id: c.generate(),
|
||||
attributes: c.generate(),
|
||||
relationships: c.generate(),
|
||||
meta: c.generate(),
|
||||
links: c.generate())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
//
|
||||
// Error+Arbitrary.swift
|
||||
// JSONAPIArbitrary
|
||||
//
|
||||
// Created by Mathew Polzin on 1/21/19.
|
||||
//
|
||||
|
||||
import SwiftCheck
|
||||
import JSONAPI
|
||||
|
||||
extension UnknownJSONAPIError: Arbitrary {
|
||||
public static var arbitrary: Gen<UnknownJSONAPIError> {
|
||||
return Gen.pure(.unknownError)
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
//
|
||||
// Id+Arbitrary.swift
|
||||
// JSONAPIArbitrary
|
||||
//
|
||||
// Created by Mathew Polzin on 1/14/19.
|
||||
//
|
||||
|
||||
import SwiftCheck
|
||||
import JSONAPI
|
||||
|
||||
extension Unidentified: Arbitrary {
|
||||
public static var arbitrary: Gen<Unidentified> {
|
||||
return Gen.pure(.init())
|
||||
}
|
||||
}
|
||||
|
||||
extension Id: Arbitrary where RawType: Arbitrary {
|
||||
public static var arbitrary: Gen<Id<RawType, IdentifiableType>> {
|
||||
return RawType.arbitrary.map { Id(rawValue: $0) }
|
||||
}
|
||||
}
|
||||
@@ -1,166 +0,0 @@
|
||||
//
|
||||
// Includes+Arbitrary.swift
|
||||
// JSONAPIArbitrary
|
||||
//
|
||||
// Created by Mathew Polzin on 1/21/19.
|
||||
//
|
||||
|
||||
import SwiftCheck
|
||||
import JSONAPI
|
||||
|
||||
extension Includes: Arbitrary where I: Arbitrary {
|
||||
public static var arbitrary: Gen<Includes<I>> {
|
||||
return I
|
||||
.arbitrary
|
||||
.proliferate
|
||||
.map(Includes.init(values:))
|
||||
}
|
||||
}
|
||||
|
||||
extension NoIncludes: Arbitrary {
|
||||
public static var arbitrary: Gen<NoIncludes> {
|
||||
return Gen.pure(NoIncludes())
|
||||
}
|
||||
}
|
||||
|
||||
extension Include1: Arbitrary where A: Arbitrary {
|
||||
public static var arbitrary: Gen<Include1<A>> {
|
||||
return Gen.one(of: [
|
||||
A.arbitrary.map(Include1.init)
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
extension Include2: Arbitrary where A: Arbitrary, B: Arbitrary {
|
||||
public static var arbitrary: Gen<Include2<A, B>> {
|
||||
return Gen.one(of: [
|
||||
A.arbitrary.map(Include2.init),
|
||||
B.arbitrary.map(Include2.init)
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
extension Include3: Arbitrary where A: Arbitrary, B: Arbitrary, C: Arbitrary {
|
||||
public static var arbitrary: Gen<Include3<A, B, C>> {
|
||||
return Gen.one(of: [
|
||||
A.arbitrary.map(Include3.init),
|
||||
B.arbitrary.map(Include3.init),
|
||||
C.arbitrary.map(Include3.init)
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
extension Include4: Arbitrary where A: Arbitrary, B: Arbitrary, C: Arbitrary, D: Arbitrary {
|
||||
public static var arbitrary: Gen<Include4<A, B, C, D>> {
|
||||
return Gen.one(of: [
|
||||
A.arbitrary.map(Include4.init),
|
||||
B.arbitrary.map(Include4.init),
|
||||
C.arbitrary.map(Include4.init),
|
||||
D.arbitrary.map(Include4.init)
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
extension Include5: Arbitrary where A: Arbitrary, B: Arbitrary, C: Arbitrary, D: Arbitrary, E: Arbitrary {
|
||||
public static var arbitrary: Gen<Include5<A, B, C, D, E>> {
|
||||
return Gen.one(of: [
|
||||
A.arbitrary.map(Include5.init),
|
||||
B.arbitrary.map(Include5.init),
|
||||
C.arbitrary.map(Include5.init),
|
||||
D.arbitrary.map(Include5.init),
|
||||
E.arbitrary.map(Include5.init)
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
extension Include6: Arbitrary where A: Arbitrary, B: Arbitrary, C: Arbitrary, D: Arbitrary, E: Arbitrary, F: Arbitrary {
|
||||
public static var arbitrary: Gen<Include6<A, B, C, D, E, F>> {
|
||||
// Note broken up because compiler cannot typecheck entire array
|
||||
// before it times out
|
||||
let set1: [Gen<Include6<A, B, C, D, E, F>>] = [
|
||||
A.arbitrary.map(Include6.init),
|
||||
B.arbitrary.map(Include6.init),
|
||||
C.arbitrary.map(Include6.init)
|
||||
]
|
||||
|
||||
let set2: [Gen<Include6<A, B, C, D, E, F>>] = [
|
||||
D.arbitrary.map(Include6.init),
|
||||
E.arbitrary.map(Include6.init),
|
||||
F.arbitrary.map(Include6.init)
|
||||
]
|
||||
|
||||
return Gen.one(of: set1 + set2)
|
||||
}
|
||||
}
|
||||
|
||||
extension Include7: Arbitrary where A: Arbitrary, B: Arbitrary, C: Arbitrary, D: Arbitrary, E: Arbitrary, F: Arbitrary, G: Arbitrary {
|
||||
public static var arbitrary: Gen<Include7<A, B, C, D, E, F, G>> {
|
||||
// Note broken up because compiler cannot typecheck entire array
|
||||
// before it times out
|
||||
let set1: [Gen<Include7<A, B, C, D, E, F, G>>] = [
|
||||
A.arbitrary.map(Include7.init),
|
||||
B.arbitrary.map(Include7.init),
|
||||
C.arbitrary.map(Include7.init)
|
||||
]
|
||||
|
||||
let set2: [Gen<Include7<A, B, C, D, E, F, G>>] = [
|
||||
D.arbitrary.map(Include7.init),
|
||||
E.arbitrary.map(Include7.init),
|
||||
F.arbitrary.map(Include7.init),
|
||||
G.arbitrary.map(Include7.init)
|
||||
]
|
||||
|
||||
return Gen.one(of: set1 + set2)
|
||||
}
|
||||
}
|
||||
|
||||
extension Include8: Arbitrary where A: Arbitrary, B: Arbitrary, C: Arbitrary, D: Arbitrary, E: Arbitrary, F: Arbitrary, G: Arbitrary, H: Arbitrary {
|
||||
public static var arbitrary: Gen<Include8<A, B, C, D, E, F, G, H>> {
|
||||
// Note broken up because compiler cannot typecheck entire array
|
||||
// before it times out
|
||||
let set1: [Gen<Include8<A, B, C, D, E, F, G, H>>] = [
|
||||
A.arbitrary.map(Include8.init),
|
||||
B.arbitrary.map(Include8.init),
|
||||
C.arbitrary.map(Include8.init)
|
||||
]
|
||||
|
||||
let set2: [Gen<Include8<A, B, C, D, E, F, G, H>>] = [
|
||||
D.arbitrary.map(Include8.init),
|
||||
E.arbitrary.map(Include8.init),
|
||||
F.arbitrary.map(Include8.init)
|
||||
]
|
||||
|
||||
let set3: [Gen<Include8<A, B, C, D, E, F, G, H>>] = [
|
||||
G.arbitrary.map(Include8.init),
|
||||
H.arbitrary.map(Include8.init)
|
||||
]
|
||||
|
||||
return Gen.one(of: set1 + set2 + set3)
|
||||
}
|
||||
}
|
||||
|
||||
extension Include9: Arbitrary where A: Arbitrary, B: Arbitrary, C: Arbitrary, D: Arbitrary, E: Arbitrary, F: Arbitrary, G: Arbitrary, H: Arbitrary, I: Arbitrary {
|
||||
public static var arbitrary: Gen<Include9<A, B, C, D, E, F, G, H, I>> {
|
||||
// Note broken up because compiler cannot typecheck entire array
|
||||
// before it times out
|
||||
let set1: [Gen<Include9<A, B, C, D, E, F, G, H, I>>] = [
|
||||
A.arbitrary.map(Include9.init),
|
||||
B.arbitrary.map(Include9.init),
|
||||
C.arbitrary.map(Include9.init)
|
||||
]
|
||||
|
||||
let set2: [Gen<Include9<A, B, C, D, E, F, G, H, I>>] = [
|
||||
D.arbitrary.map(Include9.init),
|
||||
E.arbitrary.map(Include9.init),
|
||||
F.arbitrary.map(Include9.init)
|
||||
]
|
||||
|
||||
let set3: [Gen<Include9<A, B, C, D, E, F, G, H, I>>] = [
|
||||
G.arbitrary.map(Include9.init),
|
||||
H.arbitrary.map(Include9.init),
|
||||
I.arbitrary.map(Include9.init)
|
||||
]
|
||||
|
||||
return Gen.one(of: set1 + set2 + set3)
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
//
|
||||
// Relationship+Arbitrary.swift
|
||||
// JSONAPIArbitrary
|
||||
//
|
||||
// Created by Mathew Polzin on 1/15/19.
|
||||
//
|
||||
|
||||
import SwiftCheck
|
||||
import JSONAPI
|
||||
|
||||
extension ToOneRelationship: Arbitrary where Identifiable.Identifier: Arbitrary, MetaType: Arbitrary, LinksType: Arbitrary {
|
||||
public static var arbitrary: Gen<ToOneRelationship<Identifiable, MetaType, LinksType>> {
|
||||
return Gen.compose { c in
|
||||
return .init(id: c.generate(),
|
||||
meta: c.generate(),
|
||||
links: c.generate())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ToOneRelationship where MetaType: Arbitrary, LinksType: Arbitrary {
|
||||
/// Create a generator of arbitrary ToOneRelationships that will all
|
||||
/// point to one of the given entities. This allows you to create
|
||||
/// arbitrary relationships that make sense in a broader context where
|
||||
/// the relationship must actually point to another entity.
|
||||
public static func arbitrary<E: EntityType>(givenEntities: [E]) -> Gen<ToOneRelationship<Identifiable, MetaType, LinksType>> where E.Id == Identifiable.Identifier {
|
||||
|
||||
return Gen.compose { c in
|
||||
let idGen = Gen.fromElements(of: givenEntities).map { $0.id }
|
||||
return .init(id: c.generate(using: idGen),
|
||||
meta: c.generate(),
|
||||
links: c.generate())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ToManyRelationship: Arbitrary where Relatable.Identifier: Arbitrary, MetaType: Arbitrary, LinksType: Arbitrary {
|
||||
public static var arbitrary: Gen<ToManyRelationship<Relatable, MetaType, LinksType>> {
|
||||
return Gen.compose { c in
|
||||
return .init(ids: c.generate(),
|
||||
meta: c.generate(),
|
||||
links: c.generate())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ToManyRelationship where MetaType: Arbitrary, LinksType: Arbitrary {
|
||||
/// Create a generator of arbitrary ToManyRelationships that will all
|
||||
/// point to some number of the given entities. This allows you to create
|
||||
/// arbitrary relationships that make sense in a broader context where
|
||||
/// the relationship must actually point to other existing entities.
|
||||
public static func arbitrary<E: EntityType>(givenEntities: [E]) -> Gen<ToManyRelationship<Relatable, MetaType, LinksType>> where E.Id == Relatable.Identifier {
|
||||
return Gen.compose { c in
|
||||
let idsGen = Gen.fromElements(of: givenEntities).map { $0.id }.proliferate
|
||||
return .init(ids: c.generate(using: idsGen),
|
||||
meta: c.generate(),
|
||||
links: c.generate())
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user