Skip to content

Commit

Permalink
Add callback object (not tested yet).
Browse files Browse the repository at this point in the history
  • Loading branch information
mattpolzin committed Nov 2, 2020
1 parent f02b6ad commit ef8bcdc
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 7 deletions.
76 changes: 76 additions & 0 deletions Sources/OpenAPIKit/Callback.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//
// Callback.swift
//
//
// Created by Mathew Polzin on 11/1/20.
//

import Foundation

extension OpenAPI {

/// A URL template where the placeholders are OpenAPI **Runtime Expressions** instead
/// of named variables.
///
/// See [OpenAPI Callback Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#callback-object) and [OpenAPI Runtime Expression](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#runtime-expressions) for more.
///
public struct CallbackURL: Hashable, RawRepresentable {
public let template: URLTemplate

/// The string value of the URL without variable replacement.
///
/// Variables cannot be replaced based on other information in the
/// OpenAPI document; they are only available at "runtime" which is
/// where the name of the OpenAPI structure `CallbackURL`
/// represents comes from.
public var rawValue: String {
template.rawValue
}

/// Get a URL from the runtime expression if it is a valid URL without
/// variable replacement.
///
/// Callback URLs with variables in them will not be valid URLs
/// and are therefore guaranteed to return `nil`.
public var url: URL? {
template.url
}

/// Create a CallbackURL from the string if possible.
public init?(rawValue: String) {
guard let template = URLTemplate(rawValue: rawValue) else {
return nil
}
self.template = template
}

public init(url: URL) {
template = .init(url: url)
}
}

/// A map from runtime expressions to path items to be used as
/// callbacks for the API.
///
/// See [OpenAPI Callback Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#callback-object).
///
public typealias Callback = OrderedDictionary<CallbackURL, PathItem>

public typealias CallbackMap = OrderedDictionary<String, Either<JSONReference<Callback>, Callback>>
}

extension OpenAPI.CallbackURL: Encodable {
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()

try container.encode(rawValue)
}
}

extension OpenAPI.CallbackURL: Decodable {
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()

template = try container.decode(URLTemplate.self)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ extension OpenAPI.Response: ComponentDictionaryLocatable {
public static var openAPIComponentsKeyPath: KeyPath<OpenAPI.Components, OpenAPI.ComponentDictionary<Self>> { \.responses }
}

extension OpenAPI.Callback: ComponentDictionaryLocatable {
public static var openAPIComponentsKey: String { "callbacks" }
public static var openAPIComponentsKeyPath: KeyPath<OpenAPI.Components, OpenAPI.ComponentDictionary<Self>> { \.callbacks }
}

extension OpenAPI.Parameter: ComponentDictionaryLocatable {
public static var openAPIComponentsKey: String { "parameters" }
public static var openAPIComponentsKeyPath: KeyPath<OpenAPI.Components, OpenAPI.ComponentDictionary<Self>> { \.parameters }
Expand Down
10 changes: 9 additions & 1 deletion Sources/OpenAPIKit/Components Object/Components.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ extension OpenAPI {
public var requestBodies: ComponentDictionary<Request>
public var headers: ComponentDictionary<Header>
public var securitySchemes: ComponentDictionary<SecurityScheme>
public var callbacks: ComponentDictionary<Callback>
// public var links:
// public var callbacks:

/// Dictionary of vendor extensions.
///
Expand All @@ -41,6 +41,7 @@ extension OpenAPI {
requestBodies: ComponentDictionary<Request> = [:],
headers: ComponentDictionary<Header> = [:],
securitySchemes: ComponentDictionary<SecurityScheme> = [:],
callbacks: ComponentDictionary<Callback> = [:],
vendorExtensions: [String: AnyCodable] = [:]
) {
self.schemas = schemas
Expand All @@ -50,6 +51,7 @@ extension OpenAPI {
self.requestBodies = requestBodies
self.headers = headers
self.securitySchemes = securitySchemes
self.callbacks = callbacks
self.vendorExtensions = vendorExtensions
}

Expand Down Expand Up @@ -158,6 +160,10 @@ extension OpenAPI.Components: Encodable {
try container.encode(securitySchemes, forKey: .securitySchemes)
}

if !callbacks.isEmpty {
try container.encode(callbacks, forKey: .callbacks)
}

try encodeExtensions(to: &container)
}
}
Expand Down Expand Up @@ -187,6 +193,8 @@ extension OpenAPI.Components: Decodable {

securitySchemes = try container.decodeIfPresent(OpenAPI.ComponentDictionary<OpenAPI.SecurityScheme>.self, forKey: .securitySchemes) ?? [:]

callbacks = try container.decodeIfPresent(OpenAPI.ComponentDictionary<OpenAPI.Callback>.self, forKey: .callbacks) ?? [:]

vendorExtensions = try Self.extensions(from: decoder)
} catch let error as DecodingError {
if let underlyingError = error.underlyingError as? KeyDecodingError {
Expand Down
20 changes: 19 additions & 1 deletion Sources/OpenAPIKit/Operation/Operation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,13 @@ extension OpenAPI {
/// foundResponse = document.components.lookup(successResponse)!
///
public var responses: OpenAPI.Response.Map
// public let callbacks:

/// A map of possible out-of band callbacks related to the parent operation.
///
/// The key is a unique identifier for the Callback Object. Each value in the
/// map is a Callback Object that describes a request that may be initiated
/// by the API provider and the expected responses.
public let callbacks: OpenAPI.CallbackMap

/// Indicates that the operation is deprecated or not.
///
Expand Down Expand Up @@ -103,6 +109,7 @@ extension OpenAPI {
parameters: Parameter.Array = [],
requestBody: Either<JSONReference<OpenAPI.Request>, OpenAPI.Request>,
responses: OpenAPI.Response.Map,
callbacks: OpenAPI.CallbackMap = [:],
deprecated: Bool = false,
security: [OpenAPI.SecurityRequirement]? = nil,
servers: [OpenAPI.Server]? = nil,
Expand All @@ -116,6 +123,7 @@ extension OpenAPI {
self.parameters = parameters
self.requestBody = requestBody
self.responses = responses
self.callbacks = callbacks
self.deprecated = deprecated
self.security = security
self.servers = servers
Expand All @@ -132,6 +140,7 @@ extension OpenAPI {
parameters: Parameter.Array = [],
requestBody: OpenAPI.Request? = nil,
responses: OpenAPI.Response.Map,
callbacks: OpenAPI.CallbackMap = [:],
deprecated: Bool = false,
security: [OpenAPI.SecurityRequirement]? = nil,
servers: [OpenAPI.Server]? = nil,
Expand All @@ -145,6 +154,7 @@ extension OpenAPI {
self.parameters = parameters
self.requestBody = requestBody.map(Either.init)
self.responses = responses
self.callbacks = callbacks
self.deprecated = deprecated
self.security = security
self.servers = servers
Expand All @@ -161,6 +171,7 @@ extension OpenAPI {
parameters: Parameter.Array = [],
requestBody: OpenAPI.Request? = nil,
responses: OpenAPI.Response.Map,
callbacks: OpenAPI.CallbackMap = [:],
deprecated: Bool = false,
security: [OpenAPI.SecurityRequirement]? = nil,
servers: [OpenAPI.Server]? = nil,
Expand All @@ -175,6 +186,7 @@ extension OpenAPI {
parameters: parameters,
requestBody: requestBody,
responses: responses,
callbacks: callbacks,
deprecated: deprecated,
security: security,
servers: servers,
Expand Down Expand Up @@ -229,6 +241,10 @@ extension OpenAPI.Operation: Encodable {

try container.encode(responses, forKey: .responses)

if !callbacks.isEmpty {
try container.encode(callbacks, forKey: .callbacks)
}

if deprecated {
try container.encode(deprecated, forKey: .deprecated)
}
Expand Down Expand Up @@ -264,6 +280,8 @@ extension OpenAPI.Operation: Decodable {

responses = try container.decode(OpenAPI.Response.Map.self, forKey: .responses)

callbacks = try container.decodeIfPresent(OpenAPI.CallbackMap.self, forKey: .callbacks) ?? [:]

deprecated = try container.decodeIfPresent(Bool.self, forKey: .deprecated) ?? false

security = try decodeSecurityRequirements(from: container, forKey: .security, given: nil)
Expand Down
2 changes: 1 addition & 1 deletion Sources/OpenAPIKit/URLTemplate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public struct URLTemplate: Hashable, RawRepresentable {

/// Get a URL from this templated URL if it is a valid URL already.
///
/// Templated URLS with variables in them will not be valid URLs
/// Templated URLs with variables in them will not be valid URLs
/// and are therefore guaranteed to return `nil`.
public var url: URL? {
return URL(string: rawValue)
Expand Down
8 changes: 4 additions & 4 deletions documentation/specification_coverage.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ For more information on the OpenAPIKit types, see the [full type documentation](
- [x] headers
- [x] securitySchemes
- [ ] links
- [ ] callbacks
- [x] callbacks
- [x] specification extensions (`vendorExtensions`)

### Paths Object (`OpenAPI.PathItem.Map`)
Expand Down Expand Up @@ -119,7 +119,7 @@ For more information on the OpenAPIKit types, see the [full type documentation](
- [x] parameters
- [x] requestBody
- [x] responses
- [ ] callbacks
- [x] callbacks
- [x] deprecated
- [x] security
- [x] servers
Expand Down Expand Up @@ -179,7 +179,7 @@ For more information on the OpenAPIKit types, see the [full type documentation](
- [x] specification extensions (`vendorExtensions`)

### Callback Object
- [ ] *{expression}*
- [x] *{expression}*
- [ ] specification extensions

### Example Object (`OpenAPI.Example`)
Expand Down Expand Up @@ -282,4 +282,4 @@ For more information on the OpenAPIKit types, see the [full type documentation](
- [ ] specification extensions

### Security Requirement Object (`OpenAPI.Document.SecurityRequirement`)
- [x] *{name}* (using `JSONReferences` instead of a stringy API)
- [x] *{name}* (using `JSONReferences` instead of a stringy API)

0 comments on commit ef8bcdc

Please sign in to comment.