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

fix: Fix parsing of the API response #850

Merged
merged 9 commits into from
Apr 29, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ struct MerchantMockDataManager {
static var genericPaymentMethod = ClientSessionRequestBody.PaymentMethod(
vaultOnSuccess: false,
options: nil,
descriptor: nil,
descriptor: "Random descriptor",
paymentType: nil
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ public class NolPayLinkedCardsComponent {
var phoneMetadataService: NolPayPhoneMetadataProviding?
var apiClient: PrimerAPIClientProtocol?

public init() {}
public init() {
self.apiClient = PrimerAPIClient()
}

public func getLinkedCardsFor(mobileNumber: String, completion: @escaping (Result<[PrimerNolPaymentCard], PrimerError>) -> Void) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ extension PrimerHeadlessCollectDataComponent {
let error = PrimerError.invalidValue(key: key,
value: nil,
userInfo: .errorUserInfoDictionary(),
diagnosticsId: UUID().uuidString)
diagnosticsId: UUID().uuidString)
ErrorHandler.handle(error: error)
self.errorDelegate?.didReceiveError(error: error)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,23 @@ class DefaultNetworkService: NetworkService, LogReporter {
}

do {
let response: T = try endpoint.responseFactory.model(for: data, forMetadata: response.metadata)
completion(.success(response))
let modelResponse: T
if T.self == String.self {
guard let stringResponse = try endpoint.responseFactory.responseAsString(for: data,
forMetadata: response.metadata) as? T else {
throw InternalError.failedToDecode(message: "Unexpected data type. Expected a String.",
userInfo: .errorUserInfoDictionary(),
diagnosticsId: UUID().uuidString)
}
modelResponse = stringResponse
} else {
modelResponse = try endpoint.responseFactory.model(for: data, forMetadata: response.metadata)
}
completion(.success(modelResponse))
} catch {
completion(.failure(error))
}

}
} catch {
ErrorHandler.handle(error: error)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Foundation

protocol NetworkResponseFactory: AnyObject {
func model<T>(for response: Data, forMetadata metadata: ResponseMetadata) throws -> T where T: Decodable
func responseAsString(for response: Data, forMetadata metadata: ResponseMetadata) throws -> String
}

extension Endpoint {
Expand All @@ -25,32 +26,49 @@ class JSONNetworkResponseFactory: NetworkResponseFactory, LogReporter {
let decoder = JSONDecoder()

func model<T>(for response: Data, forMetadata metadata: ResponseMetadata) throws -> T where T: Decodable {
log(data: response, metadata: metadata)
return try decodeData(response, metadata: metadata)
}

func responseAsString(for response: Data, forMetadata metadata: ResponseMetadata) throws -> String {
log(data: response, metadata: metadata)
guard let responseString = String(data: response, encoding: .utf8) else {
throw InternalError.failedToDecode(
message: "Failed to convert data to String from URL: \(metadata.responseUrl ?? "Unknown")",
userInfo: .errorUserInfoDictionary(),
diagnosticsId: UUID().uuidString
)
}
return responseString
}

private func decodeData<T: Decodable>(_ data: Data, metadata: ResponseMetadata) throws -> T {
switch metadata.statusCode {
case 200:
do {
return try decoder.decode(T.self, from: response)
return try decoder.decode(T.self, from: data)
} catch {
throw InternalError.failedToDecode(message: "Failed to decode response of type '\(T.self)' from URL: \(metadata.responseUrl ?? "Unknown")",
userInfo: .errorUserInfoDictionary(),
diagnosticsId: UUID().uuidString)
throw InternalError.failedToDecode(
message: "Failed to decode response of type '\(T.self)' from URL: \(metadata.responseUrl ?? "Unknown")",
userInfo: .errorUserInfoDictionary(),
diagnosticsId: UUID().uuidString
)
}
case 400...599:
let serverError = try? decoder.decode(PrimerServerErrorResponse.self, from: response)
throw InternalError.serverError(status: metadata.statusCode,
response: serverError?.error,
userInfo: .errorUserInfoDictionary(),
diagnosticsId: UUID().uuidString)
let serverError = try? decoder.decode(PrimerServerErrorResponse.self, from: data)
throw InternalError.serverError(
status: metadata.statusCode,
response: serverError?.error,
userInfo: .errorUserInfoDictionary(),
diagnosticsId: UUID().uuidString
)
default:
break
throw InternalError.failedToDecode(
message: "Failed to determine response from URL: \(metadata.responseUrl ?? "Unknown")",
userInfo: .errorUserInfoDictionary(),
diagnosticsId: UUID().uuidString
)
}

throw InternalError.failedToDecode(message: "Failed to determine response from URL: \(metadata.responseUrl ?? "Unknown")",
userInfo: .errorUserInfoDictionary(),
diagnosticsId: UUID().uuidString)

}

func log(data: Data, metadata: ResponseMetadata) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ enum NetworkEventType {
var endpoint: Endpoint {
switch self {
case .requestStart(_, let endpoint, _),
.requestEnd(_, let endpoint, _),
.networkConnectivity(let endpoint):
.requestEnd(_, let endpoint, _),
.networkConnectivity(let endpoint):
return endpoint
}
}
Expand Down Expand Up @@ -78,7 +78,7 @@ class DefaultNetworkReportingService: NetworkReportingService {
return false
}
guard let baseURL = primerAPI.baseURL, let url = URL(string: baseURL),
!disallowedTrackingPaths.contains(url.path) else {
!disallowedTrackingPaths.contains(url.path) else {
return false
}
return true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ internal class PrimerAPIClient: PrimerAPIClientProtocol {

func genericAPICall(clientToken: DecodedJWTToken, url: URL, completion: @escaping APICompletion<Bool>) {
let endpoint = PrimerAPI.redirect(clientToken: clientToken, url: url)
networkService.request(endpoint) { (result: Result<SuccessResponse, Error>) in
networkService.request(endpoint) { (result: Result<String, Error>) in
jnewc marked this conversation as resolved.
Show resolved Hide resolved
switch result {
case .success:
completion(.success(true))
Expand Down