Skip to content

Commit 8a59716

Browse files
committed
Add conditional warning for device authorization flow
1 parent 74f0949 commit 8a59716

File tree

3 files changed

+67
-1
lines changed

3 files changed

+67
-1
lines changed

Sources/OpenAPIKit/Security/OAuthFlows.swift

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@ extension OpenAPI {
1212
/// OpenAPI Spec "Oauth Flows Object"
1313
///
1414
/// See [OpenAPI Oauth Flows Object](https://spec.openapis.org/oas/v3.0.4.html#oauth-flows-object).
15-
public struct OAuthFlows: Equatable, Sendable {
15+
public struct OAuthFlows: HasConditionalWarnings, Sendable {
1616
public let implicit: Implicit?
1717
public let password: Password?
1818
public let clientCredentials: ClientCredentials?
1919
public let authorizationCode: AuthorizationCode?
2020
public let deviceAuthorization: DeviceAuthorization?
2121

22+
public let conditionalWarnings: [(any Condition, OpenAPI.Warning)]
23+
2224
public init(
2325
implicit: Implicit? = nil,
2426
password: Password? = nil,
@@ -31,10 +33,33 @@ extension OpenAPI {
3133
self.clientCredentials = clientCredentials
3234
self.authorizationCode = authorizationCode
3335
self.deviceAuthorization = deviceAuthorization
36+
37+
self.conditionalWarnings = [
38+
nonNilVersionWarning(fieldName: "deviceAuthorization", value: deviceAuthorization, minimumVersion: .v3_2_0)
39+
].compactMap { $0 }
3440
}
3541
}
3642
}
3743

44+
extension OpenAPI.OAuthFlows: Equatable {
45+
public static func == (lhs: Self, rhs: Self) -> Bool {
46+
lhs.implicit == rhs.implicit
47+
&& lhs.password == rhs.password
48+
&& lhs.clientCredentials == rhs.clientCredentials
49+
&& lhs.authorizationCode == rhs.authorizationCode
50+
&& lhs.deviceAuthorization == rhs.deviceAuthorization
51+
}
52+
}
53+
54+
fileprivate func nonNilVersionWarning<Subject>(fieldName: String, value: Subject?, minimumVersion: OpenAPI.Document.Version) -> (any Condition, OpenAPI.Warning)? {
55+
value.map { _ in
56+
OpenAPI.Document.ConditionalWarnings.version(
57+
lessThan: minimumVersion,
58+
doesNotSupport: "The OAuthFlows \(fieldName) field"
59+
)
60+
}
61+
}
62+
3863
extension OpenAPI.OAuthFlows {
3964
@dynamicMemberLookup
4065
public struct DeviceAuthorization: Equatable, Sendable {
@@ -93,6 +118,10 @@ extension OpenAPI.OAuthFlows: Decodable {
93118
clientCredentials = try container.decodeIfPresent(OpenAPI.OAuthFlows.ClientCredentials.self, forKey: .clientCredentials)
94119
authorizationCode = try container.decodeIfPresent(OpenAPI.OAuthFlows.AuthorizationCode.self, forKey: .authorizationCode)
95120
deviceAuthorization = try container.decodeIfPresent(OpenAPI.OAuthFlows.DeviceAuthorization.self, forKey: .deviceAuthorization)
121+
122+
self.conditionalWarnings = [
123+
nonNilVersionWarning(fieldName: "deviceAuthorization", value: deviceAuthorization, minimumVersion: .v3_2_0)
124+
].compactMap { $0 }
96125
}
97126
}
98127

Sources/OpenAPIKit/Security/SecurityScheme.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,15 @@ extension OpenAPI {
6969
}
7070
}
7171

72+
fileprivate func notFalseVersionWarning(fieldName: String, value: Bool, minimumVersion: OpenAPI.Document.Version) -> (any Condition, OpenAPI.Warning)? {
73+
guard value else { return nil }
74+
75+
return OpenAPI.Document.ConditionalWarnings.version(
76+
lessThan: minimumVersion,
77+
doesNotSupport: "The Security Scheme \(fieldName) field"
78+
)
79+
}
80+
7281
extension OpenAPI.SecurityScheme.SecurityType {
7382
public enum Name: String, Codable {
7483
case apiKey

Tests/OpenAPIKitTests/Security/OauthFlowsTests.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,18 @@ final class OAuthFlowsTests: XCTestCase {
6060
XCTAssertEqual(authorizationCodeFlow.refreshUrl, testUrl)
6161
XCTAssertEqual(authorizationCodeFlow.scopes, scopes)
6262

63+
let deviceAuthorizationFlow = OpenAPI.OAuthFlows.DeviceAuthorization(
64+
deviceAuthorizationUrl: testUrl,
65+
tokenUrl: testUrl,
66+
refreshUrl: testUrl,
67+
scopes: scopes
68+
)
69+
70+
XCTAssertEqual(deviceAuthorizationFlow.deviceAuthorizationUrl, testUrl)
71+
XCTAssertEqual(deviceAuthorizationFlow.tokenUrl, testUrl)
72+
XCTAssertEqual(deviceAuthorizationFlow.refreshUrl, testUrl)
73+
XCTAssertEqual(deviceAuthorizationFlow.scopes, scopes)
74+
6375
let flows = OpenAPI.OAuthFlows(
6476
implicit: implicitFlow,
6577
password: passwordFlow,
@@ -71,6 +83,22 @@ final class OAuthFlowsTests: XCTestCase {
7183
XCTAssertEqual(flows.password, passwordFlow)
7284
XCTAssertEqual(flows.clientCredentials, clientCredentialsFlow)
7385
XCTAssertEqual(flows.authorizationCode, authorizationCodeFlow)
86+
XCTAssertEqual(flows.conditionalWarnings.count, 0)
87+
88+
let flows2 = OpenAPI.OAuthFlows(
89+
implicit: implicitFlow,
90+
password: passwordFlow,
91+
clientCredentials: clientCredentialsFlow,
92+
authorizationCode: authorizationCodeFlow,
93+
authorizationCode: deviceAuthorizationFlow
94+
)
95+
96+
XCTAssertEqual(flows2.implicit, implicitFlow)
97+
XCTAssertEqual(flows2.password, passwordFlow)
98+
XCTAssertEqual(flows2.clientCredentials, clientCredentialsFlow)
99+
XCTAssertEqual(flows2.authorizationCode, authorizationCodeFlow)
100+
XCTAssertEqual(flows2.deviceAuthorization, deviceAuthorizationFlow)
101+
XCTAssertEqual(flows2.conditionalWarnings.count, 1)
74102
}
75103
}
76104

0 commit comments

Comments
 (0)