diff --git a/Classes/JSONAPI.m b/Classes/JSONAPI.m index f74bd95..6026dc1 100644 --- a/Classes/JSONAPI.m +++ b/Classes/JSONAPI.m @@ -158,11 +158,11 @@ - (void)inflateWithDictionary:(NSDictionary*)dictionary { // Parse errors if (dictionary[@"errors"]) { NSMutableArray *errors = [[NSMutableArray alloc] init]; - NSLog(@"ERRORS - %@", dictionary[@"errors"]); + // NSLog(@"ERRORS - %@", dictionary[@"errors"]); for (NSDictionary *data in dictionary[@"errors"]) { JSONAPIErrorResource *resource = [[JSONAPIErrorResource alloc] initWithDictionary: data]; - NSLog(@"Error resource - %@", resource); + // NSLog(@"Error resource - %@", resource); if (resource) [errors addObject:resource]; } _errors = errors; @@ -171,32 +171,38 @@ - (void)inflateWithDictionary:(NSDictionary*)dictionary { - (void)inflateWithResource:(NSObject *)resource { - NSMutableArray *resourceArray = [[NSMutableArray alloc] init]; - [resourceArray addObject:resource]; - _resources = resourceArray; + _resources = @[resource]; - NSMutableDictionary *newDictionary = [[NSMutableDictionary alloc] init]; + NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init]; + dictionary[@"data"] = [JSONAPIResourceParser dictionaryFor:resource]; - newDictionary[@"data"] = [JSONAPIResourceParser dictionaryFor:resource]; - - NSArray *included = [JSONAPIResourceParser relatedResourcesFor:resource]; - if (included.count) { - newDictionary[@"included"] = included; - - NSMutableDictionary *includedResources = [[NSMutableDictionary alloc] init]; - for (NSObject *linked in included) { - - JSONAPIResourceDescriptor *desc = [[linked class] descriptor]; - - NSMutableDictionary *typeDict = includedResources[desc.type] ?: @{}.mutableCopy; - typeDict[linked.ID] = resource; - - includedResources[desc.type] = typeDict; - } - _includedResources = includedResources; + NSArray *relatedResources = [JSONAPIResourceParser relatedResourcesFor:resource]; + if (relatedResources.count) { + _includedResources = [self mapIncludedResources:relatedResources forResource:resource]; + dictionary[@"included"] = [self parseRelatedResources:relatedResources]; } - - _dictionary = newDictionary; + _dictionary = dictionary; +} + +- (NSDictionary *)mapIncludedResources:(NSArray *)relatedResources forResource:(NSObject *)resource +{ + NSMutableDictionary *includedResources = [NSMutableDictionary new]; + for (NSObject *linked in relatedResources) { + JSONAPIResourceDescriptor *desc = [[linked class] descriptor]; + NSMutableDictionary *typeDict = includedResources[desc.type] ?: @{}.mutableCopy; + typeDict[linked.ID] = resource; + includedResources[desc.type] = typeDict; + } + return includedResources; +} + +- (NSArray *)parseRelatedResources:(NSArray *)relatedResources +{ + NSMutableArray *parsedResources = [NSMutableArray new]; + for (NSObject *linked in relatedResources) { + [parsedResources addObject:[JSONAPIResourceParser dictionaryFor:linked]]; + } + return parsedResources; } @end diff --git a/Classes/JSONAPIResourceDescriptor.h b/Classes/JSONAPIResourceDescriptor.h index 537cbde..840842f 100644 --- a/Classes/JSONAPIResourceDescriptor.h +++ b/Classes/JSONAPIResourceDescriptor.h @@ -30,6 +30,8 @@ */ @property (strong) NSString *idProperty; +@property (strong) NSString *selfLinkProperty; + /** * JSON-API "id" optional format. * @@ -95,6 +97,14 @@ */ - (void)addProperty:(NSString*)name; +/** + * Add a simple property with custom json name. + * + * @param name The name of the property in the class. + * @param jsonName The label of the property in JSON. + */ +- (void)addProperty:(NSString*)name withJsonName:(NSString *)json; + /** * Add a simple property with custom transform object. * diff --git a/Classes/JSONAPIResourceDescriptor.m b/Classes/JSONAPIResourceDescriptor.m index eeadbcd..0b43ace 100644 --- a/Classes/JSONAPIResourceDescriptor.m +++ b/Classes/JSONAPIResourceDescriptor.m @@ -53,6 +53,11 @@ - (void)addProperty:(NSString*)name { [self addProperty:name withDescription:[[JSONAPIPropertyDescriptor alloc] initWithJsonName:name]]; } +- (void)addProperty:(NSString*)name withJsonName:(NSString *)json +{ + [self addProperty:name withDescription:[[JSONAPIPropertyDescriptor alloc] initWithJsonName:json]]; +} + - (void)addProperty:(NSString*)name withDescription:(JSONAPIPropertyDescriptor*)description { [[self properties] setValue:description forKey:name]; } diff --git a/Classes/JSONAPIResourceParser.m b/Classes/JSONAPIResourceParser.m index c737a02..c0f6a47 100644 --- a/Classes/JSONAPIResourceParser.m +++ b/Classes/JSONAPIResourceParser.m @@ -180,6 +180,7 @@ + (void)set:(NSObject *)resource withDictionary:dictionary { NSDictionary *relationships = [dictionary objectForKey:@"relationships"]; NSDictionary *attributes = [dictionary objectForKey:@"attributes"]; + NSDictionary *links = [dictionary objectForKey:@"links"]; id ID = [dictionary objectForKey:@"id"]; NSFormatter *format = [descriptor idFormatter]; @@ -191,6 +192,11 @@ + (void)set:(NSObject *)resource withDictionary:dictionary { } else { [resource setValue:ID forKey:[descriptor idProperty]]; } + + if (descriptor.selfLinkProperty) { + NSString *selfLink = links[@"self"]; + [resource setValue:selfLink forKey:descriptor.selfLinkProperty]; + } // Loops through all keys to map to properties NSDictionary *properties = [descriptor properties]; @@ -332,20 +338,15 @@ + (NSArray*)relatedResourcesFor:(NSObject *)resource { NSDictionary *properties = [descriptor properties]; for (NSString *key in properties) { JSONAPIPropertyDescriptor *property = [properties objectForKey:key]; - if (property.resourceType) { - id value = [self valueForKey:key]; + id value = [resource valueForKey:key]; if ([value isKindOfClass:[NSArray class]]) { - for (NSObject *element in value) { - [related addObject:[JSONAPIResourceParser dictionaryFor:element]]; - } + [related addObjectsFromArray:value]; } else { - NSObject *attribute = value; - [related addObject:[JSONAPIResourceParser dictionaryFor:attribute]]; + [related addObject:value]; } } } - return related; } diff --git a/Project/JSONAPI/ArticleResource.m b/Project/JSONAPI/ArticleResource.m index d380acb..80a7a9e 100644 --- a/Project/JSONAPI/ArticleResource.m +++ b/Project/JSONAPI/ArticleResource.m @@ -26,6 +26,7 @@ + (JSONAPIResourceDescriptor*)descriptor { __descriptor = [[JSONAPIResourceDescriptor alloc] initWithClass:[self class] forLinkedType:@"articles"]; [__descriptor setIdProperty:@"ID"]; + [__descriptor setSelfLinkProperty:@"selfLink"]; [__descriptor addProperty:@"title"]; [__descriptor addProperty:@"date" diff --git a/Project/JSONAPI/CommentResource.m b/Project/JSONAPI/CommentResource.m index 74fc82f..c8adb10 100644 --- a/Project/JSONAPI/CommentResource.m +++ b/Project/JSONAPI/CommentResource.m @@ -25,7 +25,7 @@ + (JSONAPIResourceDescriptor*)descriptor { [__descriptor setIdProperty:@"ID"]; - [__descriptor addProperty:@"text" withDescription:[[JSONAPIPropertyDescriptor alloc] initWithJsonName:@"body"]]; + [__descriptor addProperty:@"text" withJsonName:@"body"]; [__descriptor hasOne:[PeopleResource class] withName:@"author"]; }); diff --git a/Project/JSONAPI/PeopleResource.m b/Project/JSONAPI/PeopleResource.m index 25ab71e..8d10b8a 100644 --- a/Project/JSONAPI/PeopleResource.m +++ b/Project/JSONAPI/PeopleResource.m @@ -23,7 +23,7 @@ + (JSONAPIResourceDescriptor*)descriptor { [__descriptor setIdProperty:@"ID"]; [__descriptor addProperty:@"firstName" withDescription:[[JSONAPIPropertyDescriptor alloc] initWithJsonName:@"first-name"]]; - [__descriptor addProperty:@"lastName" withDescription:[[JSONAPIPropertyDescriptor alloc] initWithJsonName:@"last-name"]]; + [__descriptor addProperty:@"lastName" withJsonName:@"last-name"]; [__descriptor addProperty:@"twitter"]; }); diff --git a/Project/JSONAPITests/JSONAPITests.m b/Project/JSONAPITests/JSONAPITests.m index 63f24af..a2807f7 100644 --- a/Project/JSONAPITests/JSONAPITests.m +++ b/Project/JSONAPITests/JSONAPITests.m @@ -12,6 +12,7 @@ #import "JSONAPIResourceDescriptor.h" #import "JSONAPIErrorResource.h" #import "JSONAPIResourceParser.h" +#import "NSDateFormatter+JSONAPIDateFormatter.h" #import "CommentResource.h" #import "PeopleResource.h" @@ -65,10 +66,12 @@ - (void)testDataArticles { ArticleResource *article = jsonAPI.resource; XCTAssert([article isKindOfClass:[ArticleResource class]], @"Article should be a ArticleResource"); XCTAssertEqualObjects(article.ID, @"1", @"Article id should be 1"); + XCTAssertTrue([article.selfLink isEqualToString:@"http://example.com/articles/1"], @"Article selfLink should be 'http://example.com/articles/1'"); XCTAssertEqualObjects(article.title, @"JSON API paints my bikeshed!", @"Article title should be 'JSON API paints my bikeshed!'"); - NSArray *dateStrings = @[@"2015-09-01T12:15:00Z",@"2015-08-01T06:15:00Z"]; - XCTAssertEqualObjects(article.versions, dateStrings, @"Article versions should contain an array of date strings"); + NSArray *dates = @[[[NSDateFormatter RFC3339DateFormatter] dateFromString:@"2015-09-01T12:15:00.000Z"], + [[NSDateFormatter RFC3339DateFormatter] dateFromString:@"2015-08-01T06:15:00.000Z"]]; + XCTAssertEqualObjects(article.versions, dates, @"Article versions should contain an array of date objects"); } - (void)testIncludedPeopleAndComments { @@ -181,13 +184,21 @@ - (void)testSerializeComplex { } - (void)testCreate { - PeopleResource *newAuthor = [[PeopleResource alloc] init]; - - newAuthor.firstName = @"Karl"; - newAuthor.lastName = @"Armstrong"; - - JSONAPI *jsonAPI = [JSONAPI jsonAPIWithResource:newAuthor]; - XCTAssertEqualObjects([jsonAPI dictionary][@"data"][@"type"], @"people", @"Did not create person!"); + NSDictionary *json = [self mainExampleJSON]; + JSONAPI *jsonAPI = [JSONAPI jsonAPIWithDictionary:json]; + + ArticleResource *article = jsonAPI.resource; + + jsonAPI = [JSONAPI jsonAPIWithResource:article]; + NSDictionary *dictionary = [jsonAPI dictionary]; + XCTAssertEqualObjects(dictionary[@"data"][@"type"], @"articles", @"Did not create article!"); + XCTAssertEqualObjects(dictionary[@"data"][@"attributes"][@"title"], @"JSON API paints my bikeshed!", @"Did not parse title!"); + XCTAssertEqual([dictionary[@"data"][@"relationships"][@"comments"][@"data"] count], 2, @"Did not parse relationships!"); + XCTAssertEqual([dictionary[@"included"] count], 3, @"Did not parse included resources!"); + XCTAssertEqualObjects(dictionary[@"included"][0][@"type"], @"people", @"Did not parse included people object!"); + XCTAssertEqualObjects(dictionary[@"included"][0][@"id"], @"9", @"Did not parse ID!"); + XCTAssertEqualObjects(dictionary[@"included"][1][@"type"], @"comments", @"Did not parse included comments object!"); + XCTAssertEqualObjects(dictionary[@"included"][1][@"relationships"][@"author"][@"data"][@"type"], @"people", @"Did not parse included comments author!"); } #pragma mark - Generic relationships tests diff --git a/Project/JSONAPITests/main_example.json b/Project/JSONAPITests/main_example.json index a1aa748..9339969 100644 --- a/Project/JSONAPITests/main_example.json +++ b/Project/JSONAPITests/main_example.json @@ -13,8 +13,8 @@ "attributes": { "title": "JSON API paints my bikeshed!", "versions": [ - "2015-09-01T12:15:00Z", - "2015-08-01T06:15:00Z" + "2015-09-01T12:15:00.000Z", + "2015-08-01T06:15:00.000Z" ] }, "relationships": {