Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support default values for schema properties #649

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ enum LiteralDescription: Equatable, Codable {
/// For example `42`.
case int(Int)

/// A double literal.
///
/// For example `42.24`.
case double(Double)

/// A Boolean literal.
///
/// For example `true`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,7 @@ struct TextBasedRenderer: RendererProtocol {
write("\"\(string)\"")
}
case let .int(int): write("\(int)")
case let .double(double): write("\(double)")
case let .bool(bool): write(bool ? "true" : "false")
case .nil: write("nil")
case .array(let items):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,19 @@ extension TypesFileTranslator {
} else {
associatedDeclarations = []
}

var defaultValue: PropertyBlueprint.DefaultValue? = nil
if let schemaDefaultValue = schema.defaultValue {
if let literalDescription = convertValueToLiteralDescription(schemaDefaultValue.value) {
defaultValue = .expression(.literal(literalDescription))
}
}

let blueprint = PropertyBlueprint(
comment: comment,
originalName: key,
typeUsage: propertyType,
default: defaultValue,
associatedDeclarations: associatedDeclarations,
context: context
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,15 @@ extension TypesFileTranslator {
/// document.
/// - arrayContext: The context for the array, including information such
/// as the element schema.
/// - defaultValue: An optional default value for the array.
/// - Throws: An error if there is an issue during translation.
/// - Returns: A list of declarations representing the translated array.
func translateArray(typeName: TypeName, openAPIDescription: String?, arrayContext: JSONSchema.ArrayContext) throws
-> [Declaration]
{
func translateArray(
typeName: TypeName,
openAPIDescription: String?,
arrayContext: JSONSchema.ArrayContext,
defaultValue: AnyCodable?
) throws -> [Declaration] {

var inline: [Declaration] = []

Expand Down Expand Up @@ -61,6 +65,20 @@ extension TypesFileTranslator {
existingType: .init(elementType.typeName.asUsage.asArray)
)
)
return inline + [arrayDecl]
inline.append(arrayDecl)
if let defaultValue, let literalDescription = convertValueToLiteralDescription(defaultValue.value) {

let defaultDecl: Declaration = .variable(
accessModifier: config.access,
isStatic: true,
kind: .let,
left: "`default`",
type: .init(elementType.typeName.asUsage.asArray),
right: .literal(literalDescription)
)
inline.append(defaultDecl)
}

return inline
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,20 @@ extension TypesFileTranslator {
} else {
associatedDeclarations = []
}

var defaultValue: PropertyBlueprint.DefaultValue? = nil
if let schemaDefaultValue = value.defaultValue {
if let literalDescription = convertValueToLiteralDescription(schemaDefaultValue.value) {
defaultValue = .expression(.literal(literalDescription))
}
}

return PropertyBlueprint(
comment: comment,
isDeprecated: value.deprecated,
originalName: key,
typeUsage: propertyType,
default: defaultValue,
associatedDeclarations: associatedDeclarations,
context: context
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,14 @@ extension TypesFileTranslator {

// If this type maps to a referenceable schema, define a typealias
if let builtinType = try typeMatcher.tryMatchReferenceableType(for: schema, components: components) {
let typealiasDecl = try translateTypealias(
return try translateTypealias(
named: typeName,
userDescription: overrides.userDescription ?? schema.description,
to: builtinType.withOptional(
overrides.isOptional ?? typeMatcher.isOptional(schema, components: components)
)
),
defaultValue: schema.defaultValue
)
return [typealiasDecl]
}

// Not a global schema, we have to actually define a type for it
Expand Down Expand Up @@ -138,7 +138,8 @@ extension TypesFileTranslator {
return try translateArray(
typeName: typeName,
openAPIDescription: overrides.userDescription ?? coreContext.description,
arrayContext: arrayContext
arrayContext: arrayContext,
defaultValue: coreContext.defaultValue
)
case let .all(of: schemas, core: coreContext):
let allOfDecl = try translateAllOrAnyOf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,55 @@ extension FileTranslator {
/// - typeName: The name of the type to give to the declared typealias.
/// - userDescription: A user-specified description from the OpenAPI document.
/// - existingTypeUsage: The existing type the alias points to.
/// - defaultValue: An optional default value for the typealias.
/// - Throws: An error if there is an issue during translation.
/// - Returns: A declaration representing the translated typealias.
func translateTypealias(named typeName: TypeName, userDescription: String?, to existingTypeUsage: TypeUsage) throws
-> Declaration
{
func translateTypealias(
named typeName: TypeName,
userDescription: String?,
to existingTypeUsage: TypeUsage,
defaultValue: AnyCodable?
) throws -> [Declaration] {
let typealiasDescription = TypealiasDescription(
accessModifier: config.access,
name: typeName.shortSwiftName,
existingType: .init(existingTypeUsage.withOptional(false))
)
let typealiasComment: Comment? = typeName.docCommentWithUserDescription(userDescription)
return .commentable(typealiasComment, .typealias(typealiasDescription))

var declarations: [Declaration] = [.commentable(typealiasComment, .typealias(typealiasDescription))]

if let defaultValue, let literalDescription = convertValueToLiteralDescription(defaultValue.value) {
let defaultDecl: Declaration = .variable(
accessModifier: config.access,
isStatic: true,
kind: .let,
left: "`default`",
type: .init(existingTypeUsage.withOptional(false)),
right: .literal(literalDescription)
)
declarations.append(defaultDecl)
}

return declarations
}

/// Converts a given value to a `LiteralDescription`.
///
/// - Parameter value: The value to be converted.
/// - Returns: A `LiteralDescription` representing the value, or `nil` if the value cannot be converted.
func convertValueToLiteralDescription(_ value: Any) -> LiteralDescription? {
switch value {
case let stringValue as String: return .string(stringValue)
case let intValue as Int: return .int(intValue)
case let boolValue as Bool: return .bool(boolValue)
case let doubleValue as Double: return .double(doubleValue)
case let arrayValue as [Any]:
let arrayExpressions = arrayValue.compactMap { element -> Expression? in
convertValueToLiteralDescription(element).map { .literal($0) }
}
return .array(arrayExpressions)
default: return nil
}
}
}
1 change: 1 addition & 0 deletions Tests/OpenAPIGeneratorCoreTests/StructureHelpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ extension LiteralDescription {
switch self {
case .string: return "string"
case .int: return "int"
case .double: return "double"
case .bool: return "bool"
case .nil: return "nil"
case .array: return "array"
Expand Down