Skip to content

Commit

Permalink
Update attribution call (#3291)
Browse files Browse the repository at this point in the history
Task/Issue URL: https://app.asana.com/0/414235014887631/1208185326103221/f
Tech Design URL:
CC:

Description:

This PR updates the attribution call.
  • Loading branch information
samsymons authored Aug 30, 2024
1 parent 7e51fdd commit 28eb63d
Show file tree
Hide file tree
Showing 4 changed files with 21 additions and 18 deletions.
1 change: 1 addition & 0 deletions Core/Pixel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ public struct PixelParameters {
public static let adAttributionCountryOrRegion = "country_or_region"
public static let adAttributionKeywordID = "keyword_id"
public static let adAttributionAdID = "ad_id"
public static let adAttributionToken = "attribution_token"

// Autofill
public static let countBucket = "count_bucket"
Expand Down
8 changes: 4 additions & 4 deletions DuckDuckGo/AdAttribution/AdAttributionFetcher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import AdServices
import Common

protocol AdAttributionFetcher {
func fetch() async -> AdServicesAttributionResponse?
func fetch() async -> (String, AdServicesAttributionResponse)?
}

/// Fetches ad attribution data for from Apple.
Expand All @@ -45,7 +45,7 @@ struct DefaultAdAttributionFetcher: AdAttributionFetcher {
self.retryInterval = retryInterval
}

func fetch() async -> AdServicesAttributionResponse? {
func fetch() async -> (String, AdServicesAttributionResponse)? {
var lastToken: String?

for _ in 0..<Constant.maxRetries {
Expand Down Expand Up @@ -79,7 +79,7 @@ struct DefaultAdAttributionFetcher: AdAttributionFetcher {
return nil
}

private func fetchAttributionData(using token: String) async throws -> AdServicesAttributionResponse {
private func fetchAttributionData(using token: String) async throws -> (String, AdServicesAttributionResponse) {
let request = createAttributionDataRequest(with: token)
let (data, response) = try await urlSession.data(for: request)

Expand All @@ -92,7 +92,7 @@ struct DefaultAdAttributionFetcher: AdAttributionFetcher {
let decoder = JSONDecoder()
let decoded = try decoder.decode(AdServicesAttributionResponse.self, from: data)

return decoded
return (token, decoded)
case 400:
throw AdAttributionFetcherError.invalidToken
case 404:
Expand Down
7 changes: 4 additions & 3 deletions DuckDuckGo/AdAttribution/AdAttributionPixelReporter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ final actor AdAttributionPixelReporter {
isSendingAttribution = false
}

if let attributionData = await self.attributionFetcher.fetch() {
if let (token, attributionData) = await self.attributionFetcher.fetch() {
if attributionData.attribution {
let parameters = self.pixelParametersForAttribution(attributionData)
let parameters = self.pixelParametersForAttribution(attributionData, attributionToken: token)
do {
try await pixelFiring.fire(
pixel: .appleAdAttribution,
Expand All @@ -77,7 +77,7 @@ final actor AdAttributionPixelReporter {
return false
}

private func pixelParametersForAttribution(_ attribution: AdServicesAttributionResponse) -> [String: String] {
private func pixelParametersForAttribution(_ attribution: AdServicesAttributionResponse, attributionToken: String) -> [String: String] {
var params: [String: String] = [:]

params[PixelParameters.adAttributionAdGroupID] = attribution.adGroupId.map(String.init)
Expand All @@ -88,6 +88,7 @@ final actor AdAttributionPixelReporter {
params[PixelParameters.adAttributionCountryOrRegion] = attribution.countryOrRegion
params[PixelParameters.adAttributionKeywordID] = attribution.keywordId.map(String.init)
params[PixelParameters.adAttributionAdID] = attribution.adId.map(String.init)
params[PixelParameters.adAttributionToken] = attributionToken

return params
}
Expand Down
23 changes: 12 additions & 11 deletions DuckDuckGoTests/AdAttributionPixelReporterTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ final class AdAttributionPixelReporterTests: XCTestCase {

func testReportsAttribution() async {
let sut = createSUT()
attributionFetcher.fetchResponse = AdServicesAttributionResponse(attribution: true)
attributionFetcher.fetchResponse = ("example", AdServicesAttributionResponse(attribution: true))

let result = await sut.reportAttributionIfNeeded()

Expand All @@ -50,7 +50,7 @@ final class AdAttributionPixelReporterTests: XCTestCase {

func testReportsOnce() async {
let sut = createSUT()
attributionFetcher.fetchResponse = AdServicesAttributionResponse(attribution: true)
attributionFetcher.fetchResponse = ("example", AdServicesAttributionResponse(attribution: true))

await fetcherStorage.markAttributionReportSuccessful()
let result = await sut.reportAttributionIfNeeded()
Expand All @@ -61,7 +61,7 @@ final class AdAttributionPixelReporterTests: XCTestCase {

func testPixelname() async {
let sut = createSUT()
attributionFetcher.fetchResponse = AdServicesAttributionResponse(attribution: true)
attributionFetcher.fetchResponse = ("example", AdServicesAttributionResponse(attribution: true))

let result = await sut.reportAttributionIfNeeded()

Expand All @@ -71,7 +71,7 @@ final class AdAttributionPixelReporterTests: XCTestCase {

func testPixelAttributesNaming() async throws {
let sut = createSUT()
attributionFetcher.fetchResponse = AdServicesAttributionResponse(attribution: true)
attributionFetcher.fetchResponse = ("example", AdServicesAttributionResponse(attribution: true))

await sut.reportAttributionIfNeeded()

Expand All @@ -84,11 +84,12 @@ final class AdAttributionPixelReporterTests: XCTestCase {
XCTAssertEqual(pixelAttributes["country_or_region"], "countryOrRegion")
XCTAssertEqual(pixelAttributes["keyword_id"], "4")
XCTAssertEqual(pixelAttributes["ad_id"], "5")
XCTAssertEqual(pixelAttributes["attribution_token"], "example")
}

func testPixelAdditionalParameters() async throws {
let sut = createSUT()
attributionFetcher.fetchResponse = AdServicesAttributionResponse(attribution: true)
attributionFetcher.fetchResponse = ("example", AdServicesAttributionResponse(attribution: true))

await sut.reportAttributionIfNeeded()

Expand All @@ -99,7 +100,7 @@ final class AdAttributionPixelReporterTests: XCTestCase {

func testPixelAttributes_WhenPartialAttributionData() async throws {
let sut = createSUT()
attributionFetcher.fetchResponse = AdServicesAttributionResponse(
attributionFetcher.fetchResponse = ("example", AdServicesAttributionResponse(
attribution: true,
orgId: 1,
campaignId: 2,
Expand All @@ -108,7 +109,7 @@ final class AdAttributionPixelReporterTests: XCTestCase {
countryOrRegion: nil,
keywordId: nil,
adId: nil
)
))

await sut.reportAttributionIfNeeded()

Expand All @@ -125,7 +126,7 @@ final class AdAttributionPixelReporterTests: XCTestCase {

func testPixelNotFiredAndMarksReport_WhenAttributionFalse() async {
let sut = createSUT()
attributionFetcher.fetchResponse = AdServicesAttributionResponse(attribution: false)
attributionFetcher.fetchResponse = ("example", AdServicesAttributionResponse(attribution: false))

let result = await sut.reportAttributionIfNeeded()

Expand All @@ -147,7 +148,7 @@ final class AdAttributionPixelReporterTests: XCTestCase {

func testDoesNotMarkSuccessful_WhenPixelFiringFailed() async {
let sut = createSUT()
attributionFetcher.fetchResponse = AdServicesAttributionResponse(attribution: true)
attributionFetcher.fetchResponse = ("example", AdServicesAttributionResponse(attribution: true))
PixelFiringMock.expectedFireError = NSError(domain: "PixelFailure", code: 1)

let result = await sut.reportAttributionIfNeeded()
Expand All @@ -172,8 +173,8 @@ class AdAttributionReporterStorageMock: AdAttributionReporterStorage {
}

class AdAttributionFetcherMock: AdAttributionFetcher {
var fetchResponse: AdServicesAttributionResponse?
func fetch() async -> AdServicesAttributionResponse? {
var fetchResponse: (String, AdServicesAttributionResponse)?
func fetch() async -> (String, AdServicesAttributionResponse)? {
fetchResponse
}
}
Expand Down

0 comments on commit 28eb63d

Please sign in to comment.