Skip to content

Commit

Permalink
BIT-91: Stub out API services (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
matt-livefront authored Aug 29, 2023
1 parent adf0a24 commit a12f732
Show file tree
Hide file tree
Showing 20 changed files with 241 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/// A protocol for an API service used to make account requests.
///
protocol AccountAPIService {}

extension APIService: AccountAPIService {}
5 changes: 5 additions & 0 deletions BitwardenShared/Core/Services/Auth/API/AuditAPIService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/// A protocol for an API service used to make audit requests.
///
protocol AuditAPIService {}

extension APIService: AuditAPIService {}
5 changes: 5 additions & 0 deletions BitwardenShared/Core/Services/Auth/API/AuthAPIService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/// A protocol for an API service used to make auth requests.
///
protocol AuthAPIService {}

extension APIService: AuthAPIService {}
5 changes: 5 additions & 0 deletions BitwardenShared/Core/Services/Auth/API/DeviceAPIService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/// A protocol for an API service used to make device requests.
///
protocol DeviceAPIService {}

extension APIService: DeviceAPIService {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/// A protocol for an API service used to make organization requests.
///
protocol OrganizationAPIService {}

extension APIService: OrganizationAPIService {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/// A protocol for an API service used to make organization user requests.
///
protocol OrganizationUserAPIService {}

extension APIService: OrganizationUserAPIService {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/// A protocol for an API service used to make passwordless login requests.
///
protocol PasswordlessLoginAPIService {}

extension APIService: PasswordlessLoginAPIService {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/// A protocol for an API service used to make two factor auth requests.
///
protocol TwoFactorAPIService {}

extension APIService: TwoFactorAPIService {}
30 changes: 30 additions & 0 deletions BitwardenShared/Core/Services/Platform/API/APIService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import Foundation
import Networking

/// A service used by the application to make API requests.
///
class APIService {
// MARK: Properties

/// The API service that is used for general requests.
let apiService: HTTPService

/// The API service used for logging events.
let eventsService: HTTPService

/// The API service used for user identity requests.
let identityService: HTTPService

// MARK: Initialization

/// Initialize an `APIService` used to make API requests.
///
/// - Parameter client: The underlying `HTTPClient` that performs the network request. Defaults
/// to `URLSession.shared`.
///
init(client: HTTPClient = URLSession.shared) {
apiService = HTTPService(baseURL: URL(string: "https://vault.bitwarden.com/api")!, client: client)
eventsService = HTTPService(baseURL: URL(string: "https://vault.bitwarden.com/events")!, client: client)
identityService = HTTPService(baseURL: URL(string: "https://vault.bitwarden.com/identity")!, client: client)
}
}
32 changes: 32 additions & 0 deletions BitwardenShared/Core/Services/Platform/API/APIServiceTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import XCTest

@testable import BitwardenShared
@testable import Networking

class APIServiceTests: BitwardenTestCase {
var subject: APIService!

override func setUp() {
super.setUp()

subject = APIService()
}

override func tearDown() {
super.tearDown()

subject = nil
}

/// `init(client:)` sets the default base URLs for the HTTP services.
func testInitDefaultURLs() {
let apiServiceBaseURL = subject.apiService.baseURL
XCTAssertEqual(apiServiceBaseURL, URL(string: "https://vault.bitwarden.com/api")!)

let eventsServiceBaseURL = subject.eventsService.baseURL
XCTAssertEqual(eventsServiceBaseURL, URL(string: "https://vault.bitwarden.com/events")!)

let identityServiceBaseURL = subject.identityService.baseURL
XCTAssertEqual(identityServiceBaseURL, URL(string: "https://vault.bitwarden.com/identity")!)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/// A protocol for an API service used to make config requests.
///
protocol ConfigAPIService {}

extension APIService: ConfigAPIService {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/// A protocol for an API service used to make event requests.
///
protocol EventAPIService {}

extension APIService: EventAPIService {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import Foundation

/// A type that wraps fixture data for use in mocking API responses during tests.
///
struct APITestData {
let data: Data

static func loadFromBundle(resource: String, extension: String) -> APITestData {
let bundle = Bundle(for: BitwardenTestCase.self)
guard let url = bundle.url(forResource: resource, withExtension: `extension`) else {
fatalError("Unable to locate file \(resource).\(`extension`) in the bundle.")
}
do {
return try APITestData(data: Data(contentsOf: url))
} catch {
fatalError("Unable to load data from \(resource).\(`extension`) in the bundle. Error: \(error)")
}
}
}

extension APITestData {
// Create static instances to load API responses from the bundle or static data.
// Example:
// static let getSync = loadFromBundle(resource: "getSync", extension: "json")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import Networking

/// An `HTTPClient` that can be used to return mocked responses.
///
class MockHTTPClient: HTTPClient {
/// A list of requests that have been received by the HTTP client.
var requests: [HTTPRequest] = []

/// Gets the next result or sets a single result for the HTTP client to return for the next request.
var result: Result<HTTPResponse, Error>? {
get {
results.first
}
set {
guard let newValue else {
results.removeAll()
return
}
results = [newValue]
}
}

/// A list of results that will be returned in order for future requests.
var results: [Result<HTTPResponse, Error>] = []

/// Sends a request and returns a mock response, if one exists.
///
/// - Parameter request: The request to make on the client.
/// - Returns: A mock response for the request, if one exists.
///
func send(_ request: HTTPRequest) async throws -> HTTPResponse {
requests.append(request)

guard !results.isEmpty else { throw MockHTTPClientError.noResultForRequest }

let result = results.removeFirst()
return try result.get()
}
}

/// Errors thrown by `MockHTTPClient`.
enum MockHTTPClientError: Error {
/// There's no results set to
case noResultForRequest
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import Foundation
import Networking

extension Result where Success == HTTPResponse, Error: Error {
static func httpSuccess(testData: APITestData) -> Result<HTTPResponse, Error> {
let response = HTTPResponse(
url: URL(string: "https://example.com")!,
statusCode: 200,
headers: [:],
body: testData.data,
requestID: UUID()
)
return .success(response)
}

static func httpFailure(
statusCode: Int = 500,
headers: [String: String] = [:],
data: Data = Data()
) -> Result<HTTPResponse, Error> {
let response = HTTPResponse(
url: URL(string: "https://example.com")!,
statusCode: statusCode,
headers: headers,
body: data,
requestID: UUID()
)
return .success(response)
}

static func httpFailure(_ error: Error) -> Result<HTTPResponse, Error> {
.failure(error)
}
}
5 changes: 5 additions & 0 deletions BitwardenShared/Core/Services/Tools/API/SendAPIService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/// A protocol for an API service used to make send requests.
///
protocol SendAPIService {}

extension APIService: SendAPIService {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/// A protocol for an API service used to make attachment requests.
///
protocol AttachmentAPIService {}

extension APIService: AttachmentAPIService {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/// A protocol for an API service used to make cipher requests.
///
protocol CipherAPIService {}

extension APIService: CipherAPIService {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/// A protocol for an API service used to make folder requests.
///
protocol FolderAPIService {}

extension APIService: FolderAPIService {}
5 changes: 5 additions & 0 deletions BitwardenShared/Core/Services/Vault/API/SyncAPIService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/// A protocol for an API service used to make sync requests.
///
protocol SyncAPIService {}

extension APIService: SyncAPIService {}

0 comments on commit a12f732

Please sign in to comment.