Skip to content

Commit

Permalink
Refactor to provide protocols for all public structs
Browse files Browse the repository at this point in the history
This helps with mocking these types when testing software depending
on this library.
  • Loading branch information
Nef10 committed Sep 22, 2021
1 parent 2fb4a6c commit 5502b33
Show file tree
Hide file tree
Showing 6 changed files with 268 additions and 230 deletions.
38 changes: 17 additions & 21 deletions Sources/Wealthsimple/TransactionError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,25 @@

import Foundation

extension Transaction {

/// Errors which can happen when retrieving a Transaction
public enum TransactionError: Error {
/// When no data is received from the HTTP request
case noDataReceived
/// When an HTTP error occurs
case httpError(error: String)
/// When the received data is not valid JSON
case invalidJson(error: String)
/// When the received JSON does not have the right type
case invalidJsonType(json: Any)
/// When the received JSON does not have all expected values
case missingResultParamenter(json: [String: Any])
/// When the received JSON does have an unexpected value
case invalidResultParamenter(json: [String: Any])
/// An error with the token occured
case tokenError(_ error: TokenError)
}

/// Errors which can happen when retrieving a Transaction
public enum TransactionError: Error {
/// When no data is received from the HTTP request
case noDataReceived
/// When an HTTP error occurs
case httpError(error: String)
/// When the received data is not valid JSON
case invalidJson(error: String)
/// When the received JSON does not have the right type
case invalidJsonType(json: Any)
/// When the received JSON does not have all expected values
case missingResultParamenter(json: [String: Any])
/// When the received JSON does have an unexpected value
case invalidResultParamenter(json: [String: Any])
/// An error with the token occured
case tokenError(_ error: TokenError)
}

extension Transaction.TransactionError: LocalizedError {
extension TransactionError: LocalizedError {
public var errorDescription: String? {
switch self {
case .noDataReceived:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,63 +10,72 @@ import Foundation
import FoundationNetworking
#endif

/// An Account at Wealthsimple
public struct Account {

/// Errors which can happen when retrieving an Account
public enum AccountError: Error {
/// When no data is received from the HTTP request
case noDataReceived
/// When an HTTP error occurs
case httpError(error: String)
/// When the received data is not valid JSON
case invalidJson(error: String)
/// When the received JSON does not have the right type
case invalidJsonType(json: Any)
/// When the received JSON does not have all expected values
case missingResultParamenter(json: [String: Any])
/// When the received JSON does have an unexpected value
case invalidResultParamenter(json: [String: Any])
/// An error with the token occured
case tokenError(_ error: TokenError)
}
/// Errors which can happen when retrieving an Account
public enum AccountError: Error {
/// When no data is received from the HTTP request
case noDataReceived
/// When an HTTP error occurs
case httpError(error: String)
/// When the received data is not valid JSON
case invalidJson(error: String)
/// When the received JSON does not have the right type
case invalidJsonType(json: Any)
/// When the received JSON does not have all expected values
case missingResultParamenter(json: [String: Any])
/// When the received JSON does have an unexpected value
case invalidResultParamenter(json: [String: Any])
/// An error with the token occured
case tokenError(_ error: TokenError)
}

/// Type of the account
///
/// Note: Currently only Canadian Accounts are supported
public enum AccountType: String {
/// Tax free savings account (CA)
case tfsa = "ca_tfsa"
/// Cash (chequing) account (CA)
case chequing = "ca_cash_msb"
/// Saving (CA)
case saving = "ca_cash"
/// Registered Retirement Savings Plan (CA)
case rrsp = "ca_rrsp"
/// Non-registered account (CA)
case nonRegistered = "ca_non_registered"
/// Non-registered crypto currency account (CA)
case nonRegisteredCrypto = "ca_non_registered_crypto"
/// Locked-in retirement account (CA)
case lira = "ca_lira"
/// Joint account (CA)
case joint = "ca_joint"
/// Registered Retirement Income Fund (CA)
case rrif = "ca_rrif"
/// Life Income Fund (CA)
case lif = "ca_lif"
}
/// Type of the account
///
/// Note: Currently only Canadian Accounts are supported
public enum AccountType: String {
/// Tax free savings account (CA)
case tfsa = "ca_tfsa"
/// Cash (chequing) account (CA)
case chequing = "ca_cash_msb"
/// Saving (CA)
case saving = "ca_cash"
/// Registered Retirement Savings Plan (CA)
case rrsp = "ca_rrsp"
/// Non-registered account (CA)
case nonRegistered = "ca_non_registered"
/// Non-registered crypto currency account (CA)
case nonRegisteredCrypto = "ca_non_registered_crypto"
/// Locked-in retirement account (CA)
case lira = "ca_lira"
/// Joint account (CA)
case joint = "ca_joint"
/// Registered Retirement Income Fund (CA)
case rrif = "ca_rrif"
/// Life Income Fund (CA)
case lif = "ca_lif"
}

private static let url = URL(string: "https://api.production.wealthsimple.com/v1/accounts")!
/// An Account at Wealthsimple
public protocol Account {

/// Type of the account
public let accountType: AccountType
var accountType: AccountType { get }
/// Operating currency of the account
public let currency: String
var currency: String { get }
/// Wealthsimple id for the account
public let id: String
var id: String { get }
/// Number of the account
public let number: String
var number: String { get }

}

struct WealthsimpleAccount: Account {

private static let url = URL(string: "https://api.production.wealthsimple.com/v1/accounts")!

let accountType: AccountType
let currency: String
let id: String
let number: String

private init(json: [String: Any]) throws {
guard let id = json["id"] as? String,
Expand Down Expand Up @@ -133,7 +142,7 @@ public struct Account {
}
var accounts = [Account]()
for result in results {
accounts.append(try Account(json: result))
accounts.append(try WealthsimpleAccount(json: result))
}
completion(.success(accounts))
} catch {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,40 +7,46 @@

import Foundation

/// An asset, like a stock or a currency
public struct Asset {

/// Errors which can happen when retrieving an Asset
public enum AssetError: Error {
/// When the received JSON does not have all expected values
case missingResultParamenter(json: [String: Any])
/// When the received JSON does have an unexpected value
case invalidResultParamenter(json: [String: Any])
}
/// Errors which can happen when retrieving an Asset
public enum AssetError: Error {
/// When the received JSON does not have all expected values
case missingResultParamenter(json: [String: Any])
/// When the received JSON does have an unexpected value
case invalidResultParamenter(json: [String: Any])
}

/// Type of the asset
public enum AssetType: String {
/// Cash
case currency
/// Equity
case equity
/// Mutal Funds
case mutualFund = "mutual_fund"
/// Bonds
case bond
/// ETFs
case exchangeTradedFund = "exchange_traded_fund"
}
/// Type of the asset
public enum AssetType: String {
/// Cash
case currency
/// Equity
case equity
/// Mutal Funds
case mutualFund = "mutual_fund"
/// Bonds
case bond
/// ETFs
case exchangeTradedFund = "exchange_traded_fund"
}

/// An asset, like a stock or a currency
public protocol Asset {
/// Symbol of the asset, e.g. currency or ticker symbol
public let symbol: String
var symbol: String { get }
/// Full name of the asset
public let name: String
var name: String { get }
/// Currency the asset is held in
public let currency: String
var currency: String { get }
/// Type of the asset, e.g. currency or ETF
public let type: AssetType
var type: AssetType { get }
}

struct WealthsimpleAsset: Asset {

let symbol: String
let name: String
let currency: String
let type: AssetType
let id: String

init(json: [String: Any]) throws {
Expand Down
12 changes: 6 additions & 6 deletions Sources/Wealthsimple/WealthsimpleDownloader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,12 @@ public final class WealthsimpleDownloader {

/// Get all Accounts the user has access to
/// - Parameter completion: Result with an array of `Account`s or an `Account.AccountError`
public func getAccounts(completion: @escaping (Result<[Account], Account.AccountError>) -> Void) {
public func getAccounts(completion: @escaping (Result<[Account], AccountError>) -> Void) {
guard let token = token else {
completion(.failure(.tokenError(.noToken)))
return
}
Account.getAccounts(token: token) {
WealthsimpleAccount.getAccounts(token: token) {
if case let .failure(error) = $0 {
if case .tokenError = error {
self.token = nil
Expand All @@ -101,12 +101,12 @@ public final class WealthsimpleDownloader {
/// - account: Account to retreive positions for
/// - date: Date of which the positions should be downloaded. If not date is provided, not date is sent to the API. The API falls back to the current date.
/// - completion: Result with an array of `Position`s or an `Position.PositionError`
public func getPositions(in account: Account, date: Date?, completion: @escaping (Result<[Position], Position.PositionError>) -> Void) {
public func getPositions(in account: Account, date: Date?, completion: @escaping (Result<[Position], PositionError>) -> Void) {
guard let token = token else {
completion(.failure(.tokenError(.noToken)))
return
}
Position.getPositions(token: token, account: account, date: date) {
WealthsimplePosition.getPositions(token: token, account: account, date: date) {
if case let .failure(error) = $0 {
if case .tokenError = error {
self.token = nil
Expand All @@ -121,12 +121,12 @@ public final class WealthsimpleDownloader {
/// - account: Account to retreive transactions from
/// - startDate: Date from which the transactions are downloaded. If not date is provided, not date is sent to the API. The API falls back to 30 days ago from today.
/// - completion: Result with an array of `Transactions`s or an `Transactions.TransactionsError`
public func getTransactions(in account: Account, startDate: Date?, completion: @escaping (Result<[Transaction], Transaction.TransactionError>) -> Void) {
public func getTransactions(in account: Account, startDate: Date?, completion: @escaping (Result<[Transaction], TransactionError>) -> Void) {
guard let token = token else {
completion(.failure(.tokenError(.noToken)))
return
}
Transaction.getTransactions(token: token, account: account, startDate: startDate) {
WealthsimpleTransaction.getTransactions(token: token, account: account, startDate: startDate) {
if case let .failure(error) = $0 {
if case .tokenError = error {
self.token = nil
Expand Down
Loading

0 comments on commit 5502b33

Please sign in to comment.