Skip to content

Commit

Permalink
Merge 505cabd into 22f6b97
Browse files Browse the repository at this point in the history
  • Loading branch information
romero-ios committed Apr 5, 2019
2 parents 22f6b97 + 505cabd commit 7675fd6
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 24 deletions.
8 changes: 8 additions & 0 deletions Source/ButtonMerchant.swift
Expand Up @@ -45,6 +45,7 @@ final public class ButtonMerchant: NSObject {
// This should never be set directly
set { }
}
private static let applicationIdRegex = "^app-[0-9a-zA-Z-]+$"

/**
The last tracked `attributionToken` from an inbound Button attributed URL.
Expand All @@ -71,6 +72,13 @@ final public class ButtonMerchant: NSObject {
*/
@objc public static func configure(applicationId: String) {
guard applicationId.range(of: applicationIdRegex, options: .regularExpression) != nil else {
print("""
Button App ID '\(applicationId)' is not valid.
You can find your App ID in the dashboard by logging in at https://app.usebutton.com/
""")
return
}
core.applicationId = applicationId
}

Expand Down
52 changes: 33 additions & 19 deletions Source/Client.swift
Expand Up @@ -30,16 +30,21 @@ internal enum Service: String {
case postInstall = "v1/web/deferred-deeplink"
case activity = "v1/activity/order"

static var baseURL = "https://api.usebutton.com/"
static var baseURL = "https://%@.mobileapi.usebutton.com/%@"

var url: URL {
return URL(string: Service.baseURL + self.rawValue)!
func url(applicationId: String?) throws -> URL {
guard let appId = applicationId else {
throw ConfigurationError.noApplicationId
}
let fullUrl = String(format: Service.baseURL, appId, self.rawValue)
return URL(string: fullUrl)!
}
}

internal protocol ClientType: class {
var session: URLSessionType { get }
var userAgent: UserAgentType { get }
var applicationId: String? { get set }
func fetchPostInstallURL(parameters: [String: Any], _ completion: @escaping (URL?, String?) -> Void)
func trackOrder(parameters: [String: Any], _ completion: ((Error?) -> Void)?)
init(session: URLSessionType, userAgent: UserAgentType)
Expand All @@ -49,33 +54,42 @@ internal final class Client: ClientType {

var session: URLSessionType
var userAgent: UserAgentType
var applicationId: String?

init(session: URLSessionType, userAgent: UserAgentType) {
self.session = session
self.userAgent = userAgent
}

func fetchPostInstallURL(parameters: [String: Any], _ completion: @escaping (URL?, String?) -> Void) {
let request = urlRequest(url: Service.postInstall.url, parameters: parameters)
enqueueRequest(request: request, completion: { data, _ in
guard let data = data,
let responseDict = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
let object = responseDict?["object"] as? [String: Any],
let action = object["action"] as? String,
let attributionObject = object["attribution"] as? [String: Any] else {
completion(nil, nil)
return
}
completion(URL(string: action)!, attributionObject["btn_ref"] as? String)
})
do {
let request = try urlRequest(url: Service.postInstall.url(applicationId: applicationId), parameters: parameters)
enqueueRequest(request: request, completion: { data, _ in
guard let data = data,
let responseDict = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
let object = responseDict?["object"] as? [String: Any],
let action = object["action"] as? String,
let attributionObject = object["attribution"] as? [String: Any] else {
completion(nil, nil)
return
}
completion(URL(string: action)!, attributionObject["btn_ref"] as? String)
})
} catch {
print(error.localizedDescription)
}
}

func trackOrder(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)
do {
let request = try urlRequest(url: Service.activity.url(applicationId: applicationId), parameters: parameters)
enqueueRequest(request: request) { _, error in
if let completion = completion {
completion(error)
}
}
} catch {
print(error.localizedDescription)
}
}
}
Expand Down
6 changes: 5 additions & 1 deletion Source/Core.swift
Expand Up @@ -47,7 +47,11 @@ internal protocol CoreType {
*/
final internal class Core: CoreType {

var applicationId: String?
var applicationId: String? {
didSet {
client.applicationId = self.applicationId
}
}
var buttonDefaults: ButtonDefaultsType
var client: ClientType
var system: SystemType
Expand Down
7 changes: 7 additions & 0 deletions Tests/IntegrationTests/IntegrationTests.swift
Expand Up @@ -143,4 +143,11 @@ class IntegrationTests: XCTestCase {

XCTAssertNil(ButtonMerchant.attributionToken)
}

func testConfigurePassesApplicationIdThroughToClient() {
let expectedApplicationId = "app-abc123"
ButtonMerchant.configure(applicationId: expectedApplicationId)

XCTAssertEqual(ButtonMerchant._core?.client.applicationId, expectedApplicationId)
}
}
19 changes: 18 additions & 1 deletion Tests/UnitTests/ButtonMerchantTests.swift
Expand Up @@ -36,7 +36,7 @@ class ButtonMerchantTests: XCTestCase {
XCTAssertEqual(ButtonMerchantVersionNumber, 1)
}

func testConfigureApplicationId() {
func testConfigureApplicationIdSetsApplicationIdOnCore() {
// Arrange
let applicationId = "app-test"
let testCore = TestCore(buttonDefaults: TestButtonDefaults(userDefaults: TestUserDefaults()),
Expand All @@ -53,6 +53,23 @@ class ButtonMerchantTests: XCTestCase {
XCTAssertEqual(testCore.applicationId, applicationId)
}

func testConfigureApplicationIdDoesNotSetInvalidApplicationIdOnCore() {
// Arrange
let applicationId = "invalid-app-id"
let testCore = TestCore(buttonDefaults: TestButtonDefaults(userDefaults: TestUserDefaults()),
client: TestClient(session: TestURLSession(),
userAgent: TestUserAgent(system: TestSystem())),
system: TestSystem(),
notificationCenter: TestNotificationCenter())

// Act
ButtonMerchant._core = testCore
ButtonMerchant.configure(applicationId: applicationId)

// Assert
XCTAssertNil(testCore.applicationId)
}

func testCreateCoreCreatesCoreWhenCoreSetToNil() {
ButtonMerchant._core = nil
ButtonMerchant.configure(applicationId: "app-test")
Expand Down
36 changes: 34 additions & 2 deletions Tests/UnitTests/ClientTests.swift
Expand Up @@ -164,12 +164,26 @@ class ClientTests: XCTestCase {
self.wait(for: [expectation], timeout: 2.0)
}

func testFetchPostInstallURLDoesNothingWhenApplicationIdIsNotSet() {
// Arrange
let testURLSession = TestURLSession()
let client = Client(session: testURLSession, userAgent: TestUserAgent())

// Act
client.fetchPostInstallURL(parameters: ["blargh": "blergh"]) { _, _ in }

// Assert
XCTAssertFalse(testURLSession.didCallDataTaskWithRequest)
XCTAssertNil(testURLSession.lastDataTask)
}

func testFetchPostInstallURLEnqueuesRequest() {
// Arrange
let testURLSession = TestURLSession()
let client = Client(session: testURLSession, userAgent: TestUserAgent())
client.applicationId = "app-abc123"
let expectedParameters = ["blargh": "blergh"]
let expectedURL = URL(string: "https://api.usebutton.com/v1/web/deferred-deeplink")!
let expectedURL = URL(string: "https://app-abc123.mobileapi.usebutton.com/v1/web/deferred-deeplink")!

// Act
client.fetchPostInstallURL(parameters: expectedParameters) { _, _ in }
Expand All @@ -191,6 +205,7 @@ class ClientTests: XCTestCase {
let expectation = XCTestExpectation(description: "fetch post install url success")
let testURLSession = TestURLSession()
let client = Client(session: testURLSession, userAgent: TestUserAgent())
client.applicationId = "app-abc123"
let expectedURL = URL(string: "https://usebutton.com")!
let expectedToken = "srctok-abc123"
let responseDict: [String: Any] = ["object": ["action": expectedURL.absoluteString,
Expand Down Expand Up @@ -218,6 +233,7 @@ class ClientTests: XCTestCase {
let expectation = XCTestExpectation(description: "fetch post install url fails bad response")
let testURLSession = TestURLSession()
let client = Client(session: testURLSession, userAgent: TestUserAgent())
client.applicationId = "app-abc123"
let url = URL(string: "https://usebutton.com")!
let responseDict = ["blargh": "blergh"]
let data = try? JSONSerialization.data(withJSONObject: responseDict)
Expand All @@ -238,12 +254,26 @@ class ClientTests: XCTestCase {
self.wait(for: [expectation], timeout: 2.0)
}

func testTrackOrderThrowsError() {
// Arrange
let testURLSession = TestURLSession()
let client = Client(session: testURLSession, userAgent: TestUserAgent())

// Act
client.trackOrder(parameters: ["blargh": "blergh"]) { _ in }

// Assert
XCTAssertFalse(testURLSession.didCallDataTaskWithRequest)
XCTAssertNil(testURLSession.lastDataTask)
}

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

// Act
client.trackOrder(parameters: expectedParameters) { _ in }
Expand All @@ -265,6 +295,7 @@ class ClientTests: XCTestCase {
let expectation = XCTestExpectation(description: "track order success")
let testURLSession = TestURLSession()
let client = Client(session: testURLSession, userAgent: TestUserAgent())
client.applicationId = "app-abc123"
let url = URL(string: "https://api.usebutton.com/v1/activity/order")!

// Act
Expand All @@ -286,6 +317,7 @@ class ClientTests: XCTestCase {
let expectation = XCTestExpectation(description: "track order fails")
let testURLSession = TestURLSession()
let client = Client(session: testURLSession, userAgent: TestUserAgent())
client.applicationId = "app-abc123"
let url = URL(string: "https://api.usebutton.com/v1/activity/order")!
let expectedError = TestError.known

Expand Down
3 changes: 2 additions & 1 deletion Tests/UnitTests/TestObjects/TestClient.swift
Expand Up @@ -34,7 +34,8 @@ class TestClient: ClientType {

var session: URLSessionType
var userAgent: UserAgentType

var applicationId: String?

var postInstallCompletion: ((URL?, String?) -> Void)?
var trackOrderCompletion: ((Error?) -> Void)?

Expand Down

0 comments on commit 7675fd6

Please sign in to comment.