Skip to content

Commit

Permalink
entity consolidation, unit test updates
Browse files Browse the repository at this point in the history
  • Loading branch information
hwjeremy committed Aug 2, 2018
1 parent 8bb02ed commit 35239ae
Show file tree
Hide file tree
Showing 30 changed files with 476 additions and 410 deletions.
34 changes: 12 additions & 22 deletions Amatino.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

44 changes: 33 additions & 11 deletions Sources/Amatino/Account.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,9 @@

import Foundation

public class AccountError: AmatinoObjectError {}

public class Account: AmatinoObject {

internal static let path = "/accounts"
internal static let errorType: AmatinoObjectError.Type = AccountError.self

private static let urlKey = "account_id"

Expand Down Expand Up @@ -144,7 +141,7 @@ public class Account: AmatinoObject {
}

public required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let container = try decoder.container(keyedBy: JSONObjectKeys.self)
id = try container.decode(Int.self, forKey: .id)
name = try container.decode(String.self, forKey: .name)
type = try container.decode(AccountType.self, forKey: .type)
Expand All @@ -164,7 +161,7 @@ public class Account: AmatinoObject {
return
}

enum CodingKeys: String, CodingKey {
enum JSONObjectKeys: String, CodingKey {
case id = "account_id"
case name
case type
Expand All @@ -190,6 +187,13 @@ public class Account: AmatinoObject {
private let description: String
private let colour: Colour?

internal var maxNameError: String { get {
return "Max name length \(maxNameLength) characters"
}}
internal var maxDescriptionError: String { get {
return "Max description length \(maxDescriptionLength) characters"
}}

public init(
name: String,
type: AccountType,
Expand Down Expand Up @@ -280,19 +284,17 @@ public class Account: AmatinoObject {

private func checkName(name: String) throws -> Void {
guard name.count < maxNameLength else {
throw ConstraintError("Max name length \(maxNameLength) characters")
throw ConstraintError(.nameLength, maxNameError)
}
}

private func checkDescription(description: String) throws -> Void {
guard description.count < maxDescriptionLength else {
throw ConstraintError("""
Max description length \(maxDescriptionLength) characters
""")
throw ConstraintError(.descriptionLength, maxDescriptionError)
}
}

enum CodingKeys: String, CodingKey {
enum JSONObjectKeys: String, CodingKey {
case name
case type = "type"
case parentAccount = "parent_account_id"
Expand All @@ -304,7 +306,7 @@ public class Account: AmatinoObject {
}

public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
var container = encoder.container(keyedBy: JSONObjectKeys.self)
try container.encode(name, forKey: .name)
try container.encode(description, forKey: .description)
try container.encode(type, forKey: .type)
Expand All @@ -319,6 +321,26 @@ public class Account: AmatinoObject {
return
}

public class ConstraintError: AmatinoError {

public let constraint: Constraint
public let constraintDescription: String

internal init(_ cause: Constraint, _ description: String? = nil) {
constraint = cause
constraintDescription = description ?? cause.rawValue
super.init(.constraintViolated)
return
}

public enum Constraint: String {
case descriptionLength = "Maximum description length exceeded"
case nameLength = "Maximum name length exceeded"
case tooManyArguments = "Maximum number of arguments exceeded"
}

}

}

}
11 changes: 3 additions & 8 deletions Sources/Amatino/AccountBalance.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@

import Foundation

class BalanceError: AmatinoObjectError {}

internal class AccountBalance: Decodable {

public let accountId: Int
Expand All @@ -29,15 +27,15 @@ internal class AccountBalance: Decodable {
forKey: .balanceTime
)
guard let bTime: Date = formatter.date(from: rawBalanceTime) else {
throw BalanceError(.incomprehensibleResponse)
throw AmatinoError(.badResponse)
}
balanceTime = bTime
let rawGeneratedTime = try container.decode(
String.self,
forKey: .generatedTime
)
guard let gTime: Date = formatter.date(from: rawGeneratedTime) else {
throw BalanceError(.incomprehensibleResponse)
throw AmatinoError(.badResponse)
}
generatedTime = gTime
globalUnitDenomination = try container.decode(
Expand All @@ -49,10 +47,7 @@ internal class AccountBalance: Decodable {
forKey: .customUnitDenomination
)
let rawMagnitude = try container.decode(String.self, forKey: .balance)
magnitude = try Magnitude(
fromString: rawMagnitude,
withError: BalanceError.self
).decimal
magnitude = try Magnitude(fromString: rawMagnitude).decimal
recursive = try container.decode(Bool.self, forKey: .recursive)
return
}
Expand Down
7 changes: 2 additions & 5 deletions Sources/Amatino/AmatinoDate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,11 @@ internal struct AmatinoDate {

let decodedDate: Date

internal init (
fromString dateString: String,
withError ErrorType: AmatinoObjectError.Type
) throws {
internal init (fromString dateString: String) throws {
let formatter = DateFormatter()
formatter.dateFormat = RequestData.dateStringFormat
guard let date: Date = formatter.date(from: dateString) else {
throw ErrorType.init(.incomprehensibleResponse)
throw AmatinoError(.badResponse)
}
decodedDate = date
return
Expand Down
71 changes: 71 additions & 0 deletions Sources/Amatino/AmatinoError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,74 @@
//

import Foundation

public class AmatinoError: Error, CustomStringConvertible {
public let kind: Kind
public let message: String

public var description: String { get { return self.message }}

internal init(_ kind: Kind) {
self.kind = kind
message = kind.rawValue
return
}

public enum Kind: String {
case jsonParseFailed = """
Amatino Swift was unable to parse the JSON sent by the Amatino API. This
likely indicates a bug, please considering opening an issue on GitHub.
"""
case badResponse = """
Amatino Swift was not able to understand the response sent by the
Amatino API. If this happens repeatedly, there may be a bug in the API.
"""
case inconsistentInternalState = """
Amatino Swift has entered an unexpected state from which it cannot
recover. Please consider filing a bug report on GitHub.
"""
case notFound = "A requested resource could not be found"
case notAuthorised = """
You are not authorised to access a requested
resource.
"""
case notAuthenticated = """
Your request was not authenticated. Your Session may have expired or
been deleted. Consider creating a new Session.
"""
case badRequest = """
Amatino could not understand your request, it may be missing a required
parameter, or be composed of incorrect types. It is the responsibility
of this library to supply correctly formed requests, so please consider
filing a bug report on GitHub.
"""
case genericServerError = """
Amatino replied with a generic error response, indicating that it has
failed internally. Either Amatino is experiencing temporary disruption,
or there is a bug in API.
"""
case constraintViolated = """
Input data violates a constraint. For example, a description may be too
long.
"""
case subscriptionProblem = """
Your Amatino subscription does not allow you to perfom this action. Your
payment method may have expired, or your plan may be disabled. Please
visit https://amatino.io/billing or contact support@amatino.io
"""
case serviceDisruption = """
Amatino is experiencing a service disruption. This should be temporary.
Check the @amatinoapi Twitter feed and https://amatino.io/blog/ for
service updates.
"""
case rateLimit = """
You have hit the Amatino API rate limiter. You might try batching your
requests (e.g. creating 10 Transactions at once). If your implementation
requires a higher rate limit, or your believe you are being erroneously
limited (e.g. behind a corporate or university NAT), please contact
support@amatino.io
"""
}
}


14 changes: 6 additions & 8 deletions Sources/Amatino/AmatinoObject.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@
// author: hugh@amatino.io
//

internal protocol AmatinoObject: Decodable {
static var errorType: AmatinoObjectError.Type { get }
}
internal protocol AmatinoObject: Decodable {}

extension AmatinoObject {

Expand All @@ -22,7 +20,7 @@ extension AmatinoObject {
let object: ObjectType
let objects: [ObjectType]
guard let dataToDecode: Data = data else {
callback(errorType.init(.inconsistentInternalState), nil)
callback(AmatinoError(.inconsistentInternalState), nil)
return
}
do {
Expand All @@ -31,7 +29,7 @@ extension AmatinoObject {
from: dataToDecode
)
guard objects.count > 0 else {
callback(errorType.init(.incomprehensibleResponse), nil)
callback(AmatinoError(.badResponse), nil)
return
}
object = objects[0]
Expand All @@ -53,7 +51,7 @@ extension AmatinoObject {
let decoder = JSONDecoder()
let objects: [ObjectType]
guard let dataToDecode: Data = data else {
callback(AmatinoObjectError(.inconsistentInternalState), nil)
callback(AmatinoError(.inconsistentInternalState), nil)
return
}
do {
Expand All @@ -62,7 +60,7 @@ extension AmatinoObject {
from: dataToDecode
)
guard objects.count > 0 else {
callback(AmatinoObjectError(.incomprehensibleResponse), nil)
callback(AmatinoError(.badResponse), nil)
return
}
callback(nil, objects)
Expand All @@ -83,7 +81,7 @@ extension AmatinoObject {
let decoder = JSONDecoder()
let object: ObjectType
guard let dataToDecode: Data = data else {
callback(errorType.init(.inconsistentInternalState), nil)
callback(AmatinoError(.badResponse), nil)
return
}
do {
Expand Down
31 changes: 0 additions & 31 deletions Sources/Amatino/AmatinoObjectError.swift

This file was deleted.

41 changes: 25 additions & 16 deletions Sources/Amatino/AmatinoRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,11 @@

import Foundation

enum AmatinoRequestError: Error {
case SessionRequired(description: String)
case URLInitialisationFailure()
case ResponseError()
case InvalidSession()
case EmptyResponse()
case JsonParse()
}

internal class AmatinoRequest {

private let agent = "Amatino Swift 0.0.5"
private let apiEndpoint = "https://api.amatino.io"
//private let apiEndpoint = "https://api.amatino.io"
private let apiEndpoint = "http://127.0.0.1:5000"
private static let apiSession = URLSession(
configuration: URLSessionConfiguration.ephemeral
)
Expand Down Expand Up @@ -72,13 +64,30 @@ internal class AmatinoRequest {
callback(error, nil)
return
}
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else {
callback(AmatinoRequestError.ResponseError(), nil)
// To Do - Descriptive error responses
return
guard let httpResponse = response as? HTTPURLResponse else {
callback(AmatinoError(.inconsistentInternalState), nil)
return
}
guard (200...299).contains(httpResponse.statusCode) else {
let error: AmatinoError
switch httpResponse.statusCode {
case 400: error = AmatinoError(.badRequest)
case 401: error = AmatinoError(.notAuthenticated)
case 402: error = AmatinoError(.subscriptionProblem)
case 403: error = AmatinoError(.notAuthorised)
case 404: error = AmatinoError(.notFound)
case 429: error = AmatinoError(.rateLimit)
case 500: error = AmatinoError(.genericServerError)
case 502, 503, 504: error = AmatinoError(.serviceDisruption)
default: error = AmatinoError(
.inconsistentInternalState
)
}
callback(error, nil)
return
}
callback(nil, data)
return
}).resume()
return
}
Expand Down Expand Up @@ -113,7 +122,7 @@ internal class AmatinoRequest {
}

guard targetURL != nil else {
throw AmatinoRequestError.URLInitialisationFailure()
throw AmatinoError(.inconsistentInternalState)
}

var request = URLRequest(url: targetURL!)
Expand Down
Loading

0 comments on commit 35239ae

Please sign in to comment.