Skip to content

Commit 681146f

Browse files
authored
Merge pull request #70 from devlucky/feature/JSONAPIErrors
Feature/jsonapi errors
2 parents 94701ba + 9b9891f commit 681146f

File tree

3 files changed

+184
-0
lines changed

3 files changed

+184
-0
lines changed

Kakapo.xcodeproj/project.pbxproj

+18
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,13 @@
8181
DE76E1CD1D0DC857009721A4 /* XCTestCase+CustomAssertions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE76E1B71D0DC857009721A4 /* XCTestCase+CustomAssertions.swift */; };
8282
DE76E1CE1D0DC857009721A4 /* XCTestCase+CustomAssertions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE76E1B71D0DC857009721A4 /* XCTestCase+CustomAssertions.swift */; };
8383
DE76E1CF1D0DC857009721A4 /* XCTestCase+CustomAssertions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE76E1B71D0DC857009721A4 /* XCTestCase+CustomAssertions.swift */; };
84+
DE82027A1D20068A00552FEC /* JSONAPIError.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE8202791D20068A00552FEC /* JSONAPIError.swift */; };
85+
DE82027B1D20068A00552FEC /* JSONAPIError.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE8202791D20068A00552FEC /* JSONAPIError.swift */; };
86+
DE82027C1D20068A00552FEC /* JSONAPIError.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE8202791D20068A00552FEC /* JSONAPIError.swift */; };
87+
DE82027D1D20068A00552FEC /* JSONAPIError.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE8202791D20068A00552FEC /* JSONAPIError.swift */; };
88+
DE82027F1D200C9000552FEC /* JSONAPIErrorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE82027E1D200C9000552FEC /* JSONAPIErrorTests.swift */; };
89+
DE8202801D200C9000552FEC /* JSONAPIErrorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE82027E1D200C9000552FEC /* JSONAPIErrorTests.swift */; };
90+
DE8202811D200C9000552FEC /* JSONAPIErrorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE82027E1D200C9000552FEC /* JSONAPIErrorTests.swift */; };
8491
/* End PBXBuildFile section */
8592

8693
/* Begin PBXContainerItemProxy section */
@@ -141,6 +148,8 @@
141148
DE76E1B51D0DC857009721A4 /* SerializerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SerializerTests.swift; sourceTree = "<group>"; };
142149
DE76E1B61D0DC857009721A4 /* URLDecomposerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLDecomposerTests.swift; sourceTree = "<group>"; };
143150
DE76E1B71D0DC857009721A4 /* XCTestCase+CustomAssertions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "XCTestCase+CustomAssertions.swift"; sourceTree = "<group>"; };
151+
DE8202791D20068A00552FEC /* JSONAPIError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSONAPIError.swift; sourceTree = "<group>"; };
152+
DE82027E1D200C9000552FEC /* JSONAPIErrorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSONAPIErrorTests.swift; sourceTree = "<group>"; };
144153
DEBD3C741D16BC9A004E0A23 /* README.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = README.playground; sourceTree = "<group>"; };
145154
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>"; };
146155
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>"; };
@@ -273,6 +282,7 @@
273282
DE76E0FE1D0DC38B009721A4 /* Info.plist */,
274283
8BE0FC7E1D16BD8300FE706A /* JSONAPILinksTests.swift */,
275284
DE76E1B11D0DC857009721A4 /* JSONAPITests.swift */,
285+
DE82027E1D200C9000552FEC /* JSONAPIErrorTests.swift */,
276286
DE76E1B21D0DC857009721A4 /* KakapoDBTests.swift */,
277287
DE76E1B31D0DC857009721A4 /* PropertyPolicyTests.swift */,
278288
DE76E1B41D0DC857009721A4 /* RouterTests.swift */,
@@ -286,6 +296,7 @@
286296
DE76E1001D0DC395009721A4 /* Source */ = {
287297
isa = PBXGroup;
288298
children = (
299+
DE8202791D20068A00552FEC /* JSONAPIError.swift */,
289300
8BE0FC831D172EC900FE706A /* JSONAPILinks.swift */,
290301
DE76E1011D0DC395009721A4 /* JSONAPISerializer.swift */,
291302
DE76E1021D0DC395009721A4 /* KakapoDB.swift */,
@@ -732,6 +743,7 @@
732743
DE76E19C1D0DC755009721A4 /* Serializer.swift in Sources */,
733744
DE76E1961D0DC755009721A4 /* JSONAPISerializer.swift in Sources */,
734745
DE76E19B1D0DC755009721A4 /* Router.swift in Sources */,
746+
DE82027B1D20068A00552FEC /* JSONAPIError.swift in Sources */,
735747
DE76E1991D0DC755009721A4 /* NSURLRequest+FixCopy.swift in Sources */,
736748
DE76E1A71D0DC762009721A4 /* CustomAssertions.swift in Sources */,
737749
);
@@ -749,6 +761,7 @@
749761
DE76E1941D0DC755009721A4 /* Serializer.swift in Sources */,
750762
DE76E18E1D0DC755009721A4 /* JSONAPISerializer.swift in Sources */,
751763
DE76E1931D0DC755009721A4 /* Router.swift in Sources */,
764+
DE82027C1D20068A00552FEC /* JSONAPIError.swift in Sources */,
752765
DE76E1911D0DC755009721A4 /* NSURLRequest+FixCopy.swift in Sources */,
753766
DE76E1A81D0DC762009721A4 /* CustomAssertions.swift in Sources */,
754767
);
@@ -764,6 +777,7 @@
764777
DE76E1BF1D0DC857009721A4 /* KakapoDBTests.swift in Sources */,
765778
DE76E1C81D0DC857009721A4 /* SerializerTests.swift in Sources */,
766779
DE76E1C51D0DC857009721A4 /* RouterTests.swift in Sources */,
780+
DE8202801D200C9000552FEC /* JSONAPIErrorTests.swift in Sources */,
767781
DE76E1CE1D0DC857009721A4 /* XCTestCase+CustomAssertions.swift in Sources */,
768782
DE76E1C21D0DC857009721A4 /* PropertyPolicyTests.swift in Sources */,
769783
);
@@ -781,6 +795,7 @@
781795
DE76E18C1D0DC754009721A4 /* Serializer.swift in Sources */,
782796
DE76E1861D0DC754009721A4 /* JSONAPISerializer.swift in Sources */,
783797
DE76E18B1D0DC754009721A4 /* Router.swift in Sources */,
798+
DE82027D1D20068A00552FEC /* JSONAPIError.swift in Sources */,
784799
DE76E1891D0DC754009721A4 /* NSURLRequest+FixCopy.swift in Sources */,
785800
DE76E1A91D0DC763009721A4 /* CustomAssertions.swift in Sources */,
786801
);
@@ -796,6 +811,7 @@
796811
DE76E1C01D0DC857009721A4 /* KakapoDBTests.swift in Sources */,
797812
DE76E1C91D0DC857009721A4 /* SerializerTests.swift in Sources */,
798813
DE76E1C61D0DC857009721A4 /* RouterTests.swift in Sources */,
814+
DE8202811D200C9000552FEC /* JSONAPIErrorTests.swift in Sources */,
799815
DE76E1CF1D0DC857009721A4 /* XCTestCase+CustomAssertions.swift in Sources */,
800816
DE76E1C31D0DC857009721A4 /* PropertyPolicyTests.swift in Sources */,
801817
);
@@ -813,6 +829,7 @@
813829
DE76E1A41D0DC756009721A4 /* Serializer.swift in Sources */,
814830
DE76E19E1D0DC756009721A4 /* JSONAPISerializer.swift in Sources */,
815831
DE76E1A31D0DC756009721A4 /* Router.swift in Sources */,
832+
DE82027A1D20068A00552FEC /* JSONAPIError.swift in Sources */,
816833
DE76E1A11D0DC756009721A4 /* NSURLRequest+FixCopy.swift in Sources */,
817834
DE76E1A61D0DC761009721A4 /* CustomAssertions.swift in Sources */,
818835
);
@@ -828,6 +845,7 @@
828845
DE76E1BE1D0DC857009721A4 /* KakapoDBTests.swift in Sources */,
829846
DE76E1C71D0DC857009721A4 /* SerializerTests.swift in Sources */,
830847
DE76E1C41D0DC857009721A4 /* RouterTests.swift in Sources */,
848+
DE82027F1D200C9000552FEC /* JSONAPIErrorTests.swift in Sources */,
831849
DE76E1CD1D0DC857009721A4 /* XCTestCase+CustomAssertions.swift in Sources */,
832850
DE76E1C11D0DC857009721A4 /* PropertyPolicyTests.swift in Sources */,
833851
);

Source/JSONAPIError.swift

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
//
2+
// JSONAPIError.swift
3+
// Kakapo
4+
//
5+
// Created by Alex Manzella on 26/06/16.
6+
// Copyright © 2016 devlucky. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
// A convenince error object that conform to JSON API
12+
public struct JSONAPIError: ResponseFieldsProvider {
13+
14+
/// An object containing references to the source of the error, optionally including any of the following members
15+
public struct Source: Serializable {
16+
/// 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].
17+
public let pointer: String?
18+
19+
/// A string indicating which URI query parameter caused the error.
20+
public let parameter: String?
21+
}
22+
23+
/// A builder for JSONAPIError
24+
public struct Builder: Serializable {
25+
26+
/// A unique identifier for this particular occurrence of the problem.
27+
public var id: String?
28+
29+
/// A link object that leads to further details about this particular occurrence of the problem.
30+
public var about: JSONAPILink?
31+
32+
/// The HTTP status code applicable to this problem, expressed as a string value.
33+
public var status: Int
34+
35+
/// An application-specific error code, expressed as a string value
36+
public var code: String?
37+
38+
/// A short, human-readable summary of the problem that SHOULD NOT change from occurrence to occurrence of the problem, except for purposes of localization
39+
public var title: String?
40+
41+
/// A human-readable explanation specific to this occurrence of the problem. Like title, this field’s value can be localized.
42+
public var detail: String?
43+
44+
/**
45+
An object containing references to the source of the error, optionally including any of the following members:
46+
47+
- 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].
48+
- parameter: a string indicating which URI query parameter caused the error.
49+
*/
50+
public var source: Source?
51+
52+
/// A meta object containing non-standard meta-information about the error.
53+
public var meta: Serializable?
54+
55+
private init(statusCode: Int) {
56+
status = statusCode
57+
}
58+
}
59+
60+
private let builder: Builder
61+
62+
// MARK: ResponseFieldsProvider
63+
64+
public var statusCode: Int {
65+
return builder.status
66+
}
67+
68+
public var body: Serializable {
69+
return builder
70+
}
71+
72+
public var headerFields: [String : String]? {
73+
return nil
74+
}
75+
76+
/**
77+
Initialize a `JSONAPIError` and build it with `JSONAPIError.Builder`
78+
79+
- parameter statusCode: The status code of the response, will be used also to provide a statusCode for your request
80+
- 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`**)
81+
82+
- returns: An error that conforms to JSON API specifications and it's ready to be serialized
83+
*/
84+
public init(statusCode: Int, errorBuilder: (error: inout Builder) -> ()) {
85+
var builder = Builder(statusCode: statusCode)
86+
errorBuilder(error: &builder)
87+
self.builder = builder
88+
}
89+
90+
}

Tests/JSONAPIErrorTests.swift

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
//
2+
// JSONAPIErrorTests.swift
3+
// Kakapo
4+
//
5+
// Created by Alex Manzella on 26/06/16.
6+
// Copyright © 2016 devlucky. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
import Quick
12+
import Nimble
13+
import SwiftyJSON
14+
@testable import Kakapo
15+
16+
class JSONAPIErrorsSpec: QuickSpec {
17+
18+
private struct ErrorDescription: Serializable {
19+
let description: String
20+
}
21+
22+
override func spec() {
23+
24+
func json(object: Serializable) -> JSON {
25+
return JSON(object.serialize()!)
26+
}
27+
28+
describe("JSON API errors") {
29+
30+
it("should serialize errors") {
31+
let error = JSONAPIError(statusCode: 404) { (error) in
32+
error.title = "test"
33+
}
34+
let object = json(error)
35+
expect(object.count).to(equal(2))
36+
expect(object["status"]).to(equal(404))
37+
expect(object["title"]).to(equal("test"))
38+
}
39+
40+
it("should serialize members of the error") {
41+
let error = JSONAPIError(statusCode: 404) { (error) in
42+
error.source = JSONAPIError.Source(pointer: "ptr", parameter: "param")
43+
error.meta = ErrorDescription(description: "test")
44+
}
45+
46+
let object = json(error)
47+
expect(object.count).to(equal(3))
48+
49+
let source = object["source"].dictionaryValue
50+
expect(source["pointer"]).to(equal("ptr"))
51+
expect(source["parameter"]).to(equal("param"))
52+
53+
let meta = object["meta"].dictionaryValue
54+
expect(meta["description"]).to(equal("test"))
55+
}
56+
57+
it("should affect the status code of the request") {
58+
let router = Router.register("http://www.test.com")
59+
60+
router.get("/users"){ request in
61+
return JSONAPIError(statusCode: 501) { (error) in
62+
error.title = "test"
63+
}
64+
}
65+
66+
var statusCode: Int? = nil
67+
NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: "http://www.test.com/users")!) { (data, response, _) in
68+
let response = response as! NSHTTPURLResponse
69+
statusCode = response.statusCode
70+
}.resume()
71+
72+
expect(statusCode).toEventually(equal(501))
73+
}
74+
}
75+
}
76+
}

0 commit comments

Comments
 (0)