Skip to content
This repository has been archived by the owner on Feb 29, 2020. It is now read-only.

Commit

Permalink
Allow query parameters with Pull
Browse files Browse the repository at this point in the history
  • Loading branch information
brettsam committed Nov 4, 2014
1 parent 2e492ba commit b0cfcb3
Show file tree
Hide file tree
Showing 9 changed files with 165 additions and 64 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -426,4 +426,7 @@
<data name="WP8LoginPage_MustBeCalledFromAuthBroker" xml:space="preserve">
<value>This page must be called from the type '{0}'.</value>
</data>
<data name="Pull_Cannot_Use_Reserved_Key" xml:space="preserve">
<value>The key '{0}' is reserved and cannot be specified as a query parameter.</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ internal class MobileServiceTable : IMobileServiceTable
/// <summary>
/// The name of the system properties query string parameter
/// </summary>
private const string SystemPropertiesQueryParameterName = "__systemproperties";
public const string SystemPropertiesQueryParameterName = "__systemproperties";

/// <summary>
/// The name of the include deleted query string parameter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,16 @@ public async Task PullAsync(string tableName, MobileServiceTableKind tableKind,
{
await this.EnsureInitializedAsync();

if (parameters.Keys.Any(k => k.Equals(MobileServiceTable.IncludeDeletedParameterName, StringComparison.OrdinalIgnoreCase)))
{
throw new ArgumentException(Resources.Pull_Cannot_Use_Reserved_Key.FormatInvariant(MobileServiceTable.IncludeDeletedParameterName));
}

if (parameters.Keys.Any(k => k.Equals(MobileServiceTable.SystemPropertiesQueryParameterName, StringComparison.OrdinalIgnoreCase)))
{
throw new ArgumentException(Resources.Pull_Cannot_Use_Reserved_Key.FormatInvariant(MobileServiceTable.SystemPropertiesQueryParameterName));
}

var table = await this.GetTable(tableName);
var queryDescription = MobileServiceTableQueryDescription.Parse(this.client.ApplicationUri, tableName, query);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -792,25 +792,25 @@ private static async Task TestIncrementalPull(MobileServiceLocalStoreMock store,
}

[AsyncTestMethod]
public async Task PullAsync_OverridesStoreSystemProperties_WhenProvidedInParameters()
public async Task PullAsync_Throws_IfSystemPropertiesProvidedInParameters()
{
await TestPullQueryOverride(new Dictionary<string, string>()
await this.TestPullQueryOverrideThrows(new Dictionary<string, string>()
{
{ "__systemProperties", "createdAt" },
{ "param1", "val1" }
},
"?$skip=0&$top=50&__systemProperties=createdAt&param1=val1&__includeDeleted=true");
"The key '__systemProperties' is reserved and cannot be specified as a query parameter.");
}

[AsyncTestMethod]
public async Task PullAsync_OverridesIncludeDeleted_WhenProvidedInParameters()
public async Task PullAsync_Throws_IfIncludeDeletedProvidedInParameters()
{
await TestPullQueryOverride(new Dictionary<string, string>()
await this.TestPullQueryOverrideThrows(new Dictionary<string, string>()
{
{ "__includeDeleted", "false" },
{ "param1", "val1" }
},
"?$skip=0&$top=50&__includeDeleted=false&param1=val1&__systemproperties=__version%2C__deleted");
"The key '__includeDeleted' is reserved and cannot be specified as a query parameter.");
}

[AsyncTestMethod]
Expand All @@ -826,26 +826,18 @@ public async Task PullAsync_Throws_WhenQueryKeyIsInvalid()
await ThrowsAsync<ArgumentException>(() => table.PullAsync("asd_^^234", table.CreateQuery(), CancellationToken.None));
}

private static async Task TestPullQueryOverride(IDictionary<string, string> parameters, string uriQuery)
private async Task TestPullQueryOverrideThrows(IDictionary<string, string> parameters, string errorMessage)
{
var hijack = new TestHttpHandler();
hijack.OnSendingRequest = req =>
{
Assert.AreEqual(req.RequestUri.Query, uriQuery);
return Task.FromResult(req);
};
hijack.AddResponseContent("[]"); // for pull

var store = new MobileServiceLocalStoreMock();
IMobileServiceClient service = new MobileServiceClient("http://www.test.com", "secret...", hijack);
IMobileServiceClient service = new MobileServiceClient("http://www.test.com", "secret...");
await service.SyncContext.InitializeAsync(store, new MobileServiceSyncHandler());

IMobileServiceSyncTable<ToDoWithStringId> table = service.GetSyncTable<ToDoWithStringId>();
var query = table.CreateQuery()
.WithParameters(parameters);

await table.PullAsync(null, query, cancellationToken: CancellationToken.None);
Assert.AreEqual(hijack.Requests.Count, 1);
var ex = await ThrowsAsync<ArgumentException>(() => table.PullAsync(null, query, cancellationToken: CancellationToken.None));
Assert.AreEqual(errorMessage, ex.Message);
}

[AsyncTestMethod]
Expand Down
30 changes: 24 additions & 6 deletions sdk/iOS/src/MSSyncContext.m
Original file line number Diff line number Diff line change
Expand Up @@ -307,9 +307,11 @@ - (void) pullWithQuery:(MSQuery *)query completion:(MSSyncBlock)completion;
error = [self errorWithDescription:@"Use of includeTotalCount is not supported in pullWithQuery:"
andErrorCode:MSInvalidParameter];
}
else if (query.parameters) {
error = [self errorWithDescription:@"Use of parameters is not supported in pullWithQuery:"
andErrorCode:MSInvalidParameter];
else if ([MSSyncContext dictionary:query.parameters containsCaseInsensitiveKey:@"__includedeleted"]){
error = [self errorWithDescription:@"Use of '__includeDeleted' is not supported in pullWithQuery parameters:" andErrorCode:MSInvalidParameter];
}
else if ([MSSyncContext dictionary:query.parameters containsCaseInsensitiveKey:@"__systemproperties"]) {
error = [self errorWithDescription:@"Use of '__systemProperties' is not supported in pullWithQuery parameters:" andErrorCode:MSInvalidParameter];
}
else if (query.syncTable) {
// Otherwise we convert the sync table to a normal table
Expand All @@ -330,15 +332,22 @@ - (void) pullWithQuery:(MSQuery *)query completion:(MSSyncBlock)completion;
return;
}

// Get the required system properties from the server
// Get the required system properties from the Store
if ([self.dataSource respondsToSelector:@selector(systemPropetiesForTable:)]) {
query.table.systemProperties = [self.dataSource systemPropetiesForTable:query.table.name];
} else {
query.table.systemProperties = MSSystemPropertyVersion;
}

// A pull should always include deleted records
query.parameters = @{@"__includeDeleted" : @YES };
// add __includeDeleted
if (!query.parameters) {
query.parameters = @{@"__includeDeleted" : @YES};
} else {
NSMutableDictionary *mutableParameters = [query.parameters mutableCopy];
[mutableParameters setObject:@YES forKey:@"__includeDeleted"];
query.parameters = mutableParameters;
}

query.table.systemProperties |= MSSystemPropertyDeleted;

// Begin the actual pull request
Expand Down Expand Up @@ -458,6 +467,15 @@ - (void) purgeWithQuery:(MSQuery *)query completion:(MSSyncBlock)completion
});
}

+ (BOOL) dictionary:(NSDictionary *)dictionary containsCaseInsensitiveKey:(NSString *)key
{
for (NSString *object in dictionary.allKeys) {
if ([object caseInsensitiveCompare:key] == NSOrderedSame) {
return YES;
}
}
return NO;
}

# pragma mark * NSError helpers

Expand Down
89 changes: 88 additions & 1 deletion sdk/iOS/test/MSSyncTableTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -915,6 +915,94 @@ -(void) testPullAddsProperFeaturesHeader
XCTAssertTrue([self waitForTest:30.0], @"Test timed out.");
}

-(void) testPullWithCustomParameters
{
NSString* stringData = @"[{\"id\": \"one\", \"text\":\"first item\"},{\"id\": \"two\", \"text\":\"second item\"}]";
MSTestFilter *testFilter = [MSTestFilter testFilterWithStatusCode:200 data:stringData];

__block NSURLRequest *actualRequest = nil;
testFilter.onInspectRequest = ^(NSURLRequest *request) {
actualRequest = request;
return request;
};

offline.upsertCalls = 0;

MSClient *filteredClient = [client clientWithFilter:testFilter];
MSSyncTable *todoTable = [filteredClient syncTableWithName:TodoTableNoVersion];
MSQuery *query = [[MSQuery alloc] initWithSyncTable:todoTable];
query.parameters = @{@"mykey": @"myvalue"};

[todoTable pullWithQuery:query completion:^(NSError *error) {
XCTAssertNil(error, @"Error found: %@", error.description);
XCTAssertEqual((int)offline.upsertCalls, 1, @"Unexpected number of upsert calls");
XCTAssertEqual((int)offline.upsertedItems, 2, @"Unexpected number of upsert calls");
done = YES;
}];

XCTAssertTrue([self waitForTest:30.0], @"Test timed out.");

XCTAssertEqualObjects(actualRequest.URL.absoluteString, @"https://someUrl/tables/TodoNoVersion?$inlinecount=none&mykey=myvalue&__includeDeleted=1&__systemProperties=__deleted");
}

-(void) testPullWithIncludeDeletedFails
{
NSString* stringData = @"[{\"id\": \"one\", \"text\":\"first item\"},{\"id\": \"two\", \"text\":\"second item\"}]";
MSTestFilter *testFilter = [MSTestFilter testFilterWithStatusCode:200 data:stringData];

__block NSURLRequest *actualRequest = nil;
testFilter.onInspectRequest = ^(NSURLRequest *request) {
actualRequest = request;
return request;
};

offline.upsertCalls = 0;

MSClient *filteredClient = [client clientWithFilter:testFilter];
MSSyncTable *todoTable = [filteredClient syncTableWithName:TodoTableNoVersion];
MSQuery *query = [[MSQuery alloc] initWithSyncTable:todoTable];
query.parameters = @{@"__includeDeleted": @NO, @"mykey": @"myvalue"};

[todoTable pullWithQuery:query completion:^(NSError *error) {
XCTAssertNotNil(error);
XCTAssertEqual(error.code, MSInvalidParameter);
XCTAssertEqual((int)offline.upsertCalls, 0, @"Unexpected number of upsert calls");
XCTAssertEqual((int)offline.upsertedItems, 0, @"Unexpected number of upsert calls");
done = YES;
}];

XCTAssertTrue([self waitForTest:30.0], @"Test timed out.");
}

-(void) testPullWithSystemPropertiesFails
{
NSString* stringData = @"[{\"id\": \"one\", \"text\":\"first item\"},{\"id\": \"two\", \"text\":\"second item\"}]";
MSTestFilter *testFilter = [MSTestFilter testFilterWithStatusCode:200 data:stringData];

__block NSURLRequest *actualRequest = nil;
testFilter.onInspectRequest = ^(NSURLRequest *request) {
actualRequest = request;
return request;
};

offline.upsertCalls = 0;

MSClient *filteredClient = [client clientWithFilter:testFilter];
MSSyncTable *todoTable = [filteredClient syncTableWithName:TodoTableNoVersion];
MSQuery *query = [[MSQuery alloc] initWithSyncTable:todoTable];
query.parameters = @{@"__systemProperties": @"__createdAt,__somethingRandom"};

[todoTable pullWithQuery:query completion:^(NSError *error) {
XCTAssertNotNil(error);
XCTAssertEqual(error.code, MSInvalidParameter);
XCTAssertEqual((int)offline.upsertCalls, 0, @"Unexpected number of upsert calls");
XCTAssertEqual((int)offline.upsertedItems, 0, @"Unexpected number of upsert calls");
done = YES;
}];

XCTAssertTrue([self waitForTest:30.0], @"Test timed out.");
}

-(void) testPushAddsProperFeaturesHeader
{
NSString* stringData = @"{\"id\": \"test1\", \"text\":\"test name\"}";
Expand Down Expand Up @@ -1053,7 +1141,6 @@ -(void) testPurgeWithPendingOperationsFails
XCTAssertTrue([self waitForTest:30.1], @"Test timed out.");
}


#pragma mark * Async Test Helper Method


Expand Down
4 changes: 2 additions & 2 deletions sdk/iOS/test/MSTableFuncTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ -(void) setUp
// application key for the Windows Mobile Azure Service below.

MSClient *client = [MSClient
clientWithApplicationURLString:@"<Microsoft Azure Mobile Service App URL>"
applicationKey:@"<Application Key>"];
clientWithApplicationURLString:@"<Microsoft Azure Mobile Service App URL>"
applicationKey:@"<Application Key>"];

XCTAssertTrue([client.applicationURL.description hasPrefix:@"https://"], @"The functional tests are currently disabled.");
self.continueAfterFailure = YES;
Expand Down
Loading

0 comments on commit b0cfcb3

Please sign in to comment.