APIDescription gets a public initializer. A new playground page explores the full potential of the Framework, in a sense.

This commit is contained in:
Mathew Polzin
2018-12-31 17:19:51 -08:00
parent d5a24c4adb
commit b46f5166ea
3 changed files with 219 additions and 0 deletions
@@ -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: EntityDescription {
static var type: String { return "authors" }
struct Attributes: JSONAPI.Attributes {
let name: Attribute<String>
}
struct Relationships: JSONAPI.Relationships {
let articles: ToManyRelationship<Article, ToManyRelationshipMetadata, ToManyRelationshipLinks>
}
}
typealias Author = JSONAPI.Entity<AuthorDescription, EntityMetadata, EntityLinks, String>
/// Description of an Article entity.
enum ArticleDescription: EntityDescription {
static var type: 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.Entity<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(entity: 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)!)
+1
View File
@@ -3,5 +3,6 @@
<pages>
<page name='Test Library'/>
<page name='Usage'/>
<page name='Full Document Verbose Generation'/>
</pages>
</playground>