From 6851729c7db29a1d34254f12977f1d6a6bf5acfd Mon Sep 17 00:00:00 2001 From: Hernan Zalazar Date: Thu, 31 May 2018 19:36:42 -0500 Subject: [PATCH 1/5] Add method to log request payload (dev only) --- Guardian/EnrollRequest.swift | 16 +++++-- Guardian/Request.swift | 61 ++++++++++++++++++------ GuardianApp/NotificationController.swift | 2 + GuardianApp/ViewController.swift | 2 + 4 files changed, 63 insertions(+), 18 deletions(-) diff --git a/Guardian/EnrollRequest.swift b/Guardian/EnrollRequest.swift index 58958f8..c424ee4 100644 --- a/Guardian/EnrollRequest.swift +++ b/Guardian/EnrollRequest.swift @@ -28,7 +28,7 @@ import Foundation - seealso: Guardian.enroll - seealso: Guardian.Enrollment */ -public struct EnrollRequest: Requestable { +public class EnrollRequest: Requestable { typealias T = Enrollment @@ -37,6 +37,7 @@ public struct EnrollRequest: Requestable { private let enrollmentUri: String? private let notificationToken: String private let keyPair: RSAKeyPair + private var logger: Logger? = nil init(api: API, enrollmentTicket: String? = nil, enrollmentUri: String? = nil, notificationToken: String, keyPair: RSAKeyPair) { self.api = api @@ -46,6 +47,11 @@ public struct EnrollRequest: Requestable { self.keyPair = keyPair } + public func log(into logger: @escaping Logger = defaultLogger) -> EnrollRequest { + self.logger = logger + return self + } + /** Executes the request in a background thread @@ -62,8 +68,12 @@ public struct EnrollRequest: Requestable { return callback(.failure(cause: GuardianError.invalidEnrollmentUri)) } - api.enroll(withTicket: ticket, identifier: Enrollment.defaultDeviceIdentifier, name: Enrollment.defaultDeviceName, notificationToken: notificationToken, publicKey: keyPair.publicKey) - .start { result in + var request = api.enroll(withTicket: ticket, identifier: Enrollment.defaultDeviceIdentifier, name: Enrollment.defaultDeviceName, notificationToken: notificationToken, publicKey: keyPair.publicKey) + + if let logger = self.logger { + request = request.log(into: logger) + } + request.start { result in switch result { case .failure(let cause): callback(.failure(cause: cause)) diff --git a/Guardian/Request.swift b/Guardian/Request.swift index e509770..f17687d 100644 --- a/Guardian/Request.swift +++ b/Guardian/Request.swift @@ -22,6 +22,12 @@ import Foundation +/// Definition of function to log a string +public typealias Logger = (String) -> () + +/// Default logger using Swift print function +let defaultLogger: Logger = { (line: String) in print(line) } + /** An asynchronous HTTP request */ @@ -31,16 +37,40 @@ public class Request: Requestable { let method: String let url: URL let payload: [String: Any]? - let headers: [String: String]? + let headers: [String: String] + var logger: Logger? = nil init(session: URLSession, method: String, url: URL, payload: [String: Any]? = nil, headers: [String: String]? = nil) { self.session = session self.method = method self.url = url self.payload = payload + let bundle = Bundle(for: _ObjectiveGuardian.classForCoder()) + var headers = headers ?? [:] + if let version = bundle.infoDictionary?["CFBundleShortVersionString"] as? String, + let clientInfo = try? JSONSerialization.data(withJSONObject: [ + "name": "Guardian.swift", + "version": version + ]) + { + headers["Auth0-Client"] = clientInfo.base64URLEncodedString() + } + + if payload != nil { + headers["Content-Type"] = "application/json" + } self.headers = headers } + /// Makes the operation logs the request payload. By default it will log to console + /// + /// - Parameter logger: function that will log the information provided + /// - Returns: the request itself for chaining + public func log(into logger: @escaping Logger = defaultLogger) -> Request { + self.logger = logger + return self + } + /** Executes the request in a background thread @@ -49,8 +79,9 @@ public class Request: Requestable { */ public func start(callback: @escaping (Result) -> ()) { let request = NSMutableURLRequest(url: url) - request.httpMethod = method - + request.httpMethod = self.method + self.headers.forEach { request.setValue($1, forHTTPHeaderField: $0) } + if let payload = payload { guard let body = try? JSONSerialization.data(withJSONObject: payload, options: []) else { callback(.failure(cause: GuardianError.invalidPayload)) @@ -59,20 +90,20 @@ public class Request: Requestable { request.httpBody = body } - let bundle = Bundle(for: _ObjectiveGuardian.classForCoder()) - if let version = bundle.infoDictionary?["CFBundleShortVersionString"] as? String, - let clientInfo = try? JSONSerialization.data(withJSONObject: [ - "name": "Guardian.swift", - "version": version - ]) - { - request.setValue(clientInfo.base64URLEncodedString(), forHTTPHeaderField: "Auth0-Client") + #if DEBUG + if let logger = self.logger { + logger("\(self.method) \(self.url)") + request.allHTTPHeaderFields?.forEach { logger("\($0): \($1)") } + logger("") + if let payload = self.payload, + let body = try? JSONSerialization.data(withJSONObject: payload, options: .prettyPrinted), + let json = String(data: body, encoding: .utf8) { + logger(json.replacingOccurrences(of: "\\n", with: "\n")) + } } + #endif - request.setValue("application/json", forHTTPHeaderField: "Content-Type") - headers?.forEach { request.setValue($1, forHTTPHeaderField: $0) } - - let task = session.dataTask(with: request as URLRequest) { data, response, error in + let task = self.session.dataTask(with: request as URLRequest) { data, response, error in if let error = error { return callback(.failure(cause: error)) } guard let httpResponse = response as? HTTPURLResponse else { return callback(.failure(cause: GuardianError.invalidResponse)) diff --git a/GuardianApp/NotificationController.swift b/GuardianApp/NotificationController.swift index 0adb861..fe1c9f6 100644 --- a/GuardianApp/NotificationController.swift +++ b/GuardianApp/NotificationController.swift @@ -38,6 +38,7 @@ class NotificationController: UIViewController { Guardian .authentication(forDomain: AppDelegate.guardianDomain, andEnrollment: enrollment) .allow(notification: notification) + .log() .start { result in print(result) switch result { @@ -58,6 +59,7 @@ class NotificationController: UIViewController { Guardian .authentication(forDomain: AppDelegate.guardianDomain, andEnrollment: enrollment) .reject(notification: notification) + .log() .start { result in print(result) switch result { diff --git a/GuardianApp/ViewController.swift b/GuardianApp/ViewController.swift index 7ac8288..56a4838 100644 --- a/GuardianApp/ViewController.swift +++ b/GuardianApp/ViewController.swift @@ -81,6 +81,7 @@ class ViewController: UIViewController, QRCodeReaderViewControllerDelegate { Guardian .enroll(forDomain: AppDelegate.guardianDomain, usingUri: result.value, notificationToken: AppDelegate.pushToken!, keyPair: keyPair) + .log() .start { result in switch result { case .failure(let cause): @@ -114,6 +115,7 @@ class ViewController: UIViewController, QRCodeReaderViewControllerDelegate { .api(forDomain: AppDelegate.guardianDomain) .device(forEnrollmentId: enrollment.id, token: enrollment.deviceToken) .delete() + .log() .start { [unowned self] result in switch result { case .failure(let cause): From 36d68b7c90cd45972520db785ff5b75497feac32 Mon Sep 17 00:00:00 2001 From: Hernan Zalazar Date: Thu, 7 Jun 2018 14:23:39 -0300 Subject: [PATCH 2/5] Make Object conform to Description protocols --- Guardian/Notification.swift | 29 +++++++++++++++++++++++++++++ Guardian/Request.swift | 27 ++++++++++++++------------- 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/Guardian/Notification.swift b/Guardian/Notification.swift index 7a760c4..4756dc8 100644 --- a/Guardian/Notification.swift +++ b/Guardian/Notification.swift @@ -200,6 +200,14 @@ class AuthenticationNotification: NSObject, Notification { self.init(domain: domain, enrollmentId: enrollmentId, transactionToken: token, challenge: challenge, startedAt: startedAt, source: source, location: location) } + + override var description: String { + return "enrollmentId: <\(self.enrollmentId)> txToken: <\(self.transactionToken)> challenge: <\(self.challenge)> startedAt: \(self.startedAt)" + } + + override var debugDescription: String { + return "domain: <\(self.domain)> enrollmentId: <\(self.enrollmentId)> txToken: <\(self.transactionToken)> challenge: <\(self.challenge)> source: <\(String(describing: self.source))> location: <\(String(describing: self.location))> startedAt: \(self.startedAt)" + } } class AuthenticationSource: NSObject, Source { @@ -243,6 +251,18 @@ class AuthenticationSource: NSObject, Source { self.os = os self.browser = browser } + + override var description: String { + let osName = self.os?.name ?? "Unknown OS" + let osVersion = self.os?.version != nil ? "(\(String(describing: self.os?.version))" : "" + let browserName = self.browser?.name ?? "Unknown Browser" + let browserVersion = self.browser?.version != nil ? "(\(String(describing: self.browser?.version))" : "" + return "\(osName) \(osVersion)".trimmingCharacters(in: .whitespaces) + " \(browserName) \(browserVersion)".trimmingCharacters(in: .whitespaces) + } + + override var debugDescription: String { + return self.description + } } class AuthenticationLocation: NSObject, Location { @@ -268,4 +288,13 @@ class AuthenticationLocation: NSObject, Location { longitude = longitudeValue as? NSNumber } } + + override var description: String { + return "\(self.name ?? "Unknown") (\(String(describing: self.latitude)), \(String(describing: self.longitude)))" + } + + override var debugDescription: String { + return self.description + } + } diff --git a/Guardian/Request.swift b/Guardian/Request.swift index f17687d..dee9ddd 100644 --- a/Guardian/Request.swift +++ b/Guardian/Request.swift @@ -31,7 +31,7 @@ let defaultLogger: Logger = { (line: String) in print(line) } /** An asynchronous HTTP request */ -public class Request: Requestable { +public class Request: Requestable, CustomDebugStringConvertible { let session: URLSession let method: String @@ -62,6 +62,18 @@ public class Request: Requestable { self.headers = headers } + public var debugDescription: String { + var description = "\(self.method) \(self.url)\n" + self.headers.forEach { description.append("\($0): \($1)") } + description.append("\n") + if let payload = self.payload, + let body = try? JSONSerialization.data(withJSONObject: payload, options: .prettyPrinted), + let json = String(data: body, encoding: .utf8) { + description.append(json.replacingOccurrences(of: "\\n", with: "\n")) + } + return description + } + /// Makes the operation logs the request payload. By default it will log to console /// /// - Parameter logger: function that will log the information provided @@ -90,18 +102,7 @@ public class Request: Requestable { request.httpBody = body } - #if DEBUG - if let logger = self.logger { - logger("\(self.method) \(self.url)") - request.allHTTPHeaderFields?.forEach { logger("\($0): \($1)") } - logger("") - if let payload = self.payload, - let body = try? JSONSerialization.data(withJSONObject: payload, options: .prettyPrinted), - let json = String(data: body, encoding: .utf8) { - logger(json.replacingOccurrences(of: "\\n", with: "\n")) - } - } - #endif + self.logger?(self.debugDescription) let task = self.session.dataTask(with: request as URLRequest) { data, response, error in if let error = error { return callback(.failure(cause: error)) } From c87b1ad0929a555a9ea61ed7b06e39f52e312cd5 Mon Sep 17 00:00:00 2001 From: Hernan Zalazar Date: Thu, 7 Jun 2018 16:10:49 -0300 Subject: [PATCH 3/5] Introduce hooks for Request/Response/Error events --- Guardian.xcodeproj/project.pbxproj | 4 +++ Guardian/EnrollRequest.swift | 40 ++++++++++++---------- Guardian/Hooks.swift | 28 +++++++++++++++ Guardian/Request.swift | 31 ++++++++++------- Guardian/Requestable.swift | 2 +- GuardianApp/NotificationController.swift | 12 +++---- GuardianApp/ViewController.swift | 12 +++---- GuardianTests/RequestSpec.swift | 43 ++++++++++++++++++++++-- 8 files changed, 128 insertions(+), 44 deletions(-) create mode 100644 Guardian/Hooks.swift diff --git a/Guardian.xcodeproj/project.pbxproj b/Guardian.xcodeproj/project.pbxproj index f52832a..6ef3a4e 100644 --- a/Guardian.xcodeproj/project.pbxproj +++ b/Guardian.xcodeproj/project.pbxproj @@ -70,6 +70,7 @@ 5F20380A1D5E40EE0005D2E2 /* Nimble.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 5F2038001D5E40C70005D2E2 /* Nimble.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 5F7969341DDABDDE006AC7BA /* AuthenticationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F1DB45C1DA4750F00264437 /* AuthenticationSpec.swift */; }; 5F7969371DDAC074006AC7BA /* JWTSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2331BFEC1DD52ED50047F1D4 /* JWTSpec.swift */; }; + 5FB4ECBC20C9A5050050B275 /* Hooks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FB4ECBB20C9A5050050B275 /* Hooks.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -193,6 +194,7 @@ 5F20380F1D5E421F0005D2E2 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 5F2038111D5E42AD0005D2E2 /* .gitignore */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = .gitignore; sourceTree = ""; }; 5F2038181D5E46210005D2E2 /* Gemfile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Gemfile; sourceTree = ""; }; + 5FB4ECBB20C9A5050050B275 /* Hooks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Hooks.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -260,6 +262,7 @@ 2374A9E21D670F5900737F2E /* Request.swift */, 2374A9EE1D672ABD00737F2E /* Result.swift */, 23AD869E1DEE10B500051F41 /* FailedRequest.swift */, + 5FB4ECBB20C9A5050050B275 /* Hooks.swift */, ); name = Networking; sourceTree = ""; @@ -592,6 +595,7 @@ 233F75271D92F27000B8C15C /* Notification.swift in Sources */, 23C67AEE1D81EF5300A38A2E /* Enrollment.swift in Sources */, 235B4B221DA3F51D00CEB5DC /* A0HMAC.m in Sources */, + 5FB4ECBC20C9A5050050B275 /* Hooks.swift in Sources */, 2331C01A1DD5FC6F0047F1D4 /* Data+Base64URL.swift in Sources */, 2331BFEB1DD5260D0047F1D4 /* JWT.swift in Sources */, 231687191DDA07FD00B09526 /* A0SHA.m in Sources */, diff --git a/Guardian/EnrollRequest.swift b/Guardian/EnrollRequest.swift index c424ee4..b4f236e 100644 --- a/Guardian/EnrollRequest.swift +++ b/Guardian/EnrollRequest.swift @@ -38,6 +38,7 @@ public class EnrollRequest: Requestable { private let notificationToken: String private let keyPair: RSAKeyPair private var logger: Logger? = nil + private var request: Request<[String: Any]> init(api: API, enrollmentTicket: String? = nil, enrollmentUri: String? = nil, notificationToken: String, keyPair: RSAKeyPair) { self.api = api @@ -45,13 +46,32 @@ public class EnrollRequest: Requestable { self.enrollmentUri = enrollmentUri self.notificationToken = notificationToken self.keyPair = keyPair + let ticket: String + if let enrollmentTicket = enrollmentTicket { + ticket = enrollmentTicket + } else if let enrollmentUri = enrollmentUri, let parameters = parameters(fromUri: enrollmentUri), let enrollmentTxId = parameters["enrollment_tx_id"] { + ticket = enrollmentTxId + } else { + self.request = FailedRequest(error: GuardianError.invalidEnrollmentUri) + return + } + + self.request = api.enroll(withTicket: ticket, identifier: Enrollment.defaultDeviceIdentifier, name: Enrollment.defaultDeviceName, notificationToken: notificationToken, publicKey: keyPair.publicKey) } - public func log(into logger: @escaping Logger = defaultLogger) -> EnrollRequest { - self.logger = logger + public func on(request: RequestHook? = nil, response: ResponseHook? = nil, error: ErrorHook? = nil) -> EnrollRequest { + let _ = self.request.on(request: request, response: response, error: error) return self } + public var description: String { + return self.request.description + } + + public var debugDescription: String { + return self.request.debugDescription + } + /** Executes the request in a background thread @@ -59,21 +79,7 @@ public class EnrollRequest: Requestable { received */ public func start(callback: @escaping (Result) -> ()) { - let ticket: String - if let enrollmentTicket = enrollmentTicket { - ticket = enrollmentTicket - } else if let enrollmentUri = enrollmentUri, let parameters = parameters(fromUri: enrollmentUri), let enrollmentTxId = parameters["enrollment_tx_id"] { - ticket = enrollmentTxId - } else { - return callback(.failure(cause: GuardianError.invalidEnrollmentUri)) - } - - var request = api.enroll(withTicket: ticket, identifier: Enrollment.defaultDeviceIdentifier, name: Enrollment.defaultDeviceName, notificationToken: notificationToken, publicKey: keyPair.publicKey) - - if let logger = self.logger { - request = request.log(into: logger) - } - request.start { result in + self.request.start { result in switch result { case .failure(let cause): callback(.failure(cause: cause)) diff --git a/Guardian/Hooks.swift b/Guardian/Hooks.swift new file mode 100644 index 0000000..27a69ed --- /dev/null +++ b/Guardian/Hooks.swift @@ -0,0 +1,28 @@ +// +// Hooks.swift +// Guardian +// +// Created by Hernan Zalazar on 07/06/2018. +// Copyright © 2018 Auth0. All rights reserved. +// + +import Foundation + +/// Hook that will be called before request is sent +public typealias RequestHook = (URLRequest) -> () +/// Hook that will be called when a response is received (any status code) along with data (if any) +public typealias ResponseHook = (HTTPURLResponse, Data?) -> () +/// Hook that will be called when request fails with error +public typealias ErrorHook = (Error) -> () + +struct Hooks { + let request: RequestHook? + let response: ResponseHook? + let error: ErrorHook? + + init(request: RequestHook? = nil, response: ResponseHook? = nil, error: ErrorHook? = nil) { + self.request = request + self.response = response + self.error = error + } +} diff --git a/Guardian/Request.swift b/Guardian/Request.swift index dee9ddd..4367913 100644 --- a/Guardian/Request.swift +++ b/Guardian/Request.swift @@ -31,20 +31,21 @@ let defaultLogger: Logger = { (line: String) in print(line) } /** An asynchronous HTTP request */ -public class Request: Requestable, CustomDebugStringConvertible { +public class Request: Requestable { let session: URLSession let method: String let url: URL let payload: [String: Any]? let headers: [String: String] - var logger: Logger? = nil + var hooks: Hooks init(session: URLSession, method: String, url: URL, payload: [String: Any]? = nil, headers: [String: String]? = nil) { self.session = session self.method = method self.url = url self.payload = payload + self.hooks = Hooks() let bundle = Bundle(for: _ObjectiveGuardian.classForCoder()) var headers = headers ?? [:] if let version = bundle.infoDictionary?["CFBundleShortVersionString"] as? String, @@ -62,6 +63,10 @@ public class Request: Requestable, CustomDebugStringConvertible { self.headers = headers } + public var description: String { + return "\(self.method) \(self.url)" + } + public var debugDescription: String { var description = "\(self.method) \(self.url)\n" self.headers.forEach { description.append("\($0): \($1)") } @@ -74,12 +79,8 @@ public class Request: Requestable, CustomDebugStringConvertible { return description } - /// Makes the operation logs the request payload. By default it will log to console - /// - /// - Parameter logger: function that will log the information provided - /// - Returns: the request itself for chaining - public func log(into logger: @escaping Logger = defaultLogger) -> Request { - self.logger = logger + public func on(request: RequestHook? = nil, response: ResponseHook? = nil, error: ErrorHook? = nil) -> Request { + self.hooks = Hooks(request: request ?? self.hooks.request, response: response ?? self.hooks.response, error: error ?? self.hooks.error) return self } @@ -90,7 +91,7 @@ public class Request: Requestable, CustomDebugStringConvertible { received */ public func start(callback: @escaping (Result) -> ()) { - let request = NSMutableURLRequest(url: url) + var request = URLRequest(url: url) request.httpMethod = self.method self.headers.forEach { request.setValue($1, forHTTPHeaderField: $0) } @@ -102,13 +103,19 @@ public class Request: Requestable, CustomDebugStringConvertible { request.httpBody = body } - self.logger?(self.debugDescription) + self.hooks.request?(request) let task = self.session.dataTask(with: request as URLRequest) { data, response, error in - if let error = error { return callback(.failure(cause: error)) } + if let error = error { + self.hooks.error?(error) + return callback(.failure(cause: error)) + } guard let httpResponse = response as? HTTPURLResponse else { - return callback(.failure(cause: GuardianError.invalidResponse)) + let cause = GuardianError.invalidResponse + self.hooks.error?(cause) + return callback(.failure(cause: cause)) } + self.hooks.response?(httpResponse, data) guard (200..<300).contains(httpResponse.statusCode) else { guard let info: [String: Any] = json(data) else { return callback(.failure(cause: GuardianError.invalidResponse(withStatus: httpResponse.statusCode))) diff --git a/Guardian/Requestable.swift b/Guardian/Requestable.swift index d7bdeec..35ed5e5 100644 --- a/Guardian/Requestable.swift +++ b/Guardian/Requestable.swift @@ -22,7 +22,7 @@ import Foundation -protocol Requestable { +protocol Requestable: CustomDebugStringConvertible, CustomStringConvertible { associatedtype T diff --git a/GuardianApp/NotificationController.swift b/GuardianApp/NotificationController.swift index fe1c9f6..a13cea5 100644 --- a/GuardianApp/NotificationController.swift +++ b/GuardianApp/NotificationController.swift @@ -35,11 +35,11 @@ class NotificationController: UIViewController { guard let notification = notification, let enrollment = AppDelegate.enrollment else { return self.dismiss(animated: true, completion: nil) } - Guardian + let request = Guardian .authentication(forDomain: AppDelegate.guardianDomain, andEnrollment: enrollment) .allow(notification: notification) - .log() - .start { result in + debugPrint(request) + request.start { result in print(result) switch result { case .success: @@ -56,11 +56,11 @@ class NotificationController: UIViewController { guard let notification = notification, let enrollment = AppDelegate.enrollment else { return self.dismiss(animated: true, completion: nil) } - Guardian + let request = Guardian .authentication(forDomain: AppDelegate.guardianDomain, andEnrollment: enrollment) .reject(notification: notification) - .log() - .start { result in + debugPrint(request) + request.start { result in print(result) switch result { case .success: diff --git a/GuardianApp/ViewController.swift b/GuardianApp/ViewController.swift index 56a4838..53c1cad 100644 --- a/GuardianApp/ViewController.swift +++ b/GuardianApp/ViewController.swift @@ -79,10 +79,10 @@ class ViewController: UIViewController, QRCodeReaderViewControllerDelegate { return } - Guardian + let request = Guardian .enroll(forDomain: AppDelegate.guardianDomain, usingUri: result.value, notificationToken: AppDelegate.pushToken!, keyPair: keyPair) - .log() - .start { result in + debugPrint(request) + request.start { result in switch result { case .failure(let cause): self.showError("Enroll failed", cause) @@ -111,12 +111,12 @@ class ViewController: UIViewController, QRCodeReaderViewControllerDelegate { @IBAction func unenrollAction(_ sender: AnyObject) { if let enrollment = AppDelegate.enrollment { - Guardian + let request = Guardian .api(forDomain: AppDelegate.guardianDomain) .device(forEnrollmentId: enrollment.id, token: enrollment.deviceToken) .delete() - .log() - .start { [unowned self] result in + debugPrint(request) + request.start { [unowned self] result in switch result { case .failure(let cause): self.showError("Unenroll failed", cause) diff --git a/GuardianTests/RequestSpec.swift b/GuardianTests/RequestSpec.swift index d0cd916..eebd035 100644 --- a/GuardianTests/RequestSpec.swift +++ b/GuardianTests/RequestSpec.swift @@ -81,7 +81,7 @@ class RequestSpec: QuickSpec { it("for Content-Type") { waitUntil(timeout: Timeout) { done in let session = MockNSURLSession(data: nil, response: nil, error: nil) - Request(session: session, method: "PATCH", url: ValidURL) + Request(session: session, method: "PATCH", url: ValidURL, payload: ["key": "value"]) .start { _ in let request = session.a0_request! expect(request.value(forHTTPHeaderField: "Content-Type")).to(equal("application/json")) @@ -125,7 +125,46 @@ class RequestSpec: QuickSpec { } } } - + + describe("hooks") { + + it("should call request hook") { + waitUntil(timeout: Timeout) { done in + let session = MockNSURLSession(data: nil, response: nil, error: NSError(domain: "auth0.com", code: ErrorCode, userInfo: nil)) + Request(session: session, method: ValidMethod, url: ValidURL) + .on(request: { request in + done() + }) + .start { _ in } + } + } + + it("should call response hook") { + waitUntil(timeout: Timeout) { done in + let messageData = "Success!!".data(using: .utf8) + let httpResponse = HTTPURLResponse(url: ValidURL, statusCode: 200, httpVersion: nil, headerFields: nil) + let session = MockNSURLSession(data: messageData, response: httpResponse, error: nil) + Request(session: session, method: ValidMethod, url: ValidURL) + .on(response: { response, data in + expect(response).to(be(httpResponse)) + done() + }) + .start { _ in } + } + } + + it("should call error hook") { + waitUntil(timeout: Timeout) { done in + let session = MockNSURLSession(data: nil, response: nil, error: NSError(domain: "auth0.com", code: ErrorCode, userInfo: nil)) + Request(session: session, method: ValidMethod, url: ValidURL) + .on(error: { error in + done() + }) + .start { _ in } + } + } + } + describe("callback") { it("should fail with forwarded error when there is an NSError") { From 971d2a1c13ca71666f554d61366da7aed666f45b Mon Sep 17 00:00:00 2001 From: Hernan Zalazar Date: Thu, 7 Jun 2018 16:36:02 -0300 Subject: [PATCH 4/5] Add doc strings --- Guardian/EnrollRequest.swift | 10 ++++++++++ Guardian/Request.swift | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/Guardian/EnrollRequest.swift b/Guardian/EnrollRequest.swift index b4f236e..dd84125 100644 --- a/Guardian/EnrollRequest.swift +++ b/Guardian/EnrollRequest.swift @@ -59,6 +59,16 @@ public class EnrollRequest: Requestable { self.request = api.enroll(withTicket: ticket, identifier: Enrollment.defaultDeviceIdentifier, name: Enrollment.defaultDeviceName, notificationToken: notificationToken, publicKey: keyPair.publicKey) } + /// Registers hooks to be called on specific events: + /// * on request being sent + /// * on response recieved (successful or not) + /// * on network error + /// + /// - Parameters: + /// - request: closure called with request information + /// - response: closure called with response and data + /// - error: closure called with network error + /// - Returns: itself for chaining public func on(request: RequestHook? = nil, response: ResponseHook? = nil, error: ErrorHook? = nil) -> EnrollRequest { let _ = self.request.on(request: request, response: response, error: error) return self diff --git a/Guardian/Request.swift b/Guardian/Request.swift index 4367913..122fc7f 100644 --- a/Guardian/Request.swift +++ b/Guardian/Request.swift @@ -79,6 +79,16 @@ public class Request: Requestable { return description } + /// Registers hooks to be called on specific events: + /// * on request being sent + /// * on response recieved (successful or not) + /// * on network error + /// + /// - Parameters: + /// - request: closure called with request information + /// - response: closure called with response and data + /// - error: closure called with network error + /// - Returns: itself for chaining public func on(request: RequestHook? = nil, response: ResponseHook? = nil, error: ErrorHook? = nil) -> Request { self.hooks = Hooks(request: request ?? self.hooks.request, response: response ?? self.hooks.response, error: error ?? self.hooks.error) return self From 97945752548f69e0e6839933809c692340bb0a61 Mon Sep 17 00:00:00 2001 From: Hernan Zalazar Date: Thu, 7 Jun 2018 16:40:28 -0300 Subject: [PATCH 5/5] Remove temp implementation --- Guardian/EnrollRequest.swift | 1 - Guardian/Request.swift | 6 ------ 2 files changed, 7 deletions(-) diff --git a/Guardian/EnrollRequest.swift b/Guardian/EnrollRequest.swift index dd84125..b68a128 100644 --- a/Guardian/EnrollRequest.swift +++ b/Guardian/EnrollRequest.swift @@ -37,7 +37,6 @@ public class EnrollRequest: Requestable { private let enrollmentUri: String? private let notificationToken: String private let keyPair: RSAKeyPair - private var logger: Logger? = nil private var request: Request<[String: Any]> init(api: API, enrollmentTicket: String? = nil, enrollmentUri: String? = nil, notificationToken: String, keyPair: RSAKeyPair) { diff --git a/Guardian/Request.swift b/Guardian/Request.swift index 122fc7f..949e423 100644 --- a/Guardian/Request.swift +++ b/Guardian/Request.swift @@ -22,12 +22,6 @@ import Foundation -/// Definition of function to log a string -public typealias Logger = (String) -> () - -/// Default logger using Swift print function -let defaultLogger: Logger = { (line: String) in print(line) } - /** An asynchronous HTTP request */