diff --git a/Adyen.xcodeproj/project.pbxproj b/Adyen.xcodeproj/project.pbxproj index 713ef63829..dc50e54c96 100644 --- a/Adyen.xcodeproj/project.pbxproj +++ b/Adyen.xcodeproj/project.pbxproj @@ -282,7 +282,7 @@ A0D48FB827109B0200C0B82C /* ArrayHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0D48FB727109B0200C0B82C /* ArrayHelpers.swift */; }; A0DB48662AFD020400348C83 /* AdyenAnalyticsRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0DB48652AFD020400348C83 /* AdyenAnalyticsRequest.swift */; }; A0DB48682AFD068400348C83 /* AdyenAnalytics.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0DB48672AFD068400348C83 /* AdyenAnalytics.swift */; }; - A0DB486A2AFD0BDC00348C83 /* AdyenAnalyticsEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0DB48692AFD0BDC00348C83 /* AdyenAnalyticsEvent.swift */; }; + A0DB486A2AFD0BDC00348C83 /* AdyenAnalyticsInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0DB48692AFD0BDC00348C83 /* AdyenAnalyticsInfo.swift */; }; A0DB486C2AFD0BEE00348C83 /* AdyenAnalyticsLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0DB486B2AFD0BEE00348C83 /* AdyenAnalyticsLog.swift */; }; A0DB486E2AFD0BFC00348C83 /* AdyenAnalyticsError.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0DB486D2AFD0BFC00348C83 /* AdyenAnalyticsError.swift */; }; A0DDA6A72A6162F500EBD6AF /* AdyenSessionResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0DDA6A62A6162F500EBD6AF /* AdyenSessionResult.swift */; }; @@ -1521,7 +1521,7 @@ A0D48FB727109B0200C0B82C /* ArrayHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrayHelpers.swift; sourceTree = ""; }; A0DB48652AFD020400348C83 /* AdyenAnalyticsRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdyenAnalyticsRequest.swift; sourceTree = ""; }; A0DB48672AFD068400348C83 /* AdyenAnalytics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdyenAnalytics.swift; sourceTree = ""; }; - A0DB48692AFD0BDC00348C83 /* AdyenAnalyticsEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdyenAnalyticsEvent.swift; sourceTree = ""; }; + A0DB48692AFD0BDC00348C83 /* AdyenAnalyticsInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdyenAnalyticsInfo.swift; sourceTree = ""; }; A0DB486B2AFD0BEE00348C83 /* AdyenAnalyticsLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdyenAnalyticsLog.swift; sourceTree = ""; }; A0DB486D2AFD0BFC00348C83 /* AdyenAnalyticsError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdyenAnalyticsError.swift; sourceTree = ""; }; A0DDA6A62A6162F500EBD6AF /* AdyenSessionResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdyenSessionResult.swift; sourceTree = ""; }; @@ -3130,7 +3130,7 @@ children = ( C9B6683627C8D7FB006950B9 /* TelemetryData.swift */, A0DB48672AFD068400348C83 /* AdyenAnalytics.swift */, - A0DB48692AFD0BDC00348C83 /* AdyenAnalyticsEvent.swift */, + A0DB48692AFD0BDC00348C83 /* AdyenAnalyticsInfo.swift */, A0DB486B2AFD0BEE00348C83 /* AdyenAnalyticsLog.swift */, A0DB486D2AFD0BFC00348C83 /* AdyenAnalyticsError.swift */, ); @@ -6662,7 +6662,7 @@ E2C0E096220B04F0008616F6 /* FormViewController.swift in Sources */, F9FE25D62626CD49001874BB /* ReadyToSubmitPaymentComponentDelegate.swift in Sources */, F99D4556262717A500880D72 /* FormErrorItemView.swift in Sources */, - A0DB486A2AFD0BDC00348C83 /* AdyenAnalyticsEvent.swift in Sources */, + A0DB486A2AFD0BDC00348C83 /* AdyenAnalyticsInfo.swift in Sources */, F96286BD256BDEB000043FE3 /* CoreBundleExtension.swift in Sources */, E7085D7D262EEF6200D0153B /* AllRegions.swift in Sources */, F919DF9124E682480027976E /* ClientKeyResponse.swift in Sources */, diff --git a/Adyen/Analytics/AnalyticsProvider/AnalyticsProvider.swift b/Adyen/Analytics/AnalyticsProvider/AnalyticsProvider.swift index f276693938..9ca4638e0a 100644 --- a/Adyen/Analytics/AnalyticsProvider/AnalyticsProvider.swift +++ b/Adyen/Analytics/AnalyticsProvider/AnalyticsProvider.swift @@ -42,14 +42,10 @@ public struct AdditionalAnalyticsFields { } @_spi(AdyenInternal) -public protocol InitialTelemetryProtocol { +public protocol AnalyticsProviderProtocol { /// Sends the initial data and retrieves the checkout attempt id as a response. - func fetchCheckoutAttemptId(with flavor: TelemetryFlavor, additionalFields: AdditionalAnalyticsFields?) -} - -@_spi(AdyenInternal) -public protocol AnalyticsProviderProtocol: InitialTelemetryProtocol { + func sendInitialAnalytics(with flavor: TelemetryFlavor, additionalFields: AdditionalAnalyticsFields?) var checkoutAttemptId: String? { get } } @@ -65,7 +61,7 @@ internal final class AnalyticsProvider: AnalyticsProviderProtocol { private var batchTimer: Timer? - private var events: [AdyenAnalytics.Event] = [] + private var infos: [AdyenAnalytics.Info] = [] private var logs: [AdyenAnalytics.Log] = [] private var errors: [AdyenAnalytics.Error] = [] @@ -82,7 +78,7 @@ internal final class AnalyticsProvider: AnalyticsProviderProtocol { // MARK: - Internal - internal func fetchCheckoutAttemptId(with flavor: TelemetryFlavor, additionalFields: AdditionalAnalyticsFields?) { + internal func sendInitialAnalytics(with flavor: TelemetryFlavor, additionalFields: AdditionalAnalyticsFields?) { guard configuration.isEnabled, configuration.isTelemetryEnabled else { checkoutAttemptId = "do-not-track" return @@ -105,8 +101,8 @@ internal final class AnalyticsProvider: AnalyticsProviderProtocol { } } - internal func send(event: AdyenAnalytics.Event) { - events.append(event) + internal func send(info: AdyenAnalytics.Info) { + infos.append(info) } internal func send(log: AdyenAnalytics.Log) { @@ -129,7 +125,7 @@ internal final class AnalyticsProvider: AnalyticsProviderProtocol { let checkoutAttemptId else { return } var request = AdyenAnalyticsRequest(checkoutAttemptId: checkoutAttemptId) - request.events = events + request.infos = infos request.logs = logs request.errors = errors @@ -140,7 +136,7 @@ internal final class AnalyticsProvider: AnalyticsProviderProtocol { } private func clearAll() { - events = [] + infos = [] logs = [] errors = [] } diff --git a/Adyen/Analytics/Models/AdyenAnalytics.swift b/Adyen/Analytics/Models/AdyenAnalytics.swift index dc57c9bc46..1684075aab 100644 --- a/Adyen/Analytics/Models/AdyenAnalytics.swift +++ b/Adyen/Analytics/Models/AdyenAnalytics.swift @@ -27,3 +27,8 @@ public final class AdyenAnalytics { internal protocol AdyenAnalyticsCommonFields: Encodable { var commonFields: AdyenAnalytics.CommonFields { get } } + +//@_spi(AdyenInternal) +//public protocol AdyenAnalyticEvent { +// var commonFields: AdyenAnalytics.CommonFields { get } +//} diff --git a/Adyen/Analytics/Models/AdyenAnalyticsEvent.swift b/Adyen/Analytics/Models/AdyenAnalyticsInfo.swift similarity index 77% rename from Adyen/Analytics/Models/AdyenAnalyticsEvent.swift rename to Adyen/Analytics/Models/AdyenAnalyticsInfo.swift index 293f6f36c4..b0f2589c5e 100644 --- a/Adyen/Analytics/Models/AdyenAnalyticsEvent.swift +++ b/Adyen/Analytics/Models/AdyenAnalyticsInfo.swift @@ -8,13 +8,13 @@ import Foundation extension AdyenAnalytics { - /// Represents an event in the analytics scheme that can occur + /// Represents an info event in the analytics scheme that can occur /// many times during the checkout flow, such as input field focus/unfocus etc. - internal struct Event: AdyenAnalyticsCommonFields { + internal struct Info: AdyenAnalyticsCommonFields { internal var commonFields: AdyenAnalytics.CommonFields - internal var type: EventType + internal var type: InfoType internal var target: String @@ -27,7 +27,7 @@ extension AdyenAnalytics { internal var validationErrorMessage: String } - internal enum EventType: String, Encodable { + internal enum InfoType: String, Encodable { case selected = "Selected" case focus = "Focus" case unfocus = "Unfocus" diff --git a/Adyen/Analytics/Requests/AdyenAnalyticsRequest.swift b/Adyen/Analytics/Requests/AdyenAnalyticsRequest.swift index ffd5e0c06e..b6354c4554 100644 --- a/Adyen/Analytics/Requests/AdyenAnalyticsRequest.swift +++ b/Adyen/Analytics/Requests/AdyenAnalyticsRequest.swift @@ -25,7 +25,7 @@ internal struct AdyenAnalyticsRequest: APIRequest { internal var channel: String = "iOS" - internal var events: [AdyenAnalytics.Event] = [] + internal var infos: [AdyenAnalytics.Info] = [] internal var logs: [AdyenAnalytics.Log] = [] @@ -37,7 +37,7 @@ internal struct AdyenAnalyticsRequest: APIRequest { private enum CodingKeys: String, CodingKey { case channel - case events + case infos = "info" case logs case errors diff --git a/Adyen/Core/Components/InstantPaymentComponent.swift b/Adyen/Core/Components/InstantPaymentComponent.swift index c7201d7af8..0865f3ebe2 100644 --- a/Adyen/Core/Components/InstantPaymentComponent.swift +++ b/Adyen/Core/Components/InstantPaymentComponent.swift @@ -56,7 +56,8 @@ public final class InstantPaymentComponent: PaymentComponent { /// Generate the payment details and invoke PaymentsComponentDelegate method. public func initiatePayment() { - // we can attempt to fetch the checkoutAttemptId but it won't be ready for the payment + // We are not attempting to fetch the checkoutAttemptId as it won't be ready for the payment + // and we don't want to block it for an analytics call. submit(data: paymentData) } } diff --git a/Adyen/Core/Core Protocols/PresentableComponent.swift b/Adyen/Core/Core Protocols/PresentableComponent.swift index bad7955221..02af4f94c7 100644 --- a/Adyen/Core/Core Protocols/PresentableComponent.swift +++ b/Adyen/Core/Core Protocols/PresentableComponent.swift @@ -73,7 +73,7 @@ extension TrackableComponent where Self: PaymentMethodAware { let flavor: TelemetryFlavor = _isDropIn ? .dropInComponent : .components(type: paymentMethod.type) let amount = context.payment?.amount let additionalFields = AdditionalAnalyticsFields(amount: amount, sessionId: AdyenAnalytics.sessionId) - context.analyticsProvider.fetchCheckoutAttemptId(with: flavor, + context.analyticsProvider.sendInitialAnalytics(with: flavor, additionalFields: additionalFields) } } diff --git a/AdyenComponents/BACS Direct Debit/Trackers/BACSDirectDebitComponentTracker.swift b/AdyenComponents/BACS Direct Debit/Trackers/BACSDirectDebitComponentTracker.swift index e348f18474..2de7b4b0ef 100644 --- a/AdyenComponents/BACS Direct Debit/Trackers/BACSDirectDebitComponentTracker.swift +++ b/AdyenComponents/BACS Direct Debit/Trackers/BACSDirectDebitComponentTracker.swift @@ -35,7 +35,7 @@ internal class BACSDirectDebitComponentTracker: BACSDirectDebitComponentTrackerP let flavor: TelemetryFlavor = isDropIn ? .dropInComponent : .components(type: paymentMethod.type) let amount = context.payment?.amount let additionalFields = AdditionalAnalyticsFields(amount: amount, sessionId: AdyenAnalytics.sessionId) - context.analyticsProvider.fetchCheckoutAttemptId(with: flavor, + context.analyticsProvider.sendInitialAnalytics(with: flavor, additionalFields: additionalFields) } diff --git a/AdyenDropIn/DropInComponentExtensions.swift b/AdyenDropIn/DropInComponentExtensions.swift index 58f9f066f7..fe39800394 100644 --- a/AdyenDropIn/DropInComponentExtensions.swift +++ b/AdyenDropIn/DropInComponentExtensions.swift @@ -25,7 +25,7 @@ extension DropInComponent: PaymentMethodListComponentDelegate { let flavor = TelemetryFlavor.dropIn(paymentMethods: paymentMethodTypes) let amount = context.payment?.amount let additionalFields = AdditionalAnalyticsFields(amount: amount, sessionId: AdyenAnalytics.sessionId) - context.analyticsProvider.fetchCheckoutAttemptId(with: flavor, additionalFields: additionalFields) + context.analyticsProvider.sendInitialAnalytics(with: flavor, additionalFields: additionalFields) } internal func didSelect(_ component: PaymentComponent, diff --git a/AdyenSession/AdyenSession.swift b/AdyenSession/AdyenSession.swift index 6915dbfd8c..02effb1e0d 100644 --- a/AdyenSession/AdyenSession.swift +++ b/AdyenSession/AdyenSession.swift @@ -119,7 +119,6 @@ public final class AdyenSession { sessionContext: sessionContext) session.delegate = delegate session.presentationDelegate = presentationDelegate - // Unfortunately needed by telemetry and this is the only way atm. AdyenAnalytics.sessionId = sessionContext.identifier completion(.success(session)) case let .failure(error): diff --git a/Tests/Adyen Tests/Analytics/AnalyticsProviderMock.swift b/Tests/Adyen Tests/Analytics/AnalyticsProviderMock.swift index 2e34223395..bfb0fc20ea 100644 --- a/Tests/Adyen Tests/Analytics/AnalyticsProviderMock.swift +++ b/Tests/Adyen Tests/Analytics/AnalyticsProviderMock.swift @@ -15,7 +15,7 @@ class AnalyticsProviderMock: AnalyticsProviderProtocol { // MARK: - checkoutAttemptId - func fetchCheckoutAttemptId(with flavor: TelemetryFlavor, additionalFields: AdditionalAnalyticsFields?) { + func sendInitialAnalytics(with flavor: TelemetryFlavor, additionalFields: AdditionalAnalyticsFields?) { initialTelemetryEventCallsCount += 1 checkoutAttemptId = _checkoutAttemptId } diff --git a/Tests/Adyen Tests/Analytics/AnalyticsProviderTests.swift b/Tests/Adyen Tests/Analytics/AnalyticsProviderTests.swift index 1ed3e6474c..d2bf83b064 100644 --- a/Tests/Adyen Tests/Analytics/AnalyticsProviderTests.swift +++ b/Tests/Adyen Tests/Analytics/AnalyticsProviderTests.swift @@ -39,7 +39,7 @@ class AnalyticsProviderTests: XCTestCase { let fetchCheckoutAttemptIdExpection = expectation(description: "checkoutAttemptId completion") // When - sut.fetchCheckoutAttemptId(with: .components(type: .achDirectDebit), additionalFields: nil) + sut.sendInitialAnalytics(with: .components(type: .achDirectDebit), additionalFields: nil) fetchCheckoutAttemptIdExpection.fulfill() wait(for: .milliseconds(200)) @@ -58,7 +58,7 @@ class AnalyticsProviderTests: XCTestCase { let sut = AnalyticsProvider(apiClient: apiClient, configuration: analyticsConfiguration) // When - sut.fetchCheckoutAttemptId(with: .components(type: .affirm), additionalFields: nil) + sut.sendInitialAnalytics(with: .components(type: .affirm), additionalFields: nil) XCTAssertEqual(sut.checkoutAttemptId, "do-not-track") } @@ -79,7 +79,7 @@ class AnalyticsProviderTests: XCTestCase { let fetchCheckoutAttemptIdExpection = expectation(description: "checkoutAttemptId completion") // When - sut.fetchCheckoutAttemptId(with: .components(type: .achDirectDebit), additionalFields: nil) + sut.sendInitialAnalytics(with: .components(type: .achDirectDebit), additionalFields: nil) fetchCheckoutAttemptIdExpection.fulfill() wait(for: .milliseconds(200)) @@ -104,7 +104,7 @@ class AnalyticsProviderTests: XCTestCase { apiClient.mockedResults = [checkoutAttemptIdResult] // When - sut.fetchCheckoutAttemptId(with: .dropInComponent, additionalFields: nil) + sut.sendInitialAnalytics(with: .dropInComponent, additionalFields: nil) // Then XCTAssertNil(sut.checkoutAttemptId, "The checkoutAttemptId is not nil.") } @@ -126,7 +126,7 @@ class AnalyticsProviderTests: XCTestCase { let fetchCheckoutAttemptIdExpection = expectation(description: "checkoutAttemptId completion") // When - sut.fetchCheckoutAttemptId(with: .components(type: .atome), additionalFields: nil) + sut.sendInitialAnalytics(with: .components(type: .atome), additionalFields: nil) fetchCheckoutAttemptIdExpection.fulfill() wait(for: .milliseconds(200)) @@ -150,7 +150,7 @@ class AnalyticsProviderTests: XCTestCase { apiClient.mockedResults = [checkoutAttemptIdResult] // When - sut.fetchCheckoutAttemptId(with: .components(type: .affirm), additionalFields: nil) + sut.sendInitialAnalytics(with: .components(type: .affirm), additionalFields: nil) // Then XCTAssertEqual(sut.checkoutAttemptId, "do-not-track") } @@ -182,7 +182,7 @@ class AnalyticsProviderTests: XCTestCase { // When - analyticsProvider.fetchCheckoutAttemptId(with: .components(type: .achDirectDebit), additionalFields: nil) + analyticsProvider.sendInitialAnalytics(with: .components(type: .achDirectDebit), additionalFields: nil) wait(for: [telemetryExpectation], timeout: 10) } @@ -219,7 +219,7 @@ class AnalyticsProviderTests: XCTestCase { // When let additionalFields = AdditionalAnalyticsFields(amount: amount) - analyticsProvider.fetchCheckoutAttemptId(with: .components(type: .achDirectDebit), additionalFields: additionalFields) + analyticsProvider.sendInitialAnalytics(with: .components(type: .achDirectDebit), additionalFields: additionalFields) wait(for: [telemetryExpectation], timeout: 10) } diff --git a/Tests/Adyen Tests/Analytics/TelemetryTrackerTests.swift b/Tests/Adyen Tests/Analytics/TelemetryTrackerTests.swift index 09d22b648c..5e6fcc7f5a 100644 --- a/Tests/Adyen Tests/Analytics/TelemetryTrackerTests.swift +++ b/Tests/Adyen Tests/Analytics/TelemetryTrackerTests.swift @@ -13,7 +13,7 @@ import XCTest class TelemetryTrackerTests: XCTestCase { var apiClient: APIClientMock! - var sut: InitialTelemetryProtocol! + var sut: AnalyticsProviderProtocol! override func setUpWithError() throws { try super.setUpWithError() @@ -31,7 +31,7 @@ class TelemetryTrackerTests: XCTestCase { } private func sendInitialTelemetry(flavor: TelemetryFlavor = .components(type: .achDirectDebit)) { - sut.fetchCheckoutAttemptId(with: flavor, additionalFields: nil) + sut.sendInitialAnalytics(with: flavor, additionalFields: nil) } func testSendTelemetryEventGivenAnalyticsIsDisabledAndTelemetryIsEnabledShouldNotSendAnyRequest() throws { @@ -63,6 +63,7 @@ class TelemetryTrackerTests: XCTestCase { // Then XCTAssertEqual(expectedRequestCalls, apiClient.counter, "One or more telemetry requests were sent.") + XCTAssertEqual(sut.checkoutAttemptId, "do-not-track") } func testSendTelemetryEventGivenTelemetryIsEnabledAndFlavorIsDropInComponentShouldNotSendAnyRequest() throws { @@ -79,6 +80,7 @@ class TelemetryTrackerTests: XCTestCase { // Then XCTAssertEqual(expectedRequestCalls, apiClient.counter, "One or more telemetry requests were sent.") + XCTAssertNil(sut.checkoutAttemptId) } func testSendTelemetryEventGivenTelemetryIsEnabledAndFlavorIsComponentsShouldSendTelemetryRequest() throws { @@ -99,6 +101,7 @@ class TelemetryTrackerTests: XCTestCase { // Then wait(for: .milliseconds(1)) XCTAssertEqual(expectedRequestCalls, apiClient.counter, "Invalid request number made.") + XCTAssertEqual(sut.checkoutAttemptId, "cb3eef98-978e-4f6f-b299-937a4450be1f1648546838056be73d8f38ee8bcc3a65ec14e41b037a59f255dcd9e83afe8c06bd3e7abcad993") } func testSendTelemetryEventGivenTelemetryIsEnabledAndFlavorIsDropInShouldSendTelemetryRequest() throws { diff --git a/Tests/Adyen Tests/Core/AdyenContextTests.swift b/Tests/Adyen Tests/Core/AdyenContextTests.swift index 7284940398..915d7a5cdb 100644 --- a/Tests/Adyen Tests/Core/AdyenContextTests.swift +++ b/Tests/Adyen Tests/Core/AdyenContextTests.swift @@ -21,10 +21,8 @@ class AdyenContextTests: XCTestCase { payment: .init(amount: oneEUR, countryCode: "NL") ) -// XCTAssertEqual(context.analyticsProvider.additionalFields?().amount, oneEUR) - + XCTAssertEqual(context.payment?.amount, oneEUR) context.update(payment: Payment(amount: twoEUR, countryCode: "NL")) - -// XCTAssertEqual(context.analyticsProvider.additionalFields?().amount, twoEUR) + XCTAssertEqual(context.payment?.amount, twoEUR) } }