Commit 5698cafc authored by Mauro E. Bender's avatar Mauro E. Bender
Browse files

Close #3 - Add nested resources

parent 5946d7e4
...@@ -22,7 +22,7 @@ public class Api { ...@@ -22,7 +22,7 @@ public class Api {
} }
// RESOURCE // RESOURCE
public func resource<T>( path: String ) -> Resource<T> { public func resource( path: String ) -> Resource {
return Resource( api: self, path: path ) return Resource( api: self, path: path )
} }
......
...@@ -19,7 +19,7 @@ public enum ApiError: ErrorType { ...@@ -19,7 +19,7 @@ public enum ApiError: ErrorType {
case Unknown( error: NSError ) case Unknown( error: NSError )
} }
public class Resource<T: JSONSerializable> { public class Resource {
public let api: Api public let api: Api
public let path: String public let path: String
...@@ -28,7 +28,7 @@ public class Resource<T: JSONSerializable> { ...@@ -28,7 +28,7 @@ public class Resource<T: JSONSerializable> {
self.path = path self.path = path
} }
public func item( id: AnyObject, parameters: [ String: String ]? = nil, public func item<T: JSONSerializable>( id: AnyObject, parameters: [ String: String ]? = nil,
headers: [ String: String]? = nil, completion: ( ( T?, error: ApiError? ) -> Void ) ) headers: [ String: String]? = nil, completion: ( ( T?, error: ApiError? ) -> Void ) )
{ {
api.get( resourceMemberPath( id ), parameters: parameters, headers: headers ) { response in api.get( resourceMemberPath( id ), parameters: parameters, headers: headers ) { response in
...@@ -36,7 +36,7 @@ public class Resource<T: JSONSerializable> { ...@@ -36,7 +36,7 @@ public class Resource<T: JSONSerializable> {
} }
} }
public func create( item: T, parameters: [ String: String ]? = nil, public func create<T: JSONSerializable>( item: T, parameters: [ String: String ]? = nil,
headers: [ String: String]? = nil, encoding: ParameterEncoding = .JSON, headers: [ String: String]? = nil, encoding: ParameterEncoding = .JSON,
completion: ( ( T?, error: ApiError? ) -> Void ) ) completion: ( ( T?, error: ApiError? ) -> Void ) )
{ {
...@@ -48,7 +48,9 @@ public class Resource<T: JSONSerializable> { ...@@ -48,7 +48,9 @@ public class Resource<T: JSONSerializable> {
} }
} }
func handleItemResult( response: Response<AnyObject, NSError>, handler: ( T?, error: ApiError? ) -> Void ) { func handleItemResult<T: JSONSerializable>( response: Response<AnyObject, NSError>,
handler: ( T?, error: ApiError? ) -> Void )
{
switch(response.result) { switch(response.result) {
case .Success(let JSON): case .Success(let JSON):
handler( T( fromJSON: JSON as! [String : AnyObject] ), error: nil ) handler( T( fromJSON: JSON as! [String : AnyObject] ), error: nil )
...@@ -69,7 +71,21 @@ public class Resource<T: JSONSerializable> { ...@@ -69,7 +71,21 @@ public class Resource<T: JSONSerializable> {
return .Unknown( error: error ) return .Unknown( error: error )
} }
public func member( memberID: AnyObject ) -> Resource {
let memberPath = resourceMemberPath( memberID )
return Resource( api: api, path: memberPath )
}
public func resource( resourcePath: String ) -> Resource {
let nestedPath = nestedResourcePath( resourcePath )
return Resource( api: api, path: nestedPath )
}
func resourceMemberPath( memberId: AnyObject ) -> String { func resourceMemberPath( memberId: AnyObject ) -> String {
return "\(path)/\(memberId)" return "\(path)/\(memberId)"
} }
func nestedResourcePath( nestedResourcePath: String ) -> String {
return "\(path)/\(nestedResourcePath)"
}
} }
...@@ -25,7 +25,7 @@ class ApiTests: QuickSpec { ...@@ -25,7 +25,7 @@ class ApiTests: QuickSpec {
let api = Api( baseURL: baseURL, apiPath: apiPath ) let api = Api( baseURL: baseURL, apiPath: apiPath )
describe( "resource" ) { describe( "resource" ) {
let resource : Resource<Item> = api.resource( "items" ) let resource : Resource = api.resource( "items" )
it( "should have the correct api" ) { expect( resource.api ) == api } it( "should have the correct api" ) { expect( resource.api ) == api }
it( "should have the correct path" ) { expect( resource.path ) == "items" } it( "should have the correct path" ) { expect( resource.path ) == "items" }
......
...@@ -34,3 +34,24 @@ extension Item: Equatable {} ...@@ -34,3 +34,24 @@ extension Item: Equatable {}
func ==(lhs: Item, rhs: Item) -> Bool { func ==(lhs: Item, rhs: Item) -> Bool {
return lhs.id == rhs.id && lhs.name == rhs.name return lhs.id == rhs.id && lhs.name == rhs.name
} }
struct Component {
let id: Int
let name: String
}
extension Component: JSONSerializable {
init(fromJSON json: [String : AnyObject]) {
self.id = json[ "id" ] as! Int
self.name = json[ "name" ] as! String
}
func toJSON() -> [String : AnyObject] {
var json = [ String: AnyObject ]()
if id > 0 { json[ "id" ] = id }
json[ "name" ] = name
return json
}
}
\ No newline at end of file
...@@ -17,6 +17,7 @@ class ResourceTests: QuickSpec { ...@@ -17,6 +17,7 @@ class ResourceTests: QuickSpec {
// Register stubs // Register stubs
stub( matchApiPath( "items/1" ), builder: json( [ "id": 1, "name": "Item 1" ] ) ) stub( matchApiPath( "items/1" ), builder: json( [ "id": 1, "name": "Item 1" ] ) )
stub( matchApiPath( "items/1/components/1" ), builder: json( [ "id": 1, "name": "Component 1" ] ) )
stub( matchApiPath( "items", method: .POST ), builder: buildItemFromRequest( 2 ) ) stub( matchApiPath( "items", method: .POST ), builder: buildItemFromRequest( 2 ) )
stub( matchApiPath( "failedItems", method: .POST ), builder: http( 422 ) ) stub( matchApiPath( "failedItems", method: .POST ), builder: http( 422 ) )
} }
...@@ -25,7 +26,7 @@ class ResourceTests: QuickSpec { ...@@ -25,7 +26,7 @@ class ResourceTests: QuickSpec {
let api = Api( baseURL: baseURL, apiPath: apiPath ) let api = Api( baseURL: baseURL, apiPath: apiPath )
describe( "Resource" ) { describe( "Resource" ) {
let resource: Resource<Item> = api.resource( "items" ) let resource: Resource = api.resource( "items" )
describe( "item" ) { describe( "item" ) {
context( "when the request is successful" ) { context( "when the request is successful" ) {
...@@ -58,9 +59,9 @@ class ResourceTests: QuickSpec { ...@@ -58,9 +59,9 @@ class ResourceTests: QuickSpec {
} }
context( "when there's an error" ) { context( "when there's an error" ) {
let failResource : Resource<Item> = api.resource( "failedItems" ) let failResource : Resource = api.resource( "failedItems" )
let itemToCreate = Item( id: 0, name: "Item 2" ) let itemToCreate = Item( id: 0, name: "Item 2" )
it( "should call the correct endpoint and return the created item" ) { it( "should call the correct endpoint and return the correct error" ) {
waitUntil { done in waitUntil { done in
failResource.create( itemToCreate ) { ( item: Item?, error: ApiError? ) in failResource.create( itemToCreate ) { ( item: Item?, error: ApiError? ) in
expect( item ).to( beNil() ) expect( item ).to( beNil() )
...@@ -72,6 +73,37 @@ class ResourceTests: QuickSpec { ...@@ -72,6 +73,37 @@ class ResourceTests: QuickSpec {
} }
} }
} }
describe( "member" ) {
let member = resource.member( 1 )
it( "should add the correct path" ) {
expect( member.api ).to( equal( resource.api ) )
expect( member.path ).to( equal( "\(resource.path)/1" ) )
}
}
describe( "resource" ) {
let nestedResource = resource.resource( "components" )
it( "should add the correct path" ) {
expect( nestedResource.api ).to( equal( resource.api ) )
expect( nestedResource.path ).to( equal( "\(resource.path)/components" ) )
}
}
context( "when getting a nested resource item" ) {
let nestedResource = resource.member( 1 ).resource( "components" )
it( "should call the correct endpoint and return correct the item" ) {
waitUntil { done in
nestedResource.item( 1 ) { ( component: Component?, error: ApiError? ) in
expect( component?.id ) == 1
expect( component?.name ) == "Component 1"
done()
}
}
}
}
} }
} }
} }
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment