Skip to content

Commit

Permalink
Support {oneOf: [{type: 'null'}, ...]} as optional
Browse files Browse the repository at this point in the history
Fixes apple#513
  • Loading branch information
brandonbloom committed Apr 1, 2024
1 parent 1ed3e18 commit ddfd63d
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ extension TypesFileTranslator {
/// - Throws: An error if there is an issue during translation.
/// - Returns: A declaration representing the translated allOf/anyOf structure.
func translateAllOrAnyOf(typeName: TypeName, openAPIDescription: String?, type: AllOrAnyOf, schemas: [JSONSchema])
throws -> Declaration
throws -> [Declaration]
{
let properties: [(property: PropertyBlueprint, isKeyValuePair: Bool)] = try schemas.enumerated()
.map { index, schema in
Expand Down Expand Up @@ -107,7 +107,7 @@ extension TypesFileTranslator {
properties: propertyValues
)
)
return structDecl
return [structDecl]
}

/// Returns a declaration for a oneOf schema.
Expand All @@ -128,7 +128,7 @@ extension TypesFileTranslator {
openAPIDescription: String?,
discriminator: OpenAPI.Discriminator?,
schemas: [JSONSchema]
) throws -> Declaration {
) throws -> [Declaration] {
let cases: [(String, [String]?, Bool, Comment?, TypeUsage, [Declaration])]
if let discriminator {
// > When using the discriminator, inline schemas will not be considered.
Expand All @@ -148,7 +148,15 @@ extension TypesFileTranslator {
return (caseName, mappedType.rawNames, true, comment, mappedType.typeName.asUsage, [])
}
} else {
cases = try schemas.enumerated()
let (schemas, nullSchemas) = schemas.partitioned(by: { typeMatcher.isNull($0) })
if schemas.count == 1, nullSchemas.count > 0, let schema = schemas.first {
return try translateSchema(
typeName: typeName,
schema: schema,
overrides: .init(isOptional: true))
}
cases = try schemas
.enumerated()
.map { index, schema in
let key = "case\(index+1)"
let childType = try typeAssigner.typeUsage(
Expand Down Expand Up @@ -242,6 +250,6 @@ extension TypesFileTranslator {
conformances: Constants.ObjectStruct.conformances,
members: caseDecls + codingKeysDecls + [decoder, encoder]
)
return .commentable(comment, enumDecl)
return [.commentable(comment, enumDecl)]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -141,29 +141,26 @@ extension TypesFileTranslator {
arrayContext: arrayContext
)
case let .all(of: schemas, core: coreContext):
let allOfDecl = try translateAllOrAnyOf(
return try translateAllOrAnyOf(
typeName: typeName,
openAPIDescription: overrides.userDescription ?? coreContext.description,
type: .allOf,
schemas: schemas
)
return [allOfDecl]
case let .any(of: schemas, core: coreContext):
let anyOfDecl = try translateAllOrAnyOf(
return try translateAllOrAnyOf(
typeName: typeName,
openAPIDescription: overrides.userDescription ?? coreContext.description,
type: .anyOf,
schemas: schemas
)
return [anyOfDecl]
case let .one(of: schemas, core: coreContext):
let oneOfDecl = try translateOneOf(
return try translateOneOf(
typeName: typeName,
openAPIDescription: overrides.userDescription ?? coreContext.description,
discriminator: coreContext.discriminator,
schemas: schemas
)
return [oneOfDecl]
default: return []
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,21 @@ struct TypeMatcher {
}
}

/// Returns a Boolean value indicating whether the schema admits only explicit null values.
/// - Parameters:
/// - schema: The schema to check.
/// - Returns: `true` if the schema admits only explicit null values, `false` otherwise.
func isNull(_ schema: JSONSchema) -> Bool {
switch schema.value {
case .null(_):
return true
case let .fragment(core):
return core.format.jsonType == .null
default:
return false
}
}

// MARK: - Private

/// Returns the type name of a built-in type that matches the specified
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1761,6 +1761,31 @@ final class SnippetBasedReferenceTests: XCTestCase {
)
}

func testOneOfRefOrNull() throws {
try self.assertSchemasTranslation(
"""
schemas:
SomeString:
type: string
NullableRef:
oneOf:
- $ref: '#/components/schemas/SomeString'
- type: 'null'
ArrayOfNullableRefs:
type: array
items:
$ref: '#/components/schemas/NullableRef'
""",
"""
public enum Schemas {
public typealias SomeString = Swift.String
public typealias NullableRef = Components.Schemas.SomeString?
public typealias ArrayOfNullableRefs = [Components.Schemas.NullableRef]
}
"""
)
}

func testComponentsResponsesResponseNoBody() throws {
try self.assertResponsesTranslation(
"""
Expand Down

0 comments on commit ddfd63d

Please sign in to comment.