mirror of
https://github.com/encounter/JSONAPI.git
synced 2026-03-30 11:18:38 -07:00
Add generic and basic error types. add tests for generic type.
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
//
|
||||
// BasicError.swift
|
||||
// JSONAPI
|
||||
//
|
||||
// Created by Mathew Polzin on 9/29/19.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Most of the JSON:API Spec defined Error fields.
|
||||
public struct BasicJSONAPIErrorPayload<IdType: Codable & Equatable>: Codable, Equatable, ErrorDictType {
|
||||
/// a unique identifier for this particular occurrence of the problem
|
||||
let id: IdType?
|
||||
// let links: Links? // we skip this for now to avoid adding complexity to using this basic type.
|
||||
/// the HTTP status code applicable to this problem
|
||||
let status: String?
|
||||
/// an application-specific error code
|
||||
let code: String?
|
||||
/// a short, human-readable summary of the problem that SHOULD NOT change from occurrence to occurrence of the problem, except for purposes of localization
|
||||
let title: String?
|
||||
/// a human-readable explanation specific to this occurrence of the problem. Like `title`, this field’s value can be localized
|
||||
let detail: String?
|
||||
/// an object containing references to the source of the error
|
||||
let source: Source?
|
||||
// let meta: Meta? // we skip this for now to avoid adding complexity to using this basic type
|
||||
|
||||
public struct Source: Codable, Equatable {
|
||||
/// a JSON Pointer [RFC6901] to the associated entity in the request document [e.g. "/data" for a primary data object, or "/data/attributes/title" for a specific attribute].
|
||||
let pointer: String?
|
||||
/// which URI query parameter caused the error
|
||||
let parameter: String?
|
||||
}
|
||||
|
||||
public var definedFields: [String: String] {
|
||||
let keysAndValues = [
|
||||
id.map { ("id", String(describing: $0)) },
|
||||
status.map { ("status", $0) },
|
||||
code.map { ("code", $0) },
|
||||
title.map { ("title", $0) },
|
||||
detail.map { ("detail", $0) },
|
||||
source.flatMap { $0.pointer.map { ("pointer", $0) } },
|
||||
source.flatMap { $0.parameter.map { ("parameter", $0) } }
|
||||
].compactMap { $0 }
|
||||
return Dictionary(uniqueKeysWithValues: keysAndValues)
|
||||
}
|
||||
}
|
||||
|
||||
/// `BasicJSONAPIError` optionally decodes many possible fields
|
||||
/// specified by the JSON:API 1.0 Spec. It gives no type-guarantees of what
|
||||
/// will be non-nil, but could provide good diagnostic information when
|
||||
/// you do not know what error structure to expect.
|
||||
///
|
||||
/// ```
|
||||
/// Fields:
|
||||
/// - id
|
||||
/// - status
|
||||
/// - code
|
||||
/// - title
|
||||
/// - detail
|
||||
/// - source
|
||||
/// - pointer
|
||||
/// - parameter
|
||||
/// ```
|
||||
///
|
||||
/// The JSON:API Spec does not dictate the type of this particular Id field,
|
||||
/// so you must specify whether to expect, for example, an `Int` or a `String`
|
||||
/// in the id field.
|
||||
///
|
||||
/// Something like `AnyCodable` from *Flight-School* could be
|
||||
/// a good option if you do not know what to expect. You could also use
|
||||
/// `Either<Int, String>` (provided by the `Poly` package that is
|
||||
/// already a dependency of `JSONAPI`).
|
||||
public typealias BasicJSONAPIError<IdType: Codable & Equatable> = GenericJSONAPIError<BasicJSONAPIErrorPayload<IdType>>
|
||||
@@ -0,0 +1,67 @@
|
||||
//
|
||||
// GenericError.swift
|
||||
// JSONAPI
|
||||
//
|
||||
// Created by Mathew Polzin on 9/29/19.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// `GenericJSONAPIError` can be used to specify whatever error
|
||||
/// payload you expect to need to parse in responses and handle any
|
||||
/// other payload structure as `.unknownError`.
|
||||
public enum GenericJSONAPIError<ErrorPayload: Codable & Equatable>: JSONAPIError {
|
||||
case unknownError
|
||||
case error(ErrorPayload)
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.singleValueContainer()
|
||||
|
||||
do {
|
||||
self = .error(try container.decode(ErrorPayload.self))
|
||||
} catch {
|
||||
self = .unknown
|
||||
}
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.singleValueContainer()
|
||||
switch self {
|
||||
case .error(let payload):
|
||||
try container.encode(payload)
|
||||
case .unknownError:
|
||||
try container.encode("unknown")
|
||||
}
|
||||
}
|
||||
|
||||
public static var unknown: Self {
|
||||
return .unknownError
|
||||
}
|
||||
}
|
||||
|
||||
public extension GenericJSONAPIError {
|
||||
var payload: ErrorPayload? {
|
||||
switch self {
|
||||
case .unknownError:
|
||||
return nil
|
||||
case .error(let payload):
|
||||
return payload
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public protocol ErrorDictType {
|
||||
var definedFields: [String: String] { get }
|
||||
}
|
||||
|
||||
extension GenericJSONAPIError: ErrorDictType where ErrorPayload: ErrorDictType {
|
||||
/// Get a dictionary of all defined fields and their values.
|
||||
public var definedFields: [String: String] {
|
||||
switch self {
|
||||
case .unknownError:
|
||||
return [:]
|
||||
case .error(let basicPayload):
|
||||
return basicPayload.definedFields
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,10 @@ public protocol JSONAPIError: Swift.Error, Equatable, Codable {
|
||||
|
||||
/// `UnknownJSONAPIError` can actually be used in any sitaution
|
||||
/// where you don't know what errors are possible _or_ you just don't
|
||||
/// care what errors might show up.
|
||||
/// care what errors might show up. If you don't know how the error
|
||||
/// will be structured but you would like to have access to more
|
||||
/// information the server might be providing in the error payload,
|
||||
/// use `BasicJSONAPIError` instead.
|
||||
public enum UnknownJSONAPIError: JSONAPIError {
|
||||
case unknownError
|
||||
|
||||
@@ -24,7 +27,7 @@ public enum UnknownJSONAPIError: JSONAPIError {
|
||||
try container.encode("unknown")
|
||||
}
|
||||
|
||||
public static var unknown: UnknownJSONAPIError {
|
||||
public static var unknown: Self {
|
||||
return .unknownError
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user