Skip to content

Commit

Permalink
Merge dfedb03 into 03a456c
Browse files Browse the repository at this point in the history
  • Loading branch information
kirtanp98 committed Jun 25, 2019
2 parents 03a456c + dfedb03 commit c85877e
Show file tree
Hide file tree
Showing 14 changed files with 454 additions and 75 deletions.
4 changes: 2 additions & 2 deletions .swiftlint.yml
Expand Up @@ -173,8 +173,8 @@ trailing_whitespace:
ignores_comments: true

type_body_length:
warning: 300
error: 350
warning: 400
error: 450

unused_optional_binding:
severity: error
Expand Down
8 changes: 8 additions & 0 deletions ButtonMerchant.xcodeproj/project.pbxproj
Expand Up @@ -85,6 +85,8 @@
DADE90C2209B9A630073144B /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = DADE90C1209B9A630073144B /* TestError.swift */; };
DAE8B96F22AF5F0700D11AF9 /* TrustEvaluator.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAE8B96D22AF5F0400D11AF9 /* TrustEvaluator.swift */; };
DC4AAD4422B13CD3005CE460 /* OrderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC4AAD4322B13CD3005CE460 /* OrderTests.swift */; };
DCF4AD0422B421FB000DA3B2 /* ReportOrderBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCF4AD0322B421FB000DA3B2 /* ReportOrderBody.swift */; };
DCF4AD0722B7E31C000DA3B2 /* ReportOrderBodyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCF4AD0522B7E2FF000DA3B2 /* ReportOrderBodyTests.swift */; };
DE0E7C2B209B915E001A5EE0 /* ApplicationId.generated.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE0E7C2A209B915E001A5EE0 /* ApplicationId.generated.swift */; };
DE1706DB20855B06009FF30B /* UIDeviceExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE1706DA20855B06009FF30B /* UIDeviceExtensions.swift */; };
DE1706DD20855B22009FF30B /* UIScreenExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE1706DC20855B22009FF30B /* UIScreenExtensions.swift */; };
Expand Down Expand Up @@ -219,6 +221,8 @@
DADE90C1209B9A630073144B /* TestError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestError.swift; sourceTree = "<group>"; };
DAE8B96D22AF5F0400D11AF9 /* TrustEvaluator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrustEvaluator.swift; sourceTree = "<group>"; };
DC4AAD4322B13CD3005CE460 /* OrderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrderTests.swift; sourceTree = "<group>"; };
DCF4AD0322B421FB000DA3B2 /* ReportOrderBody.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportOrderBody.swift; sourceTree = "<group>"; };
DCF4AD0522B7E2FF000DA3B2 /* ReportOrderBodyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportOrderBodyTests.swift; sourceTree = "<group>"; };
DDA5C34AC9410445D780EB22 /* Pods-UnitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-UnitTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-UnitTests/Pods-UnitTests.debug.xcconfig"; sourceTree = "<group>"; };
DE0E7C2A209B915E001A5EE0 /* ApplicationId.generated.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApplicationId.generated.swift; sourceTree = "<group>"; };
DE153DA4FDE6DCF451A438B1 /* Pods-Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-Example/Pods-Example.release.xcconfig"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -370,6 +374,7 @@
DE175A1D20A0ADF7005C97B9 /* Version */,
607FACEB1AFB9204008FA782 /* ButtonMerchantTests.swift */,
9E4C496520616B040053E4CA /* CoreTests.swift */,
DCF4AD0522B7E2FF000DA3B2 /* ReportOrderBodyTests.swift */,
9E4C496620616B040053E4CA /* ButtonDefaultsTests.swift */,
9E0DBB1B207BB55A0066A35D /* SystemTests.swift */,
9E5475E1206D7E0C00947A1C /* ClientTests.swift */,
Expand Down Expand Up @@ -576,6 +581,7 @@
children = (
DE175A1920A09BB1005C97B9 /* Version */,
DE865F792052F90600F4054D /* ButtonMerchant.swift */,
DCF4AD0322B421FB000DA3B2 /* ReportOrderBody.swift */,
DA0FA29F205C1B3A008296A6 /* Core.swift */,
9E77202120605506005F740B /* ButtonDefaults.swift */,
9EB1B09F207AB43E00BE0A1A /* System.swift */,
Expand Down Expand Up @@ -1104,6 +1110,7 @@
9EB1B0A0207AB43E00BE0A1A /* System.swift in Sources */,
DAE8B96F22AF5F0700D11AF9 /* TrustEvaluator.swift in Sources */,
DE2F7450208F6552001E4BD6 /* ConfigurationError.swift in Sources */,
DCF4AD0422B421FB000DA3B2 /* ReportOrderBody.swift in Sources */,
DE865FA620530BCE00F4054D /* ButtonMerchant.swift in Sources */,
9E56CE5A22B812AE00E75884 /* StringExtensions.swift in Sources */,
9E2B4317206C1335009F2886 /* EncodableExtensions.swift in Sources */,
Expand Down Expand Up @@ -1157,6 +1164,7 @@
9E4C496820616B040053E4CA /* ButtonDefaultsTests.swift in Sources */,
9E6F4341206C160C004242A1 /* TestClient.swift in Sources */,
9E4C496720616B040053E4CA /* CoreTests.swift in Sources */,
DCF4AD0722B7E31C000DA3B2 /* ReportOrderBodyTests.swift in Sources */,
DEE61B2D20656A090039E47A /* XCTestExtensions.swift in Sources */,
9E77202520607331005F740B /* ButtonMerchantTests.swift in Sources */,
9EDEB859206ECE3000D58FE2 /* PostInstallBodyTests.swift in Sources */,
Expand Down
15 changes: 15 additions & 0 deletions Source/ButtonMerchant.swift
Expand Up @@ -143,9 +143,24 @@ final public class ButtonMerchant: NSObject {
This does not replace server-side order reporting to Button.
[See: order reporting](https://developer.usebutton.com/guides/merchants/ios/report-orders-to-button#report-orders-to-buttons-order-api)
*/
@available(*, deprecated, message: "Use ButtonMerchant.reportOrder(order:completion:) instead")
@objc public static func trackOrder(_ order: Order, completion: ((Error?) -> Void)? = nil) {
core.trackOrder(order, completion)
}

/**
Reports an order.
This signal is used to power the Instant Rewards feature for Publishers to notify
their customer as quickly as possible that their order has been correctly tracked.
- Attention:
This does not replace server-side order reporting to Button.
[See: order reporting](https://developer.usebutton.com/guides/merchants/ios/report-orders-to-button#report-orders-to-buttons-order-api)
*/
@objc public static func reportOrder(_ order: Order, completion: ((Error?) -> Void)? = nil) {
core.reportOrder(order, completion)
}

/**
Discards the current session and all persisted data.
Expand Down
13 changes: 12 additions & 1 deletion Source/Client.swift
Expand Up @@ -42,6 +42,7 @@ internal protocol ClientType: class {
var userAgent: UserAgentType { get }
func fetchPostInstallURL(parameters: [String: Any], _ completion: @escaping (URL?, String?) -> Void)
func trackOrder(parameters: [String: Any], _ completion: ((Error?) -> Void)?)
func reportOrder(parameters: [String: Any], _ completion: ((Error?) -> Void)?)
init(session: URLSessionType, userAgent: UserAgentType)
}

Expand Down Expand Up @@ -69,7 +70,7 @@ internal final class Client: ClientType {
completion(URL(string: action)!, attributionObject["btn_ref"] as? String)
})
}

func trackOrder(parameters: [String: Any], _ completion: ((Error?) -> Void)?) {
let request = urlRequest(url: Service.activity.url, parameters: parameters)
enqueueRequest(request: request) { _, error in
Expand All @@ -78,6 +79,16 @@ internal final class Client: ClientType {
}
}
}

func reportOrder(parameters: [String: Any], _ completion: ((Error?) -> Void)?) {
let request = urlRequest(url: Service.activity.url, parameters: parameters)
enqueueRequest(request: request) { _, error in
if let completion = completion {
completion(error)
}
}
}

}

internal extension Client {
Expand Down
18 changes: 17 additions & 1 deletion Source/Core.swift
Expand Up @@ -36,6 +36,7 @@ internal protocol CoreType {
func trackIncomingURL(_ url: URL)
func handlePostInstallURL(_ completion: @escaping (URL?, Error?) -> Void)
func trackOrder(_ order: Order, _ completion: ((Error?) -> Void)?)
func reportOrder(_ order: Order, _ completion: ((Error?) -> Void)?)
init(buttonDefaults: ButtonDefaultsType,
client: ClientType,
system: SystemType,
Expand Down Expand Up @@ -138,7 +139,7 @@ final internal class Core: CoreType {
completion(url, nil)
}
}

@available(*, deprecated)
func trackOrder(_ order: Order, _ completion: ((Error?) -> Void)?) {
guard let appId = applicationId, !appId.isEmpty else {
Expand All @@ -157,5 +158,20 @@ final internal class Core: CoreType {

client.trackOrder(parameters: parameters, completion)
}

func reportOrder(_ order: Order, _ completion: ((Error?) -> Void)?) {
guard let appId = applicationId, !appId.isEmpty else {
if let completion = completion {
completion(ConfigurationError.noApplicationId)
}
return
}

let reportOrderBody = ReportOrderBody(system: system, applicationId: appId, attributionToken: buttonDefaults.attributionToken, order: order)

let parameters = reportOrderBody.dictionaryRepresentation

client.reportOrder(parameters: parameters, completion)
}

}
39 changes: 12 additions & 27 deletions Source/Order.swift
Expand Up @@ -26,7 +26,7 @@ import Foundation
import CommonCrypto

/**
Represents an order placed by the user to be tracked using `ButtonMerchant.trackOrder(order)`.
Represents an order placed by the user to be tracked using `ButtonMerchant.reportOrder(order)`.
*/
@objcMembers
final public class Order: NSObject, Codable {
Expand Down Expand Up @@ -59,7 +59,7 @@ final public class Order: NSObject, Codable {
/**
The customer related to the order
*/
public var customer: Customer = Customer()
public var customer: Customer?

/**
The total order value in pennies (e.g. 3999 for $39.99)
Expand Down Expand Up @@ -120,38 +120,23 @@ final public class Order: NSObject, Codable {
/**
The id for the transacting customer in your system (required).
*/
public var id: String?

/**
If you prefer to provide your own hashed email, you can set it here following the below instructions,
otherwise, use **setEmail(email:)** and we will hash it for you.
let id: String

/**
The SHA-256 hash of the transacting customer’s lowercase email, as a 64-character hex string.
The value of the e-mail address must be converted to lowercase before computing the hash.
The hash itself may use uppercase or lowercase hex characters.
*/
public var emailSha256: String?
/**
The email of the transacting customer.
Providing a value for this field will generate a SHA-256 hash
and populate the **emailSha256** field for you.
**Note**: The value of the e-mail address must be converted to lowercase before
computing the hash. The hash itself may use uppercase or lowercase hex characters.
*/
var email: String?

- Parameter email: the plain-text email to be converted to SHA-256 hash.
*/
public func setEmail(_ email: String) {
emailSha256 = email.lowercased().sha256
@objc public init(id: String) {
self.id = id
}

/**
The customer’s IDFA.
*/
public var advertisingId: String?


enum CodingKeys: String, CodingKey {
case email
case id
case emailSha256 = "email_sha256"
case advertisingId = "advertising_id"
}
}

Expand Down
69 changes: 69 additions & 0 deletions Source/ReportOrderBody.swift
@@ -0,0 +1,69 @@
//
// ReportOrderBody.swift
//
// Copyright © 2019 Button, Inc. All rights reserved. (https://usebutton.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//

import Foundation

internal struct ReportOrderBody: Codable {

let applicationId: String
let userLocalTime: String
let attributionToken: String?
let orderId: String
let currency: String
let purchaseDate: String
let customerOrderId: String?
let lineItems: [Order.LineItem]?
let customer: Order.Customer?
let source: String = "merchant-library"

enum CodingKeys: String, CodingKey {
case applicationId = "app_id"
case userLocalTime = "user_local_time"
case attributionToken = "btn_ref"
case orderId = "order_id"
case currency
case purchaseDate = "purchase_date"
case customerOrderId = "customer_order_id"
case lineItems = "line_items"
case customer
case source
}

init(system: SystemType,
applicationId: String,
attributionToken: String?,
order: Order) {

self.applicationId = applicationId
userLocalTime = system.currentDate.ISO8601String
self.attributionToken = attributionToken
self.orderId = order.id
self.currency = order.currencyCode
self.purchaseDate = order.purchaseDate
self.customerOrderId = order.customerOrderId
self.lineItems = order.lineItems
self.customer = order.customer
}

}
19 changes: 19 additions & 0 deletions Tests/UnitTests/ButtonMerchantTests.swift
Expand Up @@ -163,4 +163,23 @@ class ButtonMerchantTests: XCTestCase {
// Assert
XCTAssertEqual(testCore.testOrder, expectedOrder)
}

func testReportOrderInvokesCoreWithOrder() {
// Arrange
let testSystem = TestSystem()
let testCore = TestCore(buttonDefaults: TestButtonDefaults(userDefaults: TestUserDefaults()),
client: TestClient(session: TestURLSession(), userAgent: TestUserAgent(system: testSystem)),
system: testSystem,
notificationCenter: TestNotificationCenter())
let expectedOrder = Order(id: "test",
purchaseDate: Date(),
lineItems: [Order.LineItem(identifier: "unique-id-1234", total: 400)])

// Act
ButtonMerchant._core = testCore
ButtonMerchant.reportOrder(expectedOrder) { _ in }

// Assert
XCTAssertEqual(testCore.testOrder, expectedOrder)
}
}
66 changes: 66 additions & 0 deletions Tests/UnitTests/ClientTests.swift
Expand Up @@ -303,4 +303,70 @@ class ClientTests: XCTestCase {

self.wait(for: [expectation], timeout: 2.0)
}

func testReportOrderEnqueuesRequests() {
// Arrange
let testURLSession = TestURLSession()
let client = Client(session: testURLSession, userAgent: TestUserAgent())
let expectedParameters = ["blargh": "blergh"]
let expectedURL = URL(string: "https://api.usebutton.com/v1/activity/order")!

// Act
client.reportOrder(parameters: expectedParameters) { _ in }
let request = (testURLSession.lastDataTask?.originalRequest)!
let requestParameters = try? JSONSerialization.jsonObject(with: request.httpBody!)
let parameters = requestParameters as? [String: String]

// Assert
XCTAssertTrue(testURLSession.didCallDataTaskWithRequest)
XCTAssertNotNil(testURLSession.lastDataTask)
XCTAssertNotNil(testURLSession.lastDataTask?.originalRequest!.url)
XCTAssertEqual(testURLSession.lastDataTask?.originalRequest!.url, expectedURL)
XCTAssertNotNil(parameters)
XCTAssertEqual(parameters!, expectedParameters)
}

func testReportOrderSuccess() {
// Arrange
let expectation = XCTestExpectation(description: "report order success")
let testURLSession = TestURLSession()
let client = Client(session: testURLSession, userAgent: TestUserAgent())
let url = URL(string: "https://api.usebutton.com/v1/activity/order")!

// Act
client.reportOrder(parameters: [:]) { error in

// Assert
XCTAssertNil(error)

expectation.fulfill()
}
let response = HTTPURLResponse(url: url, statusCode: 200, httpVersion: nil, headerFields: nil)
testURLSession.lastDataTask?.completion(Data(), response, nil)

self.wait(for: [expectation], timeout: 2.0)
}

func testReportOrderFails() {
// Arrange
let expectation = XCTestExpectation(description: "report order fails")
let testURLSession = TestURLSession()
let client = Client(session: testURLSession, userAgent: TestUserAgent())
let url = URL(string: "https://api.usebutton.com/v1/activity/order")!
let expectedError = TestError.known

// Act
client.reportOrder(parameters: [:]) { error in

// Assert
XCTAssertNotNil(error)
XCTAssertEqual(error as? TestError, expectedError)

expectation.fulfill()
}
let response = HTTPURLResponse(url: url, statusCode: 200, httpVersion: nil, headerFields: nil)
testURLSession.lastDataTask?.completion(nil, response, expectedError)

self.wait(for: [expectation], timeout: 2.0)
}
}

0 comments on commit c85877e

Please sign in to comment.