Skip to content
This repository has been archived by the owner on Feb 26, 2024. It is now read-only.

Commit

Permalink
Merge pull request #29 from Sage-Bionetworks/syoung/docs-generator
Browse files Browse the repository at this point in the history
Add a generic property fall-back using Mirror
  • Loading branch information
syoung-smallwisdom authored Apr 24, 2023
2 parents 78a329a + c6cb39d commit 3ff621e
Show file tree
Hide file tree
Showing 8 changed files with 743 additions and 514 deletions.
610 changes: 105 additions & 505 deletions Sources/JsonModel/Documentable.swift

Large diffs are not rendered by default.

507 changes: 507 additions & 0 deletions Sources/JsonModel/JsonDocumentBuilder.swift

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions Sources/JsonModel/PolymorphicSerializer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,10 @@ open class GenericPolymorphicSerializer<ProtocolValue> : GenericSerializer {
examples.compactMap { $0 as? DocumentableObject }
}

public func documentableAnyOf() -> [DocumentableObject.Type] {
typeMap.compactMap { $0.value as? DocumentableObject.Type }
}

public func canDecode(_ typeName: String) -> Bool {
typeMap[typeName] != nil
}
Expand Down Expand Up @@ -370,6 +374,10 @@ extension PolymorphicSerializer {
public func documentableExamples() -> [DocumentableObject] {
return examples.compactMap { $0 as? DocumentableObject }
}

public func documentableAnyOf() -> [DocumentableObject.Type] {
documentableExamples().map { type(of: $0) }
}
}

@available(*, deprecated, message: "Use `GenericPolymorphicSerializer` instead.")
Expand Down
2 changes: 1 addition & 1 deletion Tests/JsonModelTests/AnyCodableTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ final class AnyCodableTests: XCTestCase {
"uuid" : uuid,
"url" : url,
"array" : ["cat", "dog", "duck"],
"dictionary" : ["a" : 1, "b" : "bat", "c" : true]
"dictionary" : ["a" : 1, "b" : "bat", "c" : true] as [String : Any]
]

do {
Expand Down
114 changes: 114 additions & 0 deletions Tests/JsonModelTests/DocumentableTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,42 @@ final class DocumentableTests: XCTestCase {
XCTFail("Missing definition mapping for \(key)")
}
}

func testAutoGeneratedDocs() throws {
let jsonDocBuilder = JsonDocumentBuilder(baseUrl: testBURL, rootDocument: AutoGeneratedObject())
let docs = try jsonDocBuilder.buildSchemas()

XCTAssertEqual(docs.count, 1)
guard let doc = docs.first else { return }

if let def = doc.definitions?["Child"], case .object(let child) = def {

XCTAssertEqual(doc.root.properties?["children"], .array(.init(items: .reference(.init(ref: child.id)))))
XCTAssertEqual(doc.root.properties?["favorite"], .reference(.init(ref: child.id)))

XCTAssertEqual(child.properties?["name"], .primitive(.string))
XCTAssertEqual(child.properties?["age"], .primitive(.integer))
XCTAssertEqual(child.properties?["hasPet"], .primitive(.boolean))
XCTAssertEqual(child.properties?["rating"], .primitive(.number))

if let ageRangeDef = doc.definitions?["AgeRange"], case .stringEnum(let jsonSchemaStringEnum) = ageRangeDef {
XCTAssertEqual(child.properties?["ageRange"], .reference(.init(ref: jsonSchemaStringEnum.id)))
}
else {
XCTFail("Failed to create definition for AgeRange")
}
}
else {
XCTFail("expecting a definition for child")
}

XCTAssertEqual(doc.root.properties?["jsonSchema"], .primitive(.init(format: .uri)))
XCTAssertEqual(doc.root.properties?["uuid"], .primitive(.init(format: .uuid)))
XCTAssertEqual(doc.root.properties?["createdOn"], .primitive(.init(format: .dateTime)))
XCTAssertEqual(doc.root.properties?["ages"], .array(.init(items: .primitive(.integer))))
XCTAssertEqual(doc.root.properties?["ratings"], .array(.init(items: .primitive(.number))))
XCTAssertEqual(doc.root.properties?["names"], .array(.init(items: .primitive(.string))))
}
}

// MARK: Test objects
Expand Down Expand Up @@ -335,3 +371,81 @@ extension AnotherB : DocumentableStruct {

let testBURL = URL(string: "https://foo.org/schemas/")!

struct AutoGeneratedObject : Codable, Hashable {
enum CodingKeys : String, DocumentableCodingKey {
case jsonSchema, uuid, createdOn, favorite, ages, ratings, names, children
static var requiredKeys: [CodingKeys] { [.jsonSchema, .uuid, .createdOn, .children] }
}

let jsonSchema: URL
let uuid: UUID
let createdOn: Date

let favorite: Child?
let ages: Set<Int>?
let ratings: Set<Double>?
let names: [String]?

let children: Set<Child>

init(children: [Child], createdOn: Date = Date()) {
self.favorite = children.sorted(by: { $0.rating > $1.rating }).first
self.children = Set(children)
self.jsonSchema = URL(string: "AutoGeneratedObject.json", relativeTo: testBURL)!
self.uuid = UUID()
self.createdOn = createdOn
self.ages = Set(children.map { Int($0.age) })
self.ratings = Set(children.map { Double($0.rating) })
self.names = children.map { $0.name }
}

struct Child : Codable, Hashable {
enum CodingKeys : String, DocumentableCodingKey {
case name, age, rating, hasPet, ageRange
static var requiredKeys: [CodingKeys] { allCases }
}
let name: String
let age : UInt16
let rating : Float
let hasPet : Bool
let ageRange : AgeRange
}

enum AgeRange : String, DocumentableStringEnum, StringEnumSet {
case infant, toddler, child, tweenager, teenager, adult
}
}

extension AutoGeneratedObject : DocumentableRootObject {
init() {
self.init(children: AutoGeneratedObject.Child.examples())
}

var documentDescription: String? {
"This is an example of a JSON schema that is auto generated."
}
}

extension AutoGeneratedObject : GenericDocumentableStruct {
static func examples() -> [AutoGeneratedObject] {
[.init()]
}

static func documentProperty(for codingKey: CodingKey) throws -> JsonModel.DocumentProperty {
try mirroredPropertyType(for: codingKey)
}
}

extension AutoGeneratedObject.Child : GenericDocumentableStruct {
static func examples() -> [AutoGeneratedObject.Child] {
[
.init(name: "Sue", age: 2, rating: 5.2, hasPet: false, ageRange: .toddler),
.init(name: "Bob", age: 5, rating: 8.7, hasPet: true, ageRange: .child),
]
}

static func documentProperty(for codingKey: CodingKey) throws -> JsonModel.DocumentProperty {
try mirroredPropertyType(for: codingKey)
}
}

2 changes: 1 addition & 1 deletion Tests/JsonModelTests/JsonElementTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ final class JsonElementTests: XCTestCase {
}

func testDictionary() {
let original = JsonElement(["foo":1,"goo":"two"])
let original = JsonElement(["foo":1,"goo":"two"] as [String : Any])
XCTAssertEqual(JsonElement.object(["foo":1,"goo":"two"]), original)
XCTAssertNotEqual(JsonElement.object([:]), original)

Expand Down
10 changes: 5 additions & 5 deletions Tests/JsonModelTests/JsonValueTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -203,11 +203,11 @@ final class JsonValueTests: XCTestCase {
0 : [ ["identifier" : "bar",
"items" : [ ["index" : NSNumber(value: 0)],
["index" : NSNumber(value: 1)],
["index" : NSNumber(value: 2)]]],
["index" : NSNumber(value: 2)]]] as [String : Any],
["identifier" : "goo"]
],
1 : [ "date" : date, "url" : url, "data" : data, "uuid" : uuid, "null" : NSNull()],
2 : [ ["item" : "bar", "uuid" : barUUID], ["item" : "goo", "uuid" : gooUUID]],
1 : [ "date" : date, "url" : url, "data" : data, "uuid" : uuid, "null" : NSNull()] as [String : Any],
2 : [ ["item" : "bar", "uuid" : barUUID] as [String : Any], ["item" : "goo", "uuid" : gooUUID]],
]

let ns_json = (dictionary as NSDictionary).jsonObject()
Expand All @@ -221,10 +221,10 @@ final class JsonValueTests: XCTestCase {
"0" : [ ["identifier" : "bar",
"items" : [ ["index" : NSNumber(value: 0)],
["index" : NSNumber(value: 1)],
["index" : NSNumber(value: 2)]]],
["index" : NSNumber(value: 2)]]] as [String : Any],
["identifier" : "goo"]
],
"1" : [ "date" : expectedDate, "url" : "https://foo.org", "data" : "ABC4", "uuid" : uuid.uuidString, "null" : NSNull()],
"1" : [ "date" : expectedDate, "url" : "https://foo.org", "data" : "ABC4", "uuid" : uuid.uuidString, "null" : NSNull()] as [String : Any],
"2" : [ ["item" : "bar", "uuid" : barUUID.uuidString,], ["item" : "goo", "uuid" : gooUUID.uuidString,]],
]

Expand Down
4 changes: 2 additions & 2 deletions Tests/JsonModelTests/PolymorphicWrapperTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ final class PolymorphicWrapperTests: XCTestCase {
"type" : "SampleX",
"name" : "foo",
"value" : 5
],
"array" : []
] as [String : Any],
"array" : [] as [Any]
]
XCTAssertEqual(expectedDictionary, dictionary)
}
Expand Down

0 comments on commit 3ff621e

Please sign in to comment.