Skip to content

Commit 8102024

Browse files
author
Josh Holtz
committed
Can link data and included
1 parent 6c267a3 commit 8102024

File tree

8 files changed

+160
-133
lines changed

8 files changed

+160
-133
lines changed

Classes/JSONAPI.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
@property (readonly) id resource;
2323
@property (nonatomic, strong, readonly) NSArray *resources;
24+
@property (nonatomic, strong, readonly) NSDictionary *includedResources;
2425

2526
@property (nonatomic, strong, readonly) NSError *internalError;
2627

@@ -30,4 +31,6 @@
3031
- (instancetype)initWithDictionary:(NSDictionary*)dictionary;
3132
- (instancetype)initWithString:(NSString*)string;
3233

34+
- (id)includedResource:(id)ID withType:(NSString*)type;
35+
3336
@end

Classes/JSONAPI.m

Lines changed: 34 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -60,48 +60,11 @@ - (id)resource {
6060
return _resources.firstObject;
6161
}
6262

63-
//- (id)objectForKey:(NSString*)key {
64-
// return [_dictionary objectForKey:key];
65-
//}
66-
67-
//- (id)resource {
68-
// NSString *key = @"data";
69-
// if ([key isEqualToString:@"meta"] == YES || [key isEqualToString:@"linked"] == YES) {
70-
// return nil;
71-
// }
72-
//
73-
// NSDictionary *rawResource = [_dictionary objectForKey:key];
74-
// JSONAPIResource *resource = nil;
75-
// if ([rawResource isKindOfClass:[NSDictionary class]] == YES) {
76-
// resource = [JSONAPIResource jsonAPIResource:rawResource withLinked:self.linked];
77-
// }
78-
//
79-
// // Fall back to first element in array
80-
// if (resource == nil) {
81-
// id resources = [self resources];
82-
// if ([resources isKindOfClass:[NSArray class]] == YES) {
83-
// return [resources firstObject];
84-
// }
85-
// }
86-
//
87-
// return resource;
88-
//
89-
//}
90-
91-
//- (NSArray*)resources {
92-
// NSString *key = @"data";
93-
// if ([key isEqualToString:@"meta"] == YES || [key isEqualToString:@"linked"] == YES) {
94-
// return nil;
95-
// }
96-
//
97-
// NSArray *rawResources = [_dictionary objectForKey:key];
98-
// NSArray *resources = nil;
99-
// if ([rawResources isKindOfClass:[NSArray class]] == YES) {
100-
// resources = [JSONAPIResource jsonAPIResources:rawResources withLinked:self.linked];
101-
// }
102-
//
103-
// return resources;
104-
//}
63+
- (id)includedResource:(id)ID withType:(NSString *)type {
64+
if (ID == nil) return nil;
65+
if (type == nil) return nil;
66+
return _includedResources[type][ID];
67+
}
10568

10669
#pragma mark - Private
10770

@@ -133,37 +96,36 @@ - (void)inflateWithDictionary:(NSDictionary*)dictionary {
13396
if (resource) [resources addObject:resource];
13497
}
13598
_resources = resources;
99+
100+
// Parses included resources
101+
NSArray *included = _dictionary[@"included"];
102+
NSMutableDictionary *includedResources = @{}.mutableCopy;
103+
for (NSDictionary *data in included) {
104+
105+
JSONAPIResource *resource = [self inflateResourceData:data];
106+
if (resource) {
107+
108+
NSMutableDictionary *typeDict = includedResources[resource.type] ?: @{}.mutableCopy;
109+
typeDict[resource.ID] = resource;
110+
111+
includedResources[resource.type] = typeDict;
112+
}
113+
}
114+
_includedResources = includedResources;
115+
116+
// Link included with included
117+
// TODO: Need to look into / stop circular references
118+
for (NSDictionary *typeIncluded in _includedResources.allValues) {
119+
for (JSONAPIResource *resource in typeIncluded.allValues) {
120+
[resource linkWithIncluded:self];
121+
}
122+
}
123+
124+
// Link data with included
125+
for (JSONAPIResource *resource in _resources) {
126+
[resource linkWithIncluded:self];
127+
}
136128

137-
// // Sets linked
138-
// NSMutableDictionary *creatingLinked = [NSMutableDictionary dictionary];
139-
// NSDictionary *rawLinked = [dictionary objectForKey:@"linked"];
140-
// if ([rawLinked isKindOfClass:[NSArray class]] == YES) {
141-
//
142-
// NSMutableArray *linkedToLinkWithLinked = [NSMutableArray array];
143-
//
144-
// // Loops through linked arrays
145-
// for (NSDictionary *resourceDictionary in rawLinked) {
146-
//
147-
// NSString *type = resourceDictionary[@"type"];
148-
// NSMutableDictionary *resources = creatingLinked[type] ?: @{}.mutableCopy;
149-
// [creatingLinked setObject:resources forKey:type];
150-
//
151-
// JSONAPIResource *resource = [JSONAPIResource jsonAPIResource:resourceDictionary withLinked:nil];
152-
// if (resource.ID != nil) {
153-
// [resources setObject:resource forKey:resource.ID];
154-
// [linkedToLinkWithLinked addObject:resource];
155-
// }
156-
//
157-
// }
158-
//
159-
// // Linked the linked
160-
// for (JSONAPIResource *resource in linkedToLinkWithLinked) {
161-
// [resource linkLinks:creatingLinked];
162-
// }
163-
//
164-
// }
165-
//
166-
// _linked = creatingLinked;
167129
}
168130

169131
- (id)inflateResourceData:(NSDictionary*)data {

Classes/JSONAPIResource.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
#import <Foundation/Foundation.h>
1010

11+
@class JSONAPI;
12+
1113
@interface JSONAPIResource : NSObject<NSCopying, NSCoding>
1214

1315
@property (nonatomic, strong) id ID;
@@ -18,5 +20,6 @@
1820
+ (NSArray*)jsonAPIResources:(NSArray*)array;
1921

2022
- (NSDictionary *)mapKeysToProperties;
23+
- (void)linkWithIncluded:(JSONAPI*)jsonAPI;
2124

2225
@end

Classes/JSONAPIResource.m

Lines changed: 71 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#import "JSONAPIResource.h"
1010

11+
#import "JSONAPI.h"
1112
#import "JSONAPIResourceFormatter.h"
1213
#import "JSONAPIResourceLinker.h"
1314
#import "JSONAPIResourceModeler.h"
@@ -98,6 +99,7 @@ - (void)setWithDictionary:(NSDictionary*)dict {
9899
[map addEntriesFromDictionary:@{
99100
@"id" : @"ID",
100101
@"href" : @"href",
102+
@"type" : @"type",
101103
@"links" : @"links"
102104
}];
103105

@@ -134,64 +136,75 @@ - (void)setWithDictionary:(NSDictionary*)dict {
134136
}
135137
}
136138

137-
//- (void)linkLinks:(NSDictionary*)linked {
138-
//
139-
// // Loops through links of resources
140-
// for (NSString *linkKey in self.links.allKeys) {
141-
//
142-
// // Gets linked objects for the resource
143-
// id linksTo = [self.links objectForKey:linkKey];
144-
// if ([linksTo isKindOfClass:[NSDictionary class]] == YES) {
145-
//
146-
// NSString *linkType = linksTo[@"type"];
147-
// linkType = [JSONAPIResourceLinker linkedType:linkType] ?: linkType;
148-
//
149-
// if (linksTo[@"id"] != nil) {
150-
// id linksToId = linksTo[@"id"];
151-
//
152-
// JSONAPIResource *linkedResource = [[linked objectForKey:linkType] objectForKey:linksToId];
153-
// if (linkedResource != nil) {
154-
// [self.__resourceLinks setObject:linkedResource forKey:linkKey];
155-
// }
156-
// } else if (linksTo[@"ids"] != nil) {
157-
// id linksToIds = linksTo[@"ids"];
158-
//
159-
// NSMutableArray *linkedResources = [NSMutableArray array];
160-
// [self.__resourceLinks setObject:linkedResources forKey:linkKey];
161-
//
162-
// for (id linkedId in linksToIds) {
163-
// JSONAPIResource *linkedResource = [[linked objectForKey:linkType] objectForKey:linkedId];
164-
// if (linkedResource != nil) {
165-
// [linkedResources addObject:linkedResource];
166-
// }
167-
// }
168-
// }
169-
//
170-
//
171-
// }
172-
// }
173-
//
174-
// // Link links for mapped key to properties
175-
// for (NSString *key in [self mapKeysToProperties]) {
176-
// if ([key hasPrefix:@"links."] == YES) {
177-
//
178-
// NSString *propertyName = [[self mapKeysToProperties] objectForKey:key];
179-
// NSString *linkedResource = [key stringByReplacingOccurrencesOfString:@"links." withString:@""];
180-
//
181-
// id resource = [self linkedResourceForKey:linkedResource];
182-
// if (resource != nil) {
183-
//
184-
// @try {
185-
// [self setValue:resource forKey:propertyName];
186-
// }
187-
// @catch (NSException *exception) {
188-
// NSLog(@"JSONAPIResource Warning - %@", [exception description]);
189-
// }
190-
// }
191-
//
192-
// }
193-
// }
194-
//}
139+
- (void)linkWithIncluded:(JSONAPI*)jsonAPI {
140+
141+
NSDictionary *included = jsonAPI.includedResources;
142+
143+
// Loops through links of resources
144+
for (NSString *linkKey in self.links.allKeys) {
145+
146+
// Gets linked objects for the resource
147+
id linksTo = self.links[linkKey];
148+
if ([linksTo isKindOfClass:[NSDictionary class]] == YES) {
149+
150+
id linkage = linksTo[@"linkage"];
151+
if ([linkage isKindOfClass:[NSDictionary class]] == YES) {
152+
153+
NSString *linkType = linkage[@"type"];
154+
linkType = [JSONAPIResourceLinker linkedType:linkType] ?: linkType;
155+
156+
if (linkage[@"id"] != nil) {
157+
id linksToId = linkage[@"id"];
158+
159+
JSONAPIResource *linkedResource = included[linkType][linksToId];
160+
if (linkedResource != nil) {
161+
[self.__resourceLinks setObject:linkedResource forKey:linkKey];
162+
}
163+
}
164+
165+
} else if ([linkage isKindOfClass:[NSArray class]] == YES) {
166+
167+
NSMutableArray *linkedResources = @[].mutableCopy;
168+
for (NSDictionary *linkageData in linkage) {
169+
NSString *linkType = linkageData[@"type"];
170+
linkType = [JSONAPIResourceLinker linkedType:linkType] ?: linkType;
171+
172+
if (linkageData[@"id"] != nil) {
173+
id linksToId = linkageData[@"id"];
174+
175+
JSONAPIResource *linkedResource = included[linkType][linksToId];
176+
if (linkedResource != nil) {
177+
[linkedResources addObject:linkedResources];
178+
}
179+
}
180+
}
181+
[self.__resourceLinks setObject:linkedResources forKey:linkKey];
182+
183+
}
184+
}
185+
}
186+
187+
// Link links for mapped key to properties
188+
for (NSString *key in [self mapKeysToProperties]) {
189+
if ([key hasPrefix:@"links."] == YES) {
190+
191+
NSString *propertyName = [[self mapKeysToProperties] objectForKey:key];
192+
NSString *linkedResource = [key stringByReplacingOccurrencesOfString:@"links." withString:@""];
193+
194+
id resource = [self linkedResourceForKey:linkedResource];
195+
if (resource != nil) {
196+
197+
@try {
198+
[self setValue:resource forKey:propertyName];
199+
}
200+
@catch (NSException *exception) {
201+
NSLog(@"JSONAPIResource Warning - %@", [exception description]);
202+
}
203+
}
204+
205+
}
206+
}
207+
}
195208

196209
#pragma mark - NSCopying
197210

Project/JSONAPI/CommentResource.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@
88

99
#import "JSONAPIResource.h"
1010

11+
@class PeopleResource;
12+
1113
@interface CommentResource : JSONAPIResource
1214

1315
@property (nonatomic, strong) NSString *text;
16+
@property (nonatomic, strong) PeopleResource *author;
1417

1518
@end

Project/JSONAPI/CommentResource.m

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ @implementation CommentResource
1212

1313
- (NSDictionary *)mapKeysToProperties {
1414
return @{
15-
@"text" : @"text"
15+
@"text" : @"text",
16+
@"links.author" : @"author",
1617
};
1718
}
1819

Project/JSONAPITests/JSONAPITests.m

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
#import "JSONAPI.h"
1212

13+
#import "CommentResource.h"
14+
#import "PeopleResource.h"
1315
#import "PostResource.h"
1416

1517
@interface JSONAPITests : XCTestCase
@@ -21,6 +23,8 @@ @implementation JSONAPITests
2123
- (void)setUp {
2224
[super setUp];
2325

26+
[JSONAPIResourceModeler useResource:[CommentResource class] toLinkedType:@"comments"];
27+
[JSONAPIResourceModeler useResource:[PeopleResource class] toLinkedType:@"people"];
2428
[JSONAPIResourceModeler useResource:[PostResource class] toLinkedType:@"posts"];
2529
}
2630

@@ -50,6 +54,34 @@ - (void)testDataPosts {
5054
XCTAssertEqualObjects(post.title, @"JSON API paints my bikeshed!", @"Post title should be 'JSON API paints my bikeshed!'");
5155
}
5256

57+
- (void)testIncludedPeopleAndComments {
58+
NSDictionary *json = [self mainExampleJSON];
59+
JSONAPI *jsonAPI = [JSONAPI jsonAPIWithDictionary:json];
60+
61+
XCTAssertNotNil(jsonAPI.includedResources, @"Included resources should not be nil");
62+
XCTAssertEqual(jsonAPI.includedResources.count, 2, @"Included resources should contain 2 types");
63+
64+
}
65+
66+
- (void)testDataPostAuthorAndComments {
67+
NSDictionary *json = [self mainExampleJSON];
68+
JSONAPI *jsonAPI = [JSONAPI jsonAPIWithDictionary:json];
69+
70+
PostResource *post = jsonAPI.resource;
71+
XCTAssertNotNil(post.author, @"Post's author should not be nil");
72+
XCTAssertNotNil(post.comments, @"Post's comments should not be nil");
73+
XCTAssertEqual(post.comments.count, 2, @"Post should contain 2 comments");
74+
}
75+
76+
- (void)testIncludedIsLinked {
77+
NSDictionary *json = [self mainExampleJSON];
78+
JSONAPI *jsonAPI = [JSONAPI jsonAPIWithDictionary:json];
79+
80+
CommentResource *comment = [jsonAPI includedResource:@"5" withType:@"comments"];
81+
XCTAssertNotNil(comment.author, @"Comment's author should not be nil");
82+
XCTAssertEqualObjects(comment.author.ID, @"9", @"Comment's author's ID should be 9");
83+
}
84+
5385
#pragma mark - Private
5486

5587
- (NSDictionary*)mainExampleJSON {

0 commit comments

Comments
 (0)