Skip to content

Commit 05ac2b8

Browse files
crgee1rubikzube
andauthored
[CSL 6] Handle Conversion Types with v2 (#113)
* Change to v2 endpoint * Add and update tests * Add search term to request params * Switch test order * Update readme * Update from review session * Update comments * Add conversion types and tests * Update code with endpoint bug fix * Remove variation * Simple Lint Fixes Co-authored-by: Zubin Tiku <[email protected]>
1 parent 14e7701 commit 05ac2b8

12 files changed

+127
-46
lines changed

AutocompleteClient/Constants/Constants.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@ struct Constants {
143143
static let dateTime = "_dt"
144144
static let defaultItemSectionName = "Products"
145145
static let orderID = "order_id"
146+
static let defaultConversionType = "add_to_cart"
147+
static let conversionType = "type"
146148
}
147149

148150
struct TrackSessionStart {
@@ -186,7 +188,7 @@ struct Constants {
186188
}
187189

188190
struct TrackConversion {
189-
static let format = "%@/autocomplete/%@/conversion"
191+
static let format = "%@/v2/behavioral_action/conversion"
190192
}
191193

192194
struct TrackPurchase {

AutocompleteClient/FW/Logic/Request/Builder/RequestBuilder+QueryItems.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ extension RequestBuilder {
5555
queryItems.add(URLQueryItem(name: Constants.Track.autocompleteSection, value: sectionName))
5656
}
5757

58+
func set(type: String?) {
59+
guard let conversionType = type else { return }
60+
queryItems.add(URLQueryItem(name: Constants.Track.conversionType, value: conversionType))
61+
}
62+
5863
func set(searchSection: String) {
5964
queryItems.add(URLQueryItem(name: Constants.SearchQuery.section, value: searchSection))
6065
}

AutocompleteClient/FW/Logic/Request/CIOTrackConversionData.swift

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,23 +18,50 @@ struct CIOTrackConversionData: CIORequestData {
1818
let customerID: String
1919
var sectionName: String?
2020
let revenue: Double?
21+
let conversionType: String?
2122

2223
func url(with baseURL: String) -> String {
23-
return String(format: Constants.TrackConversion.format, baseURL, self.searchTerm.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed)!)
24+
return String(format: Constants.TrackConversion.format, baseURL)
2425
}
2526

26-
init(searchTerm: String, itemName: String, customerID: String, sectionName: String? = nil, revenue: Double? = nil) {
27+
init(searchTerm: String, itemName: String, customerID: String, sectionName: String? = nil, revenue: Double? = nil, conversionType: String? = nil) {
2728
self.searchTerm = searchTerm
2829
self.itemName = itemName
2930
self.customerID = customerID
3031
self.sectionName = sectionName
3132
self.revenue = revenue
33+
self.conversionType = conversionType
34+
}
35+
36+
func httpMethod() -> String {
37+
return "POST"
3238
}
3339

3440
func decorateRequest(requestBuilder: RequestBuilder) {
35-
requestBuilder.set(name: self.itemName)
36-
requestBuilder.set(customerID: self.customerID)
3741
requestBuilder.set(autocompleteSection: self.sectionName)
38-
requestBuilder.set(revenue: self.revenue)
42+
}
43+
44+
func httpBody(baseParams: [String: Any]) -> Data? {
45+
var dict = [
46+
"search_term": self.searchTerm,
47+
"item_id": self.customerID,
48+
"item_name": self.itemName
49+
] as [String: Any]
50+
51+
if self.revenue != nil {
52+
dict["revenue"] = NSString(format: "%.2f", self.revenue!)
53+
}
54+
55+
if self.sectionName != nil {
56+
dict["section"] = self.sectionName
57+
}
58+
59+
if self.conversionType != nil {
60+
dict["type"] = self.conversionType
61+
}
62+
63+
dict.merge(baseParams) { current, _ in current }
64+
65+
return try? JSONSerialization.data(withJSONObject: dict)
3966
}
4067
}

AutocompleteClient/FW/Logic/Request/CIOTrackRecommendationResultsViewData.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ struct CIOTrackRecommendationResultsViewData: CIORequestData {
4646
"pod_id": self.podID,
4747
"url": self.url
4848
] as [String: Any]
49-
49+
5050
if self.numResultsViewed != nil {
5151
dict["num_results_viewed"] = self.numResultsViewed
5252
}

AutocompleteClient/FW/Logic/Result/CIOResult.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public class CIOResult: NSObject {
2626
let variationsObj = json["variations"] as? [JSONObject]
2727

2828
let variations: [CIOResult] = variationsObj?.compactMap { obj in return CIOResult(json: obj) } ?? []
29-
29+
3030
let strategyData = json["strategy"] as? JSONObject ?? [String: Any]()
3131

3232
let strategy: CIORecommendationsStrategy = CIORecommendationsStrategy(json: strategyData)!

AutocompleteClient/FW/Logic/Worker/ConstructorIO.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,10 +236,11 @@ public class ConstructorIO: CIOSessionManagerDelegate {
236236
/// - searchTerm: Search term that the user searched for. If nil is passed, 'TERM_UNKNOWN' will be sent to the server.
237237
/// - sectionName The name of the autocomplete section the term came from
238238
/// - completionHandler: The callback to execute on completion.
239-
public func trackConversion(itemName: String, customerID: String, revenue: Double?, searchTerm: String? = nil, sectionName: String? = nil, completionHandler: TrackingCompletionHandler? = nil) {
239+
public func trackConversion(itemName: String, customerID: String, revenue: Double?, searchTerm: String? = nil, sectionName: String? = nil, conversionType: String? = nil, completionHandler: TrackingCompletionHandler? = nil) {
240240
let section = sectionName ?? self.config.defaultItemSectionName ?? Constants.Track.defaultItemSectionName
241+
let type = conversionType ?? Constants.Track.defaultConversionType
241242
let term = searchTerm == nil ? "TERM_UNKNOWN" : (searchTerm!.isEmpty) ? "TERM_UNKNOWN" : searchTerm
242-
let data = CIOTrackConversionData(searchTerm: term!, itemName: itemName, customerID: customerID, sectionName: section, revenue: revenue)
243+
let data = CIOTrackConversionData(searchTerm: term!, itemName: itemName, customerID: customerID, sectionName: section, revenue: revenue, conversionType: type)
243244
let request = self.buildRequest(data: data)
244245
executeTracking(request, completionHandler: completionHandler)
245246
}

AutocompleteClientTests/FW/Logic/Request/TrackConversionRequestBuilderTests.swift

Lines changed: 57 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ class TrackConversionRequestBuilderTests: XCTestCase {
1616
fileprivate let itemName = "some item name"
1717
fileprivate let customerID = "custIDq3éû qd"
1818
fileprivate let sectionName = "some section name@"
19+
fileprivate let conversionType = "like"
20+
fileprivate let revenue = 12.45
1921

2022
fileprivate var encodedSearchTerm: String = ""
2123
fileprivate var encodedItemName: String = ""
@@ -38,13 +40,15 @@ class TrackConversionRequestBuilderTests: XCTestCase {
3840
builder.build(trackData: tracker)
3941
let request = builder.getRequest()
4042
let url = request.url!.absoluteString
43+
let payload = try? JSONSerialization.jsonObject(with: request.httpBody!, options: []) as? [String: Any]
4144

42-
XCTAssertEqual(request.httpMethod, "GET")
43-
XCTAssertTrue(url.hasPrefix("https://ac.cnstrc.com/autocomplete/\(encodedSearchTerm)/conversion?"))
44-
XCTAssertTrue(url.contains("name=\(encodedItemName)"), "URL should contain the item id.")
45-
XCTAssertTrue(url.contains("customer_id=\(encodedCustomerID)"), "URL should contain the customer ID.")
45+
XCTAssertEqual(request.httpMethod, "POST")
46+
XCTAssertTrue(url.hasPrefix("https://ac.cnstrc.com/v2/behavioral_action/conversion?"))
4647
XCTAssertTrue(url.contains("c=cioios-"), "URL should contain the version string.")
4748
XCTAssertTrue(url.contains("key=\(testACKey)"), "URL should contain the api key.")
49+
XCTAssertEqual(payload?["item_name"] as? String, itemName)
50+
XCTAssertEqual(payload?["item_id"] as? String, customerID)
51+
XCTAssertEqual(payload?["search_term"] as? String, searchTerm)
4852
}
4953

5054
func testTrackConversionBuilder_WithCustomBaseURL() {
@@ -54,53 +58,82 @@ class TrackConversionRequestBuilderTests: XCTestCase {
5458
builder.build(trackData: tracker)
5559
let request = builder.getRequest()
5660
let url = request.url!.absoluteString
61+
let payload = try? JSONSerialization.jsonObject(with: request.httpBody!, options: []) as? [String: Any]
5762

5863
XCTAssertTrue(url.hasPrefix(customBaseURL))
64+
XCTAssertEqual(payload?["item_name"] as? String, itemName)
65+
XCTAssertEqual(payload?["item_id"] as? String, customerID)
66+
XCTAssertEqual(payload?["search_term"] as? String, searchTerm)
5967
}
6068

6169
func testTrackConversionBuilder_WithSectionName() {
6270
let tracker = CIOTrackConversionData(searchTerm: self.searchTerm, itemName: self.itemName, customerID: self.customerID, sectionName: sectionName)
6371
builder.build(trackData: tracker)
6472
let request = builder.getRequest()
6573
let url = request.url!.absoluteString
74+
let payload = try? JSONSerialization.jsonObject(with: request.httpBody!, options: []) as? [String: Any]
6675

67-
XCTAssertEqual(request.httpMethod, "GET")
68-
XCTAssertTrue(url.hasPrefix("https://ac.cnstrc.com/autocomplete/\(encodedSearchTerm)/conversion?"))
69-
XCTAssertTrue(url.contains("name=\(encodedItemName)"), "URL should contain the item id.")
70-
XCTAssertTrue(url.contains("customer_id=\(encodedCustomerID)"), "URL should contain the customer ID.")
71-
XCTAssertTrue(url.contains("section=\(encodedSectionName)"), "URL should contain the autocomplete section name.")
76+
XCTAssertEqual(request.httpMethod, "POST")
77+
XCTAssertTrue(url.hasPrefix("https://ac.cnstrc.com/v2/behavioral_action/conversion?"))
7278
XCTAssertTrue(url.contains("c=cioios-"), "URL should contain the version string.")
79+
XCTAssertTrue(url.contains("section=\(encodedSectionName)"), "URL should contain the section.")
7380
XCTAssertTrue(url.contains("key=\(testACKey)"), "URL should contain the api key.")
81+
XCTAssertEqual(payload?["section"] as? String, sectionName)
82+
XCTAssertEqual(payload?["item_name"] as? String, itemName)
83+
XCTAssertEqual(payload?["item_id"] as? String, customerID)
84+
XCTAssertEqual(payload?["search_term"] as? String, searchTerm)
7485
}
7586

76-
func testTrackConversionBuilder_WithRevenue() {
77-
let tracker = CIOTrackConversionData(searchTerm: searchTerm, itemName: itemName, customerID: customerID, revenue: 9999)
87+
func testTrackConversionBuilder_WithConversionType() {
88+
let tracker = CIOTrackConversionData(searchTerm: self.searchTerm, itemName: self.itemName, customerID: self.customerID, conversionType: self.conversionType)
7889
builder.build(trackData: tracker)
7990
let request = builder.getRequest()
8091
let url = request.url!.absoluteString
92+
let payload = try? JSONSerialization.jsonObject(with: request.httpBody!, options: []) as? [String: Any]
8193

82-
XCTAssertEqual(request.httpMethod, "GET")
83-
XCTAssertTrue(url.hasPrefix("https://ac.cnstrc.com/autocomplete/\(encodedSearchTerm)/conversion?"))
84-
XCTAssertTrue(url.contains("name=\(encodedItemName)"), "URL should contain the item id.")
85-
XCTAssertTrue(url.contains("customer_id=\(encodedCustomerID)"), "URL should contain the customer ID.")
86-
XCTAssertTrue(url.contains("revenue=9999.00"), "URL should contain the revenue parameter.")
94+
XCTAssertEqual(request.httpMethod, "POST")
95+
XCTAssertTrue(url.hasPrefix("https://ac.cnstrc.com/v2/behavioral_action/conversion?"))
8796
XCTAssertTrue(url.contains("c=cioios-"), "URL should contain the version string.")
8897
XCTAssertTrue(url.contains("key=\(testACKey)"), "URL should contain the api key.")
98+
XCTAssertEqual(payload?["type"] as? String, conversionType)
99+
XCTAssertEqual(payload?["item_name"] as? String, itemName)
100+
XCTAssertEqual(payload?["item_id"] as? String, customerID)
101+
XCTAssertEqual(payload?["search_term"] as? String, searchTerm)
89102
}
90103

91-
func testTrackConversionBuilder_WithSectionNameAndRevenue() {
92-
let tracker = CIOTrackConversionData(searchTerm: searchTerm, itemName: itemName, customerID: customerID, sectionName: sectionName, revenue: 12.345)
104+
func testTrackConversionBuilder_WithRevenue() {
105+
let tracker = CIOTrackConversionData(searchTerm: searchTerm, itemName: itemName, customerID: customerID, revenue: revenue)
93106
builder.build(trackData: tracker)
94107
let request = builder.getRequest()
95108
let url = request.url!.absoluteString
109+
let payload = try? JSONSerialization.jsonObject(with: request.httpBody!, options: []) as? [String: Any]
96110

97-
XCTAssertEqual(request.httpMethod, "GET")
98-
XCTAssertTrue(url.hasPrefix("https://ac.cnstrc.com/autocomplete/\(encodedSearchTerm)/conversion?"))
99-
XCTAssertTrue(url.contains("name=\(encodedItemName)"), "URL should contain the item id.")
100-
XCTAssertTrue(url.contains("customer_id=\(encodedCustomerID)"), "URL should contain the customer ID.")
101-
XCTAssertTrue(url.contains("section=\(encodedSectionName)"), "URL should contain the autocomplete section name.")
102-
XCTAssertTrue(url.contains("revenue=12.35"), "URL should contain the revenue parameter.")
111+
XCTAssertEqual(request.httpMethod, "POST")
112+
XCTAssertTrue(url.hasPrefix("https://ac.cnstrc.com/v2/behavioral_action/conversion?"))
113+
XCTAssertTrue(url.contains("c=cioios-"), "URL should contain the version string.")
114+
XCTAssertTrue(url.contains("key=\(testACKey)"), "URL should contain the api key.")
115+
XCTAssertEqual(payload?["revenue"] as? String, String(revenue))
116+
XCTAssertEqual(payload?["item_name"] as? String, itemName)
117+
XCTAssertEqual(payload?["item_id"] as? String, customerID)
118+
XCTAssertEqual(payload?["search_term"] as? String, searchTerm)
119+
}
120+
121+
func testTrackConversionBuilder_WithSectionNameRevenueAndType() {
122+
let tracker = CIOTrackConversionData(searchTerm: searchTerm, itemName: itemName, customerID: customerID, sectionName: sectionName, revenue: revenue, conversionType: conversionType)
123+
builder.build(trackData: tracker)
124+
let request = builder.getRequest()
125+
let url = request.url!.absoluteString
126+
let payload = try? JSONSerialization.jsonObject(with: request.httpBody!, options: []) as? [String: Any]
127+
XCTAssertEqual(request.httpMethod, "POST")
128+
XCTAssertTrue(url.hasPrefix("https://ac.cnstrc.com/v2/behavioral_action/conversion?"))
103129
XCTAssertTrue(url.contains("c=cioios-"), "URL should contain the version string.")
130+
XCTAssertTrue(url.contains("section=\(encodedSectionName)"), "URL should contain the section.")
104131
XCTAssertTrue(url.contains("key=\(testACKey)"), "URL should contain the api key.")
132+
XCTAssertEqual(payload?["revenue"] as? String, String(revenue))
133+
XCTAssertEqual(payload?["type"] as? String, conversionType)
134+
XCTAssertEqual(payload?["section"] as? String, sectionName)
135+
XCTAssertEqual(payload?["item_name"] as? String, itemName)
136+
XCTAssertEqual(payload?["item_id"] as? String, customerID)
137+
XCTAssertEqual(payload?["search_term"] as? String, searchTerm)
105138
}
106139
}

AutocompleteClientTests/FW/Logic/Request/TrackPurchaseRequestBuilderTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ class TrackPurchaseRequestBuilderTests: XCTestCase {
7878
XCTAssertTrue(url.hasPrefix("https://ac.cnstrc.com/v2/behavioral_action/purchase?"))
7979
XCTAssertEqual(payload?["revenue"] as? Double, revenue)
8080
}
81-
81+
8282
func testTrackPurchaseBuilder_WithOrderID() {
8383
let tracker = CIOTrackPurchaseData(customerIDs: self.customerIDs, sectionName: self.sectionName, orderID: self.orderID)
8484
builder.build(trackData: tracker)

AutocompleteClientTests/FW/Logic/Worker/ConstructorIOIntegrationTests.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class ConstructorIOIntegrationTests: XCTestCase {
3131
fileprivate let numResultsPerPage = 5
3232
fileprivate let numResultsViewed = 5
3333
fileprivate let resultPage = 1
34+
fileprivate let conversionType = "add_to_cart"
3435

3536
var constructor: ConstructorIO!
3637

@@ -136,7 +137,7 @@ class ConstructorIOIntegrationTests: XCTestCase {
136137

137138
func testConversion() {
138139
let expectation = XCTestExpectation(description: "Tracking 204")
139-
self.constructor.trackConversion(itemName: itemName, customerID: customerID, revenue: revenue, searchTerm: searchTerm, sectionName: sectionName, completionHandler: { response in
140+
self.constructor.trackConversion(itemName: itemName, customerID: customerID, revenue: revenue, searchTerm: searchTerm, sectionName: sectionName, conversionType: conversionType, completionHandler: { response in
140141
let cioError = response.error as? CIOError
141142
XCTAssertNil(cioError)
142143
expectation.fulfill()

0 commit comments

Comments
 (0)