Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

COIOS-802: Identify native redirect flow (v5) #1879

Draft
wants to merge 21 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
ceae25f
Add NativeRedirectAction
nauaros Oct 15, 2024
9bd40ca
Update unit tests
nauaros Oct 25, 2024
39675e8
Test native redirect flow when nativeRedirectData is nil
nauaros Oct 25, 2024
7efd84d
Merge branch 'develop' into COIOS-802_identify_native_redirect_flow
goergisn Oct 25, 2024
d574e11
Flag internallty nativeRedirect flow
nauaros Oct 28, 2024
a8f3f1c
Merge remote-tracking branch 'refs/remotes/origin/COIOS-802_identify_…
nauaros Oct 28, 2024
d339c28
Update unit tests
nauaros Oct 28, 2024
c5756a8
Merge branch 'develop' into COIOS-802_identify_native_redirect_flow
nauaros Oct 29, 2024
1a3c809
Expose redirect type
nauaros Oct 29, 2024
5c64d5c
Make RedirectAction value type
nauaros Oct 29, 2024
e6a8a46
Update documentation
nauaros Oct 29, 2024
ad894f8
Update documentation
nauaros Oct 29, 2024
9b827b6
Merge branch 'develop' into COIOS-802_identify_native_redirect_flow
nauaros Oct 29, 2024
68a0bb0
Refactor RedirectType decoding
nauaros Oct 31, 2024
ae3ed8d
Merge branch 'develop' into COIOS-802_identify_native_redirect_flow
nauaros Oct 31, 2024
8f36bd1
Explicitly indicate decoding value
nauaros Oct 31, 2024
63c1355
Make redirect type private
nauaros Nov 1, 2024
3d9f471
Change redirect type to be internal
nauaros Nov 4, 2024
896e151
Merge branch 'develop' into COIOS-802_identify_native_redirect_flow
goergisn Nov 14, 2024
10e1eab
Merge branch 'develop' into COIOS-802_identify_native_redirect_flow
goergisn Nov 15, 2024
6721b79
Merge branch 'develop' into COIOS-802_identify_native_redirect_flow
goergisn Nov 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AdyenActions/Actions/Action.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public enum Action: Decodable {

/// Indicates the user should be redirected to a URL.
case redirect(RedirectAction)

/// Indicates the user should be redirected to an SDK.
case sdk(SDKAction)

Expand Down
50 changes: 45 additions & 5 deletions AdyenActions/Actions/RedirectAction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,64 @@ import Foundation
/// Describes an action in which the user is redirected to a URL.
public struct RedirectAction: Decodable {

/// Defines the type of redirect flow utilized by the `RedirectAction` object.
public enum RedirectType: String, Decodable {
// swiftlint:disable redundant_string_enum_value
case redirect = "redirect"
case nativeRedirect = "nativeRedirect"

public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let type = try container.decode(String.self)
self = RedirectType(rawValue: type) ?? .redirect
}
}

/// The URL to which to redirect the user.
public let url: URL

/// The server-generated payment data that should be submitted to the `/payments/details` endpoint.
public let paymentData: String?


/// Redirect type.
public let type: RedirectType
nauaros marked this conversation as resolved.
Show resolved Hide resolved

/// Native redirect data.
public let nativeRedirectData: String?
nauaros marked this conversation as resolved.
Show resolved Hide resolved

/// Initializes a redirect action.
///
/// - Parameters:
/// - url: The URL to which to redirect the user.
/// - paymentData: The server-generated payment data that should be submitted to the `/payments/details` endpoint.
/// - nativeRedirectData: Native redirect data.
public init(url: URL, paymentData: String?, nativeRedirectData: String? = nil) {
/// - type: The redirect flow used by the action. Defaults to `redirect`.
/// - nativeRedirectData: Native redirect data. Defaults to `nil`.
public init(
url: URL,
paymentData: String?,
type: RedirectType = .redirect,
nativeRedirectData: String? = nil
) {
self.url = url
self.paymentData = paymentData
self.type = type
self.nativeRedirectData = nativeRedirectData
}

public init(from decoder: any Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.url = try container.decode(URL.self, forKey: .url)
self.paymentData = try container.decodeIfPresent(String.self, forKey: .paymentData)
self.type = try container.decode(RedirectType.self, forKey: .type)
self.nativeRedirectData = try container.decodeIfPresent(String.self, forKey: .nativeRedirectData)
}

// MARK: - Private

private enum CodingKeys: CodingKey {
case url
case paymentData
case type
case nativeRedirectData
}
}
13 changes: 9 additions & 4 deletions AdyenActions/Components/Redirect/RedirectComponent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -172,14 +172,19 @@ public final class RedirectComponent: ActionComponent {
}

private func didOpen(url returnURL: URL, _ action: RedirectAction) throws {
if let redirectStateData = action.nativeRedirectData {
try handleNativeMobileRedirect(withReturnURL: returnURL, redirectStateData: redirectStateData, action)
} else {
switch action.type {
case .nativeRedirect:
try handleNativeMobileRedirect(
withReturnURL: returnURL,
redirectStateData: action.nativeRedirectData,
action
)
case .redirect:
try notifyDelegateDidProvide(redirectDetails: RedirectDetails(returnURL: returnURL), action)
}
}

private func handleNativeMobileRedirect(withReturnURL returnURL: URL, redirectStateData: String, _ action: RedirectAction) throws {
private func handleNativeMobileRedirect(withReturnURL returnURL: URL, redirectStateData: String?, _ action: RedirectAction) throws {
guard let queryString = returnURL.query else {
throw Error.invalidRedirectParameters
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,12 @@ class RedirectComponentTests: XCTestCase {
}
delegate.onDidFail = { _, _ in XCTFail("Should not call onDidFail") }

let action = RedirectAction(url: URL(string: "https://google.com")!, paymentData: nil, nativeRedirectData: "test_nativeRedirectData")
let action = RedirectAction(
url: URL(string: "https://google.com")!,
paymentData: nil,
type: .nativeRedirect,
nativeRedirectData: "test_nativeRedirectData"
)
sut.handle(action)
XCTAssertTrue(RedirectComponent.applicationDidOpen(from: URL(string: "url://?queryParam=value")!))

Expand Down Expand Up @@ -311,7 +316,12 @@ class RedirectComponentTests: XCTestCase {
XCTFail("Should not call onDidProvide")
}

let action = RedirectAction(url: URL(string: "https://google.com")!, paymentData: nil, nativeRedirectData: "test_nativeRedirectData")
let action = RedirectAction(
url: URL(string: "https://google.com")!,
paymentData: nil,
type: .nativeRedirect,
nativeRedirectData: "test_nativeRedirectData"
)
sut.handle(action)
XCTAssertFalse(RedirectComponent.applicationDidOpen(from: URL(string: "url://")!))

Expand Down Expand Up @@ -343,10 +353,54 @@ class RedirectComponentTests: XCTestCase {
redirectExpectation.fulfill()
}

let action = RedirectAction(url: URL(string: "https://google.com")!, paymentData: nil, nativeRedirectData: "test_nativeRedirectData")
let action = RedirectAction(
url: URL(string: "https://google.com")!,
paymentData: nil,
type: .nativeRedirect,
nativeRedirectData: "test_nativeRedirectData"
)
sut.handle(action)
XCTAssertTrue(RedirectComponent.applicationDidOpen(from: URL(string: "url://?queryParam=value")!))

waitForExpectations(timeout: 10)
}

func testNativeRedirectWithNativeRedirectDataNilShouldPerformNativeRedirectResultRequest() {
// Given
let apiClient = APIClientMock()
let sut = RedirectComponent(context: Dummy.context, apiClient: apiClient.retryAPIClient(with: SimpleScheduler(maximumCount: 2)))
apiClient.mockedResults = [.success(try! RedirectDetails(returnURL: URL(string: "url://?redirectResult=test_redirectResult")!))]

let appLauncher = AppLauncherMock()
sut.appLauncher = appLauncher
let appLauncherExpectation = expectation(description: "Expect appLauncher.openUniversalAppUrl() to be called")
appLauncher.onOpenUniversalAppUrl = { url, completion in
XCTAssertEqual(url, URL(string: "https://google.com")!)
completion?(true)
appLauncherExpectation.fulfill()
}

let delegate = ActionComponentDelegateMock()
sut.delegate = delegate
let redirectExpectation = expectation(description: "Expect redirect to be proccessed")
delegate.onDidProvide = { data, component in
XCTAssertTrue(component === sut)
XCTAssertNotNil(data.details)
redirectExpectation.fulfill()
}
delegate.onDidFail = { _, _ in XCTFail("Should not call onDidFail") }

// When
let action = RedirectAction(
url: URL(string: "https://google.com")!,
paymentData: nil,
type: .nativeRedirect,
nativeRedirectData: nil
)
sut.handle(action)

// Then
XCTAssertTrue(RedirectComponent.applicationDidOpen(from: URL(string: "url://?queryParam=value")!))
waitForExpectations(timeout: 10)
}
}
Loading