Last active
September 20, 2017 02:32
-
-
Save khanlou/c8f170e4e9ced8e90a4c64527aa23a3f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Vapor | |
import HTTP | |
import Foundation | |
extension Node { | |
var backDoorToRealValues: Any { | |
return self.wrapped.backDoorToRealValues | |
} | |
} | |
extension StructuredData { | |
internal var backDoorToRealValues: Any { | |
switch self { | |
case .array(let values): | |
return values.map { $0.backDoorToRealValues } | |
case .bool(let value): | |
return value | |
case .bytes(let bytes): | |
return bytes | |
case .null: | |
return NSNull() | |
case .number(let number): | |
switch number { | |
case .double(let value): | |
return value | |
case .int(let value): | |
return value | |
case .uint(let value): | |
return value | |
} | |
case .object(let values): | |
var dictionary: [String: Any] = [:] | |
for (key, value) in values { | |
dictionary[key] = value.backDoorToRealValues | |
} | |
return dictionary | |
case .string(let value): | |
return value | |
case .date(let value): | |
return value | |
} | |
} | |
} | |
extension Node { | |
func toJSON() throws -> JSON { | |
return JSON(node: self) | |
} | |
} | |
public struct JSONError: ExternallyVisibleError, AbortError, Error { | |
public let status = Status.badRequest | |
public let metadata: Node? = nil | |
public var code: Int { | |
return status.statusCode | |
} | |
public var reason: String | |
public var externalMessage: String { | |
return reason | |
} | |
public init(reason: String) { | |
self.reason = reason | |
} | |
public static func missingKey(_ missingKey: String) -> JSONError { | |
return self.init(reason: "This endpoint expects the JSON key \(missingKey), but it wasn't present.") | |
} | |
public static func typeMismatch(key: String, expectedType: String, actualType: String) -> JSONError { | |
return self.init(reason:"This endpoint expects the JSON key '\(key)'. It was present, but did not have the expected type \(expectedType). It had type '\(actualType).'") | |
} | |
public static func jsonMissing() -> JSONError { | |
return self.init(reason: "The endpoint requires a JSON body and a \"Content-Type\" of \"application/json\".") | |
} | |
public static func missingChildKeys(key: String, fetchedValue: Any) -> JSONError { | |
return self.init(reason: "The value '\(fetchedValue)' at key '\(key)' could not be transformed. It may be missing child keys.") | |
} | |
} | |
public class NiceJSON { | |
let json: JSON? | |
public init(json: JSON?) { | |
self.json = json | |
} | |
var dictionary: [String: Any]? { | |
return json?.wrapped.backDoorToRealValues as? [String: Any] | |
} | |
public func fetch<T>(_ key: String) throws -> T { | |
guard let dictionary = dictionary else { throw JSONError.jsonMissing() } | |
let fetchedOptional = dictionary[key] | |
guard let fetched = fetchedOptional else { | |
throw JSONError.missingKey(key) | |
} | |
guard let typed = fetched as? T else { | |
throw JSONError.typeMismatch(key: key, expectedType: String(describing: T.self), actualType: String(describing: type(of: fetched))) | |
} | |
return typed | |
} | |
public func fetchOptional<T>(_ key: String) throws -> T? { | |
let fetchedOptional = dictionary?[key] | |
guard let fetched = fetchedOptional else { | |
return nil | |
} | |
if fetched is NSNull { | |
return nil | |
} | |
guard let typed = fetched as? T else { | |
throw JSONError.typeMismatch(key: key, expectedType: String(describing: T.self), actualType: String(describing: type(of: fetched))) | |
} | |
return typed | |
} | |
public func fetch<T, U>(_ key: String, transformation: (T) -> U?) throws -> U { | |
let fetched: T = try fetch(key) | |
guard let transformed = transformation(fetched) else { | |
throw JSONError.missingChildKeys(key: key, fetchedValue: fetched) | |
} | |
return transformed | |
} | |
public func fetchOptional<T, U>(_ key: String, transformation: (T) -> U?) throws -> U? { | |
let fetchedOptional: T? = try fetchOptional(key) | |
return fetchedOptional.flatMap(transformation) | |
} | |
public func fetchIntUIntOrDouble(_ key: String) throws -> Double { | |
if let valueAsUInt = try? (self.fetch(key) as UInt) { | |
return Double(valueAsUInt) | |
} else if let valueAsInt = try? (self.fetch(key) as Int) { | |
return Double(valueAsInt) | |
} else if let startDateAsDouble = try? (self.fetch(key) as Double) { | |
return startDateAsDouble | |
} else { | |
if let value = self.dictionary?[key] { | |
throw JSONError.typeMismatch(key: key, expectedType: "Number", actualType: String(describing: type(of: value))) | |
} else { | |
throw JSONError.missingKey(key) | |
} | |
} | |
} | |
public func fetchDate(_ key: String) throws -> Date { | |
let double = try self.fetchIntUIntOrDouble(key) | |
return Date(timeIntervalSince1970: double) | |
} | |
} | |
extension Request { | |
public var niceJSON: NiceJSON { | |
return NiceJSON(json: json) | |
} | |
} | |
extension JSON { | |
static var success: JSON { | |
return try! JSON(node: ["success": true]) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment