Skip to content

Commit

Permalink
Merge branch 'main' into Release-3.12.0
Browse files Browse the repository at this point in the history
  • Loading branch information
ValentinaIancu-Gini committed Nov 23, 2024
2 parents 5938aa9 + 9ad53f7 commit 24193cd
Show file tree
Hide file tree
Showing 8 changed files with 280 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public enum APIDomain {
/// A custom domain with optional path and custom token source
case custom(domain: String, path: String? = nil, tokenSource: AlternativeTokenSource?)

var domainString: String {
public var domainString: String {
switch self {
case .default: return "pay-api.gini.net"
case .custom(let domain, _, _): return domain
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@

import Foundation

public final class Token {

public final class Token: Hashable {
var expiration: Date
var scope: String?
var type: String?
Expand All @@ -28,6 +27,16 @@ public final class Token {
case accessToken = "access_token"
}

public static func == (lhs: Token, rhs: Token) -> Bool {
lhs.hashValue == rhs.hashValue
}

public func hash(into hasher: inout Hasher) {
hasher.combine(expiration)
hasher.combine(scope)
hasher.combine(type)
hasher.combine(accessToken)
}
}

extension Token: Decodable {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ extension GiniBankAPI {
/**
* Creates a Gini Bank API Library to be used with a transparent proxy and a custom api access token source.
*/
public init(customApiDomain: String,
public init(customApiDomain: String = APIDomain.default.domainString,
alternativeTokenSource: AlternativeTokenSource,
logLevel: LogLevel = .none,
sessionDelegate: URLSessionDelegate? = nil) {
Expand Down Expand Up @@ -134,6 +134,7 @@ extension GiniBankAPI {
}

private func save(_ client: Client) {
guard !runningUnitTests() else { return }
do {
try KeychainStore().save(item: KeychainManagerItem(key: .clientId,
value: client.id,
Expand All @@ -149,5 +150,12 @@ extension GiniBankAPI {
"Check that the Keychain capability is enabled in your project")
}
}

private func runningUnitTests() -> Bool {
#if canImport(XCTest)
return ProcessInfo.processInfo.environment["XCTestConfigurationFilePath"] != nil
#endif
return false
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,16 +75,16 @@ class IntegrationTests: XCTestCase {
}

func testResolvePaymentRequest(){
let expect = expectation(description: "it resolves the payment request")
let message = "You can't resolve the previously resolved payment request"
let expect = expectation(description: message)

let paymentService = giniBankAPILib.paymentService()
paymentService.resolvePaymentRequest(id: paymentRequestID, recipient: "Dr. med. Hackler", iban: "DE13760700120500154000", bic: "", amount: "335.50:EUR", purpose: "ReNr AZ356789Z"){ result in
switch result {
case .success(let resolvedRequest):
XCTAssertEqual(resolvedRequest.links?.payment, "https://pay-api.gini.net/paymentRequests/a6466506-acf1-4896-94c8-9b398d4e0ee1/payment")
expect.fulfill()
XCTFail(message)
case .failure(let error):
XCTFail(String(describing: error))
expect.fulfill()
}
}
wait(for: [expect], timeout: 10)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,16 +89,16 @@ class PinningIntegrationTests: XCTestCase {
}

func testResolvePaymentRequest(){
let expect = expectation(description: "it resolves the payment request")
let message = "You can't resolve the previously resolved payment request"
let expect = expectation(description: message)

let paymentService = giniBankAPILib.paymentService()
paymentService.resolvePaymentRequest(id: paymentRequestID, recipient: "Dr. med. Hackler", iban: "DE13760700120500154000", bic: "", amount: "335.50:EUR", purpose: "ReNr AZ356789Z"){ result in
switch result {
case .success(let resolvedRequest):
XCTAssertEqual(resolvedRequest.links?.payment, "https://pay-api.gini.net/paymentRequests/a6466506-acf1-4896-94c8-9b398d4e0ee1/payment")
expect.fulfill()
case .failure(let error):
XCTFail(String(describing: error))
case .success(let resolvedRequest):
XCTFail(message)
case .failure(let error):
expect.fulfill()
}
}
wait(for: [expect], timeout: 10)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,24 @@ open class GiniBankNetworkingScreenApiCoordinator: GiniScreenAPICoordinator, Gin
self.trackingDelegate = trackingDelegate
}

private init(resultsDelegate: GiniCaptureResultsDelegate,
configuration: GiniBankConfiguration,
documentMetadata: Document.Metadata?,
trackingDelegate: GiniCaptureTrackingDelegate?,
lib: GiniBankAPI) {
documentService = DocumentService(lib: lib, metadata: documentMetadata)
configurationService = lib.configurationService()
let captureConfiguration = configuration.captureConfiguration()
super.init(withDelegate: nil, giniConfiguration: captureConfiguration)

visionDelegate = self
GiniBank.setConfiguration(configuration)
giniBankConfiguration = configuration
giniBankConfiguration.documentService = documentService
self.resultsDelegate = resultsDelegate
self.trackingDelegate = trackingDelegate
}

public init(resultsDelegate: GiniCaptureResultsDelegate,
configuration: GiniBankConfiguration,
documentMetadata: Document.Metadata?,
Expand Down Expand Up @@ -193,6 +211,22 @@ open class GiniBankNetworkingScreenApiCoordinator: GiniScreenAPICoordinator, Gin
lib: lib)
}

convenience init(alternativeTokenSource tokenSource: AlternativeTokenSource,
resultsDelegate: GiniCaptureResultsDelegate,
configuration: GiniBankConfiguration,
documentMetadata: Document.Metadata?,
trackingDelegate: GiniCaptureTrackingDelegate?) {
let lib = GiniBankAPI
.Builder(alternativeTokenSource: tokenSource)
.build()

self.init(resultsDelegate: resultsDelegate,
configuration: configuration,
documentMetadata: documentMetadata,
trackingDelegate: trackingDelegate,
lib: lib)
}

private func deliver(result: ExtractionResult, analysisDelegate: AnalysisDelegate) {
let hasExtractions = result.extractions.count > 0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ extension GiniBank {
all screens and transitions out of the box, including the networking.

- parameter client: `GiniClient` with the information needed to enable document analysis
- parameter importedDocuments: There should be either images or one PDF, and they should be validated before calling this method.
- parameter resultsDelegate: Results delegate object where you can get the results of the analysis.
- parameter configuration: The configuration to set.
- parameter documentMetadata: Additional HTTP headers to send when uploading documents
Expand Down Expand Up @@ -46,6 +47,34 @@ extension GiniBank {
return screenCoordinator.startSDK(withDocuments: importedDocuments)
}

/**
Returns a view controller which will handle the analysis process.
It's the easiest way to get started with the Gini Bank SDK as it comes pre-configured and handles
all screens and transitions out of the box, including the networking.

- parameter tokenSource: Alternative token source
- parameter importedDocuments: There should be either images or one PDF, and they should be validated before calling this method.
- parameter resultsDelegate: Results delegate object where you can get the results of the analysis.
- parameter configuration: The configuration to set.
- parameter documentMetadata: Additional HTTP headers to send when uploading documents
- parameter trackingDelegate: A delegate object to receive user events

- returns: A presentable view controller.
*/
public class func viewController(withAlternativeTokenSource tokenSource: AlternativeTokenSource,
importedDocuments: [GiniCaptureDocument]? = nil,
configuration: GiniBankConfiguration,
resultsDelegate: GiniCaptureResultsDelegate,
documentMetadata: Document.Metadata? = nil,
trackingDelegate: GiniCaptureTrackingDelegate? = nil) -> UIViewController {
let screenCoordinator = GiniBankNetworkingScreenApiCoordinator(alternativeTokenSource: tokenSource,
resultsDelegate: resultsDelegate,
configuration: configuration,
documentMetadata: documentMetadata,
trackingDelegate: trackingDelegate)
return screenCoordinator.startSDK(withDocuments: importedDocuments)
}

// MARK: - Screen API with Custom Networking - Initializers for 'UIViewController'

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
//
// NetworkingScreenApiCoordinatorTests.swift
//
// Copyright © 2024 Gini GmbH. All rights reserved.
//

@testable import GiniBankAPILibrary
@testable import GiniBankSDK
@testable import GiniCaptureSDK
import XCTest

private class MockTokenSource: AlternativeTokenSource {
var token: Token?
init(token: Token? = nil) {
self.token = token
}
func fetchToken(completion: @escaping (Result<Token, GiniError>) -> Void) {
if let token {
completion(.success(token))
} else {
completion(.failure(.requestCancelled))
}
}
}

private class MockCaptureResultsDelegate: GiniCaptureResultsDelegate {
func giniCaptureAnalysisDidFinishWith(result: AnalysisResult) {
}

func giniCaptureDidCancelAnalysis() {
}

func giniCaptureDidEnterManually() {
}
}

private class MockTrackingDelegate: GiniCaptureTrackingDelegate {
func onOnboardingScreenEvent(event: Event<OnboardingScreenEventType>) {
}

func onCameraScreenEvent(event: Event<CameraScreenEventType>) {
}

func onReviewScreenEvent(event: Event<ReviewScreenEventType>) {
}

func onAnalysisScreenEvent(event: Event<AnalysisScreenEventType>) {
}
}

final class NetworkingScreenApiCoordinatorTests: XCTestCase {
private var tokenSource: MockTokenSource!
private var resultsDelegate: MockCaptureResultsDelegate!
private var configuration: GiniBankConfiguration!
private var metadata: Document.Metadata!
private var trackingDelegate: MockTrackingDelegate!

override func setUp() {
tokenSource = makeTokenSource()
resultsDelegate = MockCaptureResultsDelegate()
configuration = GiniBankConfiguration()
metadata = Document.Metadata(branchId: "branch")
trackingDelegate = MockTrackingDelegate()
}

func testInitWithAlternativeTokenSource() throws {
let (coordinator, service) = try makeCoordinatorAndService()

// check domain
XCTAssertEqual(service.apiDomain.domainString, "pay-api.gini.net", "Service api domain should match our default")

// check token
let receivedToken = try XCTUnwrap(
login(service: service),
"Should log in successfully"
)
XCTAssertEqual(receivedToken, tokenSource.token, "Received token should match the expected token")

// check for delegates/configs
XCTAssertNotNil(
coordinator.resultsDelegate as? MockCaptureResultsDelegate,
"Coordinator should have correct results delegate instance"
)
XCTAssertEqual(coordinator.giniBankConfiguration, configuration, "Coordinator should have correct configuration instance")
XCTAssertNotNil(
coordinator.trackingDelegate as? MockTrackingDelegate,
"Coordinator should have correct tracking delegate instance"
)
XCTAssertEqual(coordinator.documentService.metadata?.headers, metadata.headers, "Metadata headers should match")
}

func testViewControllerWithAlternativeTokenSource() throws {
let (coordinator, service) = try makeCoordinatorAndService(fromViewController: true)

// check domain
XCTAssertEqual(service.apiDomain.domainString, "pay-api.gini.net", "Service api domain should match our default")

// check token
let receivedToken = try XCTUnwrap(
login(service: service),
"Should log in successfully"
)
XCTAssertEqual(receivedToken, tokenSource.token, "Received token should match the expected token")

// check for delegates/configs
XCTAssertNotNil(
coordinator.resultsDelegate as? MockCaptureResultsDelegate,
"Coordinator should have correct results delegate instance"
)
XCTAssertEqual(coordinator.giniBankConfiguration, configuration, "Coordinator should have correct configuration instance")
XCTAssertNotNil(
coordinator.trackingDelegate as? MockTrackingDelegate,
"Coordinator should have correct tracking delegate instance"
)
XCTAssertEqual(coordinator.documentService.metadata?.headers, metadata.headers, "Metadata headers should match")
}
}

private extension NetworkingScreenApiCoordinatorTests {
func makeTokenSource() -> MockTokenSource {
MockTokenSource(
token:
Token(
expiration: .init(),
scope: "the_scope",
type: "the_type",
accessToken: "some_totally_random_gibberish"
)
)
}

func makeCoordinatorAndService(fromViewController: Bool = false) throws -> (GiniBankNetworkingScreenApiCoordinator, DefaultDocumentService) {
let coordinator: GiniBankNetworkingScreenApiCoordinator
if fromViewController {
let viewController = try XCTUnwrap(
GiniBank.viewController(
withAlternativeTokenSource: tokenSource,
configuration: configuration,
resultsDelegate: resultsDelegate,
documentMetadata: metadata,
trackingDelegate: trackingDelegate
) as? ContainerNavigationController,
"There should be an instance of `ContainerNavigationController`"
)
coordinator = try XCTUnwrap(
viewController.coordinator as? GiniBankNetworkingScreenApiCoordinator,
"The instance of `ContainerNavigationController` should have a coordinator of type `GiniBankNetworkingScreenApiCoordinator"
)
} else {
coordinator = GiniBankNetworkingScreenApiCoordinator(
alternativeTokenSource: tokenSource,
resultsDelegate: resultsDelegate,
configuration: configuration,
documentMetadata: metadata,
trackingDelegate: trackingDelegate
)
}
let documentService = try XCTUnwrap(
coordinator.documentService as? GiniCaptureSDK.DocumentService,
"The coordinator should have a document service of type `GiniCaptureSDK.DocumentService"
)
let captureNetworkService = try XCTUnwrap(
documentService.captureNetworkService as? DefaultCaptureNetworkService,
"The document service should have a capture network service of type `DefaultCaptureNetworkService"
)


return (coordinator, captureNetworkService.documentService)
}

func login(service: DefaultDocumentService) throws -> Token? {
let logInExpectation = self.expectation(description: "login")
var receivedToken: Token?
service.sessionManager.logIn { result in
switch result {
case .success(let token):
receivedToken = token
logInExpectation.fulfill()
case .failure(let error):
XCTFail("Failure: \(error.localizedDescription)")
}
}
wait(for: [logInExpectation], timeout: 1)
return receivedToken
}
}

0 comments on commit 24193cd

Please sign in to comment.