Skip to content

Commit

Permalink
Correctly handle properties with array of enum values (#190)
Browse files Browse the repository at this point in the history
* Better decoding of array properties of enums

* Regenerate
  • Loading branch information
MortenGregersen authored Jun 22, 2024
1 parent 25f424b commit e590b04
Show file tree
Hide file tree
Showing 8 changed files with 77 additions and 31 deletions.
8 changes: 4 additions & 4 deletions Sources/Bagbutik-Models/AppStore/TerritoryAvailability.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,13 @@ public struct TerritoryAvailability: Codable, Identifiable {

public struct Attributes: Codable {
public var available: Bool?
public var contentStatuses: Items?
public var contentStatuses: [ContentStatuses]?
public var preOrderEnabled: Bool?
public var preOrderPublishDate: String?
public var releaseDate: String?

public init(available: Bool? = nil,
contentStatuses: Items? = nil,
contentStatuses: [ContentStatuses]? = nil,
preOrderEnabled: Bool? = nil,
preOrderPublishDate: String? = nil,
releaseDate: String? = nil)
Expand All @@ -69,7 +69,7 @@ public struct TerritoryAvailability: Codable, Identifiable {
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: AnyCodingKey.self)
available = try container.decodeIfPresent(Bool.self, forKey: "available")
contentStatuses = try container.decodeIfPresent(Items.self, forKey: "contentStatuses")
contentStatuses = try container.decodeIfPresent([ContentStatuses].self, forKey: "contentStatuses")
preOrderEnabled = try container.decodeIfPresent(Bool.self, forKey: "preOrderEnabled")
preOrderPublishDate = try container.decodeIfPresent(String.self, forKey: "preOrderPublishDate")
releaseDate = try container.decodeIfPresent(String.self, forKey: "releaseDate")
Expand All @@ -84,7 +84,7 @@ public struct TerritoryAvailability: Codable, Identifiable {
try container.encodeIfPresent(releaseDate, forKey: "releaseDate")
}

public enum Items: String, Codable, CaseIterable {
public enum ContentStatuses: String, Codable, CaseIterable {
case available = "AVAILABLE"
case availableForPreorder = "AVAILABLE_FOR_PREORDER"
case availableForPreorderOnDate = "AVAILABLE_FOR_PREORDER_ON_DATE"
Expand Down
20 changes: 10 additions & 10 deletions Sources/Bagbutik-Models/XcodeCloud/CiScheduledStartCondition.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public struct CiScheduledStartCondition: Codable {
*/
public struct Schedule: Codable {
/// A list of days you configure for the start condition that starts a new build on a schedule.
public var days: Items?
public var days: [Days]?
/// A string indicating the frequency of the start condition that starts a new build on a schedule.
public var frequency: Frequency?
/// An integer that represents the hour you configure for the start condition that starts a new build on a schedule.
Expand All @@ -52,7 +52,7 @@ public struct CiScheduledStartCondition: Codable {
/// A string that represents the time zone you configure for the start condition that starts a new build on a schedule.
public var timezone: String?

public init(days: Items? = nil,
public init(days: [Days]? = nil,
frequency: Frequency? = nil,
hour: Int? = nil,
minute: Int? = nil,
Expand All @@ -67,7 +67,7 @@ public struct CiScheduledStartCondition: Codable {

public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: AnyCodingKey.self)
days = try container.decodeIfPresent(Items.self, forKey: "days")
days = try container.decodeIfPresent([Days].self, forKey: "days")
frequency = try container.decodeIfPresent(Frequency.self, forKey: "frequency")
hour = try container.decodeIfPresent(Int.self, forKey: "hour")
minute = try container.decodeIfPresent(Int.self, forKey: "minute")
Expand All @@ -83,13 +83,7 @@ public struct CiScheduledStartCondition: Codable {
try container.encodeIfPresent(timezone, forKey: "timezone")
}

public enum Frequency: String, Codable, CaseIterable {
case daily = "DAILY"
case hourly = "HOURLY"
case weekly = "WEEKLY"
}

public enum Items: String, Codable, CaseIterable {
public enum Days: String, Codable, CaseIterable {
case friday = "FRIDAY"
case monday = "MONDAY"
case saturday = "SATURDAY"
Expand All @@ -98,5 +92,11 @@ public struct CiScheduledStartCondition: Codable {
case tuesday = "TUESDAY"
case wednesday = "WEDNESDAY"
}

public enum Frequency: String, Codable, CaseIterable {
case daily = "DAILY"
case hourly = "HOURLY"
case weekly = "WEEKLY"
}
}
}
2 changes: 1 addition & 1 deletion Sources/BagbutikSpecDecoder/Schemas/EnumSchema.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public struct EnumSchema: Decodable, Equatable {

public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
var name = container.codingPath.last!.stringValue.capitalizingFirstLetter()
var name = container.codingPath.last(where: { $0.stringValue != "items"})!.stringValue.capitalizingFirstLetter()
if name == "Type" {
var parentType = container.codingPath.dropLast(1).last!.stringValue
if parentType == "properties" {
Expand Down
2 changes: 2 additions & 0 deletions Sources/BagbutikSpecDecoder/Schemas/ObjectSchema.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ public struct ObjectSchema: Decodable, Equatable {
return .oneOf(name: name, schema: schema)
case .enumSchema(let schema):
return .enumSchema(schema)
case .arrayOfEnumSchema(let schema):
return .enumSchema(schema)
default:
return nil
}
Expand Down
30 changes: 18 additions & 12 deletions Sources/BagbutikSpecDecoder/Schemas/PropertyType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ public indirect enum PropertyType: Decodable, Equatable, CustomStringConvertible
case schema(ObjectSchema)
/// An enum schema
case enumSchema(EnumSchema)
/// An array of enum schema
case arrayOfEnumSchema(EnumSchema)
/// An array of schema
case arrayOfSchemaRef(String)
/// An array of object schema
Expand Down Expand Up @@ -44,27 +46,29 @@ public indirect enum PropertyType: Decodable, Equatable, CustomStringConvertible
public var description: String {
switch self {
case .simple(let simplePropertyType):
return simplePropertyType.description
simplePropertyType.description
case .constant:
return SimplePropertyType.string.description
SimplePropertyType.string.description
case .schemaRef(let schemaName):
return schemaName
schemaName
case .schema(let schema):
return schema.name
schema.name
case .enumSchema(let schema):
return schema.name
schema.name
case .arrayOfEnumSchema(let schema):
"[\(schema.name)]"
case .arrayOfSchemaRef(let schemaName):
return "[\(schemaName)]"
"[\(schemaName)]"
case .arrayOfSubSchema(let schema):
return "[\(schema.name)]"
"[\(schema.name)]"
case .arrayOfSimple(let simplePropertyType):
return "[\(simplePropertyType.description)]"
"[\(simplePropertyType.description)]"
case .oneOf(let name, _):
return name
name
case .arrayOfOneOf(let name, _):
return "[\(name)]"
"[\(name)]"
case .dictionary(let propertyType):
return "[String: \(propertyType.description)]"
"[String: \(propertyType.description)]"
}
}

Expand Down Expand Up @@ -102,6 +106,8 @@ public indirect enum PropertyType: Decodable, Equatable, CustomStringConvertible
let propertyType = try container.decode(PropertyType.self, forKey: .items)
if propertyType.isSimple {
self = .arrayOfSimple(.init(type: propertyType.description))
} else if case .enumSchema(let enumSchema) = propertyType {
self = .arrayOfEnumSchema(enumSchema)
} else {
self = propertyType
}
Expand All @@ -110,7 +116,7 @@ public indirect enum PropertyType: Decodable, Equatable, CustomStringConvertible
if let dictionaryValueType = try container.decodeIfPresent(PropertyType.self, forKey: .additionalProperties) {
self = .dictionary(dictionaryValueType)
} else {
self = .schema(try ObjectSchema(from: decoder))
self = try .schema(ObjectSchema(from: decoder))
}
} else {
if type == "number" { type = "Double" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,8 @@ final class ObjectSchemaRendererTests: XCTestCase {
properties: ["firstName": .init(type: .simple(.init(type: "string"))),
"lastName": .init(type: .simple(.init(type: "string"))),
"self": .init(type: .simple(.init(type: "string"))),
"id": .init(type: .constant("person"))],
"id": .init(type: .constant("person")),
"days": .init(type: .arrayOfEnumSchema(.init(name: "Days", type: "string", caseValues: ["MONDAY", "TUESDAY"])))],
requiredProperties: ["firstName"])
// When
let rendered = try renderer.render(objectSchema: schema, otherSchemas: [:])
Expand All @@ -294,6 +295,7 @@ final class ObjectSchemaRendererTests: XCTestCase {
<some://url>
*/
public struct Person: Codable, Identifiable {
public var days: [Days]?
/// The firstname of the person
public let firstName: String
/// The unique id for the person
Expand All @@ -302,17 +304,20 @@ final class ObjectSchemaRendererTests: XCTestCase {
/// A reference to the person
public var itself: String?
public init(firstName: String,
public init(days: [Days]? = nil,
firstName: String,
lastName: String? = nil,
self itself: String? = nil)
{
self.days = days
self.firstName = firstName
self.lastName = lastName
self.itself = itself
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: AnyCodingKey.self)
days = try container.decodeIfPresent([Days].self, forKey: "days")
firstName = try container.decode(String.self, forKey: "firstName")
id = try container.decodeIfPresent(String.self, forKey: "id")
lastName = try container.decodeIfPresent(String.self, forKey: "lastName")
Expand All @@ -321,11 +326,17 @@ final class ObjectSchemaRendererTests: XCTestCase {
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: AnyCodingKey.self)
try container.encodeIfPresent(days, forKey: "days")
try container.encode(firstName, forKey: "firstName")
try container.encodeIfPresent(id, forKey: "id")
try container.encodeIfPresent(lastName, forKey: "lastName")
try container.encodeIfPresent(itself, forKey: "self")
}
public enum Days: String, Codable, CaseIterable {
case monday = "MONDAY"
case tuesday = "TUESDAY"
}
}
"""#)
Expand Down
27 changes: 27 additions & 0 deletions Tests/BagbutikSpecDecoderTests/Schemas/ObjectSchemaTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,33 @@ final class ObjectSchemaTests: XCTestCase {
XCTAssertEqual(associatedOneOfSchema.options[0].typeName, "String")
XCTAssertEqual(associatedOneOfSchema.options[1].typeName, "Properties")
}
func testDecodingPropertyOfStringEnumArray() throws {
// https://github.com/MortenGregersen/Bagbutik/issues/189
// Given
let json = #"""
{
"Schedule" : {
"type" : "object",
"properties" : {
"days" : {
"type" : "array",
"items" : {
"type" : "string",
"enum" : [ "MONDAY", "TUESDAY" ]
}
}
}
}
}
"""#
// When
let objectSchema = try decodeObjectSchema(from: json)
// Then
guard case .arrayOfEnumSchema(let enumSchema) = objectSchema.properties["days"]?.type else { XCTFail(); return }
XCTAssertEqual(enumSchema.name, "Days")
XCTAssertEqual(enumSchema.cases, [.init(id: "monday", value: "MONDAY"), .init(id: "tuesday", value: "TUESDAY")])
}
func testDecodingUnknownPropertyType() throws {
// Given
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,10 +298,10 @@ final class PropertyTypeTests: XCTestCase {
}
XCTAssertEqual(metaPropertyType.description, "[String: String]")
guard let daysPropertyType = propertyTypes["days"], case let .enumSchema(daysSchema) = daysPropertyType else {
guard let daysPropertyType = propertyTypes["days"], case let .arrayOfEnumSchema(daysSchema) = daysPropertyType else {
return XCTFail("Wrong property type")
}
XCTAssertEqual(daysSchema.name, "Items")
XCTAssertEqual(daysSchema.name, "Days")
XCTAssertEqual(daysSchema.cases.count, 7)
}
}

0 comments on commit e590b04

Please sign in to comment.