Skip to content

Commit fd21a8f

Browse files
author
arasan01_dev
authored
Merge pull request #3 from Lucien/feat/optional-security-requirements
Add support to optional Security Requirements and top-level inheritance yonaskolb#310
2 parents cb8f993 + 3782fe9 commit fd21a8f

74 files changed

Lines changed: 224 additions & 83 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Sources/SwagGenKit/CodeFormatter.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,7 @@ public class CodeFormatter {
380380
var context: Context = [:]
381381

382382
context["name"] = securityRequirement.name
383+
context["isRequired"] = securityRequirement.isRequired
383384
context["scopes"] = securityRequirement.scopes
384385
context["scope"] = securityRequirement.scopes.first
385386

Sources/Swagger/Operation.swift

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,11 @@ public struct Operation {
4141

4242
extension Operation {
4343

44-
public init(path: String, method: Method, pathParameters: [PossibleReference<Parameter>], jsonDictionary: JSONDictionary) throws {
44+
public init(path: String,
45+
method: Method,
46+
pathParameters: [PossibleReference<Parameter>],
47+
jsonDictionary: JSONDictionary,
48+
topLevelSecurityRequirements: [SecurityRequirement]?) throws {
4549
json = jsonDictionary
4650
self.path = path
4751
self.method = method
@@ -57,7 +61,10 @@ extension Operation {
5761

5862
identifier = jsonDictionary.json(atKeyPath: "operationId")
5963
tags = (jsonDictionary.json(atKeyPath: "tags")) ?? []
60-
securityRequirements = jsonDictionary.json(atKeyPath: "security")
64+
securityRequirements = type(of: self).getSecurityRequirements(
65+
from: jsonDictionary,
66+
topLevelSecurityRequirements: topLevelSecurityRequirements
67+
)
6168

6269
let allResponses: [String: PossibleReference<Response>] = try jsonDictionary.json(atKeyPath: "responses")
6370
var mappedResponses: [OperationResponse] = []
@@ -88,4 +95,18 @@ extension Operation {
8895

8996
deprecated = (jsonDictionary.json(atKeyPath: "deprecated")) ?? false
9097
}
98+
99+
private static func getSecurityRequirements(
100+
from jsonDictionary: JSONDictionary,
101+
topLevelSecurityRequirements: [SecurityRequirement]?
102+
) -> [SecurityRequirement]? {
103+
// Even an empty list of operation security requirements
104+
// can remove top-level security requirements.
105+
guard let securityRequirements: [SecurityRequirement] =
106+
jsonDictionary.json(atKeyPath: "security") else {
107+
return topLevelSecurityRequirements
108+
}
109+
110+
return securityRequirements.updatingRequiredFlag()
111+
}
91112
}

Sources/Swagger/Path.swift

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,22 @@ public struct Path {
99

1010
extension Path: NamedMappable {
1111

12-
public init(name: String, jsonDictionary: JSONDictionary) throws {
12+
public init(name: String,
13+
jsonDictionary: JSONDictionary,
14+
topLevelSecurityRequirements: [SecurityRequirement]?) throws {
1315
path = name
1416
parameters = (jsonDictionary.json(atKeyPath: "parameters")) ?? []
1517

1618
var mappedOperations: [Operation] = []
1719
for (key, value) in jsonDictionary {
1820
if let method = Operation.Method(rawValue: key) {
1921
if let json = value as? [String: Any] {
20-
let operation = try Operation(path: path, method: method, pathParameters: parameters, jsonDictionary: json)
22+
let operation = try Operation(
23+
path: path,
24+
method: method,
25+
pathParameters: parameters,
26+
jsonDictionary: json,
27+
topLevelSecurityRequirements: topLevelSecurityRequirements)
2128
mappedOperations.append(operation)
2229
}
2330
}
Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,37 @@
11
import Foundation
22
import JSONUtilities
33

4-
public struct SecurityRequirement: JSONObjectConvertible {
4+
public struct SecurityRequirement: Equatable {
55
public let name: String
6+
public let isRequired: Bool
67
public let scopes: [String]
78

9+
public static let optionalMarker: SecurityRequirement = {
10+
.init(name: "optional-marker", isRequired: false, scopes: [])
11+
}()
12+
}
13+
14+
extension SecurityRequirement: JSONObjectConvertible {
815
public init(jsonDictionary: JSONDictionary) throws {
9-
name = jsonDictionary.keys.first!
16+
guard let firstKey = jsonDictionary.keys.first else {
17+
self = .optionalMarker
18+
return
19+
}
20+
name = firstKey
21+
isRequired = true
1022
scopes = try jsonDictionary.json(atKeyPath: .key(name))
1123
}
1224
}
25+
26+
extension Array where Element == SecurityRequirement {
27+
func updatingRequiredFlag() -> [Element] {
28+
// Check if there is an optional security requirement marker
29+
guard (contains(where: { $0 == .optionalMarker })) else {
30+
return self
31+
}
32+
// Remove the optional marker and set all security requirements as optional
33+
return self
34+
.drop { $0 == .optionalMarker }
35+
.map { .init(name: $0.name, isRequired: false, scopes: $0.scopes) }
36+
}
37+
}

Sources/Swagger/SwaggerSpec.swift

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ public enum TransferScheme: String {
3030
}
3131

3232
public protocol NamedMappable {
33-
init(name: String, jsonDictionary: JSONDictionary) throws
33+
init(name: String,
34+
jsonDictionary: JSONDictionary,
35+
topLevelSecurityRequirements: [SecurityRequirement]?) throws
3436
}
3537

3638
extension SwaggerSpec {
@@ -69,12 +71,20 @@ extension SwaggerSpec: JSONObjectConvertible {
6971

7072
public init(jsonDictionary: JSONDictionary) throws {
7173

72-
func decodeNamed<T: NamedMappable>(jsonDictionary: JSONDictionary, key: String) throws -> [T] {
74+
func decodeNamed<T: NamedMappable>(
75+
jsonDictionary: JSONDictionary,
76+
key: String,
77+
topLevelSecurityRequirements: [SecurityRequirement]?
78+
) throws -> [T] {
7379
var values: [T] = []
7480
if let dictionary = jsonDictionary[key] as? [String: Any] {
7581
for (key, value) in dictionary {
7682
if let dictionary = value as? [String: Any] {
77-
let value = try T(name: key, jsonDictionary: dictionary)
83+
let value = try T(
84+
name: key,
85+
jsonDictionary: dictionary,
86+
topLevelSecurityRequirements: topLevelSecurityRequirements
87+
)
7888
values.append(value)
7989
}
8090
}
@@ -91,13 +101,15 @@ extension SwaggerSpec: JSONObjectConvertible {
91101

92102
info = try jsonDictionary.json(atKeyPath: "info")
93103
servers = jsonDictionary.json(atKeyPath: "servers") ?? []
94-
securityRequirements = jsonDictionary.json(atKeyPath: "security")
104+
securityRequirements = type(of: self).getSecurityRequirements(from: jsonDictionary)
95105
if jsonDictionary["components"] != nil {
96106
components = try jsonDictionary.json(atKeyPath: "components")
97107
} else {
98108
components = Components()
99109
}
100-
paths = try decodeNamed(jsonDictionary: jsonDictionary, key: "paths")
110+
paths = try decodeNamed(jsonDictionary: jsonDictionary,
111+
key: "paths",
112+
topLevelSecurityRequirements: securityRequirements)
101113
operations = paths.reduce([]) { $0 + $1.operations }
102114
.sorted(by: { (lhs, rhs) -> Bool in
103115
if lhs.path == rhs.path {
@@ -110,4 +122,15 @@ extension SwaggerSpec: JSONObjectConvertible {
110122
try resolver.resolve()
111123
self = resolver.spec
112124
}
125+
126+
private static func getSecurityRequirements(
127+
from jsonDictionary: JSONDictionary
128+
) -> [SecurityRequirement]? {
129+
guard let securityRequirements: [SecurityRequirement] =
130+
jsonDictionary.json(atKeyPath: "security") else {
131+
return nil
132+
}
133+
134+
return securityRequirements.updatingRequiredFlag()
135+
}
113136
}

Specs/Petstore/generated/Swift/Sources/APIService.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,12 @@ extension APIService: CustomStringConvertible {
3737

3838
public struct SecurityRequirement {
3939
public let type: String
40+
public let isRequired: Bool
4041
public let scopes: [String]
4142

42-
public init(type: String, scopes: [String]) {
43+
public init(type: String, isRequired: Bool, scopes: [String]) {
4344
self.type = type
45+
self.isRequired = isRequired
4446
self.scopes = scopes
4547
}
4648
}

Specs/Petstore/generated/Swift/Sources/Requests/Pets/CreatePets.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ extension Petstore.Pets {
1010
/** Create a pet */
1111
public enum CreatePets {
1212

13-
public static let service = APIService<Response>(id: "createPets", tag: "pets", method: "POST", path: "/pets", hasBody: false, securityRequirements: [SecurityRequirement(type: "petstore_auth", scopes: ["write:pets", "read:pets"])])
13+
public static let service = APIService<Response>(id: "createPets", tag: "pets", method: "POST", path: "/pets", hasBody: false, securityRequirements: [SecurityRequirement(type: "petstore_auth", isRequired: true, scopes: ["write:pets", "read:pets"])])
1414

1515
public final class Request: APIRequest<Response> {
1616

Specs/Petstore/generated/Swift/Sources/Requests/Pets/ListPets.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ extension Petstore.Pets {
1010
/** List all pets */
1111
public enum ListPets {
1212

13-
public static let service = APIService<Response>(id: "listPets", tag: "pets", method: "GET", path: "/pets", hasBody: false, securityRequirements: [])
13+
public static let service = APIService<Response>(id: "listPets", tag: "pets", method: "GET", path: "/pets", hasBody: false, securityRequirements: [SecurityRequirement(type: "petstore_auth", isRequired: false, scopes: ["read:pets"])])
1414

1515
public final class Request: APIRequest<Response> {
1616

Specs/Petstore/generated/Swift/Sources/Requests/Pets/ShowPetById.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ extension Petstore.Pets {
1010
/** Info for a specific pet */
1111
public enum ShowPetById {
1212

13-
public static let service = APIService<Response>(id: "showPetById", tag: "pets", method: "GET", path: "/pets/{petId}", hasBody: false, securityRequirements: [])
13+
public static let service = APIService<Response>(id: "showPetById", tag: "pets", method: "GET", path: "/pets/{petId}", hasBody: false, securityRequirements: [SecurityRequirement(type: "petstore_auth", isRequired: true, scopes: ["read:pets"])])
1414

1515
public final class Request: APIRequest<Response> {
1616

Specs/Petstore/generated/Swift/Sources/Requests/Pets/UpdatePetWithForm.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ extension Petstore.Pets {
1010
/** Updates a pet in the store with form data */
1111
public enum UpdatePetWithForm {
1212

13-
public static let service = APIService<Response>(id: "updatePetWithForm", tag: "pets", method: "POST", path: "/pets/{petId}", hasBody: true, securityRequirements: [SecurityRequirement(type: "petstore_auth", scopes: ["write:pets", "read:pets"])])
13+
public static let service = APIService<Response>(id: "updatePetWithForm", tag: "pets", method: "POST", path: "/pets/{petId}", hasBody: true, securityRequirements: [])
1414

1515
public final class Request: APIRequest<Response> {
1616

0 commit comments

Comments
 (0)