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

[Bug] Fix mixed path components #491

Merged
merged 2 commits into from
Dec 15, 2023
Merged
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 @@ -12,6 +12,7 @@
//
//===----------------------------------------------------------------------===//
import OpenAPIKit
import Foundation

/// A wrapper of an OpenAPI operation that includes the information
/// about the parent containers of the operation, such as its path
Expand Down Expand Up @@ -244,6 +245,11 @@ extension OperationDescription {
)
}

/// The regular expression for parsing subcomponents of path components.
///
/// Either a parameter `{foo}` or a constant value `foo`.
private static let pathParameterRegex = try! NSRegularExpression(pattern: #"(\{[a-zA-Z0-9_]+\})|([^{}]+)"#)

/// Returns a string that contains the template to be generated for
/// the client that fills in path parameters, and an array expression
/// with the parameter values.
Expand All @@ -257,19 +263,35 @@ extension OperationDescription {
// in which the parameters are used.
var newComponents: [String] = []
for component in path.components {
guard component.hasPrefix("{") && component.hasSuffix("}") else {
newComponents.append(component)
continue
}
let componentName = String(component.dropFirst().dropLast())
guard pathParameterNames.contains(componentName) else {
throw GenericError(
message:
"Parameter '\(componentName)' used in the path '\(self.path.rawValue)', but not found in the defined list of path parameters."
)
let matches = Self.pathParameterRegex.matches(
in: component,
options: [],
range: NSRange(location: 0, length: component.utf16.count)
)
var subcomponents: [String] = []
for match in matches {
for i in 1..<match.numberOfRanges {
let range = match.range(at: i)
guard range.location != NSNotFound, let swiftRange = Range(range, in: component) else {
continue
}
let value = component[swiftRange]
if value.hasPrefix("{") && value.hasSuffix("}") {
let componentName = String(value.dropFirst().dropLast())
guard pathParameterNames.contains(componentName) else {
throw GenericError(
message:
"Parameter '\(componentName)' used in the path '\(self.path.rawValue)', but not found in the defined list of path parameters."
)
}
orderedPathParameters.append(componentName)
subcomponents.append("{}")
} else {
subcomponents.append(String(value))
}
}
}
orderedPathParameters.append(componentName)
newComponents.append("{}")
newComponents.append(subcomponents.joined())
}
let newPath = OpenAPI.Path(newComponents, trailingSlash: path.trailingSlash)
let names: [Expression] = orderedPathParameters.map { param in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2349,7 +2349,7 @@ final class SnippetBasedReferenceTests: XCTestCase {
func testRequestWithPathParams() throws {
try self.assertRequestInTypesClientServerTranslation(
"""
/foo/a/{a}/b/{b}:
/foo/a/{a}/b/{b}/c{num}:
get:
parameters:
- name: b
Expand All @@ -2362,6 +2362,11 @@ final class SnippetBasedReferenceTests: XCTestCase {
required: true
schema:
type: string
- name: num
in: path
required: true
schema:
type: integer
operationId: getFoo
responses:
default:
Expand All @@ -2372,12 +2377,15 @@ final class SnippetBasedReferenceTests: XCTestCase {
public struct Path: Sendable, Hashable {
public var b: Swift.String
public var a: Swift.String
public var num: Swift.Int
public init(
b: Swift.String,
a: Swift.String
a: Swift.String,
num: Swift.Int
) {
self.b = b
self.a = a
self.num = num
}
}
public var path: Operations.getFoo.Input.Path
Expand All @@ -2389,10 +2397,11 @@ final class SnippetBasedReferenceTests: XCTestCase {
client: """
{ input in
let path = try converter.renderedPath(
template: "/foo/a/{}/b/{}",
template: "/foo/a/{}/b/{}/c{}",
parameters: [
input.path.a,
input.path.b
input.path.b,
input.path.num
]
)
var request: HTTPTypes.HTTPRequest = .init(
Expand All @@ -2415,6 +2424,11 @@ final class SnippetBasedReferenceTests: XCTestCase {
in: metadata.pathParameters,
name: "a",
as: Swift.String.self
),
num: try converter.getPathParameterAsURI(
in: metadata.pathParameters,
name: "num",
as: Swift.Int.self
)
)
return Operations.getFoo.Input(path: path)
Expand Down