Skip to content

Commit

Permalink
✨ Support enum in query parameters (#142)
Browse files Browse the repository at this point in the history
  • Loading branch information
MadsBogeskov authored Mar 5, 2024
1 parent 3684794 commit 55fabe0
Show file tree
Hide file tree
Showing 8 changed files with 159 additions and 61 deletions.
11 changes: 7 additions & 4 deletions Sources/SwaggerSwiftCore/API Factory/APIFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,12 @@ struct APIFactory {
var networkRequestFunctions = [APIRequest]()
var inlineModelDefinitions = [ModelDefinition]()
for swaggerPath in swagger.paths {
let (pathNetworkRequestFunctions, pathCurrentInlineDefinitions) = try apisAndModels(fromPath: swaggerPath.value,
servicePath: swaggerPath.key,
swagger: swagger,
swaggerFile: swaggerFile)
let (pathNetworkRequestFunctions, pathCurrentInlineDefinitions) = try apisAndModels(
fromPath: swaggerPath.value,
servicePath: swaggerPath.key,
swagger: swagger,
swaggerFile: swaggerFile
)

networkRequestFunctions.append(contentsOf: pathNetworkRequestFunctions)
inlineModelDefinitions.append(contentsOf: pathCurrentInlineDefinitions)
Expand Down Expand Up @@ -130,6 +132,7 @@ struct APIFactory {
break
}
}

return allDefinitions
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,12 +141,8 @@ public struct APIRequestFactory {
}
}

let queries: [QueryElement] = queryParameters.compactMap {
guard case ParameterLocation.query = $0.location else {
return nil
}

switch $0.location {
let queries: [QueryElement] = queryParameters.compactMap { parameter in
switch parameter.location {
case .query(let type, _):
switch type {
case .string(let format, let enumValues, _, _, _):
Expand All @@ -167,43 +163,54 @@ public struct APIRequestFactory {
case .email: fallthrough
case .unsupported:
return QueryElement(
fieldName: $0.name,
fieldValue: $0.name.camelized,
isOptional: $0.required == false,
fieldName: parameter.name,
fieldValue: parameter.name.camelized,
isOptional: parameter.required == false,
valueType: valueType
)
case .date: fallthrough
case .dateTime:
return QueryElement(
fieldName: $0.name,
fieldValue: $0.name.camelized,
isOptional: $0.required == false,
fieldName: parameter.name,
fieldValue: parameter.name.camelized,
isOptional: parameter.required == false,
valueType: .date
)
}
} else {
return QueryElement(
fieldName: $0.name,
fieldValue: $0.name.camelized,
isOptional: $0.required == false,
fieldName: parameter.name,
fieldValue: parameter.name.camelized,
isOptional: parameter.required == false,
valueType: valueType
)
}
case .array:
case .array(let items, let collectionFormat, maxItems: _, minItems: _, uniqueItems: _):
if case .string(_, let enumValues, _, _, _) = items.type {
if enumValues != nil {
return QueryElement(
fieldName: parameter.name,
fieldValue: parameter.name.camelized,
isOptional: parameter.required == false,
valueType: .array(isEnum: true, collectionFormat: collectionFormat)
)
}
}

return QueryElement(
fieldName: $0.name,
fieldValue: $0.name.camelized,
isOptional: $0.required == false,
valueType: .array
fieldName: parameter.name,
fieldValue: parameter.name.camelized,
isOptional: parameter.required == false,
valueType: .array(isEnum: false, collectionFormat: collectionFormat)
)
case .number: fallthrough
case .integer: fallthrough
case .boolean: fallthrough
case .file:
return QueryElement(
fieldName: $0.name,
fieldValue: $0.name.camelized,
isOptional: $0.required == false,
fieldName: parameter.name,
fieldValue: parameter.name.camelized,
isOptional: parameter.required == false,
valueType: .default
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public struct RequestParameterFactory {
/// - swaggerFile: the swagger file
/// - Returns: the list of all parameters to the API request
func make(forOperation operation: SwaggerSwiftML.Operation, functionName: String, responseTypes: [ResponseTypeMap], pathParameters: [Parameter], swagger: Swagger, swaggerFile: SwaggerFile) throws -> ([FunctionParameter], [ModelDefinition], ReturnType) {
let parameters = (operation.parameters ?? []).map {
let parameters: [Parameter] = (operation.parameters ?? []).map {
swagger.findParameter(node: $0)
} + pathParameters

Expand All @@ -42,12 +42,20 @@ public struct RequestParameterFactory {
}

// Path
let (pathParameters, pathModels) = try resolvePathParameters(parameters: parameters, typePrefix: typeName, swagger: swagger)
let (pathParameters, pathModels) = try resolvePathParameters(
parameters: parameters,
typePrefix: typeName,
swagger: swagger
)
resolvedParameters.append(contentsOf: pathParameters)
resolvedModelDefinitions.append(contentsOf: pathModels)

// Query
let (queryParameters, queryModels) = try resolveQueryParameters(parameters: parameters, typePrefix: typeName, swagger: swagger)
let (queryParameters, queryModels) = try resolveQueryParameters(
parameters: parameters,
typePrefix: typeName,
swagger: swagger
)
resolvedParameters.append(contentsOf: queryParameters)
resolvedModelDefinitions.append(contentsOf: queryModels)

Expand All @@ -65,9 +73,20 @@ public struct RequestParameterFactory {
resolvedModelDefinitions.append(contentsOf: formDataModels)

// Completion
let (successTypeName, successInlineModels) = createResultEnumType(types: responseTypes, failure: false, functionName: functionName, swagger: swagger)
let (successTypeName, successInlineModels) = createResultEnumType(
types: responseTypes,
failure: false,
functionName: functionName,
swagger: swagger
)
resolvedModelDefinitions.append(contentsOf: successInlineModels)
let (failureTypeName, failureInlineModels) = createResultEnumType(types: responseTypes, failure: true, functionName: functionName, swagger: swagger)

let (failureTypeName, failureInlineModels) = createResultEnumType(
types: responseTypes,
failure: true,
functionName: functionName,
swagger: swagger
)
resolvedModelDefinitions.append(contentsOf: failureInlineModels)

let returnType = ReturnType(
Expand Down Expand Up @@ -102,7 +121,8 @@ public struct RequestParameterFactory {
description: nil,
typeName: typeName,
values: fields,
isCodable: false)
isCodable: false,
collectionFormat: nil)
)

return (typeName, [enumeration])
Expand Down Expand Up @@ -337,7 +357,8 @@ public struct RequestParameterFactory {
description: parameter.description,
typeName: typeName,
values: enumValues,
isCodable: true)))
isCodable: true,
collectionFormat: nil)))

let param = FunctionParameter(description: parameter.description,
name: parameter.name,
Expand Down
7 changes: 4 additions & 3 deletions Sources/SwaggerSwiftCore/Generator/Generator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,10 @@ public struct Generator {
swaggerPath: service.value.path ?? swaggerFile.path
)

let apiSpec = try await APIFactory(apiRequestFactory: apiRequestFactory,
modelTypeResolver: modelTypeResolver)
.generate(for: swagger, withSwaggerFile: swaggerFile)
let apiSpec = try await APIFactory(
apiRequestFactory: apiRequestFactory,
modelTypeResolver: modelTypeResolver
).generate(for: swagger, withSwaggerFile: swaggerFile)

return apiSpec
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ public struct ModelTypeResolver {
description: schema.description,
typeName: enumTypeName,
values: enumValues ?? [],
isCodable: true))
isCodable: true,
collectionFormat: nil))
return ResolvedModel(.enumeration(typeName: enumTypeName), [model])
}

Expand Down Expand Up @@ -130,11 +131,17 @@ public struct ModelTypeResolver {
serviceName: swagger.serviceName)

if case .enumeration(let enumTypeName) = type {
let model = ModelDefinition.enumeration(Enumeration(serviceName: swagger.serviceName,
description: schema.description,
typeName: enumTypeName,
values: enumValues ?? [],
isCodable: true))
let model = ModelDefinition.enumeration(
Enumeration(
serviceName: swagger.serviceName,
description: schema.description,
typeName: enumTypeName,
values: enumValues ?? [],
isCodable: true,

collectionFormat: nil
)
)
return (.enumeration(typeName: enumTypeName), [model])
} else {
return (type, [])
Expand Down
1 change: 1 addition & 0 deletions Sources/SwaggerSwiftCore/Models/Enumeration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ struct Enumeration {
let typeName: String
let values: [String]
let isCodable: Bool
let collectionFormat: CollectionFormat?

static func toCasename(_ str: String, _ isCodable: Bool) -> String {
let str = isCodable ? str.camelized : str
Expand Down
22 changes: 20 additions & 2 deletions Sources/SwaggerSwiftCore/Models/QueryElement.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import SwaggerSwiftML

struct QueryElement {
enum ValueType {
case date
case `enum`
case array
case array(isEnum: Bool, collectionFormat: CollectionFormat?)
case `default`
}

Expand All @@ -27,7 +29,17 @@ extension QueryElement {
queryItems.append(URLQueryItem(name: \"\(self.fieldName)\", value: \(fieldName)Value))
}
"""
case .array:
case .array(let isEnum, let collectionFormat):
if isEnum {
if collectionFormat == .csv {
return """
if let \(fieldName)Value = \(self.fieldName) {
queryItems.append(URLQueryItem(name: \"\(self.fieldName)\", value: \(fieldName)Value.map { $0.rawValue }.joined(separator: ",")))
}
"""
}
}

return """
if let \(fieldName)Value = \(self.fieldName) {
queryItems.append(URLQueryItem(name: \"\(self.fieldName)\", value: \(fieldName)Value))
Expand All @@ -47,6 +59,12 @@ extension QueryElement {
switch self.valueType {
case .enum:
fieldValue = "\(self.fieldValue).rawValue"
case .array(let isEnum, let collectionFormat):
if isEnum && collectionFormat == .csv {
fieldValue = "\(self.fieldValue).map { $0.rawValue }.joined(separator: \",\")"
} else {
fieldValue = "\(self.fieldValue)"
}
default:
fieldValue = "\(self.fieldValue)"
}
Expand Down
72 changes: 56 additions & 16 deletions Sources/SwaggerSwiftCore/Utilities/ParameterType+ToString.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,37 @@ extension ParameterType {
switch format {
case .none:
if let enumValues = enumValues {
return (.object(typeName: enumTypeName), [.enumeration(.init(serviceName: swagger.serviceName,
description: description,
typeName: enumTypeName,
values: enumValues,
isCodable: true))])
return (
.object(typeName: enumTypeName),
[.enumeration(
.init(
serviceName: swagger.serviceName,
description: description,
typeName: enumTypeName,
values: enumValues,
isCodable: true,
collectionFormat: nil
)
)]
)
} else {
return (.string(defaultValue: nil), [])
}
case .some(let some):
if let enumValues = enumValues {
return (.object(typeName: enumTypeName), [.enumeration(.init(serviceName: swagger.serviceName,
description: description,
typeName: enumTypeName,
values: enumValues,
isCodable: true))])
return (
.object(typeName: enumTypeName),
[.enumeration(
.init(
serviceName: swagger.serviceName,
description: description,
typeName: enumTypeName,
values: enumValues,
isCodable: true,
collectionFormat: nil
)
)]
)
} else {
return (try typeOfDataFormat(some), [])
}
Expand All @@ -43,21 +59,40 @@ extension ParameterType {
}
case .boolean:
return (.boolean(defaultValue: nil), [])
case .array(let items, collectionFormat: _, maxItems: _, minItems: _, uniqueItems: _):
let (type, embedddedDefinitions) = try typeOfItems(items.type, typePrefix: typePrefix, swagger: swagger)
case .array(let items, let collectionFormat, maxItems: _, minItems: _, uniqueItems: _):
let (type, embedddedDefinitions) = try typeOfItems(
items.type,
collectionFormat: collectionFormat,
typePrefix: typePrefix,
swagger: swagger
)

return (.array(typeName: type), embedddedDefinitions)
case .file:
return (.object(typeName: "FormData"), [])
}
}
}

private func typeOfItems(_ itemsType: ItemsType, typePrefix: String, swagger: Swagger) throws -> (TypeType, [ModelDefinition]) {
private func typeOfItems(_ itemsType: ItemsType, collectionFormat: CollectionFormat, typePrefix: String, swagger: Swagger) throws -> (TypeType, [ModelDefinition]) {
switch itemsType {
case .string(format: let format, let enumValues, _, _, _):
let modelDefinitions: [ModelDefinition]
if let enumValues = enumValues {
modelDefinitions = [.enumeration(Enumeration(serviceName: swagger.serviceName, description: nil, typeName: "\(typePrefix)Enum", values: enumValues, isCodable: true))]
let modelDefinitions: [ModelDefinition] = [
.enumeration(
Enumeration(
serviceName: swagger.serviceName,
description: nil,
typeName: "\(typePrefix)Enum",
values: enumValues,
isCodable: true,
collectionFormat: collectionFormat
)
)
]

return (.object(typeName: "\(typePrefix)Enum"), modelDefinitions)
} else {
modelDefinitions = []
}
Expand All @@ -81,8 +116,13 @@ private func typeOfItems(_ itemsType: ItemsType, typePrefix: String, swagger: Sw
}
case .boolean:
return (.boolean(defaultValue: nil), [])
case .array(let itemsType, collectionFormat: _, maxItems: _, minItems: _, uniqueItems: _):
return try typeOfItems(itemsType.type, typePrefix: typePrefix, swagger: swagger)
case .array(let itemsType, let collectionFormat, maxItems: _, minItems: _, uniqueItems: _):
return try typeOfItems(
itemsType.type,
collectionFormat: collectionFormat,
typePrefix: typePrefix,
swagger: swagger
)
case .object(required: _, properties: _, allOf: _):
fatalError("I dont think this can happen")
}
Expand Down

0 comments on commit 55fabe0

Please sign in to comment.