-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Labels
enhancementNew feature or requestNew feature or request
Description
Add a method to fetch the capabilities of a server, both without and with authentication (due to the difference in response for security reasons).
The API must be extendable for third-parties. Downstream projects must be abled to define their own capabilities which can be checked for equally.
Xcode Playground Code
import Foundation
// MARK: - Capability System
enum CapabilityError: Error {
case keyNotFound
}
///
/// A representation of a server capability.
///
protocol Capability {
///
/// Assert that the capability is available based on specific verification code.
///
static func assert(_ object: [String: Any]) -> Bool
///
/// Create a native Swift representation based on the given capabilities server response object.
///
init(_ object: [String : Any]) throws
}
///
/// A container for the capabilities retrieved from a server.
///
struct CapabilitySet {
///
/// The parsed JSON object but without final types.
///
private let object: [String: Any]
init(object: [String : Any]) {
self.object = object
}
///
/// Attempt to extract a capability from the container.
///
/// - Returns: The failure to find the given capability is not an error but expected run time variation depending on server configuration. Hence, the returned value is `nil` in case the capability was not found in the returned capability set.
///
/// - Throws: An error is thrown in case a capability was found but could not be parsed correctly from the server response.
///
func get(_ type: Capability.Type) throws -> (any Capability)? {
guard type.assert(object) else {
return nil
}
return try type.init(object)
}
}
// MARK: - Capability Implementations
///
/// Whether or not the server is capable of trashing and restoring files or just deletes them directly.
///
struct Trash: Capability {
static func assert(_ object: [String : Any]) -> Bool {
object.keys.contains("undelete")
}
init(_ object: [String : Any]) {
// Nothing to do with the argument in this case.
}
}
///
/// This does not expose all the theming properties to keep the example on point.
///
struct Theming: Capability {
let name: String
let color: String
init(_ object: [String : Any]) throws {
guard let theming = object["theming"] as? [String: Any] else {
throw CapabilityError.keyNotFound
}
guard let name = theming["name"] as? String else {
throw CapabilityError.keyNotFound
}
guard let color = theming["color"] as? String else {
throw CapabilityError.keyNotFound
}
self.name = name
self.color = color
}
static func assert(_ object: [String : Any]) -> Bool {
object.keys.contains("theming")
}
}
// MARK: - Mock Server
struct Server {
private let theming: Bool
private let trash: Bool
init(theming: Bool = false, trash: Bool = false) {
self.theming = theming
self.trash = trash
}
func fetchCapabilities() -> CapabilitySet {
var object: [String: Any] = [:]
if theming {
object["theming"] = [
"name": "Rainmaker",
"url": "http://localhost:8081",
"slogan": "Rainmaker Test Backend",
"color": "#89C1FD",
"color-text": "#000000",
"color-element": "#89C1FD",
"color-element-bright": "#89C1FD",
"color-element-dark": "#89C1FD",
"logo": "http://localhost:8081/core/img/logo/logo.svg?v=11",
"background": "#2d73be",
"background-text": "#ffffff",
"background-plain": "1",
"background-default": "1",
"logoheader": "http://localhost:8081/core/img/logo/logo.svg?v=11",
"favicon": "http://localhost:8081/core/img/logo/logo.svg?v=11"
]
}
if trash {
object["undelete"] = "1"
}
return CapabilitySet(object: object)
}
}
// MARK: - Example Usage
let server = Server(theming: true)
let capabilities = server.fetchCapabilities()
do {
if let capability = try capabilities.get(Theming.self) {
print("Theming supported on server 1: \(capability)")
} else {
print("Theming unsupported on server 1.")
}
} catch {
print("Error: \(error)")
}Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or request