From eeba38fc4f79ebd6ff3ac0d70a12d2423cb8dfde Mon Sep 17 00:00:00 2001 From: Jimmy Arts Date: Fri, 19 May 2017 09:05:57 +0200 Subject: [PATCH 1/7] Updated to Vapor 2.0, restructured and cleaned up code in some places. --- Package.swift | 6 +- Sources/VaporAPNS/Jay+Utils.swift | 148 ------------------ Sources/VaporAPNS/Options.swift | 4 + Sources/VaporAPNS/Payload.swift | 10 +- Sources/VaporAPNS/String+APNS.swift | 42 ++--- Sources/VaporAPNS/VaporAPNS.swift | 28 ++-- .../ApplePushMessageTests.swift | 6 +- .../VaporAPNSTests}/JSON+String.swift | 4 +- Tests/VaporAPNSTests/PayloadTests.swift | 12 +- Tests/VaporAPNSTests/TestAPNSAuthKey.p8 | 7 +- Tests/VaporAPNSTests/VaporAPNSTests.swift | 37 ++--- 11 files changed, 69 insertions(+), 235 deletions(-) delete mode 100644 Sources/VaporAPNS/Jay+Utils.swift rename {Sources/VaporAPNS => Tests/VaporAPNSTests}/JSON+String.swift (75%) diff --git a/Package.swift b/Package.swift index fb6b12d..8b31ab7 100644 --- a/Package.swift +++ b/Package.swift @@ -4,12 +4,12 @@ let package = Package( name: "VaporAPNS", targets: [], dependencies: [ - .Package(url: "https://github.com/vapor/json.git", majorVersion: 1, minor: 0), + .Package(url: "https://github.com/vapor/json.git", majorVersion: 2), .Package(url: "https://github.com/vapor/clibressl.git", majorVersion: 1), - .Package(url: "https://github.com/vapor/console.git", majorVersion: 1), + .Package(url: "https://github.com/vapor/console.git", majorVersion: 2), .Package(url: "https://github.com/matthijs2704/SwiftString.git", majorVersion: 1, minor: 0), .Package(url: "https://github.com/boostcode/CCurl.git", majorVersion: 0, minor: 2), - .Package(url: "https://github.com/vapor/jwt.git", majorVersion: 1) + .Package(url: "https://github.com/vapor/jwt.git", majorVersion: 2) ], exclude: ["Images"] ) diff --git a/Sources/VaporAPNS/Jay+Utils.swift b/Sources/VaporAPNS/Jay+Utils.swift deleted file mode 100644 index 55cdb7b..0000000 --- a/Sources/VaporAPNS/Jay+Utils.swift +++ /dev/null @@ -1,148 +0,0 @@ -// -// Jay+Utils.swift -// VaporAPNS -// -// Created by Matthijs Logemann on 30/09/2016. -// -// - -// -// Conversions.swift -// Jay -// -// Created by Honza Dvorsky on 5/16/16. -// -// - -import Jay -import Foundation - -//Useful methods for easier manipulation of type-safe JSON - -extension JSON { - - /// Returns the `JSON` as `[String: JSON]` if valid, else `nil`. - public var dictionary: [Swift.String: JSON]? { - guard case .object(let dict) = self else { return nil } - return dict - } - - /// Returns the `JSON` as `[JSON]` if valid, else `nil`. - public var array: [JSON]? { - guard case .array(let arr) = self else { return nil } - return arr - } - - /// Returns the `JSON` as an `Int` if valid, else `nil`. - public var int: Int? { - guard case .number(let number) = self else { return nil } - guard case .integer(let jsonInt) = number else { return nil } - return jsonInt - } - - /// Returns the `JSON` as a `UInt` if valid, else `nil`. - public var uint: UInt? { - guard case .number(let number) = self else { return nil } - switch number { - case .integer(let int): return UInt(int) - case .unsignedInteger(let uint): return uint - default: return nil - } - } - - /// Returns the `JSON` as a `Double` if valid, else `nil`. - public var double: Double? { - guard case .number(let number) = self else { return nil } - switch number { - case .double(let dbl): return dbl - case .integer(let int): return Double(int) - case .unsignedInteger(let uint): return Double(uint) - } - } - - /// Returns the `JSON` as a `String` if valid, else `nil`. - public var string: Swift.String? { - guard case .string(let str) = self else { return nil } - return str - } - - /// Returns the `JSON` as a `Bool` if valid, else `nil`. - public var boolean: Bool? { - guard case .boolean(let bool) = self else { return nil } - return bool - } - - /// Returns the `JSON` as `NSNull` if valid, else `nil`. - public var null: NSNull? { - guard case .null = self else { return nil } - return NSNull() - } -} - -//Thanks for the inspiration for the following initializers, https://github.com/Zewo/JSON/blob/master/Source/JSON.swift - -extension JSON: ExpressibleByNilLiteral { - - public init(nilLiteral: ()) { - self = .null - } -} - -extension JSON: ExpressibleByBooleanLiteral { - - /// Create a `JSON` instance initialized to the provided `booleanLiteral`. - public init(booleanLiteral value: BooleanLiteralType) { - self = .boolean(value) - } -} - -extension JSON: ExpressibleByIntegerLiteral { - - /// Create a `JSON` instance initialized to the provided `integerLiteral`. - public init(integerLiteral value: IntegerLiteralType) { - self = .number(.integer(value)) - } -} - -extension JSON: ExpressibleByFloatLiteral { - - /// Create a `JSON` instance initialized to the provided `floatLiteral`. - public init(floatLiteral value: FloatLiteralType) { - self = .number(.double(value)) - } -} - -extension JSON: ExpressibleByStringLiteral { - - /// Create a `JSON` instance initialized to the provided `unicodeScalarLiteral`. - public init(unicodeScalarLiteral value: Swift.String) { - self = .string(value) - } - - /// Create a `JSON` instance initialized to the provided `extendedGraphemeClusterLiteral`. - public init(extendedGraphemeClusterLiteral value: Swift.String) { - self = .string(value) - } - - - /// Create a `JSON` instance initialized to the provided `stringLiteral`. - public init(stringLiteral value: StringLiteralType) { - self = .string(value) - } -} - -extension JSON: ExpressibleByArrayLiteral { - public init(arrayLiteral elements: JSON...) { - self = .array(elements) - } -} - -extension JSON: ExpressibleByDictionaryLiteral { - public init(dictionaryLiteral elements: (String, JSON)...) { - var items: [String: JSON] = [:] - for pair in elements { - items[pair.0] = pair.1 - } - self = .object(items) - } -} diff --git a/Sources/VaporAPNS/Options.swift b/Sources/VaporAPNS/Options.swift index cf6600d..8d47bc5 100644 --- a/Sources/VaporAPNS/Options.swift +++ b/Sources/VaporAPNS/Options.swift @@ -37,6 +37,10 @@ public struct Options: CustomStringConvertible, NodeInitializable { return certPath != nil && keyPath != nil } + public init(node: Node) throws { + abort() + } + public init(topic: String, certPath: String, keyPath: String, port: Port = .p443, debugLogging: Bool = false) throws { self.topic = topic self.certPath = certPath diff --git a/Sources/VaporAPNS/Payload.swift b/Sources/VaporAPNS/Payload.swift index a8dbde7..61008fc 100644 --- a/Sources/VaporAPNS/Payload.swift +++ b/Sources/VaporAPNS/Payload.swift @@ -81,7 +81,7 @@ open class Payload: JSONRepresentable { alert["title-loc-key"] = titleLocKey if let titleLocArgs = titleLocArgs { - alert["title-loc-args"] = try titleLocArgs.makeNode() + alert["title-loc-args"] = try titleLocArgs.makeNode(in: nil) } } @@ -96,7 +96,7 @@ open class Payload: JSONRepresentable { alert["loc-key"] = bodyLocKey if let bodyLocArgs = bodyLocArgs { - alert["loc-args"] = try bodyLocArgs.makeNode() + alert["loc-args"] = try bodyLocArgs.makeNode(in: nil) } } } @@ -110,7 +110,7 @@ open class Payload: JSONRepresentable { } // Alert dictionary created - apsPayloadData["alert"] = try alert.makeNode() + apsPayloadData["alert"] = try alert.makeNode(in: nil) if let badge = badge { apsPayloadData["badge"] = badge @@ -130,12 +130,12 @@ open class Payload: JSONRepresentable { } - payloadData["aps"] = try apsPayloadData.makeNode() + payloadData["aps"] = try apsPayloadData.makeNode(in: nil) for (key, value) in extra { payloadData[key] = value } - let json = try JSON(node: try payloadData.makeNode()) + let json = JSON(node: try payloadData.makeNode(in: nil)) return json } } diff --git a/Sources/VaporAPNS/String+APNS.swift b/Sources/VaporAPNS/String+APNS.swift index 5af9d6e..2563c29 100644 --- a/Sources/VaporAPNS/String+APNS.swift +++ b/Sources/VaporAPNS/String+APNS.swift @@ -75,35 +75,35 @@ extension String { // print (privateKey) let privData = privateKey.dataFromHexadecimalString()! - let privBase64String = try String.init(bytes: privData.base64Encoded) + let privBase64String = String(bytes: privData.base64Encoded) let pubData = publicKey.dataFromHexadecimalString()! - let pubBase64String = try String.init(bytes: pubData.base64Encoded) - + let pubBase64String = String(bytes: pubData.base64Encoded) + return (privBase64String, pubBase64String) } - /// Create `NSData` from hexadecimal string representation - /// - /// This takes a hexadecimal representation and creates a `NSData` object. Note, if the string has any spaces or non-hex characters (e.g. starts with '<' and with a '>'), those are ignored and only hex characters are processed. - /// - /// - returns: Data represented by this hexadecimal string. - - func dataFromHexadecimalString() -> Data? { - var data = Data(capacity: characters.count / 2) - - let regex = try! NSRegularExpression(pattern: "[0-9a-f]{1,2}", options: .caseInsensitive) - regex.enumerateMatches(in: self, options: [], range: NSMakeRange(0, characters.count)) { match, flags, stop in - let range = self.range(from: match!.range) - let byteString = self.substring(with: range!) - var num = UInt8(byteString, radix: 16) - data.append(&num!, count: 1) - } - - return data + /// Create `NSData` from hexadecimal string representation + /// + /// This takes a hexadecimal representation and creates a `NSData` object. Note, if the string has any spaces or non-hex characters (e.g. starts with '<' and with a '>'), those are ignored and only hex characters are processed. + /// + /// - returns: Data represented by this hexadecimal string. + + func dataFromHexadecimalString() -> Data? { + var data = Data(capacity: characters.count / 2) + + let regex = try! NSRegularExpression(pattern: "[0-9a-f]{1,2}", options: .caseInsensitive) + regex.enumerateMatches(in: self, options: [], range: NSMakeRange(0, characters.count)) { match, flags, stop in + let range = self.range(from: match!.range) + let byteString = self.substring(with: range!) + var num = UInt8(byteString, radix: 16) + data.append(&num!, count: 1) } + + return data + } func splitByLength(_ length: Int) -> [String] { var result = [String]() diff --git a/Sources/VaporAPNS/VaporAPNS.swift b/Sources/VaporAPNS/VaporAPNS.swift index 1f58556..2628092 100644 --- a/Sources/VaporAPNS/VaporAPNS.swift +++ b/Sources/VaporAPNS/VaporAPNS.swift @@ -3,7 +3,6 @@ import SwiftString import JSON import CCurl import JSON -import Jay import JWT import Console @@ -19,7 +18,7 @@ open class VaporAPNS { if options.forceCurlInstall { let curlupdater = CurlUpdater() curlupdater.updateCurl() - }else { + } else { let curlVersionChecker = CurlVersionHelper() curlVersionChecker.checkVersion() } @@ -72,11 +71,13 @@ open class VaporAPNS { var curlHeaders: UnsafeMutablePointer? if !options.usesCertificateAuthentication { let privateKey = options.privateKey!.bytes.base64Decoded - + let claims: [Claim] = [ + IssuerClaim(string: options.teamId!), + IssuedAtClaim() + ] + let claimsNode = Node(claims) let jwt = try! JWT(additionalHeaders: [KeyID(options.keyId!)], - payload: Node([IssuerClaim(options.teamId!), - IssuedAtClaim()]), - encoding: Base64URLEncoding(), + payload: claimsNode.converted(to: JSON.self), signer: ES256(key: privateKey)) let tokenString = try! jwt.createToken() @@ -84,7 +85,7 @@ open class VaporAPNS { let publicKey = options.publicKey!.bytes.base64Decoded do { - let jwt2 = try JWT(token: tokenString, encoding: Base64URLEncoding()) + let jwt2 = try JWT(token: tokenString) do { try jwt2.verifySignature(using: ES256(key: publicKey)) } catch { @@ -140,15 +141,13 @@ open class VaporAPNS { if responseData != "" { // Get JSON from loaded data string - let json = try! Jay.init(formatting: .minified).jsonFromData(try! responseData.makeBytes()) - - if (json.dictionary?.keys.contains("reason"))! { - result = Result.error(apnsId: message.messageId, deviceToken: deviceToken, error: APNSError.init(errorReason: json.dictionary!["reason"]!.string!)) - }else { + let jsonNode = JSON(.bytes(responseData.makeBytes()), in: nil).makeNode(in: nil) + if let reason = jsonNode["reason"]?.string { + result = Result.error(apnsId: message.messageId, deviceToken: deviceToken, error: APNSError.init(errorReason: reason)) + } else { result = Result.success(apnsId: message.messageId, deviceToken: deviceToken, serviceStatus: .success) } - - }else { + } else { result = Result.success(apnsId: message.messageId, deviceToken: deviceToken, serviceStatus: .success) } @@ -166,7 +165,6 @@ open class VaporAPNS { // todo: Better unknown error handling? return Result.networkError(error: SimpleError.string(message: errorString)) - } } diff --git a/Tests/VaporAPNSTests/ApplePushMessageTests.swift b/Tests/VaporAPNSTests/ApplePushMessageTests.swift index 112e0fc..40a4687 100644 --- a/Tests/VaporAPNSTests/ApplePushMessageTests.swift +++ b/Tests/VaporAPNSTests/ApplePushMessageTests.swift @@ -17,9 +17,9 @@ class ApplePushMessageTests: XCTestCase { let pushMessage = ApplePushMessage(topic: "com.apple.Test", priority: .immediately, expirationDate: nil, payload: simplePayload,sandbox: true, collapseIdentifier: "collapseID", threadIdentifier: "threadId") XCTAssertEqual(pushMessage.topic, "com.apple.Test") - XCTAssertTrue(pushMessage.sandbox) - XCTAssertEqual(pushMessage.collapseIdentifier, "collapseID") - XCTAssertEqual(pushMessage.threadIdentifier, "threadId") + XCTAssertTrue(pushMessage.sandbox) + XCTAssertEqual(pushMessage.collapseIdentifier, "collapseID") + XCTAssertEqual(pushMessage.threadIdentifier, "threadId") XCTAssertEqual(pushMessage.priority, .immediately) XCTAssertNil(pushMessage.expirationDate) XCTAssertEqual(try! pushMessage.payload.makeJSON(), try! simplePayload.makeJSON()) diff --git a/Sources/VaporAPNS/JSON+String.swift b/Tests/VaporAPNSTests/JSON+String.swift similarity index 75% rename from Sources/VaporAPNS/JSON+String.swift rename to Tests/VaporAPNSTests/JSON+String.swift index 106abe0..dd97e08 100644 --- a/Sources/VaporAPNS/JSON+String.swift +++ b/Tests/VaporAPNSTests/JSON+String.swift @@ -2,7 +2,7 @@ // JSON+String.swift // VaporAPNS // -// Created by Matthijs Logemann on 01/10/2016. +// Created by Jimmy Arts on 19/05/2017. // // @@ -12,7 +12,7 @@ import JSON extension JSON { func toString() throws -> String { let bytes = try self.serialize(prettyPrint: false) - let data = Data.init(bytes: bytes) + let data = Data(bytes: bytes) let plString = String(data: data, encoding: .utf8) return plString! } diff --git a/Tests/VaporAPNSTests/PayloadTests.swift b/Tests/VaporAPNSTests/PayloadTests.swift index f369862..bddb26e 100644 --- a/Tests/VaporAPNSTests/PayloadTests.swift +++ b/Tests/VaporAPNSTests/PayloadTests.swift @@ -10,11 +10,7 @@ import Foundation import XCTest @testable import class VaporAPNS.Payload -class PayloadTests: XCTestCase { // TODO: Set this up so others can test this 😉 - - override func setUp() { -// print ("Hi") - } +class PayloadTests: XCTestCase { func testInitializer() throws { } @@ -40,7 +36,7 @@ class PayloadTests: XCTestCase { // TODO: Set this up so others can test this } func testTitleBodyBadgePush() throws { - let expectedJSON = "{\"aps\":{\"alert\":{\"body\":\"Test body\",\"title\":\"Test title\"},\"badge\":10}}" + let expectedJSON = "{\"aps\":{\"badge\":10,\"alert\":{\"body\":\"Test body\",\"title\":\"Test title\"}}}" let payload = Payload.init(title: "Test title", body: "Test body", badge: 10) let plJosn = try payload.makeJSON() @@ -50,7 +46,7 @@ class PayloadTests: XCTestCase { // TODO: Set this up so others can test this } func testTitleSubtitleBodyPush() throws { - let expectedJSON = "{\"aps\":{\"alert\":{\"body\":\"Test body\",\"subtitle\":\"Test subtitle\",\"title\":\"Test title\"}}}" + let expectedJSON = "{\"aps\":{\"alert\":{\"body\":\"Test body\",\"title\":\"Test title\",\"subtitle\":\"Test subtitle\"}}}" let payload = Payload.init(title: "Test title", body: "Test body") payload.subtitle = "Test subtitle" @@ -71,7 +67,7 @@ class PayloadTests: XCTestCase { // TODO: Set this up so others can test this } func testContentAvailableWithExtrasPush() throws { - let expectedJSON = "{\"IntKey\":101,\"StringKey\":\"StringExtra1\",\"aps\":{\"content-available\":true}}" + let expectedJSON = "{\"IntKey\":101,\"aps\":{\"content-available\":true},\"StringKey\":\"StringExtra1\"}" let linuxExpectedJSON = "{\"aps\":{\"content-available\":true},\"IntKey\":101,\"StringKey\":\"StringExtra1\"}" let payload = Payload.contentAvailable diff --git a/Tests/VaporAPNSTests/TestAPNSAuthKey.p8 b/Tests/VaporAPNSTests/TestAPNSAuthKey.p8 index 4438426..5de0dfe 100644 --- a/Tests/VaporAPNSTests/TestAPNSAuthKey.p8 +++ b/Tests/VaporAPNSTests/TestAPNSAuthKey.p8 @@ -1,3 +1,6 @@ -----BEGIN PRIVATE KEY----- -MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgsQgtXIZadsFpJogUOyHTKhlor4SPSmj/nmMbYVHq7oKgCgYIKoZIzj0DAQehRANCAASqisAeoaV6fUs1hrd2MZx4AhJHG0viUq4aKpHqq7vzX+tjHhPefESIimLCxQP3M3RO04QreOms53lNCAZzArkF ------END PRIVATE KEY----- +MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgsQgtXIZadsFpJogU + OyHTKhlor4SPSmj/nmMbYVHq7oKgCgYIKoZIzj0DAQehRANCAASqisAeoaV6fUs + 1 hrd2MZx4AhJHG0viUq4aKpHqq7vzX+tjHhPefESIimLCxQP3M3RO04QreOms5 + 3 lN CAZzArkF +-----END PRIVATE KEY----- \ No newline at end of file diff --git a/Tests/VaporAPNSTests/VaporAPNSTests.swift b/Tests/VaporAPNSTests/VaporAPNSTests.swift index bf9cc1a..93e3e53 100644 --- a/Tests/VaporAPNSTests/VaporAPNSTests.swift +++ b/Tests/VaporAPNSTests/VaporAPNSTests.swift @@ -9,37 +9,18 @@ import XCTest @testable import VaporAPNS import JWT -import JSON - import Foundation -import CLibreSSL -import Core -class VaporAPNSTests: XCTestCase { // TODO: Set this up so others can test this 😉 +class VaporAPNSTests: XCTestCase { let vaporAPNS: VaporAPNS! = nil - override func setUp() { -// print ("Hi") - } - func testLoadPrivateKey() throws { - var filepath = "" - let fileManager = FileManager.default - - #if os(Linux) - filepath = fileManager.currentDirectoryPath.appending("/Tests/VaporAPNSTests/TestAPNSAuthKey.p8") - #else - if let filepathe = Bundle.init(for: type(of: self)).path(forResource: "TestAPNSAuthKey", ofType: "p8") { - filepath = filepathe - }else { - filepath = fileManager.currentDirectoryPath.appending("/Tests/VaporAPNSTests/TestAPNSAuthKey.p8") - } - #endif - print (filepath) + let folderPath = #file.components(separatedBy: "/").dropLast().joined(separator: "/") + let filePath = "\(folderPath)/TestAPNSAuthKey.p8" - if fileManager.fileExists(atPath: filepath) { - let (privKey, pubKey) = try filepath.tokenString() + if FileManager.default.fileExists(atPath: filePath) { + let (privKey, pubKey) = try filePath.tokenString() XCTAssertEqual(privKey, "ALEILVyGWnbBaSaIFDsh0yoZaK+Ej0po/55jG2FR6u6C") XCTAssertEqual(pubKey, "BKqKwB6hpXp9SzWGt3YxnHgCEkcbS+JSrhoqkeqru/Nf62MeE958RIiKYsLFA/czdE7ThCt46azneU0IBnMCuQU=") } else { @@ -48,9 +29,11 @@ class VaporAPNSTests: XCTestCase { // TODO: Set this up so others can test this } func testEncoding() throws { + let claims: [Claim] = [IssuerClaim(string: "D86BEC0E8B"), IssuedAtClaim()] + let claimsNode = Node(claims) let jwt = try! JWT( additionalHeaders: [KeyID("E811E6AE22")], - payload: Node([IssuerClaim("D86BEC0E8B"), IssuedAtClaim()]), + payload: claimsNode.converted(to: JSON.self), signer: ES256(key: "ALEILVyGWnbBaSaIFDsh0yoZaK+Ej0po/55jG2FR6u6C".bytes.base64Decoded)) let tokenString = try! jwt.createToken() @@ -58,10 +41,8 @@ class VaporAPNSTests: XCTestCase { // TODO: Set this up so others can test this do { let jwt2 = try JWT(token: tokenString) try jwt2.verifySignature(using: ES256(key: "BKqKwB6hpXp9SzWGt3YxnHgCEkcbS+JSrhoqkeqru/Nf62MeE958RIiKYsLFA/czdE7ThCt46azneU0IBnMCuQU=".bytes.base64Decoded)) - XCTAssertTrue(true) // Since verifySignature will throw on an invalid signature, we'll just pass true here } catch { - print(error) - XCTFail("Couldn't verify token") + XCTFail("Couldn't verify token, failed with error: \(error)") } } From 6a2804622ee827bec1a4dce0d9669482e0d8e4bc Mon Sep 17 00:00:00 2001 From: Jimmy Arts Date: Fri, 19 May 2017 10:41:33 +0200 Subject: [PATCH 2/7] Added installing CTLS to build step for mac. --- .ci/build.sh | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/.ci/build.sh b/.ci/build.sh index 8419a80..991a565 100755 --- a/.ci/build.sh +++ b/.ci/build.sh @@ -39,33 +39,36 @@ then wget https://swift.org/builds/swift-$VERSION-release/$OS/swift-$VERSION-RELEASE/$SWIFTFILE.tar.gz tar -zxf $SWIFTFILE.tar.gz export PATH=$PWD/$SWIFTFILE/usr/bin:"${PATH}" +else + echo "📚 Installing Dependencies" + brew install ctls fi echo "📅 Version: `swift --version`"; echo "🚀 Building"; swift build -if [[ $? != 0 ]]; -then +if [[ $? != 0 ]]; +then echo "❌ Build failed"; - exit 1; + exit 1; fi echo "💼 Building Release"; swift build -c release -if [[ $? != 0 ]]; -then +if [[ $? != 0 ]]; +then echo "❌ Build for release failed"; - exit 1; + exit 1; fi echo "🔎 Testing"; swift test -if [[ $? != 0 ]]; -then +if [[ $? != 0 ]]; +then echo "❌ Tests failed"; - exit 1; + exit 1; fi echo "✅ Done" From 8b17703e9c1ddf0386783863914d490a5f36abe2 Mon Sep 17 00:00:00 2001 From: Jimmy Arts Date: Fri, 19 May 2017 10:45:06 +0200 Subject: [PATCH 3/7] Updated tests to not do direct string comparison but compare Nodes. --- Tests/VaporAPNSTests/PayloadTests.swift | 72 ++++++++++++++++--------- 1 file changed, 46 insertions(+), 26 deletions(-) diff --git a/Tests/VaporAPNSTests/PayloadTests.swift b/Tests/VaporAPNSTests/PayloadTests.swift index bddb26e..e208aa4 100644 --- a/Tests/VaporAPNSTests/PayloadTests.swift +++ b/Tests/VaporAPNSTests/PayloadTests.swift @@ -8,6 +8,7 @@ import Foundation import XCTest +import JSON @testable import class VaporAPNS.Payload class PayloadTests: XCTestCase { @@ -16,67 +17,86 @@ class PayloadTests: XCTestCase { } func testSimplePush() throws { - let expectedJSON = "{\"aps\":{\"alert\":{\"body\":\"Test\"}}}" + let expectedJSON = Node(node: .object(["aps": .object(["alert": .object(["body": .string("Test")])])]), in: nil) - let payload = Payload.init(message: "Test") - let plJosn = try payload.makeJSON() - let plString = try plJosn.toString() + let payload = Payload(message: "Test") + let plJSON = try payload.makeJSON() + let plNode = plJSON.makeNode(in: nil) - XCTAssertEqual(plString, expectedJSON) + XCTAssertNotNil(plNode["aps"]?["alert"]?["body"]?.string) + XCTAssertEqual(plNode["aps"]?["alert"]?["body"]?.string, expectedJSON["aps"]?["alert"]?["body"]?.string) } func testTitleBodyPush() throws { - let expectedJSON = "{\"aps\":{\"alert\":{\"body\":\"Test body\",\"title\":\"Test title\"}}}" + let expectedJSON = Node(node: .object(["aps": .object(["alert": .object(["body": .string("Test body"), "title": .string("Test title")])])]), in: nil) let payload = Payload.init(title: "Test title", body: "Test body") - let plJosn = try payload.makeJSON() - let plString = try plJosn.toString() + let plJSON = try payload.makeJSON() + let plNode = plJSON.makeNode(in: nil) - XCTAssertEqual(plString, expectedJSON) + XCTAssertNotNil(plNode["aps"]?["alert"]?["body"]?.string) + XCTAssertEqual(plNode["aps"]?["alert"]?["body"]?.string, expectedJSON["aps"]?["alert"]?["body"]?.string) + XCTAssertNotNil(plNode["aps"]?["alert"]?["title"]?.string) + XCTAssertEqual(plNode["aps"]?["alert"]?["title"]?.string, expectedJSON["aps"]?["alert"]?["title"]?.string) } func testTitleBodyBadgePush() throws { - let expectedJSON = "{\"aps\":{\"badge\":10,\"alert\":{\"body\":\"Test body\",\"title\":\"Test title\"}}}" + let expectedJSON = Node(node: .object(["aps": .object(["alert": .object(["body": .string("Test body"), "title": .string("Test title")]), "badge": .number(.int(10))])]), in: nil) let payload = Payload.init(title: "Test title", body: "Test body", badge: 10) - let plJosn = try payload.makeJSON() - let plString = try plJosn.toString() + let plJSON = try payload.makeJSON() + let plNode = plJSON.makeNode(in: nil) - XCTAssertEqual(plString, expectedJSON) + XCTAssertNotNil(plNode["aps"]?["alert"]?["body"]?.string) + XCTAssertEqual(plNode["aps"]?["alert"]?["body"]?.string, expectedJSON["aps"]?["alert"]?["body"]?.string) + XCTAssertNotNil(plNode["aps"]?["alert"]?["title"]?.string) + XCTAssertEqual(plNode["aps"]?["alert"]?["title"]?.string, expectedJSON["aps"]?["alert"]?["title"]?.string) + XCTAssertNotNil(plNode["aps"]?["badge"]?.int) + XCTAssertEqual(plNode["aps"]?["badge"]?.int, expectedJSON["aps"]?["badge"]?.int) } func testTitleSubtitleBodyPush() throws { - let expectedJSON = "{\"aps\":{\"alert\":{\"body\":\"Test body\",\"title\":\"Test title\",\"subtitle\":\"Test subtitle\"}}}" + let expectedJSON = Node(node: .object(["aps": .object(["alert": .object(["body": .string("Test body"), "title": .string("Test title"), "subtitle": .string("Test subtitle")])])]), in: nil) let payload = Payload.init(title: "Test title", body: "Test body") payload.subtitle = "Test subtitle" - let plJosn = try payload.makeJSON() - let plString = try plJosn.toString() + let plJSON = try payload.makeJSON() + let plNode = plJSON.makeNode(in: nil) - XCTAssertEqual(plString, expectedJSON) + XCTAssertNotNil(plNode["aps"]?["alert"]?["body"]?.string) + XCTAssertEqual(plNode["aps"]?["alert"]?["body"]?.string, expectedJSON["aps"]?["alert"]?["body"]?.string) + XCTAssertNotNil(plNode["aps"]?["alert"]?["title"]?.string) + XCTAssertEqual(plNode["aps"]?["alert"]?["title"]?.string, expectedJSON["aps"]?["alert"]?["title"]?.string) + XCTAssertNotNil(plNode["aps"]?["alert"]?["subtitle"]?.string) + XCTAssertEqual(plNode["aps"]?["alert"]?["subtitle"]?.string, expectedJSON["aps"]?["alert"]?["subtitle"]?.string) } func testContentAvailablePush() throws { - let expectedJSON = "{\"aps\":{\"content-available\":true}}" + let expectedJSON = Node(node: .object(["aps": .object(["content-available": .bool(true)])]), in: nil) let payload = Payload.contentAvailable - let plJosn = try payload.makeJSON() - let plString = try plJosn.toString() + let plJSON = try payload.makeJSON() + let plNode = plJSON.makeNode(in: nil) - XCTAssertEqual(plString, expectedJSON) + XCTAssertNotNil(plNode["aps"]?["content-available"]?.bool) + XCTAssertEqual(plNode["aps"]?["content-available"]?.bool, expectedJSON["aps"]?["content-available"]?.bool) } func testContentAvailableWithExtrasPush() throws { - let expectedJSON = "{\"IntKey\":101,\"aps\":{\"content-available\":true},\"StringKey\":\"StringExtra1\"}" - let linuxExpectedJSON = "{\"aps\":{\"content-available\":true},\"IntKey\":101,\"StringKey\":\"StringExtra1\"}" + let expectedJSON = Node(node: .object(["aps": .object(["content-available": .bool(true)]), "IntKey": .number(.int(101)), "StringKey": .string("StringExtra1")]), in: nil) let payload = Payload.contentAvailable payload.extra["StringKey"] = "StringExtra1" payload.extra["IntKey"] = 101 - let plJosn = try payload.makeJSON() - let plString = try plJosn.toString() + let plJSON = try payload.makeJSON() + let plNode = plJSON.makeNode(in: nil) - XCTAssertTrue(plString == expectedJSON || plString == linuxExpectedJSON) + XCTAssertNotNil(plNode["aps"]?["content-available"]?.bool) + XCTAssertEqual(plNode["aps"]?["content-available"]?.bool, expectedJSON["aps"]?["content-available"]?.bool) + XCTAssertNotNil(plNode["StringKey"]?.string) + XCTAssertEqual(plNode["StringKey"]?.string, expectedJSON["StringKey"]?.string) + XCTAssertNotNil(plNode["IntKey"]?.int) + XCTAssertEqual(plNode["IntKey"]?.int, expectedJSON["IntKey"]?.int) } static var allTests : [(String, (PayloadTests) -> () throws -> Void)] { From 01892c604b28359bc427c8f8598da947c878f50f Mon Sep 17 00:00:00 2001 From: Jimmy Arts Date: Fri, 19 May 2017 10:58:15 +0200 Subject: [PATCH 4/7] Added vapor homebrew tap to allow discovery of ctls cask. --- .ci/build.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/.ci/build.sh b/.ci/build.sh index 991a565..5eb50b8 100755 --- a/.ci/build.sh +++ b/.ci/build.sh @@ -41,6 +41,7 @@ then export PATH=$PWD/$SWIFTFILE/usr/bin:"${PATH}" else echo "📚 Installing Dependencies" + brew tap vapor/homebrew-tap brew install ctls fi From 5eb0e904bb7b60d83b3fa915f4d977235716fb4c Mon Sep 17 00:00:00 2001 From: Jimmy Arts Date: Fri, 19 May 2017 14:31:18 +0200 Subject: [PATCH 5/7] Reverted Options to proper Node init. Fixed indentation in Payload.swift. --- Sources/VaporAPNS/Options.swift | 39 +++++------- Sources/VaporAPNS/Payload.swift | 108 ++++++++++++++++---------------- 2 files changed, 69 insertions(+), 78 deletions(-) diff --git a/Sources/VaporAPNS/Options.swift b/Sources/VaporAPNS/Options.swift index 8d47bc5..14c666b 100644 --- a/Sources/VaporAPNS/Options.swift +++ b/Sources/VaporAPNS/Options.swift @@ -37,10 +37,6 @@ public struct Options: CustomStringConvertible, NodeInitializable { return certPath != nil && keyPath != nil } - public init(node: Node) throws { - abort() - } - public init(topic: String, certPath: String, keyPath: String, port: Port = .p443, debugLogging: Bool = false) throws { self.topic = topic self.certPath = certPath @@ -100,10 +96,10 @@ public struct Options: CustomStringConvertible, NodeInitializable { } - public init(node: Node, in context: Context) throws { + public init(node: Node) throws { if let topic = node["topic"]?.string { self.topic = topic - }else { + } else { throw InitializeError.noTopic } @@ -111,33 +107,28 @@ public struct Options: CustomStringConvertible, NodeInitializable { self.port = port } - var hasAnyAuthentication = false - var hasBothAuthentication = false - - if let certPath = node["certificatePath"]?.string, let keyPath = node["keyPath"]?.string { - hasAnyAuthentication = true - self.certPath = certPath - self.keyPath = keyPath - - } + var hasAuthentication = false if let privateKeyLocation = node["keyPath"]?.string, let keyId = node["keyId"]?.string { - if hasAnyAuthentication { hasBothAuthentication = true } - hasAnyAuthentication = true + hasAuthentication = true let (priv, pub) = try privateKeyLocation.tokenString() self.privateKey = priv self.publicKey = pub self.keyId = keyId } - - guard hasAnyAuthentication else { - throw InitializeError.noAuthentication + + if let certPath = node["certificatePath"]?.string, let keyPath = node["keyPath"]?.string { + if hasAuthentication { + print ("You've seem to have specified both authentication methods, choosing preferred APNS Auth Key method...") + } else { + hasAuthentication = true + self.certPath = certPath + self.keyPath = keyPath + } } - if hasBothAuthentication { - print ("You've seem to have specified both authentication methods, choosing preferred APNS Auth Key method...") - certPath = nil - keyPath = nil + guard hasAuthentication else { + throw InitializeError.noAuthentication } } diff --git a/Sources/VaporAPNS/Payload.swift b/Sources/VaporAPNS/Payload.swift index 61008fc..f8d0b0e 100644 --- a/Sources/VaporAPNS/Payload.swift +++ b/Sources/VaporAPNS/Payload.swift @@ -69,65 +69,65 @@ open class Payload: JSONRepresentable { if contentAvailable { apsPayloadData["content-available"] = true } else { - - // Create alert dictionary - var alert: [String: NodeRepresentable] = [:] - - if let title = title { - alert["title"] = title - } - - if let titleLocKey = titleLocKey { - alert["title-loc-key"] = titleLocKey - if let titleLocArgs = titleLocArgs { - alert["title-loc-args"] = try titleLocArgs.makeNode(in: nil) + // Create alert dictionary + var alert: [String: NodeRepresentable] = [:] + + if let title = title { + alert["title"] = title } - } - - if let subtitle = subtitle { - alert["subtitle"] = subtitle - } - - if let body = body { - alert["body"] = body - }else { - if let bodyLocKey = bodyLocKey { - alert["loc-key"] = bodyLocKey + + if let titleLocKey = titleLocKey { + alert["title-loc-key"] = titleLocKey - if let bodyLocArgs = bodyLocArgs { - alert["loc-args"] = try bodyLocArgs.makeNode(in: nil) + if let titleLocArgs = titleLocArgs { + alert["title-loc-args"] = try titleLocArgs.makeNode(in: nil) } } - } - - if let actionLocKey = actionLocKey { - alert["action-loc-key"] = actionLocKey - } - - if let launchImage = launchImage { - alert["launch-image"] = launchImage - } - // Alert dictionary created - - apsPayloadData["alert"] = try alert.makeNode(in: nil) - - if let badge = badge { - apsPayloadData["badge"] = badge - } - - if let sound = sound { - apsPayloadData["sound"] = sound - } - - if let category = category { - apsPayloadData["category"] = category - } - - if hasMutableContent { - apsPayloadData["mutable-content"] = 1 - } - + + if let subtitle = subtitle { + alert["subtitle"] = subtitle + } + + if let body = body { + alert["body"] = body + } else { + if let bodyLocKey = bodyLocKey { + alert["loc-key"] = bodyLocKey + + if let bodyLocArgs = bodyLocArgs { + alert["loc-args"] = try bodyLocArgs.makeNode(in: nil) + } + } + } + + if let actionLocKey = actionLocKey { + alert["action-loc-key"] = actionLocKey + } + + if let launchImage = launchImage { + alert["launch-image"] = launchImage + } + // Alert dictionary created + + apsPayloadData["alert"] = try alert.makeNode(in: nil) + + if let badge = badge { + apsPayloadData["badge"] = badge + } + + if let sound = sound { + apsPayloadData["sound"] = sound + } + + if let category = category { + apsPayloadData["category"] = category + } + + if hasMutableContent { + apsPayloadData["mutable-content"] = 1 + } + } payloadData["aps"] = try apsPayloadData.makeNode(in: nil) From 0871a204d25f927d2a27a18c5cef8833f61b1bc2 Mon Sep 17 00:00:00 2001 From: Jimmy Arts Date: Thu, 1 Jun 2017 16:22:06 +0200 Subject: [PATCH 6/7] Fixed PR comment, changed JWT claim construction --- Sources/VaporAPNS/Options.swift | 5 ++--- Sources/VaporAPNS/VaporAPNS.swift | 10 +++++----- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Sources/VaporAPNS/Options.swift b/Sources/VaporAPNS/Options.swift index 14c666b..1104958 100644 --- a/Sources/VaporAPNS/Options.swift +++ b/Sources/VaporAPNS/Options.swift @@ -97,11 +97,10 @@ public struct Options: CustomStringConvertible, NodeInitializable { public init(node: Node) throws { - if let topic = node["topic"]?.string { - self.topic = topic - } else { + guard let topic = node["topic"]?.string else { throw InitializeError.noTopic } + self.topic = topic if let portRaw = node["port"]?.int, let port = Port(rawValue: portRaw) { self.port = port diff --git a/Sources/VaporAPNS/VaporAPNS.swift b/Sources/VaporAPNS/VaporAPNS.swift index 2628092..4ed5b53 100644 --- a/Sources/VaporAPNS/VaporAPNS.swift +++ b/Sources/VaporAPNS/VaporAPNS.swift @@ -71,13 +71,13 @@ open class VaporAPNS { var curlHeaders: UnsafeMutablePointer? if !options.usesCertificateAuthentication { let privateKey = options.privateKey!.bytes.base64Decoded - let claims: [Claim] = [ - IssuerClaim(string: options.teamId!), - IssuedAtClaim() + let claims: [Node] = [ + IssuerClaim(string: options.teamId!).node, + IssuedAtClaim().node ] - let claimsNode = Node(claims) + let claimsPayload = try! claims.makeNode(in: nil).converted(to: JSON.self) let jwt = try! JWT(additionalHeaders: [KeyID(options.keyId!)], - payload: claimsNode.converted(to: JSON.self), + payload: claimsPayload, signer: ES256(key: privateKey)) let tokenString = try! jwt.createToken() From a9c9091d7d71a81376ed824ef0d6b1e77513acc2 Mon Sep 17 00:00:00 2001 From: Jimmy Arts Date: Fri, 2 Jun 2017 10:37:55 +0200 Subject: [PATCH 7/7] Updated claims JSON --- Sources/VaporAPNS/VaporAPNS.swift | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Sources/VaporAPNS/VaporAPNS.swift b/Sources/VaporAPNS/VaporAPNS.swift index 4ed5b53..5671c8a 100644 --- a/Sources/VaporAPNS/VaporAPNS.swift +++ b/Sources/VaporAPNS/VaporAPNS.swift @@ -71,13 +71,12 @@ open class VaporAPNS { var curlHeaders: UnsafeMutablePointer? if !options.usesCertificateAuthentication { let privateKey = options.privateKey!.bytes.base64Decoded - let claims: [Node] = [ - IssuerClaim(string: options.teamId!).node, - IssuedAtClaim().node + let claims: [Claim] = [ + IssuerClaim(string: options.teamId!), + IssuedAtClaim() ] - let claimsPayload = try! claims.makeNode(in: nil).converted(to: JSON.self) let jwt = try! JWT(additionalHeaders: [KeyID(options.keyId!)], - payload: claimsPayload, + payload: JSON(claims), signer: ES256(key: privateKey)) let tokenString = try! jwt.createToken()