mirror of
https://github.com/bitwarden/ios.git
synced 2025-12-11 13:54:06 -06:00
[BWA-155] Move ResponseValidationHandler to BitwardenKit (#1486)
This commit is contained in:
parent
93a98c7f06
commit
4e17cf18c2
@ -1,3 +1,4 @@
|
||||
import BitwardenKit
|
||||
import Foundation
|
||||
|
||||
public extension Error {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import BitwardenKit
|
||||
import BitwardenKitMocks
|
||||
import Networking
|
||||
import TestHelpers
|
||||
import XCTest
|
||||
|
||||
@ -1,29 +0,0 @@
|
||||
import BitwardenKit
|
||||
|
||||
// MARK: - ServerError
|
||||
|
||||
/// An enumeration of server errors.
|
||||
///
|
||||
enum ServerError: Error, Equatable {
|
||||
/// A generic error.
|
||||
///
|
||||
/// - Parameter errorResponse: The error response returned from the API request.
|
||||
///
|
||||
case error(errorResponse: ErrorResponseModel)
|
||||
|
||||
/// A validation error.
|
||||
///
|
||||
/// - Parameter validationErrorResponse: The validation error response returned from the API request.
|
||||
///
|
||||
case validationError(validationErrorResponse: ResponseValidationErrorModel)
|
||||
|
||||
/// A computed property that returns an error message based on the case.
|
||||
var message: String {
|
||||
switch self {
|
||||
case let .error(errorResponse):
|
||||
errorResponse.singleMessage()
|
||||
case let .validationError(validationErrorResponse):
|
||||
validationErrorResponse.errorModel.message
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,57 +0,0 @@
|
||||
import BitwardenKit
|
||||
import Networking
|
||||
import TestHelpers
|
||||
import XCTest
|
||||
|
||||
@testable import AuthenticatorShared
|
||||
|
||||
class ResponseValidationHandlerTests: BitwardenTestCase {
|
||||
// MARK: Properties
|
||||
|
||||
var subject: ResponseValidationHandler!
|
||||
|
||||
// MARK: Setup & Teardown
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
|
||||
subject = ResponseValidationHandler()
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
super.tearDown()
|
||||
|
||||
subject = nil
|
||||
}
|
||||
|
||||
// MARK: Tests
|
||||
|
||||
/// `handle(_:)` doesn't throw an error for successful status codes.
|
||||
func test_handle_validResponse() async throws {
|
||||
for statusCode in [200, 250, 299] {
|
||||
var response = HTTPResponse.success(statusCode: statusCode)
|
||||
let handledResponse = try await subject.handle(&response)
|
||||
XCTAssertEqual(handledResponse, response)
|
||||
}
|
||||
}
|
||||
|
||||
/// `handle(_:)` throws a `ServerError` if the response is able to be parsed as a `ErrorResponseModel`.
|
||||
func test_handle_throwsServerError() async throws {
|
||||
var response = HTTPResponse.failure(statusCode: 400, body: APITestData.bitwardenErrorMessage.data)
|
||||
|
||||
try await assertAsyncThrows(error: ServerError.error(errorResponse: ErrorResponseModel(response: response))) {
|
||||
_ = try await subject.handle(&response)
|
||||
}
|
||||
}
|
||||
|
||||
/// `handle(_:)` throws a `ResponseValidationError` for any non-2XX status codes that aren't
|
||||
/// able to be parsed as a `ErrorResponseModel`.
|
||||
func test_handle_throwsResponseValidationError() async {
|
||||
for statusCode in [400, 499, 500, 599] {
|
||||
var response = HTTPResponse.failure(statusCode: statusCode)
|
||||
await assertAsyncThrows(error: ResponseValidationError(response: response)) {
|
||||
_ = try await subject.handle(&response)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,3 +1,4 @@
|
||||
import BitwardenKit
|
||||
import BitwardenSdk
|
||||
import Foundation
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
// swiftlint:disable:this file_name
|
||||
|
||||
import BitwardenKit
|
||||
import BitwardenKitMocks
|
||||
import Foundation
|
||||
import Networking
|
||||
import TestHelpers
|
||||
|
||||
@ -0,0 +1,48 @@
|
||||
import Foundation
|
||||
import Networking
|
||||
|
||||
// MARK: - ResponseValidationErrorModel
|
||||
|
||||
/// An Response validation error returned from an API request.
|
||||
///
|
||||
public struct ResponseValidationErrorModel: Codable, Equatable {
|
||||
// MARK: Properties
|
||||
|
||||
/// A string that represents the error code.
|
||||
public let error: String
|
||||
|
||||
/// A string that provides a description of the error.
|
||||
public let errorDescription: String?
|
||||
|
||||
/// An `ErrorModel` object that provides more details about the error.
|
||||
public let errorModel: ErrorModel
|
||||
|
||||
/// Public instance of synthesized initializer.
|
||||
public init(error: String, errorDescription: String?, errorModel: ErrorModel) {
|
||||
self.error = error
|
||||
self.errorDescription = errorDescription
|
||||
self.errorModel = errorModel
|
||||
}
|
||||
}
|
||||
|
||||
public struct ErrorModel: Codable, Equatable {
|
||||
// MARK: Properties
|
||||
|
||||
/// A string that provides a message about the error.
|
||||
public let message: String
|
||||
|
||||
/// A string that represents an object associated with the error.
|
||||
public let object: String
|
||||
|
||||
/// Public instance of synthesized initializer.
|
||||
public init(message: String, object: String) {
|
||||
self.message = message
|
||||
self.object = object
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: JSONResponse
|
||||
|
||||
extension ResponseValidationErrorModel: JSONResponse {
|
||||
public static let decoder = JSONDecoder.pascalOrSnakeCaseDecoder
|
||||
}
|
||||
@ -1,7 +1,8 @@
|
||||
import BitwardenKitMocks
|
||||
import TestHelpers
|
||||
import XCTest
|
||||
|
||||
@testable import BitwardenShared
|
||||
@testable import BitwardenKit
|
||||
|
||||
// MARK: - ResponseValidationErrorModelTests
|
||||
|
||||
@ -137,7 +137,3 @@ public extension APITestData {
|
||||
bundle: BitwardenKitMocksBundleFinder.bundle
|
||||
)
|
||||
}
|
||||
|
||||
class BitwardenKitMocksBundleFinder {
|
||||
static let bundle = Bundle(for: BitwardenKitMocksBundleFinder.self)
|
||||
}
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
import BitwardenKit
|
||||
import Foundation
|
||||
|
||||
// MARK: - ServerError
|
||||
|
||||
/// An enumeration of server errors.
|
||||
///
|
||||
enum ServerError: Error, Equatable, CustomNSError {
|
||||
public enum ServerError: Error, Equatable, CustomNSError {
|
||||
/// A generic error.
|
||||
///
|
||||
/// - Parameter errorResponse: The error response returned from the API request.
|
||||
@ -19,7 +18,7 @@ enum ServerError: Error, Equatable, CustomNSError {
|
||||
case validationError(validationErrorResponse: ResponseValidationErrorModel)
|
||||
|
||||
/// A computed property that returns an error message based on the case.
|
||||
var message: String {
|
||||
public var message: String {
|
||||
switch self {
|
||||
case let .error(errorResponse):
|
||||
errorResponse.singleMessage()
|
||||
@ -0,0 +1,11 @@
|
||||
import Foundation
|
||||
import TestHelpers
|
||||
|
||||
public extension APITestData {
|
||||
/// A standard Bitwarden error message of "You do not have permissions to edit this."
|
||||
static let bitwardenErrorMessage = loadFromJsonBundle(
|
||||
resource: "BitwardenErrorMessage",
|
||||
bundle: BitwardenKitMocksBundleFinder.bundle
|
||||
)
|
||||
}
|
||||
|
||||
@ -0,0 +1,5 @@
|
||||
import Foundation
|
||||
|
||||
class BitwardenKitMocksBundleFinder {
|
||||
static let bundle = Bundle(for: BitwardenKitMocksBundleFinder.self)
|
||||
}
|
||||
@ -1,15 +1,21 @@
|
||||
import BitwardenKit
|
||||
import Networking
|
||||
|
||||
// MARK: - ResponseValidationError
|
||||
|
||||
/// An error indicating that the response was invalid and didn't contain a successful HTTP status code.
|
||||
///
|
||||
struct ResponseValidationError: Error, Equatable {
|
||||
public struct ResponseValidationError: Error, Equatable {
|
||||
// MARK: Properties
|
||||
|
||||
/// The received HTTP response.
|
||||
let response: HTTPResponse
|
||||
public let response: HTTPResponse
|
||||
|
||||
// MARK: Initializers
|
||||
|
||||
/// Public version of synthesized initializer.
|
||||
public init(response: HTTPResponse) {
|
||||
self.response = response
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ResponseValidationHandler
|
||||
@ -17,8 +23,17 @@ struct ResponseValidationError: Error, Equatable {
|
||||
/// A `ResponseHandler` that validates that HTTP responses contain successful (2XX) HTTP status
|
||||
/// codes or tries to parse the error otherwise.
|
||||
///
|
||||
class ResponseValidationHandler: ResponseHandler {
|
||||
func handle(_ response: inout HTTPResponse) async throws -> HTTPResponse {
|
||||
public final class ResponseValidationHandler: ResponseHandler {
|
||||
/// Public version of synthesized initializer.
|
||||
public init() {}
|
||||
|
||||
/// Handles receiving a `HTTPResponse`. The handler can view or modify the response before
|
||||
/// returning it to continue to handler chain.
|
||||
///
|
||||
/// - Parameter response: The `HTTPResponse` that was received by the `HTTPClient`.
|
||||
/// - Returns: The original or modified `HTTPResponse`.
|
||||
///
|
||||
public func handle(_ response: inout HTTPResponse) async throws -> HTTPResponse {
|
||||
guard (200 ..< 300).contains(response.statusCode) else {
|
||||
// If the response can be parsed, throw an error containing the response message.
|
||||
if let errorResponse = try? ErrorResponseModel(response: response) {
|
||||
@ -1,9 +1,8 @@
|
||||
import BitwardenKit
|
||||
import Networking
|
||||
import TestHelpers
|
||||
import XCTest
|
||||
|
||||
@testable import BitwardenShared
|
||||
@testable import BitwardenKit
|
||||
|
||||
class ResponseValidationHandlerTests: BitwardenTestCase {
|
||||
// MARK: Properties
|
||||
@ -1,35 +0,0 @@
|
||||
import Foundation
|
||||
import Networking
|
||||
|
||||
// MARK: - ResponseValidationErrorModel
|
||||
|
||||
/// An Response validation error returned from an API request.
|
||||
///
|
||||
struct ResponseValidationErrorModel: Codable, Equatable {
|
||||
// MARK: Properties
|
||||
|
||||
/// A string that represents the error code.
|
||||
let error: String
|
||||
|
||||
/// A string that provides a description of the error.
|
||||
let errorDescription: String?
|
||||
|
||||
/// An `ErrorModel` object that provides more details about the error.
|
||||
let errorModel: ErrorModel
|
||||
}
|
||||
|
||||
struct ErrorModel: Codable, Equatable {
|
||||
// MARK: Properties
|
||||
|
||||
/// A string that provides a message about the error.
|
||||
let message: String
|
||||
|
||||
/// A string that represents an object associated with the error.
|
||||
let object: String
|
||||
}
|
||||
|
||||
// MARK: JSONResponse
|
||||
|
||||
extension ResponseValidationErrorModel: JSONResponse {
|
||||
static let decoder = JSONDecoder.pascalOrSnakeCaseDecoder
|
||||
}
|
||||
@ -1,3 +1,4 @@
|
||||
import BitwardenKit
|
||||
import Foundation
|
||||
|
||||
public extension Error {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import BitwardenKit
|
||||
import BitwardenKitMocks
|
||||
import Networking
|
||||
import TestHelpers
|
||||
import XCTest
|
||||
|
||||
@ -1,8 +0,0 @@
|
||||
{
|
||||
"message": "You do not have permissions to edit this.",
|
||||
"validationErrors": null,
|
||||
"exceptionMessage": null,
|
||||
"exceptionStackTrace": null,
|
||||
"innerExceptionMessage": null,
|
||||
"object": "error"
|
||||
}
|
||||
@ -1,38 +0,0 @@
|
||||
import BitwardenKit
|
||||
import Networking
|
||||
|
||||
// MARK: - ResponseValidationError
|
||||
|
||||
/// An error indicating that the response was invalid and didn't contain a successful HTTP status code.
|
||||
///
|
||||
struct ResponseValidationError: Error, Equatable {
|
||||
// MARK: Properties
|
||||
|
||||
/// The received HTTP response.
|
||||
let response: HTTPResponse
|
||||
}
|
||||
|
||||
// MARK: - ResponseValidationHandler
|
||||
|
||||
/// A `ResponseHandler` that validates that HTTP responses contain successful (2XX) HTTP status
|
||||
/// codes or tries to parse the error otherwise.
|
||||
///
|
||||
final class ResponseValidationHandler: ResponseHandler {
|
||||
func handle(_ response: inout HTTPResponse) async throws -> HTTPResponse {
|
||||
guard (200 ..< 300).contains(response.statusCode) else {
|
||||
// If the response can be parsed, throw an error containing the response message.
|
||||
if let errorResponse = try? ErrorResponseModel(response: response) {
|
||||
throw ServerError.error(errorResponse: errorResponse)
|
||||
}
|
||||
|
||||
// If the response can be parsed, throw an error containing the response message.
|
||||
if let validationErrorResponse = try? ResponseValidationErrorModel(response: response) {
|
||||
throw ServerError.validationError(validationErrorResponse: validationErrorResponse)
|
||||
}
|
||||
|
||||
// Otherwise, throw a generic response validation error.
|
||||
throw ResponseValidationError(response: response)
|
||||
}
|
||||
return response
|
||||
}
|
||||
}
|
||||
@ -1,3 +1,4 @@
|
||||
import BitwardenKit
|
||||
import Networking
|
||||
import XCTest
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import BitwardenKit
|
||||
import BitwardenSdk
|
||||
import Foundation
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import BitwardenKit
|
||||
import BitwardenSdk
|
||||
|
||||
// MARK: - DeleteAccountProcessor
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import BitwardenKit
|
||||
import BitwardenSdk
|
||||
import Foundation
|
||||
|
||||
|
||||
@ -22,8 +22,3 @@ public struct APITestData {
|
||||
loadFromBundle(resource: resource, extension: "json", bundle: bundle)
|
||||
}
|
||||
}
|
||||
|
||||
public extension APITestData {
|
||||
/// A standard Bitwarden error message of "You do not have permissions to edit this."
|
||||
static let bitwardenErrorMessage = loadFromJsonBundle(resource: "BitwardenErrorMessage")
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user