Skip to content

Commit

Permalink
Support reference in array (#150)
Browse files Browse the repository at this point in the history
* ✨ Add support for references in query element arrays

* ✨ Add support for references in query element arrays
  • Loading branch information
MadsBogeskov authored Nov 21, 2024
1 parent 3da4e37 commit eb0c3b2
Show file tree
Hide file tree
Showing 17 changed files with 260 additions and 249 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/build_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,23 @@ on:

jobs:
build:
runs-on: macos-13
runs-on: macos-latest
steps:
- uses: actions/checkout@v4

- name: Set Xcode
run: xcodes select 15.2
run: xcodes select 16.1

- name: Build
run: swift build -v

test:
runs-on: macos-13
runs-on: macos-latest
steps:
- uses: actions/checkout@v4

- name: Set Xcode
run: xcodes select 15.2
run: xcodes select 16.1

- name: Run tests
run: swift test -v --parallel --xunit-output test.xml
Expand Down
4 changes: 2 additions & 2 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/lunarway/SwaggerSwiftML",
"state" : {
"revision" : "a973b5151c225defbe13663108d4b11c07cd3064",
"version" : "1.0.19"
"revision" : "08d62c3c8bb32638299222ab5c6acf67aba98aad",
"version" : "2.0.0"
}
},
{
Expand Down
4 changes: 2 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ let package = Package(
platforms: [.macOS(.v12)],
products: [.executable(name: "swaggerswift", targets: ["SwaggerSwift"])],
dependencies: [
.package(url: "https://github.com/apple/swift-argument-parser", .upToNextMajor(from: "1.1.1")),
.package(url: "https://github.com/lunarway/SwaggerSwiftML", from: "1.0.19")
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.1.1"),
.package(url: "https://github.com/lunarway/SwaggerSwiftML", from: "2.0.0")
],
targets: [
.executableTarget(
Expand Down
18 changes: 8 additions & 10 deletions Sources/SwaggerSwiftCore/API Factory/APIFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ struct APIFactory {

func generate(for swagger: Swagger, withSwaggerFile swaggerFile: SwaggerFile) throws -> (APIDefinition, [ModelDefinition]) {
let (apiFunctions, inlineModelDefinitions) = try getApiList(fromSwagger: swagger, swaggerFile: swaggerFile)
let modelDefinitions = getModelDefinitions(fromSwagger: swagger)
let responseModelDefinitions = getResponseModelDefinitions(fromSwagger: swagger)
let modelDefinitions = try getModelDefinitions(fromSwagger: swagger)
let responseModelDefinitions = try getResponseModelDefinitions(fromSwagger: swagger)

// Model Definitions can be a lot of things - when resolving the inheritance tree for
// the model definitions we just need the actual models, and a model can only inherit
Expand Down Expand Up @@ -63,9 +63,7 @@ struct APIFactory {
var apis = [APIRequest]()
var modelDefinitions = [ModelDefinition]()

let parameters: [Parameter] = (path.parameters ?? []).map {
return swagger.findParameter(node: $0)
}
let parameters: [Parameter] = try (path.parameters ?? []).map(swagger.findParameter(node:))

for httpMethod in HTTPMethod.allCases {
guard let operation = path.operationForMethod(httpMethod)
Expand All @@ -90,14 +88,14 @@ struct APIFactory {
/// Get the global set of model definitions. This is the normal specified list, and not the inline definitions that are defined in e.g. path definitions and other places
/// - Parameter swagger: the swagger
/// - Returns: model definitions
private func getModelDefinitions(fromSwagger swagger: Swagger) -> [ModelDefinition] {
private func getModelDefinitions(fromSwagger swagger: Swagger) throws -> [ModelDefinition] {
guard let definitions = swagger.definitions else {
return []
}

var allDefinitions = [ModelDefinition]()
for (typeName, schema) in definitions {
let resolved = modelTypeResolver.resolve(forSchema: schema,
let resolved = try modelTypeResolver.resolve(forSchema: schema,
typeNamePrefix: typeName,
namespace: swagger.serviceName,
swagger: swagger)
Expand Down Expand Up @@ -139,7 +137,7 @@ struct APIFactory {
/// Get the global set of response model definitions
/// - Parameter swagger: the swagger
/// - Returns: the set of global response model definitions
private func getResponseModelDefinitions(fromSwagger swagger: Swagger) -> [ModelDefinition] {
private func getResponseModelDefinitions(fromSwagger swagger: Swagger) throws -> [ModelDefinition] {
var modelDefinitions = [ModelDefinition]()
for (typeName, response) in swagger.responses ?? [:] {
guard let schema = response.schema else {
Expand All @@ -161,7 +159,7 @@ struct APIFactory {
case .reference(let reference):
if let (_, typeSchema) = swagger.definitions?.first(where: { reference == "#/definitions/\($0.key)" }) {
// we dont need the type part as it just represents the primary model definition returned from this function
let resolvedModel = modelTypeResolver.resolve(forSchema: typeSchema,
let resolvedModel = try modelTypeResolver.resolve(forSchema: typeSchema,
typeNamePrefix: typeName,
namespace: swagger.serviceName,
swagger: swagger)
Expand All @@ -172,7 +170,7 @@ struct APIFactory {
}
case .node(let schema):
// we dont need the type part as it just represents the primary model definition returned from this function
let resolvedModel = modelTypeResolver.resolve(forSchema: schema,
let resolvedModel = try modelTypeResolver.resolve(forSchema: schema,
typeNamePrefix: typeName,
namespace: swagger.serviceName,
swagger: swagger)
Expand Down
141 changes: 82 additions & 59 deletions Sources/SwaggerSwiftCore/API Request Factory/APIRequestFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,20 @@ public struct APIRequestFactory {

var inlineResponseModels = [ModelDefinition]()

let responses: [Response] = operation.responses.compactMap {
let responses: [Response] = try operation.responses.compactMap {
let statusCodeString = $0.key
guard let statusCode = HTTPStatusCode(rawValue: statusCodeString) else {
fatalError("Unknown status code received: \(statusCodeString)")
}

guard let requestResponse = $0.value else { return nil }

if let (responseType, embeddedDefinitions) = parse(request: requestResponse,
httpMethod: httpMethod,
servicePath: servicePath,
statusCode: statusCodeString,
swagger: swagger,
modelTypeResolver: modelTypeResolver) {
if let (responseType, embeddedDefinitions) = try parse(request: requestResponse,
httpMethod: httpMethod,
servicePath: servicePath,
statusCode: statusCodeString,
swagger: swagger,
modelTypeResolver: modelTypeResolver) {
return .init(statusCode: statusCode,
responseType: responseType,
inlineModels: embeddedDefinitions)
Expand All @@ -64,8 +64,8 @@ public struct APIRequestFactory {

inlineResponseModels.append(contentsOf: inlineModels)

let allParameters: [Parameter] = (operation.parameters ?? []).map {
swagger.findParameter(node: $0)
let allParameters: [Parameter] = try (operation.parameters ?? []).map {
try swagger.findParameter(node: $0)
} + pathParameters

let requestSpecificHeaders = allParameters
Expand All @@ -90,7 +90,7 @@ public struct APIRequestFactory {

let headers = requestSpecificHeaders

let queryItems = resolveQueries(parameters: allParameters, swagger: swagger)
let queryItems = try resolveQueries(parameters: allParameters, swagger: swagger)

let apiResponseTypes = apiResponseTypeFactory.make(
forResponses: responses,
Expand Down Expand Up @@ -132,7 +132,7 @@ public struct APIRequestFactory {
/// - parameters: the total set of parameters available to the api request
/// - swagger: the swagger spec
/// - Returns: the list of query elements that should be set in the request
private func resolveQueries(parameters: [Parameter], swagger: Swagger) -> [QueryElement] {
private func resolveQueries(parameters: [Parameter], swagger: Swagger) throws -> [QueryElement] {
let queryParameters = parameters.filter {
if case ParameterLocation.query = $0.location {
return true
Expand All @@ -141,51 +141,56 @@ public struct APIRequestFactory {
}
}

let queries: [QueryElement] = queryParameters.compactMap { parameter in
switch parameter.location {
case .query(let type, _):
switch type {
case .string(let format, let enumValues, _, _, _):
let isEnum = (enumValues?.count ?? 0) > 0
let valueType: QueryElement.ValueType = isEnum ? .enum : .default

if let format = format {
switch format {
case .int32: fallthrough
case .long: fallthrough
case .float: fallthrough
case .double: fallthrough
case .string: fallthrough
case .byte: fallthrough
case .binary: fallthrough
case .boolean: fallthrough
case .password: fallthrough
case .email: fallthrough
case .unsupported:
return QueryElement(
fieldName: parameter.name,
fieldValue: parameter.name.camelized,
isOptional: parameter.required == false,
valueType: valueType
)
case .date: fallthrough
case .dateTime:
return QueryElement(
fieldName: parameter.name,
fieldValue: parameter.name.camelized,
isOptional: parameter.required == false,
valueType: .date
)
}
} else {
return try queryParameters.map { try queryParameterToQueryElement(parameter: $0, swagger: swagger) }
}

private func queryParameterToQueryElement(parameter: Parameter, swagger: Swagger) throws -> QueryElement {
switch parameter.location {
case .query(let type, _):
switch type {
case .string(let format, let enumValues, _, _, _):
let isEnum = (enumValues?.count ?? 0) > 0
let valueType: QueryElement.ValueType = isEnum ? .enum : .default

if let format = format {
switch format {
case .int32: fallthrough
case .long: fallthrough
case .float: fallthrough
case .double: fallthrough
case .string: fallthrough
case .byte: fallthrough
case .binary: fallthrough
case .boolean: fallthrough
case .password: fallthrough
case .email: fallthrough
case .unsupported:
return QueryElement(
fieldName: parameter.name,
fieldValue: parameter.name.camelized,
isOptional: parameter.required == false,
valueType: valueType
)
case .date: fallthrough
case .dateTime:
return QueryElement(
fieldName: parameter.name,
fieldValue: parameter.name.camelized,
isOptional: parameter.required == false,
valueType: .date
)
}
case .array(let items, let collectionFormat, maxItems: _, minItems: _, uniqueItems: _):
} else {
return QueryElement(
fieldName: parameter.name,
fieldValue: parameter.name.camelized,
isOptional: parameter.required == false,
valueType: valueType
)
}
case .array(let items, let collectionFormat, _, _, _):
switch items {
case .node(let items):
if case .string(_, let enumValues, _, _, _) = items.type {
if enumValues != nil {
return QueryElement(
Expand All @@ -196,30 +201,48 @@ public struct APIRequestFactory {
)
}
}

return QueryElement(
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:
case .reference(let reference):
let schema = try swagger.findSchema(reference: reference)

if case .string(_, let enumValues, _, _, _, _) = schema.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: parameter.name,
fieldValue: parameter.name.camelized,
isOptional: parameter.required == false,
valueType: .default
valueType: .array(isEnum: false, collectionFormat: collectionFormat)
)
}
default:
fatalError("This should not happen")
case .number: fallthrough
case .integer: fallthrough
case .boolean: fallthrough
case .file:
return QueryElement(
fieldName: parameter.name,
fieldValue: parameter.name.camelized,
isOptional: parameter.required == false,
valueType: .default
)
}
default:
fatalError("This should not happen")
}

return queries
}

private func consumeMimeType(forOperation operation: SwaggerSwiftML.Operation, swagger: Swagger, httpMethod: String, servicePath: String) throws -> APIRequestConsumes {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +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: [Parameter] = (operation.parameters ?? []).map {
swagger.findParameter(node: $0)
} + pathParameters
let parameters: [Parameter] = try (operation.parameters ?? []).map(swagger.findParameter(node:)) + pathParameters

var resolvedParameters = [FunctionParameter]()
var resolvedModelDefinitions = [ModelDefinition]()
Expand Down Expand Up @@ -60,7 +58,7 @@ public struct RequestParameterFactory {
resolvedModelDefinitions.append(contentsOf: queryModels)

// Body
if let (bodyParameter, bodyModels) = resolveBodyParameters(parameters: parameters,
if let (bodyParameter, bodyModels) = try resolveBodyParameters(parameters: parameters,
typePrefix: typeName,
namespace: swagger.serviceName,
swagger: swagger) {
Expand Down Expand Up @@ -279,7 +277,7 @@ public struct RequestParameterFactory {
return (functionParameters, modelDefinitions)
}

private func resolveBodyParameters(parameters: [SwaggerSwiftML.Parameter], typePrefix: String, namespace: String, swagger: Swagger) -> (FunctionParameter, [ModelDefinition])? {
private func resolveBodyParameters(parameters: [SwaggerSwiftML.Parameter], typePrefix: String, namespace: String, swagger: Swagger) throws -> (FunctionParameter, [ModelDefinition])? {
var schemaNode: Node<Schema>?
var parameter: Parameter?
for param in parameters {
Expand All @@ -296,7 +294,7 @@ public struct RequestParameterFactory {

switch schemaNode {
case .node(let schema):
let resolvedType = modelTypeResolver.resolve(forSchema: schema,
let resolvedType = try modelTypeResolver.resolve(forSchema: schema,
typeNamePrefix: typePrefix,
namespace: namespace,
swagger: swagger)
Expand All @@ -310,13 +308,8 @@ public struct RequestParameterFactory {

return (param, resolvedType.inlineModelDefinitions)
case .reference(let reference):
guard let schema = swagger.findSchema(reference: reference) else {
return nil
}

guard let modelDefinition = ModelReference(rawValue: reference) else {
return nil
}
let schema = try swagger.findSchema(reference: reference)
let modelDefinition = try ModelReference(rawValue: reference)

let type = schema.type(named: modelDefinition.typeName)

Expand Down
2 changes: 1 addition & 1 deletion Sources/SwaggerSwiftCore/Extensions/DataFormat.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import SwaggerSwiftML

extension DataFormat: CustomStringConvertible {
extension DataFormat: @retroactive CustomStringConvertible {
public var description: String {
switch self {
case .int32:
Expand Down
2 changes: 1 addition & 1 deletion Sources/SwaggerSwiftCore/Extensions/Schema.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ extension Schema {
case .boolean(let defaultValue):
return .boolean(defaultValue: defaultValue)
case .array:
return .array(typeName: .object(typeName: name))
return .array(type: .object(typeName: name))
case .object:
return .object(typeName: name)
case .freeform:
Expand Down
Loading

0 comments on commit eb0c3b2

Please sign in to comment.