From 368d5695e56e0efefd0c3dbc2d99b91d87e822fe Mon Sep 17 00:00:00 2001 From: Kostis Stefanou Date: Thu, 7 Mar 2024 15:20:37 +0200 Subject: [PATCH 1/5] [Generator] Add support of deepObject style in query params https://github.com/apple/swift-openapi-generator/issues/259 ~Depends on https://github.com/apple/swift-openapi-runtime/pull/100 landing first and getting released, and the version dependency being bumped here.~ Added `deepObject` style to serializer & parser in order to support nested keys on query parameters. Support nested keys on query parameters. Adapted snippet tests (SnippetBasedReferenceTests) --- .../Translator/CommonTypes/Constants.swift | 3 ++ .../Parameters/TypedParameter.swift | 6 ++- .../SnippetBasedReferenceTests.swift | 48 ++++++++++++++++++- 3 files changed, 54 insertions(+), 3 deletions(-) diff --git a/Sources/_OpenAPIGeneratorCore/Translator/CommonTypes/Constants.swift b/Sources/_OpenAPIGeneratorCore/Translator/CommonTypes/Constants.swift index d1fbedcf..72eed786 100644 --- a/Sources/_OpenAPIGeneratorCore/Translator/CommonTypes/Constants.swift +++ b/Sources/_OpenAPIGeneratorCore/Translator/CommonTypes/Constants.swift @@ -319,6 +319,9 @@ enum Constants { /// The form style. static let form = "form" + + /// The deepObject style. + static let deepObject = "deepObject" } } diff --git a/Sources/_OpenAPIGeneratorCore/Translator/Parameters/TypedParameter.swift b/Sources/_OpenAPIGeneratorCore/Translator/Parameters/TypedParameter.swift index e8eb0700..2abd3d7d 100644 --- a/Sources/_OpenAPIGeneratorCore/Translator/Parameters/TypedParameter.swift +++ b/Sources/_OpenAPIGeneratorCore/Translator/Parameters/TypedParameter.swift @@ -130,7 +130,10 @@ extension FileTranslator { let location = parameter.location switch location { case .query: - guard case .form = style else { + switch style { + case .form, .deepObject: + break + default: try diagnostics.emitUnsupported( "Query params of style \(style.rawValue), explode: \(explode)", foundIn: foundIn @@ -243,6 +246,7 @@ extension OpenAPI.Parameter.SchemaContext.Style { var runtimeName: String { switch self { case .form: return Constants.Components.Parameters.Style.form + case .deepObject: return Constants.Components.Parameters.Style.deepObject default: preconditionFailure("Unsupported style") } } diff --git a/Tests/OpenAPIGeneratorReferenceTests/SnippetBasedReferenceTests.swift b/Tests/OpenAPIGeneratorReferenceTests/SnippetBasedReferenceTests.swift index ba5e5905..5e3aa25c 100644 --- a/Tests/OpenAPIGeneratorReferenceTests/SnippetBasedReferenceTests.swift +++ b/Tests/OpenAPIGeneratorReferenceTests/SnippetBasedReferenceTests.swift @@ -2514,6 +2514,18 @@ final class SnippetBasedReferenceTests: XCTestCase { type: array items: type: string + - name: sort + in: query + required: true + style: deepObject + explode: true + schema: + type: object + properties: + option1: + type: string + option2: + type: string responses: default: description: Response @@ -2524,18 +2536,36 @@ final class SnippetBasedReferenceTests: XCTestCase { public var single: Swift.String? public var manyExploded: [Swift.String]? public var manyUnexploded: [Swift.String]? + public struct sortPayload: Codable, Hashable, Sendable { + public var option1: Swift.String? + public var option2: Swift.String? + public init( + option1: Swift.String? = nil, + option2: Swift.String? = nil + ) { + self.option1 = option1 + self.option2 = option2 + } + public enum CodingKeys: String, CodingKey { + case option1 + case option2 + } + } + public var sort: Operations.get_sol_foo.Input.Query.sortPayload public init( single: Swift.String? = nil, manyExploded: [Swift.String]? = nil, - manyUnexploded: [Swift.String]? = nil + manyUnexploded: [Swift.String]? = nil, + sort: Operations.get_sol_foo.Input.Query.sortPayload ) { self.single = single self.manyExploded = manyExploded self.manyUnexploded = manyUnexploded + self.sort = sort } } public var query: Operations.get_sol_foo.Input.Query - public init(query: Operations.get_sol_foo.Input.Query = .init()) { + public init(query: Operations.get_sol_foo.Input.Query) { self.query = query } } @@ -2572,6 +2602,13 @@ final class SnippetBasedReferenceTests: XCTestCase { name: "manyUnexploded", value: input.query.manyUnexploded ) + try converter.setQueryItemAsURI( + in: &request, + style: .deepObject, + explode: true, + name: "sort", + value: input.query.sort + ) return (request, nil) } """, @@ -2598,6 +2635,13 @@ final class SnippetBasedReferenceTests: XCTestCase { explode: false, name: "manyUnexploded", as: [Swift.String].self + ), + sort: try converter.getRequiredQueryItemAsURI( + in: request.soar_query, + style: .deepObject, + explode: true, + name: "sort", + as: Operations.get_sol_foo.Input.Query.sortPayload.self ) ) return Operations.get_sol_foo.Input(query: query) From 0e8371fbf082cd245fa5d637b2e4d83ff08e4aeb Mon Sep 17 00:00:00 2001 From: Kostis Stefanou Date: Tue, 16 Apr 2024 16:41:07 +0300 Subject: [PATCH 2/5] Update SwiftOpenAPIRuntime library dependency --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index 25151e5d..24fcaffb 100644 --- a/Package.swift +++ b/Package.swift @@ -60,7 +60,7 @@ let package = Package( // Tests-only: Runtime library linked by generated code, and also // helps keep the runtime library new enough to work with the generated // code. - .package(url: "https://github.com/apple/swift-openapi-runtime", from: "1.3.2"), + .package(url: "https://github.com/apple/swift-openapi-runtime", from: "1.4.0"), .package(url: "https://github.com/apple/swift-http-types", from: "1.0.2"), ], targets: [ From 7c84475f86891fded29d669c98dfea93e1163dee Mon Sep 17 00:00:00 2001 From: Kostis Stefanou Date: Wed, 17 Apr 2024 18:48:22 +0300 Subject: [PATCH 3/5] Update petstore.yaml with deepObject parameter --- .../Resources/Docs/petstore.yaml | 12 ++++++++ .../ReferenceSources/Petstore/Client.swift | 7 +++++ .../ReferenceSources/Petstore/Server.swift | 7 +++++ .../ReferenceSources/Petstore/Types.swift | 28 +++++++++++++++++++ 4 files changed, 54 insertions(+) diff --git a/Tests/OpenAPIGeneratorReferenceTests/Resources/Docs/petstore.yaml b/Tests/OpenAPIGeneratorReferenceTests/Resources/Docs/petstore.yaml index ac8a417d..a1c7365b 100644 --- a/Tests/OpenAPIGeneratorReferenceTests/Resources/Docs/petstore.yaml +++ b/Tests/OpenAPIGeneratorReferenceTests/Resources/Docs/petstore.yaml @@ -69,6 +69,18 @@ paths: schema: format: uuid type: string + - name: sort + in: query + required: false + style: deepObject + explode: true + schema: + type: object + properties: + id: + type: string + name: + type: string - $ref: '#/components/parameters/query.born-since' responses: '200': diff --git a/Tests/OpenAPIGeneratorReferenceTests/Resources/ReferenceSources/Petstore/Client.swift b/Tests/OpenAPIGeneratorReferenceTests/Resources/ReferenceSources/Petstore/Client.swift index 75c9bf22..04eaaf6a 100644 --- a/Tests/OpenAPIGeneratorReferenceTests/Resources/ReferenceSources/Petstore/Client.swift +++ b/Tests/OpenAPIGeneratorReferenceTests/Resources/ReferenceSources/Petstore/Client.swift @@ -87,6 +87,13 @@ public struct Client: APIProtocol { name: "My-Request-UUID", value: input.headers.My_hyphen_Request_hyphen_UUID ) + try converter.setQueryItemAsURI( + in: &request, + style: .deepObject, + explode: true, + name: "sort", + value: input.query.sort + ) try converter.setQueryItemAsURI( in: &request, style: .form, diff --git a/Tests/OpenAPIGeneratorReferenceTests/Resources/ReferenceSources/Petstore/Server.swift b/Tests/OpenAPIGeneratorReferenceTests/Resources/ReferenceSources/Petstore/Server.swift index 80d642b3..d255b183 100644 --- a/Tests/OpenAPIGeneratorReferenceTests/Resources/ReferenceSources/Petstore/Server.swift +++ b/Tests/OpenAPIGeneratorReferenceTests/Resources/ReferenceSources/Petstore/Server.swift @@ -187,6 +187,13 @@ fileprivate extension UniversalServer where APIHandler: APIProtocol { name: "feeds", as: Operations.listPets.Input.Query.feedsPayload.self ), + sort: try converter.getOptionalQueryItemAsURI( + in: request.soar_query, + style: .deepObject, + explode: true, + name: "sort", + as: Operations.listPets.Input.Query.sortPayload.self + ), since: try converter.getOptionalQueryItemAsURI( in: request.soar_query, style: .form, diff --git a/Tests/OpenAPIGeneratorReferenceTests/Resources/ReferenceSources/Petstore/Types.swift b/Tests/OpenAPIGeneratorReferenceTests/Resources/ReferenceSources/Petstore/Types.swift index 47b0390d..db5a5772 100644 --- a/Tests/OpenAPIGeneratorReferenceTests/Resources/ReferenceSources/Petstore/Types.swift +++ b/Tests/OpenAPIGeneratorReferenceTests/Resources/ReferenceSources/Petstore/Types.swift @@ -1855,6 +1855,31 @@ public enum Operations { public typealias feedsPayload = [Operations.listPets.Input.Query.feedsPayloadPayload] /// - Remark: Generated from `#/paths/pets/GET/query/feeds`. public var feeds: Operations.listPets.Input.Query.feedsPayload? + /// - Remark: Generated from `#/paths/pets/GET/query/sort`. + public struct sortPayload: Codable, Hashable, Sendable { + /// - Remark: Generated from `#/paths/pets/GET/query/sort/id`. + public var id: Swift.String? + /// - Remark: Generated from `#/paths/pets/GET/query/sort/name`. + public var name: Swift.String? + /// Creates a new `sortPayload`. + /// + /// - Parameters: + /// - id: + /// - name: + public init( + id: Swift.String? = nil, + name: Swift.String? = nil + ) { + self.id = id + self.name = name + } + public enum CodingKeys: String, CodingKey { + case id + case name + } + } + /// - Remark: Generated from `#/paths/pets/GET/query/sort`. + public var sort: Operations.listPets.Input.Query.sortPayload? /// Supply this parameter to filter pets born since the provided date. /// /// - Remark: Generated from `#/paths/pets/GET/query/since`. @@ -1865,16 +1890,19 @@ public enum Operations { /// - limit: How many items to return at one time (max 100) /// - habitat: /// - feeds: + /// - sort: /// - since: Supply this parameter to filter pets born since the provided date. public init( limit: Swift.Int32? = nil, habitat: Operations.listPets.Input.Query.habitatPayload? = nil, feeds: Operations.listPets.Input.Query.feedsPayload? = nil, + sort: Operations.listPets.Input.Query.sortPayload? = nil, since: Components.Parameters.query_period_born_hyphen_since? = nil ) { self.limit = limit self.habitat = habitat self.feeds = feeds + self.sort = sort self.since = since } } From adfa77efbe029a745ce7c26297bc9427bcda5e9d Mon Sep 17 00:00:00 2001 From: Kostis Stefanou Date: Sat, 26 Oct 2024 15:53:02 +0300 Subject: [PATCH 4/5] Add Unite tests for DeepObject. --- Package.swift | 2 +- .../Translator/Parameters/TypedParameter.swift | 4 +++- Tests/PetstoreConsumerTests/Test_Client.swift | 4 ++-- Tests/PetstoreConsumerTests/Test_Server.swift | 3 ++- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Package.swift b/Package.swift index 24fcaffb..8aea3577 100644 --- a/Package.swift +++ b/Package.swift @@ -60,7 +60,7 @@ let package = Package( // Tests-only: Runtime library linked by generated code, and also // helps keep the runtime library new enough to work with the generated // code. - .package(url: "https://github.com/apple/swift-openapi-runtime", from: "1.4.0"), + .package(url: "https://github.com/apple/swift-openapi-runtime", from: "1.5.0"), .package(url: "https://github.com/apple/swift-http-types", from: "1.0.2"), ], targets: [ diff --git a/Sources/_OpenAPIGeneratorCore/Translator/Parameters/TypedParameter.swift b/Sources/_OpenAPIGeneratorCore/Translator/Parameters/TypedParameter.swift index 2abd3d7d..0806f9ae 100644 --- a/Sources/_OpenAPIGeneratorCore/Translator/Parameters/TypedParameter.swift +++ b/Sources/_OpenAPIGeneratorCore/Translator/Parameters/TypedParameter.swift @@ -131,7 +131,9 @@ extension FileTranslator { switch location { case .query: switch style { - case .form, .deepObject: + case .form: + break + case .deepObject where explode: break default: try diagnostics.emitUnsupported( diff --git a/Tests/PetstoreConsumerTests/Test_Client.swift b/Tests/PetstoreConsumerTests/Test_Client.swift index 5b8b81fa..2b79da23 100644 --- a/Tests/PetstoreConsumerTests/Test_Client.swift +++ b/Tests/PetstoreConsumerTests/Test_Client.swift @@ -40,7 +40,7 @@ final class Test_Client: XCTestCase { XCTAssertEqual(operationID, "listPets") XCTAssertEqual( request.path, - "/pets?limit=24&habitat=water&feeds=herbivore&feeds=carnivore&since=2023-01-18T10%3A04%3A11Z" + "/pets?limit=24&habitat=water&feeds=herbivore&feeds=carnivore&sort%5Bid%5D=ascending&sort%5Bname%5D=descending&since=2023-01-18T10%3A04%3A11Z" ) XCTAssertEqual(baseURL.absoluteString, "/api") XCTAssertEqual(request.method, .get) @@ -66,7 +66,7 @@ final class Test_Client: XCTestCase { } let response = try await client.listPets( .init( - query: .init(limit: 24, habitat: .water, feeds: [.herbivore, .carnivore], since: .test), + query: .init(limit: 24, habitat: .water, feeds: [.herbivore, .carnivore], sort: .init(id: "ascending", name: "descending"), since: .test), headers: .init(My_hyphen_Request_hyphen_UUID: "abcd-1234") ) ) diff --git a/Tests/PetstoreConsumerTests/Test_Server.swift b/Tests/PetstoreConsumerTests/Test_Server.swift index 5f6be366..c11bd49f 100644 --- a/Tests/PetstoreConsumerTests/Test_Server.swift +++ b/Tests/PetstoreConsumerTests/Test_Server.swift @@ -33,6 +33,7 @@ final class Test_Server: XCTestCase { XCTAssertEqual(input.query.habitat, .water) XCTAssertEqual(input.query.since, .test) XCTAssertEqual(input.query.feeds, [.carnivore, .herbivore]) + XCTAssertEqual(input.query.sort, .init(id: "ascending", name: "descending")) XCTAssertEqual(input.headers.My_hyphen_Request_hyphen_UUID, "abcd-1234") return .ok( .init( @@ -43,7 +44,7 @@ final class Test_Server: XCTestCase { }) let (response, responseBody) = try await server.listPets( .init( - soar_path: "/api/pets?limit=24&habitat=water&feeds=carnivore&feeds=herbivore&since=\(Date.testString)", + soar_path: "/api/pets?sort%5Bid%5D=ascending&sort%5Bname%5D=descending", method: .get, headerFields: [.init("My-Request-UUID")!: "abcd-1234"] ), From acd69848dadad490149101f3a93acd7459dca519 Mon Sep 17 00:00:00 2001 From: kostis stefanou Date: Mon, 28 Oct 2024 21:59:15 +0200 Subject: [PATCH 5/5] Update Tests/OpenAPIGeneratorReferenceTests/SnippetBasedReferenceTests.swift Co-authored-by: Honza Dvorsky --- .../SnippetBasedReferenceTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/OpenAPIGeneratorReferenceTests/SnippetBasedReferenceTests.swift b/Tests/OpenAPIGeneratorReferenceTests/SnippetBasedReferenceTests.swift index 5e3aa25c..06699fa4 100644 --- a/Tests/OpenAPIGeneratorReferenceTests/SnippetBasedReferenceTests.swift +++ b/Tests/OpenAPIGeneratorReferenceTests/SnippetBasedReferenceTests.swift @@ -2516,7 +2516,7 @@ final class SnippetBasedReferenceTests: XCTestCase { type: string - name: sort in: query - required: true + required: false style: deepObject explode: true schema: