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

Feature/requestBodies param support #152

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
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
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,10 @@ fixedWidthIntegers | whether to use types like Int32 and Int64 | `Bool` | false
homepage | homepage in podspec | `String` | https://github.com/yonaskolb/SwagGen
modelPrefix | model by adding a prefix and model file name | `String` | null
modelSuffix | model by adding a suffix and model file name | `String` | null
modelRequestBodyPrefix | applied to model classes and enums in requestBodies models only | `String` | null
modelRequestBodySuffix | applied to model classes in requestBodies models only | `String` | null
modelResponsePrefix | applied to model classes and enums in response models only | `String` | null
modelResponseSuffix | applied to model classes in response models only | `String` | null
mutableModels | whether model properties are mutable | `Bool` | true
modelType | whether each model is a `struct` or `class` | `String` | class
modelInheritance | whether models use inheritance. Must be false for structs | Bool | true
Expand Down
133 changes: 108 additions & 25 deletions Sources/SwagGenKit/CodeFormatter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ public class CodeFormatter {
let templateConfig: TemplateConfig
var modelPrefix: String
var modelSuffix: String
var modelRequestBodyPrefix: String
var modelRequestBodySuffix: String
var modelResponsePrefix: String
var modelResponseSuffix: String
var modelInheritance: Bool
var modelNames: [String: String]
var enumNames: [String: String]
Expand All @@ -20,6 +24,10 @@ public class CodeFormatter {
self.templateConfig = templateConfig
modelPrefix = templateConfig.getStringOption("modelPrefix") ?? ""
modelSuffix = templateConfig.getStringOption("modelSuffix") ?? ""
modelRequestBodyPrefix = templateConfig.getStringOption("modelRequestBodyPrefix") ?? ""
modelRequestBodySuffix = templateConfig.getStringOption("modelRequestBodySuffix") ?? ""
modelResponsePrefix = templateConfig.getStringOption("modelResponsePrefix") ?? ""
modelResponseSuffix = templateConfig.getStringOption("modelResponseSuffix") ?? ""
modelInheritance = templateConfig.getBooleanOption("modelInheritance") ?? true
modelNames = templateConfig.options["modelNames"] as? [String: String] ?? [:]
enumNames = templateConfig.options["enumNames"] as? [String: String] ?? [:]
Expand Down Expand Up @@ -51,6 +59,8 @@ public class CodeFormatter {
.sorted { $0.0.lowercased() < $1.0.lowercased() }
.map { ["name": $0, "operations": $1.map(getOperationContext)] }
context["schemas"] = spec.components.schemas.map(getSchemaContent).sorted { sortContext(by: "type", value1: $0, value2: $1) }
context["requestBodies"] = spec.components.requestBodies.map(getRequestsBodiesContext).sorted { sortContext(by: "type", value1: $0, value2: $1) }
context["responses"] = spec.components.responses.map(getResponsesContext).sorted { sortContext(by: "type", value1: $0, value2: $1) }
context["info"] = getSpecInformationContext(spec.info)
context["servers"] = spec.servers.enumerated().map(getServerContext)
if let server = spec.servers.first, server.variables.isEmpty {
Expand All @@ -75,6 +85,7 @@ public class CodeFormatter {
var context: Context = [:]
context["name"] = name
context["enum"] = variable.enumValues
context["safeEnum"] = variable.enumSafeValues
context["defaultValue"] = variable.defaultValue
context["description"] = variable.description
return context
Expand All @@ -101,40 +112,96 @@ public class CodeFormatter {

return context
}

func getSchemaContent(_ schema: ComponentObject<Schema>) -> Context {
var context = getSchemaContext(schema.value)

context["type"] = getSchemaTypeName(schema)

let schemaType = getSchemaType(name: schema.name, schema: schema.value)

switch schema.value.type {
case .string, .boolean, .integer, .number:
context["simpleType"] = schemaType
context["aliasType"] = schemaType
if let enumValue = schema.value.getEnum(name: schema.name, description: schema.value.metadata.description) {
context["enum"] = getEnumContext(enumValue)
}
case .reference:
context["referenceType"] = schemaType
context["aliasType"] = schemaType
case .array:
context["arrayType"] = schemaType
context["aliasType"] = schemaType
default: break

func getRequestsBodiesContext(_ requestBody: ComponentObject<RequestBody>) -> Context {

guard let schema = requestBody.value.content.jsonSchema else {
return [:]
}

var context = getSchemaContent(schema, type: requestBody.name)
context["name"] = requestBody.name
context["type"] = getRequestBodyTypeName(requestBody)

return context
}

func getResponsesContext(_ response: ComponentObject<Response>) -> Context {

guard let schema = response.value.content?.jsonSchema else {
return [:]
}

var context = getSchemaContent(schema, type: response.name)
context["name"] = response.name
context["type"] = getResponseTypeName(response)

return context
}

func getSchemaContent(_ schema: ComponentObject<Schema>) -> Context {
let type = getSchemaTypeName(schema)
var context = getSchemaContent(schema.value, type: type)

context["name"] = schema.name
context["type"] = type

return context
}

func getSchemaContent(_ schema: Schema, type: String) -> Context {
var context = getSchemaContext(schema)

context["type"] = type

return context
}

func getRequestBodyTypeName(_ requestBody: ComponentObject<RequestBody>) -> String {
guard let schema = requestBody.value.content.defaultSchema else {
return requestBody.name
}

var name = requestBody.name
if schema.canBeEnum,
schema.getEnum(name: name, description: schema.metadata.description) != nil {
name = getEnumType(name)
} else {
name = getRequestBodyModelType(name)
name = getModelType(name)
}

return name
}

func getResponseTypeName(_ response: ComponentObject<Response>) -> String {
guard let schema = response.value.content?.defaultSchema else {
return response.name
}

var name = response.name
if schema.canBeEnum,
schema.getEnum(name: name, description: schema.metadata.description) != nil {
name = getEnumType(name)
} else {
name = getResponseModelType(name)
name = getModelType(name)
}

return name
}

func getSchemaTypeName(_ schema: ComponentObject<Schema>) -> String {
var name = schema.name

if schema.value.canBeEnum,
schema.value.getEnum(name: schema.name, description: schema.value.metadata.description) != nil {
return getEnumType(schema.name)
schema.value.getEnum(name: name, description: schema.value.metadata.description) != nil {
name = getEnumType(name)
} else {
return getModelType(schema.name)
name = getModelType(name)
}

return name
}

func getInlineSchemaContext(_ schema: Schema, name: String) -> Context? {
Expand Down Expand Up @@ -518,6 +585,22 @@ public class CodeFormatter {
let type = name.upperCamelCased()
return escapeType("\(modelPrefix)\(type)\(modelSuffix)")
}

func getRequestBodyModelType(_ name: String) -> String {
if let modelName = modelNames[name] {
return modelName
}
let type = name.upperCamelCased()
return escapeType("\(modelRequestBodyPrefix)\(type)\(modelRequestBodySuffix)")
}

func getResponseModelType(_ name: String) -> String {
if let modelName = modelNames[name] {
return modelName
}
let type = name.upperCamelCased()
return escapeType("\(modelResponsePrefix)\(type)\(modelResponseSuffix)")
}

func getSchemaType(name: String, schema: Schema, checkEnum: Bool = true) -> String {
return "UNKNOWN_SCHEMA_TYPE"
Expand Down
21 changes: 21 additions & 0 deletions Sources/Swagger/Server.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public struct Server {
public struct Variable {
public let defaultValue: String
public let enumValues: [String]?
public let enumSafeValues: [String: String]?
public let description: String?
}
}
Expand All @@ -31,5 +32,25 @@ extension Server.Variable: JSONObjectConvertible {
defaultValue = try jsonDictionary.json(atKeyPath: "default")
enumValues = jsonDictionary.json(atKeyPath: "enum")
description = jsonDictionary.json(atKeyPath: "description")

var enumsList: [String: String] = [:]

enumValues?.forEach {
enumsList[$0.cleanEnumValue()] = $0
}
enumSafeValues = enumsList
}
}

private extension String {
func cleanEnumValue() -> String {
var valueNameComponents = replacingOccurrences(of: "[\\[\\]^+<>\\. ]", with: "_", options: .regularExpression, range: nil)
.components(separatedBy: "_")
let firstPart = valueNameComponents.first ?? ""
valueNameComponents.removeFirst(1)

return firstPart + valueNameComponents.map{
$0.prefix(1).uppercased() + $0.dropFirst()
}.joined(separator: "")
}
}
8 changes: 6 additions & 2 deletions Templates/Swift/template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ options:
safeArrayDecoding: false # filter out invalid items in array instead of throwing
modelPrefix: null # applied to model classes and enums
modelSuffix: null # applied to model classes
modelRequestBodyPrefix: null # applied to model classes and enums in requestBodies models only
modelRequestBodySuffix: null # applied to model classes in requestBodies models only
modelResponsePrefix: null # applied to model classes and enums in response models only
modelResponseSuffix: null # applied to model classes in response models only
modelType: class # can be struct or class
modelInheritance: true # must be false for struct modelType
modelProtocol: APIModel # the protocol all models conform to
Expand Down Expand Up @@ -48,7 +52,7 @@ templateFiles:
destination: "Sources/Enums/{{ enumName }}.swift"
- path: Sources/Model.swift
context: schemas
destination: "Sources/Models/{{ type }}.swift"
destination: "Sources/Models/Response/{{ type }}.swift"
- path: Sources/Request.swift
context: operations
destination: "Sources/Requests{% if tag %}/{{ tag|upperCamelCase }}{% endif %}/{{ type }}.swift"
destination: "Sources/Requests{% if tag %}/{{ tag|upperCamelCase }}{% endif %}/{{ type }}.swift"