Skip to content

Commit

Permalink
Merge pull request #70 from devlucky/feature/JSONAPIErrors
Browse files Browse the repository at this point in the history
Feature/jsonapi errors
  • Loading branch information
MP0w authored Jun 26, 2016
2 parents 94701ba + 9b9891f commit 681146f
Show file tree
Hide file tree
Showing 3 changed files with 184 additions and 0 deletions.
18 changes: 18 additions & 0 deletions Kakapo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,13 @@
DE76E1CD1D0DC857009721A4 /* XCTestCase+CustomAssertions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE76E1B71D0DC857009721A4 /* XCTestCase+CustomAssertions.swift */; };
DE76E1CE1D0DC857009721A4 /* XCTestCase+CustomAssertions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE76E1B71D0DC857009721A4 /* XCTestCase+CustomAssertions.swift */; };
DE76E1CF1D0DC857009721A4 /* XCTestCase+CustomAssertions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE76E1B71D0DC857009721A4 /* XCTestCase+CustomAssertions.swift */; };
DE82027A1D20068A00552FEC /* JSONAPIError.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE8202791D20068A00552FEC /* JSONAPIError.swift */; };
DE82027B1D20068A00552FEC /* JSONAPIError.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE8202791D20068A00552FEC /* JSONAPIError.swift */; };
DE82027C1D20068A00552FEC /* JSONAPIError.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE8202791D20068A00552FEC /* JSONAPIError.swift */; };
DE82027D1D20068A00552FEC /* JSONAPIError.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE8202791D20068A00552FEC /* JSONAPIError.swift */; };
DE82027F1D200C9000552FEC /* JSONAPIErrorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE82027E1D200C9000552FEC /* JSONAPIErrorTests.swift */; };
DE8202801D200C9000552FEC /* JSONAPIErrorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE82027E1D200C9000552FEC /* JSONAPIErrorTests.swift */; };
DE8202811D200C9000552FEC /* JSONAPIErrorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE82027E1D200C9000552FEC /* JSONAPIErrorTests.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -141,6 +148,8 @@
DE76E1B51D0DC857009721A4 /* SerializerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SerializerTests.swift; sourceTree = "<group>"; };
DE76E1B61D0DC857009721A4 /* URLDecomposerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLDecomposerTests.swift; sourceTree = "<group>"; };
DE76E1B71D0DC857009721A4 /* XCTestCase+CustomAssertions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "XCTestCase+CustomAssertions.swift"; sourceTree = "<group>"; };
DE8202791D20068A00552FEC /* JSONAPIError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSONAPIError.swift; sourceTree = "<group>"; };
DE82027E1D200C9000552FEC /* JSONAPIErrorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSONAPIErrorTests.swift; sourceTree = "<group>"; };
DEBD3C741D16BC9A004E0A23 /* README.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = README.playground; sourceTree = "<group>"; };
DFC29110721D31BE30199824 /* Pods-Kakapo iOSTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Kakapo iOSTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Kakapo iOSTests/Pods-Kakapo iOSTests.debug.xcconfig"; sourceTree = "<group>"; };
E357918ACA29146B64834E63 /* Pods-Kakapo tvOSTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Kakapo tvOSTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-Kakapo tvOSTests/Pods-Kakapo tvOSTests.release.xcconfig"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -273,6 +282,7 @@
DE76E0FE1D0DC38B009721A4 /* Info.plist */,
8BE0FC7E1D16BD8300FE706A /* JSONAPILinksTests.swift */,
DE76E1B11D0DC857009721A4 /* JSONAPITests.swift */,
DE82027E1D200C9000552FEC /* JSONAPIErrorTests.swift */,
DE76E1B21D0DC857009721A4 /* KakapoDBTests.swift */,
DE76E1B31D0DC857009721A4 /* PropertyPolicyTests.swift */,
DE76E1B41D0DC857009721A4 /* RouterTests.swift */,
Expand All @@ -286,6 +296,7 @@
DE76E1001D0DC395009721A4 /* Source */ = {
isa = PBXGroup;
children = (
DE8202791D20068A00552FEC /* JSONAPIError.swift */,
8BE0FC831D172EC900FE706A /* JSONAPILinks.swift */,
DE76E1011D0DC395009721A4 /* JSONAPISerializer.swift */,
DE76E1021D0DC395009721A4 /* KakapoDB.swift */,
Expand Down Expand Up @@ -732,6 +743,7 @@
DE76E19C1D0DC755009721A4 /* Serializer.swift in Sources */,
DE76E1961D0DC755009721A4 /* JSONAPISerializer.swift in Sources */,
DE76E19B1D0DC755009721A4 /* Router.swift in Sources */,
DE82027B1D20068A00552FEC /* JSONAPIError.swift in Sources */,
DE76E1991D0DC755009721A4 /* NSURLRequest+FixCopy.swift in Sources */,
DE76E1A71D0DC762009721A4 /* CustomAssertions.swift in Sources */,
);
Expand All @@ -749,6 +761,7 @@
DE76E1941D0DC755009721A4 /* Serializer.swift in Sources */,
DE76E18E1D0DC755009721A4 /* JSONAPISerializer.swift in Sources */,
DE76E1931D0DC755009721A4 /* Router.swift in Sources */,
DE82027C1D20068A00552FEC /* JSONAPIError.swift in Sources */,
DE76E1911D0DC755009721A4 /* NSURLRequest+FixCopy.swift in Sources */,
DE76E1A81D0DC762009721A4 /* CustomAssertions.swift in Sources */,
);
Expand All @@ -764,6 +777,7 @@
DE76E1BF1D0DC857009721A4 /* KakapoDBTests.swift in Sources */,
DE76E1C81D0DC857009721A4 /* SerializerTests.swift in Sources */,
DE76E1C51D0DC857009721A4 /* RouterTests.swift in Sources */,
DE8202801D200C9000552FEC /* JSONAPIErrorTests.swift in Sources */,
DE76E1CE1D0DC857009721A4 /* XCTestCase+CustomAssertions.swift in Sources */,
DE76E1C21D0DC857009721A4 /* PropertyPolicyTests.swift in Sources */,
);
Expand All @@ -781,6 +795,7 @@
DE76E18C1D0DC754009721A4 /* Serializer.swift in Sources */,
DE76E1861D0DC754009721A4 /* JSONAPISerializer.swift in Sources */,
DE76E18B1D0DC754009721A4 /* Router.swift in Sources */,
DE82027D1D20068A00552FEC /* JSONAPIError.swift in Sources */,
DE76E1891D0DC754009721A4 /* NSURLRequest+FixCopy.swift in Sources */,
DE76E1A91D0DC763009721A4 /* CustomAssertions.swift in Sources */,
);
Expand All @@ -796,6 +811,7 @@
DE76E1C01D0DC857009721A4 /* KakapoDBTests.swift in Sources */,
DE76E1C91D0DC857009721A4 /* SerializerTests.swift in Sources */,
DE76E1C61D0DC857009721A4 /* RouterTests.swift in Sources */,
DE8202811D200C9000552FEC /* JSONAPIErrorTests.swift in Sources */,
DE76E1CF1D0DC857009721A4 /* XCTestCase+CustomAssertions.swift in Sources */,
DE76E1C31D0DC857009721A4 /* PropertyPolicyTests.swift in Sources */,
);
Expand All @@ -813,6 +829,7 @@
DE76E1A41D0DC756009721A4 /* Serializer.swift in Sources */,
DE76E19E1D0DC756009721A4 /* JSONAPISerializer.swift in Sources */,
DE76E1A31D0DC756009721A4 /* Router.swift in Sources */,
DE82027A1D20068A00552FEC /* JSONAPIError.swift in Sources */,
DE76E1A11D0DC756009721A4 /* NSURLRequest+FixCopy.swift in Sources */,
DE76E1A61D0DC761009721A4 /* CustomAssertions.swift in Sources */,
);
Expand All @@ -828,6 +845,7 @@
DE76E1BE1D0DC857009721A4 /* KakapoDBTests.swift in Sources */,
DE76E1C71D0DC857009721A4 /* SerializerTests.swift in Sources */,
DE76E1C41D0DC857009721A4 /* RouterTests.swift in Sources */,
DE82027F1D200C9000552FEC /* JSONAPIErrorTests.swift in Sources */,
DE76E1CD1D0DC857009721A4 /* XCTestCase+CustomAssertions.swift in Sources */,
DE76E1C11D0DC857009721A4 /* PropertyPolicyTests.swift in Sources */,
);
Expand Down
90 changes: 90 additions & 0 deletions Source/JSONAPIError.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
//
// JSONAPIError.swift
// Kakapo
//
// Created by Alex Manzella on 26/06/16.
// Copyright © 2016 devlucky. All rights reserved.
//

import Foundation

// A convenince error object that conform to JSON API
public struct JSONAPIError: ResponseFieldsProvider {

/// An object containing references to the source of the error, optionally including any of the following members
public struct Source: Serializable {
/// A JSON `Pointer` ([RFC6901](https://tools.ietf.org/html/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].
public let pointer: String?

/// A string indicating which URI query parameter caused the error.
public let parameter: String?
}

/// A builder for JSONAPIError
public struct Builder: Serializable {

/// A unique identifier for this particular occurrence of the problem.
public var id: String?

/// A link object that leads to further details about this particular occurrence of the problem.
public var about: JSONAPILink?

/// The HTTP status code applicable to this problem, expressed as a string value.
public var status: Int

/// An application-specific error code, expressed as a string value
public var 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
public var title: String?

/// A human-readable explanation specific to this occurrence of the problem. Like title, this field’s value can be localized.
public var detail: String?

/**
An object containing references to the source of the error, optionally including any of the following members:

- pointer: a JSON `Pointer` ([RFC6901](https://tools.ietf.org/html/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].
- parameter: a string indicating which URI query parameter caused the error.
*/
public var source: Source?

/// A meta object containing non-standard meta-information about the error.
public var meta: Serializable?

private init(statusCode: Int) {
status = statusCode
}
}

private let builder: Builder

// MARK: ResponseFieldsProvider

public var statusCode: Int {
return builder.status
}

public var body: Serializable {
return builder
}

public var headerFields: [String : String]? {
return nil
}

/**
Initialize a `JSONAPIError` and build it with `JSONAPIError.Builder`

- parameter statusCode: The status code of the response, will be used also to provide a statusCode for your request
- parameter errorBuilder: A builder that can be used to fill the error objects, it contains all you need to provide an error object confiorming to JSON API (**see `JSONAPIError.Builder`**)

- returns: An error that conforms to JSON API specifications and it's ready to be serialized
*/
public init(statusCode: Int, errorBuilder: (error: inout Builder) -> ()) {
var builder = Builder(statusCode: statusCode)
errorBuilder(error: &builder)
self.builder = builder
}

}
76 changes: 76 additions & 0 deletions Tests/JSONAPIErrorTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//
// JSONAPIErrorTests.swift
// Kakapo
//
// Created by Alex Manzella on 26/06/16.
// Copyright © 2016 devlucky. All rights reserved.
//

import Foundation

import Quick
import Nimble
import SwiftyJSON
@testable import Kakapo

class JSONAPIErrorsSpec: QuickSpec {

private struct ErrorDescription: Serializable {
let description: String
}

override func spec() {

func json(object: Serializable) -> JSON {
return JSON(object.serialize()!)
}

describe("JSON API errors") {

it("should serialize errors") {
let error = JSONAPIError(statusCode: 404) { (error) in
error.title = "test"
}
let object = json(error)
expect(object.count).to(equal(2))
expect(object["status"]).to(equal(404))
expect(object["title"]).to(equal("test"))
}

it("should serialize members of the error") {
let error = JSONAPIError(statusCode: 404) { (error) in
error.source = JSONAPIError.Source(pointer: "ptr", parameter: "param")
error.meta = ErrorDescription(description: "test")
}

let object = json(error)
expect(object.count).to(equal(3))

let source = object["source"].dictionaryValue
expect(source["pointer"]).to(equal("ptr"))
expect(source["parameter"]).to(equal("param"))

let meta = object["meta"].dictionaryValue
expect(meta["description"]).to(equal("test"))
}

it("should affect the status code of the request") {
let router = Router.register("http://www.test.com")

router.get("/users"){ request in
return JSONAPIError(statusCode: 501) { (error) in
error.title = "test"
}
}

var statusCode: Int? = nil
NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: "http://www.test.com/users")!) { (data, response, _) in
let response = response as! NSHTTPURLResponse
statusCode = response.statusCode
}.resume()

expect(statusCode).toEventually(equal(501))
}
}
}
}

0 comments on commit 681146f

Please sign in to comment.