-
Notifications
You must be signed in to change notification settings - Fork 127
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
base: develop
Are you sure you want to change the base?
Changes from 17 commits
ceae25f
9bd40ca
39675e8
7efd84d
d574e11
a8f3f1c
d339c28
c5756a8
1a3c809
5c64d5c
e6a8a46
ad894f8
9b827b6
68a0bb0
ae3ed8d
8f36bd1
63c1355
3d9f471
896e151
10e1eab
6721b79
7f206e2
3f5babf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -9,24 +9,63 @@ 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? | ||||||
|
||||||
|
||||||
private let type: RedirectType | ||||||
|
||||||
/// 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) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. are we sure the RedirectType will always be returned? otherwise we could do:
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there any other way to check its optionality? https://docs.adyen.com/api-explorer/Checkout/71/post/payments#responses-200-action-CheckoutRedirectAction-type |
||||||
self.nativeRedirectData = try container.decodeIfPresent(String.self, forKey: .nativeRedirectData) | ||||||
} | ||||||
|
||||||
// MARK: - Private | ||||||
|
||||||
private enum CodingKeys: CodingKey { | ||||||
case url | ||||||
case paymentData | ||||||
case type | ||||||
case nativeRedirectData | ||||||
} | ||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Documentation says it's just a string with no further explanation. Is this an URL or anything else? Should we have tests for this string? |
||
) | ||
sut.handle(action) | ||
XCTAssertTrue(RedirectComponent.applicationDidOpen(from: URL(string: "url://?queryParam=value")!)) | ||
|
||
|
@@ -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://")!)) | ||
|
||
|
@@ -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) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need to add the string values when they are matching?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just a guess - to not break anything if someone decides to rename enum case in the future?