Skip to content

Commit

Permalink
Submit and 3ds events (#1670)
Browse files Browse the repository at this point in the history
Submit and 3ds related log events.

Since there are several log events sent in a row, clearing the whole
array approach proved insufficient so we are removing only events that
are sent in that request.
  • Loading branch information
erenbesel authored May 14, 2024
1 parent 8d6da30 commit e8422b8
Show file tree
Hide file tree
Showing 28 changed files with 360 additions and 71 deletions.
12 changes: 6 additions & 6 deletions Adyen.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,8 @@
A0BDF3F02BD28817001FF7E5 /* AddressAnalyticsValidationError.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0BDF3EF2BD28817001FF7E5 /* AddressAnalyticsValidationError.swift */; };
A0BDF3F22BD29E69001FF7E5 /* PostalCodeValidatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0BDF3F12BD29E69001FF7E5 /* PostalCodeValidatorTests.swift */; };
A0BDF3F42BD6A82C001FF7E5 /* AnalyticsConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0BDF3F32BD6A82C001FF7E5 /* AnalyticsConstants.swift */; };
A0BDF3F72BEB9CCF001FF7E5 /* AnalyticsProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9C0005D280474DF00CE2EEC /* AnalyticsProviderTests.swift */; };
A0BDF3F82BEB9CF6001FF7E5 /* AnalyticsEventDataSourceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A05A9B7D2B8F8DF300E5E178 /* AnalyticsEventDataSourceTests.swift */; };
A0C9B59B288AE34600D6BDAB /* InstallmentOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0F41E5526CBCA6E0089AD6C /* InstallmentOptions.swift */; };
A0D48FB827109B0200C0B82C /* ArrayHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0D48FB727109B0200C0B82C /* ArrayHelpers.swift */; };
A0DB48662AFD020400348C83 /* AnalyticsRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0DB48652AFD020400348C83 /* AnalyticsRequest.swift */; };
Expand All @@ -374,10 +376,8 @@
A0F455A12968472B001742C7 /* PartialPaymentMethodDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0F455A02968472B001742C7 /* PartialPaymentMethodDetails.swift */; };
A0FA143F26D65A5300627127 /* InstallmentPickerElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0FA143E26D65A5300627127 /* InstallmentPickerElement.swift */; };
A0FA145726D65B6200627127 /* FormCardInstallmentsItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0FA145626D65B6200627127 /* FormCardInstallmentsItemView.swift */; };
B62D48B32BBE8DBE001EF01A /* AnalyticsEventDataSourceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A05A9B7D2B8F8DF300E5E178 /* AnalyticsEventDataSourceTests.swift */; };
B62D48B42BBE8DBE001EF01A /* AnalyticsFlavorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C97C16AB280702B200534419 /* AnalyticsFlavorTests.swift */; };
B62D48B52BBE8DBE001EF01A /* AnalyticsEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C97C16A928059A5A00534419 /* AnalyticsEventTests.swift */; };
B62D48B62BBE8DBE001EF01A /* AnalyticsProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9C0005D280474DF00CE2EEC /* AnalyticsProviderTests.swift */; };
B62D48B72BBE8DBE001EF01A /* AnalyticsProviderMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9C0005B280468E100CE2EEC /* AnalyticsProviderMock.swift */; };
B62D48C02BBE8DF5001EF01A /* APIClientMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = E757F02525D2ED4C007C813D /* APIClientMock.swift */; };
B62D48C42BBE8F25001EF01A /* XCTestCase+Wait.swift in Sources */ = {isa = PBXBuildFile; fileRef = B62D48C22BBE8ED6001EF01A /* XCTestCase+Wait.swift */; };
Expand Down Expand Up @@ -6611,6 +6611,7 @@
B6D808282BCD166D00F3F5EB /* PostalAddressMocks.swift in Sources */,
B6D808232BCD151F00F3F5EB /* ActionHandlingComponentMock.swift in Sources */,
B6D8082D2BCD178B00F3F5EB /* XCTestCase+Result.swift in Sources */,
A0BDF3F82BEB9CF6001FF7E5 /* AnalyticsEventDataSourceTests.swift in Sources */,
B6D808362BCD1F8900F3F5EB /* PhoneExtensionsRepositoryTests.swift in Sources */,
B6D808122BCD147D00F3F5EB /* BrowserInfoTests.swift in Sources */,
B6D808032BCD140E00F3F5EB /* AssetsAccessTests.swift in Sources */,
Expand All @@ -6621,7 +6622,6 @@
B6D808382BCD1F8900F3F5EB /* AssertsTests.swift in Sources */,
B6D808392BCD1F8900F3F5EB /* LengthValidatorTests.swift in Sources */,
B6D808342BCD1F8900F3F5EB /* LazyOptionalTests.swift in Sources */,
B62D48B62BBE8DBE001EF01A /* AnalyticsProviderTests.swift in Sources */,
B6D808072BCD147600F3F5EB /* TimeIntervalExtensionsTests.swift in Sources */,
B6D808082BCD147600F3F5EB /* URLExtensionsTest.swift in Sources */,
B6D8080E2BCD147D00F3F5EB /* APIContextTests.swift in Sources */,
Expand All @@ -6632,14 +6632,14 @@
B6D808252BCD155400F3F5EB /* String+UIImage.swift in Sources */,
B6D8081A2BCD151F00F3F5EB /* ImageLoaderMock.swift in Sources */,
B6D8082C2BCD176E00F3F5EB /* XCTestCase+Coder.swift in Sources */,
A0BDF3F72BEB9CCF001FF7E5 /* AnalyticsProviderTests.swift in Sources */,
81A91AB72BEBEA21001E00C8 /* OpenExternalAppDetectorTests.swift in Sources */,
B6D808222BCD151F00F3F5EB /* PaymentComponentDelegateMock.swift in Sources */,
B6D808102BCD147D00F3F5EB /* BackoffSchedulerTests.swift in Sources */,
B6D808452BCD2B0600F3F5EB /* RSAOAEP256AlgorithmTests.swift in Sources */,
B6D8082F2BCD1F8900F3F5EB /* KeyboardObserverTests.swift in Sources */,
B6D8081F2BCD151F00F3F5EB /* FormatterMock.swift in Sources */,
B6D808322BCD1F8900F3F5EB /* BalanceCheckerTests.swift in Sources */,
B62D48B32BBE8DBE001EF01A /* AnalyticsEventDataSourceTests.swift in Sources */,
B6D808192BCD151F00F3F5EB /* PaymentMethodMock.swift in Sources */,
B6D808052BCD147600F3F5EB /* KeyedDecodingContainerExtensionsTests.swift in Sources */,
B6D808092BCD147600F3F5EB /* StringExtensionsTests.swift in Sources */,
Expand Down Expand Up @@ -8115,7 +8115,7 @@
GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_LSApplicationCategoryType = "";
IPHONEOS_DEPLOYMENT_TARGET = 17.2;
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = "";
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
Expand All @@ -8141,7 +8141,7 @@
GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_LSApplicationCategoryType = "";
IPHONEOS_DEPLOYMENT_TARGET = 17.2;
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = "";
MTL_ENABLE_DEBUG_INFO = NO;
Expand Down
13 changes: 11 additions & 2 deletions Adyen/Analytics/AnalyticsProvider/AnalyticsProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,10 @@ internal final class AnalyticsProvider: AnalyticsProviderProtocol {

apiClient.perform(request) { [weak self] result in
guard let self else { return }
// clear the current events on successful send
// clear the sent events on successful send
switch result {
case .success:
self.eventDataSource.removeAllEvents()
self.removeEvents(sentBy: request)
self.startNextTimer()
case .failure:
break
Expand Down Expand Up @@ -146,6 +146,15 @@ internal final class AnalyticsProvider: AnalyticsProviderProtocol {
}
}

private func removeEvents(sentBy request: AnalyticsRequest) {
let collection = AnalyticsEventWrapper(
infos: request.infos,
logs: request.logs,
errors: request.errors
)
eventDataSource.removeEvents(matching: collection)
}

private func startNextTimer() {
batchTimer?.invalidate()
batchTimer = Timer.scheduledTimer(withTimeInterval: batchInterval, repeats: true) { [weak self] _ in
Expand Down
36 changes: 29 additions & 7 deletions Adyen/Analytics/Models/AnalyticsEventDataSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ internal class AnalyticsEventDataSource: AnyAnalyticsEventDataSource {
private var logs: [AnalyticsEventLog] = []
private var errors: [AnalyticsEventError] = []

// MARK: - AnyAnalyticsEventDataSource
// MARK: - AnalyticsEventDataAddition

internal func add(info: AnalyticsEventInfo) {
infos.append(info)
Expand All @@ -28,12 +28,6 @@ internal class AnalyticsEventDataSource: AnyAnalyticsEventDataSource {
errors.append(error)
}

internal func removeAllEvents() {
infos = []
logs = []
errors = []
}

internal func allEvents() -> AnalyticsEventWrapper? {
if infos.isEmpty, logs.isEmpty, errors.isEmpty {
return nil
Expand All @@ -43,4 +37,32 @@ internal class AnalyticsEventDataSource: AnyAnalyticsEventDataSource {
errors: errors)
}

// MARK: - AnalyticsEventDataRemoval

internal func removeAllEvents() {
infos = []
logs = []
errors = []
}

internal func removeEvents(matching collection: AnalyticsEventWrapper) {
remove(infoEvents: collection.infos)
remove(logEvents: collection.logs)
remove(errorEvents: collection.errors)
}

private func remove(infoEvents: [AnalyticsEventInfo]) {
let infoIds = infoEvents.map(\.id)
infos.removeAll { infoIds.contains($0.id) }
}

private func remove(logEvents: [AnalyticsEventLog]) {
let logIds = logEvents.map(\.id)
logs.removeAll { logIds.contains($0.id) }
}

private func remove(errorEvents: [AnalyticsEventError]) {
let errorIds = errorEvents.map(\.id)
errors.removeAll { errorIds.contains($0.id) }
}
}
5 changes: 5 additions & 0 deletions Adyen/Analytics/Models/AnalyticsEventLog.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ public struct AnalyticsEventLog: AnalyticsEvent {
case qrCode = "QrCode"
case bankTransfer = "BankTransfer"
case sdk = "Sdk"
case fingerprintSent = "FingerprintDataSentMobile"
case fingerprintComplete = "FingerprintCompleted"
case challengeDataSent = "ChallengeDataSentMobile"
case challengeDisplayed = "ChallengeDisplayed"
case challengeComplete = "ChallengeCompleted"
}

public init(component: String, type: LogType, subType: LogSubType? = nil) {
Expand Down
34 changes: 25 additions & 9 deletions Adyen/Analytics/Models/ThreadSafeAnalyticsEventDataSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,24 @@

import Foundation

internal protocol AnyAnalyticsEventDataSource {

internal protocol AnalyticsEventDataAddition {
func add(info: AnalyticsEventInfo)
func add(log: AnalyticsEventLog)
func add(error: AnalyticsEventError)

func allEvents() -> AnalyticsEventWrapper?
}

internal protocol AnalyticsEventDataRemoval {

func removeAllEvents()

func allEvents() -> AnalyticsEventWrapper?
/// Removes events matching given collection.
func removeEvents(matching collection: AnalyticsEventWrapper)
}

internal protocol AnyAnalyticsEventDataSource: AnalyticsEventDataAddition & AnalyticsEventDataRemoval {}

internal struct AnalyticsEventWrapper {
internal var infos: [AnalyticsEventInfo]
internal var logs: [AnalyticsEventLog]
Expand All @@ -38,7 +45,7 @@ internal final class ThreadSafeAnalyticsEventDataSource: AnyAnalyticsEventDataSo
self.queue = DispatchQueue(label: Constants.queueLabel, attributes: .concurrent)
}

// MARK: - AnyAnalyticsEventHandler
// MARK: - AnalyticsEventDataAddition

internal func add(info: AnalyticsEventInfo) {
queue.sync(flags: .barrier) {
Expand All @@ -58,15 +65,24 @@ internal final class ThreadSafeAnalyticsEventDataSource: AnyAnalyticsEventDataSo
}
}

internal func removeAllEvents() {
internal func allEvents() -> AnalyticsEventWrapper? {
queue.sync {
dataSource.allEvents()
}
}

// MARK: - AnalyticsEventDataRemoval

internal func removeEvents(matching collection: AnalyticsEventWrapper) {
queue.sync(flags: .barrier) {
dataSource.removeAllEvents()
dataSource.removeEvents(matching: collection)
}
}

internal func allEvents() -> AnalyticsEventWrapper? {
queue.sync {
dataSource.allEvents()
internal func removeAllEvents() {
queue.sync(flags: .barrier) {
dataSource.removeAllEvents()
}
}

}
8 changes: 7 additions & 1 deletion Adyen/Core/Core Protocols/PaymentComponent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ extension PaymentComponent {
/// - data: The Payment data to be submitted
/// - component: The component from which the payment originates.
public func submit(data: PaymentComponentData, component: PaymentComponent? = nil) {
sendSubmitEvent()

let component = component ?? self

let updatedData = data.replacing(checkoutAttemptId: component.context.analyticsProvider?.checkoutAttemptId)
Expand All @@ -41,7 +43,11 @@ extension PaymentComponent {
updatedData.dataByAddingBrowserInfo { [weak self] in
self?.delegate?.didSubmit($0, from: component)
}

}

private func sendSubmitEvent() {
let logEvent = AnalyticsEventLog(component: paymentMethod.type.rawValue, type: .submit)
context.analyticsProvider?.add(log: logEvent)
}

}
Expand Down
32 changes: 32 additions & 0 deletions AdyenActions/AdyenActionComponent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,9 @@ public final class AdyenActionComponent: ActionComponent, ActionHandlingComponen
///
/// - Parameter action: The action to handle.
public func handle(_ action: Action) {

sendHandleEvent(for: action)

switch action {
case let .redirect(redirectAction):
handle(redirectAction)
Expand All @@ -158,6 +161,11 @@ public final class AdyenActionComponent: ActionComponent, ActionHandlingComponen
}
}

private func sendHandleEvent(for action: Action) {
let logEvent = AnalyticsEventLog(component: action.analyticsType, type: .action)
context.analyticsProvider?.add(log: logEvent)
}

// MARK: - Private

private func handle(_ action: RedirectAction) {
Expand Down Expand Up @@ -305,3 +313,27 @@ public final class AdyenActionComponent: ActionComponent, ActionHandlingComponen
currentActionComponent = component
}
}

private extension Action {

var analyticsType: String {
switch self {
case .redirect:
return "redirect"
case .sdk:
return "sdk"
case .threeDS2Fingerprint:
return "threeDS2Fingerprint"
case .threeDS2Challenge:
return "threeDS2Challenge"
case .threeDS2:
return "threeDS2"
case .await:
return "await"
case .voucher, .document:
return "voucher"
case .qrCode:
return "qrCode"
}
}
}
Loading

0 comments on commit e8422b8

Please sign in to comment.