Skip to content
This repository was archived by the owner on Apr 18, 2023. It is now read-only.

Commit bedc0b6

Browse files
authored
Merge pull request #22 from microsoftgraph/vidadhee/GraphTasks
Add Page Iterator implementation
2 parents 9534cbe + 24804b8 commit bedc0b6

File tree

6 files changed

+790
-0
lines changed

6 files changed

+790
-0
lines changed

MSGraphClientSDK/MSGraphClientSDK.xcodeproj/project.pbxproj

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@
3232
E52AD9F22133C8F9005FED47 /* MSURLSessionDataTaskTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E52AD9F12133C8F9005FED47 /* MSURLSessionDataTaskTests.m */; };
3333
E52AD9F42133E171005FED47 /* MSURLSessionDownloadTaskTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E52AD9F32133E171005FED47 /* MSURLSessionDownloadTaskTests.m */; };
3434
E52AD9F62133E740005FED47 /* MSURLSessionUploadTaskTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E52AD9F52133E73F005FED47 /* MSURLSessionUploadTaskTests.m */; };
35+
E54D121F225B59B300EB517A /* MSPageIterator.m in Sources */ = {isa = PBXBuildFile; fileRef = E54D121D225B59B300EB517A /* MSPageIterator.m */; };
36+
E54D1220225B59B300EB517A /* MSPageIterator.h in Headers */ = {isa = PBXBuildFile; fileRef = E54D121E225B59B300EB517A /* MSPageIterator.h */; settings = {ATTRIBUTES = (Public, ); }; };
37+
E54D1223225C882A00EB517A /* MSPageIteratorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E54D1222225C882A00EB517A /* MSPageIteratorTests.m */; };
38+
E54D1225225C9C7B00EB517A /* PagedResponse.json in Resources */ = {isa = PBXBuildFile; fileRef = E54D1224225C9C7A00EB517A /* PagedResponse.json */; };
3539
E566782E2150BC4F00C6B7AE /* MSRedirectHandlerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E566782D2150BC4F00C6B7AE /* MSRedirectHandlerTests.m */; };
3640
E56678302150E1AF00C6B7AE /* MSAuthenticationHandlerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E566782F2150E1AF00C6B7AE /* MSAuthenticationHandlerTests.m */; };
3741
E56B101C2220086300ED640F /* MSRedirectHandlerOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = E56B101A2220086300ED640F /* MSRedirectHandlerOptions.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -113,6 +117,10 @@
113117
E52AD9F12133C8F9005FED47 /* MSURLSessionDataTaskTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MSURLSessionDataTaskTests.m; sourceTree = "<group>"; };
114118
E52AD9F32133E171005FED47 /* MSURLSessionDownloadTaskTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MSURLSessionDownloadTaskTests.m; sourceTree = "<group>"; };
115119
E52AD9F52133E73F005FED47 /* MSURLSessionUploadTaskTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MSURLSessionUploadTaskTests.m; sourceTree = "<group>"; };
120+
E54D121D225B59B300EB517A /* MSPageIterator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MSPageIterator.m; sourceTree = "<group>"; };
121+
E54D121E225B59B300EB517A /* MSPageIterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MSPageIterator.h; sourceTree = "<group>"; };
122+
E54D1222225C882A00EB517A /* MSPageIteratorTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MSPageIteratorTests.m; sourceTree = "<group>"; };
123+
E54D1224225C9C7A00EB517A /* PagedResponse.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = PagedResponse.json; sourceTree = "<group>"; };
116124
E566782D2150BC4F00C6B7AE /* MSRedirectHandlerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MSRedirectHandlerTests.m; sourceTree = "<group>"; };
117125
E566782F2150E1AF00C6B7AE /* MSAuthenticationHandlerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MSAuthenticationHandlerTests.m; sourceTree = "<group>"; };
118126
E56B101A2220086300ED640F /* MSRedirectHandlerOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MSRedirectHandlerOptions.h; sourceTree = "<group>"; };
@@ -232,6 +240,7 @@
232240
E5069AD7211C1AD500432372 /* MSGraphClientSDKTests */ = {
233241
isa = PBXGroup;
234242
children = (
243+
E54D1221225C880700EB517A /* GraphTasks */,
235244
E5204FFC2173A79100E64160 /* GraphContent */,
236245
E5B6DB6921497A3300836E1D /* Frameworks */,
237246
E573EE1121367A4000C5E884 /* HTTPClient */,
@@ -314,6 +323,23 @@
314323
path = Middleware;
315324
sourceTree = "<group>";
316325
};
326+
E54D11DE22534D7000EB517A /* GraphTasks */ = {
327+
isa = PBXGroup;
328+
children = (
329+
E54D121E225B59B300EB517A /* MSPageIterator.h */,
330+
E54D121D225B59B300EB517A /* MSPageIterator.m */,
331+
);
332+
path = GraphTasks;
333+
sourceTree = "<group>";
334+
};
335+
E54D1221225C880700EB517A /* GraphTasks */ = {
336+
isa = PBXGroup;
337+
children = (
338+
E54D1222225C882A00EB517A /* MSPageIteratorTests.m */,
339+
);
340+
path = GraphTasks;
341+
sourceTree = "<group>";
342+
};
317343
E56B101E22237F3900ED640F /* Options */ = {
318344
isa = PBXGroup;
319345
children = (
@@ -341,6 +367,7 @@
341367
E573EE0E2136568700C5E884 /* Resources */ = {
342368
isa = PBXGroup;
343369
children = (
370+
E54D1224225C9C7A00EB517A /* PagedResponse.json */,
344371
E52050052175B10800E64160 /* BatchResponse.json */,
345372
E573EE0F2136569F00C5E884 /* UserPhoto.jpg */,
346373
);
@@ -439,6 +466,7 @@
439466
E5DC73DD211412BF0040BAB6 /* MSGraphClientSDK.h */,
440467
E5069AC2211AACB600432372 /* Authentication */,
441468
E5069AC5211AAE6900432372 /* Common */,
469+
E54D11DE22534D7000EB517A /* GraphTasks */,
442470
E5204FE6216F1D4200E64160 /* GraphContent */,
443471
E5DC73E5211418040040BAB6 /* HTTPClient */,
444472
E5DC73EA211418040040BAB6 /* Middleware */,
@@ -529,6 +557,7 @@
529557
E59EBFEF221D3B6800B4B279 /* MSRetryHandlerOptions.h in Headers */,
530558
E51E616C216DE4D300F1B3C0 /* MSRetryHandler.h in Headers */,
531559
E56B101C2220086300ED640F /* MSRedirectHandlerOptions.h in Headers */,
560+
E54D1220225B59B300EB517A /* MSPageIterator.h in Headers */,
532561
E5DC73DF211412BF0040BAB6 /* MSGraphClientSDK.h in Headers */,
533562
);
534563
runOnlyForDeploymentPostprocessing = 0;
@@ -614,6 +643,7 @@
614643
isa = PBXResourcesBuildPhase;
615644
buildActionMask = 2147483647;
616645
files = (
646+
E54D1225225C9C7B00EB517A /* PagedResponse.json in Resources */,
617647
E573EE102136569F00C5E884 /* UserPhoto.jpg in Resources */,
618648
E52050062175B10800E64160 /* BatchResponse.json in Resources */,
619649
);
@@ -646,6 +676,7 @@
646676
E52AD9F42133E171005FED47 /* MSURLSessionDownloadTaskTests.m in Sources */,
647677
E573EE092134167400C5E884 /* MSClientFactoryTests.m in Sources */,
648678
E566782E2150BC4F00C6B7AE /* MSRedirectHandlerTests.m in Sources */,
679+
E54D1223225C882A00EB517A /* MSPageIteratorTests.m in Sources */,
649680
E573EE0B213427ED00C5E884 /* MSURLSessionTaskDelegateTests.m in Sources */,
650681
E5ECE09E2142992F00A289C2 /* MSMiddlewareFactoryTests.m in Sources */,
651682
E5069AE2211C323D00432372 /* MSURLSessionManagerTests.m in Sources */,
@@ -678,6 +709,7 @@
678709
E51E616D216DE4D300F1B3C0 /* MSRetryHandler.m in Sources */,
679710
E5DC7408211418050040BAB6 /* MSURLSessionTaskDelegate.m in Sources */,
680711
E5DC7409211418050040BAB6 /* MSAuthenticationHandler.m in Sources */,
712+
E54D121F225B59B300EB517A /* MSPageIterator.m in Sources */,
681713
E5DC741D21142C430040BAB6 /* MSHTTPClient.m in Sources */,
682714
);
683715
runOnlyForDeploymentPostprocessing = 0;
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//
2+
// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information.
3+
//
4+
5+
#import <Foundation/Foundation.h>
6+
7+
/*
8+
This class is designed to ease the process of iterating through paged responses.
9+
*/
10+
11+
#import "MSHTTPClient.h"
12+
13+
// Block to be called while iterating through the page
14+
typedef void (^MSPageIteratorBlock)(NSDictionary *itemDictionary, BOOL *stop);
15+
16+
@interface MSPageIterator : NSObject
17+
18+
// Boolean property indicating whether all the pages have been iterated.
19+
@property (nonatomic, readonly) BOOL isComplete;
20+
21+
/*
22+
Initializes and returns an instance of MSPageIterator class
23+
24+
@param data Instance of NSData which will be used to populate the values and next link
25+
@param httpClient Instance of MSHTTPClient which will be used to fetch values from next link URL.
26+
@param iteratorBlock Block to be executed while iterating through the values
27+
@return MSPageIterator instance
28+
*/
29+
- (instancetype)initWithData:(NSData *)data client:(MSHTTPClient *)httpClient andIteratorBlock:(MSPageIteratorBlock)iteratorBlock;
30+
31+
// This method starts the iteration through the page values.
32+
- (void)iterate;
33+
@end
34+
35+
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
//
2+
// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information.
3+
//
4+
5+
#import "MSPageIterator.h"
6+
#import "MSURLSessionDataTask.h"
7+
8+
@interface MSPageIterator()
9+
10+
@property (nonatomic, strong) MSPageIteratorBlock iteratorCompletionBlock;
11+
@property (nonatomic, strong) NSDictionary *dataDictionary;
12+
@property (nonatomic, strong) MSHTTPClient *httpClient;
13+
@property (nonatomic, strong) MSURLSessionDataTask *dataTask;
14+
15+
@end
16+
17+
@implementation MSPageIterator
18+
@synthesize isComplete;
19+
20+
- (instancetype)initWithData:(nonnull NSData *)data client:(nonnull MSHTTPClient *)httpClient andIteratorBlock:(nonnull MSPageIteratorBlock)iteratorBlock
21+
{
22+
self = [super init];
23+
if(self)
24+
{
25+
NSParameterAssert(data);
26+
NSParameterAssert(httpClient);
27+
NSParameterAssert(iteratorBlock);
28+
29+
isComplete = NO;
30+
_dataDictionary = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
31+
_httpClient = httpClient;
32+
_iteratorCompletionBlock = iteratorBlock;
33+
}
34+
return self;
35+
}
36+
37+
- (void)iterate
38+
{
39+
BOOL stop = NO;
40+
for (NSDictionary *dictionary in [self.dataDictionary objectForKey:@"value"])
41+
{
42+
_iteratorCompletionBlock(dictionary, &stop);
43+
if (stop)
44+
{
45+
break;
46+
}
47+
}
48+
if(![self.dataDictionary objectForKey:@"@odata.nextLink"] ||
49+
[[self.dataDictionary objectForKey:@"@odata.nextLink"] isEqualToString:@""])
50+
{
51+
stop = YES;
52+
isComplete = YES;
53+
_iteratorCompletionBlock(nil, &stop);
54+
}else if(!stop)
55+
{
56+
[self createNextPageTask];
57+
[self fetchNextPage];
58+
}
59+
}
60+
61+
- (void)fetchNextPage
62+
{
63+
[_dataTask execute];
64+
}
65+
66+
- (void)createNextPageTask
67+
{
68+
NSString *nextPageURLString = [self.dataDictionary objectForKey:@"@odata.nextLink"];
69+
_dataTask = [_httpClient dataTaskWithRequest:[NSMutableURLRequest requestWithURL:[NSURL URLWithString:nextPageURLString]] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
70+
if(!error && [(NSHTTPURLResponse *)response statusCode] == 200)
71+
{
72+
self->_dataDictionary = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
73+
[self iterate];
74+
}
75+
}];
76+
}
77+
78+
@end

MSGraphClientSDK/MSGraphClientSDK/MSGraphClientSDK.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ FOUNDATION_EXPORT const unsigned char MSGraphClientSDKVersionString[];
4444
#import "MSGraphClientSDK/MSRetryHandlerOptions.h"
4545
#import "MSGraphClientSDK/MSRedirectHandlerOptions.h"
4646

47+
#import "MSGraphClientSDK/MSPageIterator.h"
4748

4849
#endif
4950

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
//
2+
// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information.
3+
//
4+
5+
#import <XCTest/XCTest.h>
6+
#import "MSGraphClientSDKTests.h"
7+
#import "MSPageIterator.h"
8+
9+
@interface MSPageIterator()
10+
11+
@property (nonatomic, strong) NSDictionary *dataDictionary;
12+
@property (nonatomic, strong) MSURLSessionDataTask *dataTask;
13+
14+
- (void)fetchNextPage;
15+
16+
@end
17+
18+
@interface MSURLSessionTask()
19+
20+
- (void)taskCompletedWithData:(id)data response:(NSURLResponse *)response andError:(NSError *)error;
21+
22+
@end
23+
24+
25+
@interface MSPageIteratorTests : MSGraphClientSDKTests
26+
27+
@end
28+
29+
@implementation MSPageIteratorTests {
30+
NSData *demoBatchResponseData;
31+
NSDictionary *demoResponseDictionary;
32+
}
33+
34+
- (void)setUp {
35+
[super setUp];
36+
NSString *jsonPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"PagedResponse" ofType:@".json"];
37+
XCTAssertNotNil(jsonPath);
38+
demoBatchResponseData = [NSData dataWithContentsOfFile:jsonPath];
39+
demoResponseDictionary = [NSJSONSerialization JSONObjectWithData:demoBatchResponseData options:kNilOptions error:nil];
40+
// Put setup code here. This method is called before the invocation of each test method in the class.
41+
}
42+
43+
- (void)tearDown {
44+
// Put teardown code here. This method is called after the invocation of each test method in the class.
45+
[super tearDown];
46+
}
47+
48+
- (void)testInit {
49+
XCTAssertThrows([[MSPageIterator alloc] initWithData:nil client:self.mockClient andIteratorBlock:^(NSDictionary *itemDictionary, BOOL *stop) {
50+
}]);
51+
52+
XCTAssertThrows([[MSPageIterator alloc] initWithData:[NSData new] client:nil andIteratorBlock:^(NSDictionary *itemDictionary, BOOL *stop) {
53+
}]);
54+
55+
XCTAssertThrows([[MSPageIterator alloc] initWithData:[NSData new] client:self.mockClient andIteratorBlock:nil]);
56+
57+
XCTAssertNotNil([[MSPageIterator alloc] initWithData:[NSData new] client:self.mockClient andIteratorBlock:^(NSDictionary *itemDictionary, BOOL *stop) {
58+
}]);
59+
}
60+
61+
- (void)testIntraPageIterateAndStop {
62+
__block int i=0;
63+
__block NSArray *valueArray = [demoResponseDictionary objectForKey:@"value"];
64+
65+
XCTestExpectation *iterationWaitExpectation = [[XCTestExpectation alloc] initWithDescription:@"Waiting for iteration to complete"];
66+
67+
MSPageIterator *pageIterator = [[MSPageIterator alloc] initWithData:demoBatchResponseData client:self.mockClient andIteratorBlock:^(NSDictionary *itemDictionary, BOOL *stop) {
68+
XCTAssertEqualObjects(itemDictionary, [valueArray objectAtIndex:i]);
69+
i++;
70+
if(i==10){
71+
*stop = true;
72+
[iterationWaitExpectation fulfill];
73+
}
74+
}];
75+
[pageIterator iterate];
76+
[self waitForExpectations:@[iterationWaitExpectation] timeout:2.0];
77+
78+
XCTAssertFalse(pageIterator.isComplete);
79+
}
80+
81+
- (void)testInterPageIterateAndIsComplete {
82+
XCTestExpectation *iterationWaitExpectation = [[XCTestExpectation alloc] initWithDescription:@"Waiting for iteration to complete"];
83+
84+
__block MSPageIterator *pageIterator = [[MSPageIterator alloc] initWithData:demoBatchResponseData client:self.mockClient andIteratorBlock:^(NSDictionary *itemDictionary, BOOL *stop) {
85+
if([pageIterator isComplete]){
86+
[iterationWaitExpectation fulfill];
87+
}
88+
}];
89+
90+
MSPageIterator *partialMock = OCMPartialMock(pageIterator);
91+
OCMStub([partialMock fetchNextPage]).andDo(^(NSInvocation *invocation){
92+
MSURLSessionDataTask *partialTask = OCMPartialMock(pageIterator.dataTask);
93+
OCMStub([partialTask execute]).andDo(^(NSInvocation *invocation){
94+
NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:[NSURL URLWithString:MSGraphBaseURL] statusCode:MSExpectedResponseCodesOK HTTPVersion:@"foo" headerFields:nil];
95+
NSMutableDictionary *nextPageDictionary = [self->demoResponseDictionary mutableCopy];
96+
[nextPageDictionary setObject:@"" forKey:@"@odata.nextLink"];
97+
NSData *nextPageData = [NSJSONSerialization dataWithJSONObject:nextPageDictionary options:kNilOptions error:nil];
98+
[pageIterator.dataTask taskCompletedWithData:nextPageData response:response andError:nil];
99+
});
100+
[partialTask execute];
101+
102+
});
103+
[pageIterator iterate];
104+
[self waitForExpectations:@[iterationWaitExpectation] timeout:12.0];
105+
106+
XCTAssertTrue(pageIterator.isComplete);
107+
}
108+
@end

MSGraphClientSDK/MSGraphClientSDKTests/Resources/PagedResponse.json

Lines changed: 536 additions & 0 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)