Beispiel #1
0
        public async Task DeleteAsync_WithQuery_SendsNotification()
        {
            var store           = new MobileServiceLocalStoreMock();
            var trackingContext = new StoreTrackingContext(StoreOperationSource.Local, string.Empty);
            var eventManager    = new MobileServiceEventManagerMock <IMobileServiceEvent>();
            var settings        = new MobileServiceSyncSettingsManager(store);
            var changeTracker   = new LocalStoreChangeTracker(store, trackingContext, eventManager, settings);

            JObject item = EnqueueSimpleObjectResponse(store);

            StoreOperationCompletedEvent operationEvent = null;

            eventManager.PublishAsyncFunc = t =>
            {
                operationEvent = t as StoreOperationCompletedEvent;
                return(Task.FromResult(0));
            };

            MobileServiceTableQueryDescription query = new MobileServiceTableQueryDescription("test");

            query.Filter = new BinaryOperatorNode(BinaryOperatorKind.Equal, new MemberAccessNode(null, MobileServiceSystemColumns.Id), new ConstantNode("123"));

            await changeTracker.DeleteAsync(query);

            Assert.IsNotNull(operationEvent);
            Assert.AreEqual(operationEvent.Operation.Kind, LocalStoreOperationKind.Delete);
            Assert.AreEqual(operationEvent.Operation.RecordId, "123");
            Assert.AreEqual(operationEvent.Operation.TableName, "test");
        }
        public async virtual Task <MobileServiceTableOperation> PeekAsync(long prevSequenceId, MobileServiceTableKind tableKind, IEnumerable <string> tableNames)
        {
            MobileServiceTableQueryDescription query = CreateQuery();

            var tableKindNode = Compare(BinaryOperatorKind.Equal, "tableKind", (int)tableKind);
            var sequenceNode  = Compare(BinaryOperatorKind.GreaterThan, "sequence", prevSequenceId);

            query.Filter = new BinaryOperatorNode(BinaryOperatorKind.And, tableKindNode, sequenceNode);

            if (tableNames != null && tableNames.Any())
            {
                BinaryOperatorNode nameInList = tableNames.Select(t => Compare(BinaryOperatorKind.Equal, "tableName", t))
                                                .Aggregate((first, second) => new BinaryOperatorNode(BinaryOperatorKind.Or, first, second));
                query.Filter = new BinaryOperatorNode(BinaryOperatorKind.And, query.Filter, nameInList);
            }

            query.Ordering.Add(new OrderByNode(new MemberAccessNode(null, "sequence"), OrderByDirection.Ascending));
            query.Top = 1;

            JObject op = await this.store.FirstOrDefault(query);

            if (op == null)
            {
                return(null);
            }

            return(MobileServiceTableOperation.Deserialize(op));
        }
 public async Task DoesNotSaveTheMaxUpdatedAt_IfThereAreNoResults()
 {
     var query = new MobileServiceTableQueryDescription("test");
     var result = new JArray();
     string expectedOdata = "$filter=(updatedAt ge datetimeoffset'2013-01-01T00%3A00%3A00.0000000%2B00%3A00')&$orderby=updatedAt&$skip=0&$top=50";
     await TestIncrementalSync(query, result, DateTime.MinValue, savesMax: false, firstQuery: expectedOdata, secondQuery: null);
 }
        /// <summary>
        /// Deletes items from local table that match the given query.
        /// </summary>
        /// <param name="query">A query to find records to delete.</param>
        /// <returns>A task that completes when delete query has executed.</returns>
        /// <exception cref="ArgumentNullException">You must supply a query value</exception>
        public override Task DeleteAsync(MobileServiceTableQueryDescription query)
        {
            if (query == null)
            {
                throw new ArgumentNullException("query");
            }

            this.EnsureInitialized();

            var    formatter = new SqlQueryFormatter(query);
            string sql       = formatter.FormatDelete();

            return(this.operationSemaphore.WaitAsync()
                   .ContinueWith(t =>
            {
                try
                {
                    this.ExecuteNonQueryInternal(sql, formatter.Parameters);
                }
                finally
                {
                    this.operationSemaphore.Release();
                }
            }));
        }
Beispiel #5
0
        public async Task ReadAsync_ReadsItems()
        {
            await PrepareTodoTable();

            // insert rows and make sure they are inserted
            TestUtilities.ExecuteNonQuery(TestDbName, "INSERT INTO todo (id, __createdAt) VALUES ('abc', 1), ('def', 2), ('ghi', 3)");
            long count = TestUtilities.CountRows(TestDbName, TestTable);

            Assert.AreEqual(count, 3L);

            using (var store = new MobileServiceSQLiteStore(TestDbName))
            {
                DefineTestTable(store);
                await store.InitializeAsync();

                var    query = MobileServiceTableQueryDescription.Parse(TestTable, "$filter=__createdAt gt 1&$inlinecount=allpages");
                JToken item  = await store.ReadAsync(query);

                Assert.IsNotNull(item);
                var  results     = item["results"].Value <JArray>();
                long resultCount = item["count"].Value <long>();

                Assert.AreEqual(results.Count, 2);
                Assert.AreEqual(resultCount, 2L);
            }
        }
        public virtual async Task <long> CountPending(string tableName)
        {
            MobileServiceTableQueryDescription query = CreateQuery();

            query.Filter = new BinaryOperatorNode(BinaryOperatorKind.Equal, new MemberAccessNode(null, "tableName"), new ConstantNode(tableName));
            return(await this.store.CountAsync(query));
        }
Beispiel #7
0
        /// <summary>
        /// Reads data from local store by executing the query.
        /// </summary>
        /// <param name="query">The query to execute on local store.</param>
        /// <returns>A task that will return with results when the query finishes.</returns>
        public override Task <JToken> ReadAsync(MobileServiceTableQueryDescription query)
        {
            if (query == null)
            {
                throw new ArgumentNullException("query");
            }

            this.EnsureInitialized();

            var    formatter = new SqlQueryFormatter(query);
            string sql       = formatter.FormatSelect();

            IList <JObject> rows   = this.ExecuteQuery(query.TableName, sql, formatter.Parameters);
            JToken          result = new JArray(rows.ToArray());

            if (query.IncludeTotalCount)
            {
                sql = formatter.FormatSelectCount();
                IList <JObject> countRows = this.ExecuteQuery(query.TableName, sql, formatter.Parameters);
                long            count     = countRows[0].Value <long>("count");
                result = new JObject()
                {
                    { "results", result },
                    { "count", count }
                };
            }

            return(Task.FromResult(result));
        }
        private async Task TestIncrementalSync(MobileServiceTableQueryDescription query, JArray result, DateTime maxUpdatedAt, bool savesMax, string firstQuery, string secondQuery)
        {
            var action = new PullAction(this.table.Object, MobileServiceTableKind.Table, this.context.Object, "latestItems", query, null, null, this.opQueue.Object, this.settings.Object, this.store.Object, MobileServiceRemoteTableOptions.All, null, CancellationToken.None);

            this.opQueue.Setup(q => q.LockTableAsync(It.IsAny <string>(), It.IsAny <CancellationToken>())).Returns(Task.FromResult <IDisposable>(null));
            this.opQueue.Setup(q => q.CountPending(It.IsAny <string>())).Returns(Task.FromResult(0L));
            this.table.Setup(t => t.ReadAsync(firstQuery, It.IsAny <IDictionary <string, string> >(), It.IsAny <MobileServiceFeatures>()))
            .Returns(Task.FromResult(QueryResult.Parse(result, null, false)));

            if (result.Any())
            {
                this.table.Setup(t => t.ReadAsync(secondQuery, It.IsAny <IDictionary <string, string> >(), It.IsAny <MobileServiceFeatures>()))
                .Returns(Task.FromResult(QueryResult.Parse(new JArray(), null, false)));
            }

            if (result.Any())
            {
                this.store.Setup(s => s.UpsertAsync("test", It.IsAny <IEnumerable <JObject> >(), true)).Returns(Task.FromResult(0));
            }

            this.settings.Setup(s => s.GetDeltaTokenAsync("test", "latestItems")).Returns(Task.FromResult(new DateTimeOffset(2013, 1, 1, 0, 0, 0, TimeSpan.Zero)));
            if (savesMax)
            {
                this.settings.Setup(s => s.SetDeltaTokenAsync("test", "latestItems", maxUpdatedAt)).Returns(Task.FromResult(0));
            }

            await action.ExecuteAsync();

            this.store.VerifyAll();
            this.opQueue.VerifyAll();
            this.table.VerifyAll();
            this.settings.VerifyAll();

            store.Verify(s => s.DeleteAsync("test", It.IsAny <IEnumerable <string> >()), Times.Never(), "There shouldn't be any call to delete");
        }
Beispiel #9
0
        public async Task DeleteAsync(MobileServiceTableQueryDescription query)
        {
            if (query == null)
            {
                throw new ArgumentNullException("query");
            }

            string[] recordIds = null;

            if (!query.TableName.StartsWith(MobileServiceLocalSystemTables.Prefix) && this.trackingContext.Source != StoreOperationSource.LocalPurge)
            {
                QueryResult result = await this.store.QueryAsync(query);

                recordIds = result.Values.Select(j => this.objectReader.GetId((JObject)j)).ToArray();
            }

            await this.store.DeleteAsync(query);

            if (recordIds != null)
            {
                foreach (var id in recordIds)
                {
                    TrackStoreOperation(query.TableName, id, LocalStoreOperationKind.Delete);
                }
            }
        }
        public void ToODataString_EscapesThe_Uri()
        {
            //updatedat gt datetimeoffset'2014-04-04T07:00:00.0000000+00:00'
            var datetime1 = new ConstantNode(new DateTimeOffset(2014, 4, 4, 7, 0, 0, TimeSpan.FromHours(0)));
            var updatedAt = new MemberAccessNode(null, "updatedat");
            var gt1       = new BinaryOperatorNode(BinaryOperatorKind.GreaterThan, updatedAt, datetime1);

            // updatedat gt datetime'2014-04-04T07:0:0.000Z'
            var datetime2 = new ConstantNode(new DateTime(2014, 4, 4, 7, 0, 0, DateTimeKind.Utc));
            var someDate  = new MemberAccessNode(null, "someDate");
            var gt2       = new BinaryOperatorNode(BinaryOperatorKind.GreaterThan, someDate, datetime2);

            // startswith(text,'this&''%%=,?#')
            var text       = new MemberAccessNode(null, "text");
            var value      = new ConstantNode("this&'%%=,?#");
            var startswith = new FunctionCallNode("startswith", new QueryNode[] { text, value });

            //updatedat gt datetimeoffset'2014-04-04T07:00:00.0000000+00:00' and startswith(text,'this&''%%=,?#')
            var and2 = new BinaryOperatorNode(BinaryOperatorKind.And, gt2, startswith);

            var and1 = new BinaryOperatorNode(BinaryOperatorKind.And, gt1, and2);

            var desc = new MobileServiceTableQueryDescription("someTable")
            {
                Filter = and1
            };

            Assert.AreEqual(desc.ToODataString(), EscapedODataString);
        }
        public void Projection()
        {
            // Query syntax
            MobileServiceTableQueryDescription odata = Compile <Product, string>(table =>
                                                                                 from p in table
                                                                                 select p.Name);

            Assert.AreEqual(1, odata.Selection.Count);
            Assert.AreEqual("Name", odata.Selection[0]);
            Assert.AreEqual(typeof(Product), odata.ProjectionArgumentType);
            Assert.AreEqual(
                "ZUMO",
                odata.Projection.DynamicInvoke(
                    new Product {
                Name = "ZUMO", Price = 0, InStock = true
            }));

            // Chaining
            odata = Compile <Product, string>(table => table.Select(p => p.Name));
            Assert.AreEqual(1, odata.Selection.Count);
            Assert.AreEqual("Name", odata.Selection[0]);
            Assert.AreEqual(typeof(Product), odata.ProjectionArgumentType);
            Assert.AreEqual(
                "ZUMO",
                odata.Projection.DynamicInvoke(
                    new Product {
                Name = "ZUMO", Price = 0, InStock = true
            }));

            // Verify that we don't blow up by trying to include the Foo
            // property in the compiled query
            Compile((IMobileServiceTable <Product> table) =>
                    from p in table
                    select new { Foo = p.Name });
        }
        public async Task DoesNotUpsertAnObject_IfItDoesNotHaveAnId()
        {
            var query = new MobileServiceTableQueryDescription("test");
            var action = new PullAction(this.table.Object, MobileServiceTableKind.Table, this.context.Object, null, query, null, null, this.opQueue.Object, this.settings.Object, this.store.Object, MobileServiceRemoteTableOptions.All, pullOptions: null, reader: null, cancellationToken: CancellationToken.None);

            var itemWithId = new JObject() { { "id", "abc" }, { "text", "has id" } };
            var itemWithoutId = new JObject() { { "text", "no id" } };
            var result = new JArray(new[]{
                itemWithId,
                itemWithoutId
            });
            this.opQueue.Setup(q => q.LockTableAsync(It.IsAny<string>(), It.IsAny<CancellationToken>())).Returns(Task.FromResult<IDisposable>(null));
            this.opQueue.Setup(q => q.CountPending(It.IsAny<string>())).Returns(Task.FromResult(0L));
            this.table.SetupSequence(t => t.ReadAsync(It.IsAny<string>(), It.IsAny<IDictionary<string, string>>(), It.IsAny<MobileServiceFeatures>()))
                      .Returns(Task.FromResult(QueryResult.Parse(result, null, false)))
                      .Returns(Task.FromResult(QueryResult.Parse(new JArray(), null, false)));
            this.store.Setup(s => s.UpsertAsync("test", It.IsAny<IEnumerable<JObject>>(), true))
                      .Returns(Task.FromResult(0))
                      .Callback<string, IEnumerable<JObject>, bool>((tableName, items, fromServer) =>
                        {
                            Assert.AreEqual(1, items.Count());
                            Assert.AreEqual(itemWithId, items.First());
                        });

            await action.ExecuteAsync();

            store.VerifyAll();
            opQueue.VerifyAll();
            table.VerifyAll();

            store.Verify(s => s.DeleteAsync("test", It.IsAny<IEnumerable<string>>()), Times.Never(), "There shouldn't be any call to delete");
        }
        public void Bug466610UsingShorts()
        {
            MobileServiceTableQueryDescription query = Compile <Product, Product>(table =>
                                                                                  table.Where(p => p.DisplayAisle == 2));

            Assert.AreEqual(query.Filter, "(DisplayAisle eq 2)");

            query = Compile <Product, Product>(table =>
                                               from p in table
                                               where p.DisplayAisle == (short)3
                                               select p);
            Assert.AreEqual(query.Filter, "(DisplayAisle eq 3)");

            short closedOverVariable = (short)5;

            query = Compile <Product, Product>(table =>
                                               from p in table
                                               where p.DisplayAisle == closedOverVariable
                                               select p);
            Assert.AreEqual(query.Filter, "(DisplayAisle eq 5)");

            query = Compile <Product, Product>(table =>
                                               table.Where(p => p.OptionFlags == 7));
            Assert.AreEqual(query.Filter, "(OptionFlags eq 7)");

            // Verify non-numeric conversions still aren't supported
            object aisle = 12.0;

            Throws <NotSupportedException>(() =>
                                           Compile <Product, Product>(table =>
                                                                      from p in table
                                                                      where (object)p.DisplayAisle == aisle
                                                                      select p));
        }
Beispiel #14
0
 private static async Task <T> Query <T>(string query) where T : JToken
 {
     using (MobileServiceSQLiteStore store = await SetupTestTable())
     {
         return((T)await store.ReadAsync(MobileServiceTableQueryDescription.Parse(TestTable, query)));
     }
 }
        protected async override Task <bool> HandleDirtyTable()
        {
            if (this.Query.Filter != null || !this.force)
            {
                throw new InvalidOperationException("The table cannot be purged because it has pending operations.");
            }

            var delOperationsQuery = new MobileServiceTableQueryDescription(MobileServiceLocalSystemTables.OperationQueue);

            delOperationsQuery.Filter = new BinaryOperatorNode(BinaryOperatorKind.Equal, new MemberAccessNode(null, "tableName"), new ConstantNode(this.Table.TableName));

            // count ops to be deleted
            delOperationsQuery.IncludeTotalCount = true;
            delOperationsQuery.Top = 0;
            long toRemove = QueryResult.Parse(await this.Store.ReadAsync(delOperationsQuery), null, validate: false).TotalCount;

            // delete operations
            delOperationsQuery.Top = null;
            await this.Store.DeleteAsync(delOperationsQuery);

            // delete errors
            var delErrorsQuery = new MobileServiceTableQueryDescription(MobileServiceLocalSystemTables.SyncErrors);

            delErrorsQuery.Filter = delOperationsQuery.Filter;
            await this.Store.DeleteAsync(delErrorsQuery);

            // update queue operation count
            this.OperationQueue.UpdateOperationCount(-toRemove);

            return(true);
        }
        private async Task Delete(MobileServiceTableQueryDescription query)
        {
            await this.store.DeleteAsync(query);

            OperationsInfo operationsInfo = this.operationsInfo.Value;

            Interlocked.Decrement(ref operationsInfo.Count);
        }
        public async Task <IEnumerable <MobileServiceFileMetadata> > GetMetadataAsync(string tableName, string objectId)
        {
            var query = MobileServiceTableQueryDescription.Parse(FileMetadataTableName, string.Format("$filter=parentDataItemType eq '{0}' and parentDataItemId eq '{1}'", tableName, objectId));

            var result = await this.store.ReadAsync(query);

            return(result.ToObject <List <MobileServiceFileMetadata> >());
        }
Beispiel #18
0
        public void FormatSelect_InvalidQuery()
        {
            string odata = "$filter=(2 ! ??)";
            var    ex    = Assert.Throws <MobileServiceODataException>(() => MobileServiceTableQueryDescription.Parse("test", odata));

            Assert.Equal("The specified odata query has syntax errors.", ex.Message);
            Assert.Equal(3, ex.ErrorPosition);
        }
Beispiel #19
0
 public PullStrategy(MobileServiceTableQueryDescription query,
                     PullCursor cursor,
                     MobileServiceRemoteTableOptions options)
 {
     this.Query        = query;
     this.Cursor       = cursor;
     this.SupportsSkip = options.HasFlag(MobileServiceRemoteTableOptions.Skip);
     this.SupportsTop  = options.HasFlag(MobileServiceRemoteTableOptions.Top);
 }
        public async Task RemoveAsync(string id)
        {
            var query = new MobileServiceTableQueryDescription(FileOperationTableName);

            query.Filter = new BinaryOperatorNode(BinaryOperatorKind.Equal, new MemberAccessNode(null, MobileServiceSystemColumns.Id), new ConstantNode(id));
            query.Top    = 1;

            await Delete(query);
        }
Beispiel #21
0
        public Task <JToken> ReadAsync(MobileServiceTableQueryDescription query)
        {
            if (query == null)
            {
                throw new ArgumentNullException("query");
            }

            return(this.store.ReadAsync(query));
        }
        public void Ordering()
        {
            // Query syntax
            MobileServiceTableQueryDescription odata = Compile <Product, Product>(table =>
                                                                                  from p in table
                                                                                  orderby p.Price ascending
                                                                                  select p);

            Assert.AreEqual(1, odata.Ordering.Count);
            Assert.AreEqual("Price", odata.Ordering[0].Key);
            Assert.IsTrue(odata.Ordering[0].Value);

            // Chaining
            odata = Compile <Product, Product>(table => table.OrderBy(p => p.Price));
            Assert.AreEqual(1, odata.Ordering.Count);
            Assert.AreEqual("Price", odata.Ordering[0].Key);
            Assert.IsTrue(odata.Ordering[0].Value);

            // Query syntax descending
            odata = Compile <Product, Product>(table =>
                                               from p in table
                                               orderby p.Price descending
                                               select p);
            Assert.AreEqual(1, odata.Ordering.Count);
            Assert.AreEqual("Price", odata.Ordering[0].Key);
            Assert.IsFalse(odata.Ordering[0].Value);

            // Chaining descending
            odata = Compile <Product, Product>(table => table.OrderByDescending(p => p.Price));
            Assert.AreEqual(1, odata.Ordering.Count);
            Assert.AreEqual("Price", odata.Ordering[0].Key);
            Assert.IsFalse(odata.Ordering[0].Value);

            // Query syntax with multiple
            odata = Compile <Product, Product>(table =>
                                               from p in table
                                               orderby p.Price ascending
                                               orderby p.Name descending
                                               select p);
            Assert.AreEqual(2, odata.Ordering.Count);
            Assert.AreEqual("Price", odata.Ordering[0].Key);
            Assert.IsTrue(odata.Ordering[0].Value);
            Assert.AreEqual("Name", odata.Ordering[1].Key);
            Assert.IsFalse(odata.Ordering[1].Value);

            // Chaining with multiple
            odata = Compile <Product, Product>(table =>
                                               table
                                               .OrderBy(p => p.Price)
                                               .OrderByDescending(p => p.Name));
            Assert.AreEqual(2, odata.Ordering.Count);
            Assert.AreEqual("Price", odata.Ordering[0].Key);
            Assert.IsTrue(odata.Ordering[0].Value);
            Assert.AreEqual("Name", odata.Ordering[1].Key);
            Assert.IsFalse(odata.Ordering[1].Value);
        }
        private static MobileServiceTableQueryDescription Compile <T, U>(Func <IMobileServiceTable <T>, MobileServiceTableQuery <U> > getQuery)
        {
            MobileServiceClient                service = new MobileServiceClient("http://www.test.com");
            IMobileServiceTable <T>            table   = service.GetTable <T>();
            MobileServiceTableQuery <U>        query   = getQuery(table);
            MobileServiceTableQueryDescription odata   = query.Compile();

            App.Harness.Log(">>> " + odata.ToString());
            return(odata);
        }
        public override async Task DeleteAsync(MobileServiceTableQueryDescription query)
        {
            // ignore operations on system tables as we are in readthru mode
            if (IsSystemTable(query.TableName))
            {
                return;
            }

            await this.store.DeleteAsync(query);
        }
        public async Task PurgeAsync(string tableName, MobileServiceTableKind tableKind, string queryId, string query, bool force, CancellationToken cancellationToken)
        {
            await this.EnsureInitializedAsync();

            var table = await this.GetTable(tableName);

            var queryDescription = MobileServiceTableQueryDescription.Parse(tableName, query);
            var action           = new PurgeAction(table, tableKind, queryId, queryDescription, force, this, this.opQueue, this.settings, this.Store, cancellationToken);

            await this.ExecuteSyncAction(action);
        }
        public async Task <JToken> ReadAsync(string tableName, string query)
        {
            await this.EnsureInitializedAsync();

            var queryDescription = MobileServiceTableQueryDescription.Parse(tableName, query);

            using (await this.storeQueueLock.ReaderLockAsync())
            {
                return(await this.Store.ReadAsync(queryDescription));
            }
        }
Beispiel #27
0
        public async Task <List <Patient> > GetItemsAsync()
        {
            var    query  = new MobileServiceTableQueryDescription("Patient");
            JToken jtoken = await database.ReadAsync(query);

            ObservableCollection <Patient> patientlist = new ObservableCollection <Patient>();

            var converted_result = jtoken.Select(x => JsonConvert.DeserializeObject <Patient>(x.ToString()));

            return(converted_result.ToList());
        }
        public async Task <MobileServiceTableOperation> GetOperationByItemIdAsync(string tableName, string itemId)
        {
            MobileServiceTableQueryDescription query = CreateQuery();

            query.Filter = new BinaryOperatorNode(BinaryOperatorKind.And,
                                                  Compare(BinaryOperatorKind.Equal, "tableName", tableName),
                                                  Compare(BinaryOperatorKind.Equal, "itemId", itemId));
            JObject op = await this.store.FirstOrDefault(query);

            return(MobileServiceTableOperation.Deserialize(op));
        }
        public void BasicQuery()
        {
            // Query syntax
            MobileServiceTableQueryDescription odata = Compile <Product, Product>(table =>
                                                                                  from p in table
                                                                                  select p);

            Assert.AreEqual("Product", odata.TableName);
            Assert.IsNull(odata.Filter);
            Assert.AreEqual(0, odata.Selection.Count);
            Assert.AreEqual(0, odata.Ordering.Count);
        }
 public async Task DoesNotSaveTheMaxUpdatedAt_IfResultsHaveOlderUpdatedAt()
 {
     var query = new MobileServiceTableQueryDescription("test");
     query.Filter = new BinaryOperatorNode(BinaryOperatorKind.Equal, new ConstantNode(4), new ConstantNode(3));
     var result = new JArray(new[]
     {
         new JObject() { { "id", "abc" }, { "text", "has id"}, { "updatedAt", "1985-07-17" } },
     });
     string firstQuery = "$filter=((4 eq 3) and (updatedAt ge datetimeoffset'2013-01-01T00%3A00%3A00.0000000%2B00%3A00'))&$orderby=updatedAt&$skip=0&$top=50";
     string secondQuery = "$filter=((4 eq 3) and (updatedAt ge datetimeoffset'2013-01-01T00%3A00%3A00.0000000%2B00%3A00'))&$orderby=updatedAt&$skip=1&$top=50";
     await TestIncrementalSync(query, result, new DateTime(2014, 07, 09), savesMax: false, firstQuery: firstQuery, secondQuery: secondQuery);
 }
        public async Task DoesNotSaveTheMaxUpdatedAt_IfThereAreNoResults()
        {
            string testName  = "test";
            string queryName = "latestItems";

            var store    = new Mock <IMobileServiceLocalStore>(MockBehavior.Strict);
            var settings = new Mock <MobileServiceSyncSettingsManager>(MockBehavior.Strict);
            var opQueue  = new Mock <OperationQueue>(MockBehavior.Strict, store.Object);
            var handler  = new Mock <IMobileServiceSyncHandler>(MockBehavior.Strict);
            var client   = new Mock <MobileServiceClient>();

            client.Object.Serializer = new MobileServiceSerializer();
            var context = new Mock <MobileServiceSyncContext>(client.Object);
            var table   = new Mock <MobileServiceTable <ToDoWithSystemPropertiesType> >(testName, client.Object);

            var    query      = new MobileServiceTableQueryDescription(testName);
            var    result     = new JArray();
            string firstQuery = "$filter=(updatedAt ge 2013-01-01T00%3A00%3A00.0000000%2B00%3A00)&$orderby=updatedAt&$skip=0&$top=50";

            var action = new PullAction(
                table.Object, MobileServiceTableKind.Table,
                context.Object,
                queryName, query,
                null, null,
                opQueue.Object,
                settings.Object,
                store.Object,
                MobileServiceRemoteTableOptions.All,
                pullOptions: null,
                reader: null,
                cancellationToken: CancellationToken.None);

            opQueue
            .Setup(q => q.LockTableAsync(It.IsAny <string>(), It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult <IDisposable>(null));
            opQueue
            .Setup(q => q.CountPending(It.IsAny <string>()))
            .Returns(Task.FromResult(0L));
            table
            .Setup(t => t.ReadAsync(firstQuery, It.IsAny <IDictionary <string, string> >(), It.IsAny <MobileServiceFeatures>()))
            .Returns(Task.FromResult(QueryResult.Parse(result, null, false)));
            settings
            .Setup(s => s.GetDeltaTokenAsync(testName, queryName))
            .Returns(Task.FromResult(new DateTimeOffset(2013, 1, 1, 0, 0, 0, TimeSpan.Zero)));

            await action.ExecuteAsync();

            store.VerifyAll();
            opQueue.VerifyAll();
            table.VerifyAll();
            settings.VerifyAll();
        }
        public override async Task<JToken> ReadAsync(MobileServiceTableQueryDescription query)
        {
            // first lookup in local store
            JToken result = await this.store.ReadAsync(query);
            JArray items = GetItems(result);

            // if local store does not have results
            if ((items == null || items.Count == 0) && !IsSystemTable(query.TableName))
            {
                // then lookup the server
                result = await this.client.GetTable(query.TableName).ReadAsync(query.ToODataString());
                items = GetItems(result);

                // insert the results in the local store
                await this.store.UpsertAsync(query.TableName, items.Cast<JObject>(), fromServer: true);
            }

            return result;
        }
        /// <summary>
        /// Deletes items from local table that match the given query.
        /// </summary>
        /// <param name="query">A query to find records to delete.</param>
        /// <returns>A task that completes when delete query has executed.</returns>
        public override Task DeleteAsync(MobileServiceTableQueryDescription query)
        {
            if (query == null)
            {
                throw new ArgumentNullException("query");
            }

            this.EnsureInitialized();

            var formatter = new SqlQueryFormatter(query);
            string sql = formatter.FormatDelete();

            this.ExecuteNonQuery(sql, formatter.Parameters);

            return Task.FromResult(0);
        }
        /// <summary>
        /// Reads data from local store by executing the query.
        /// </summary>
        /// <param name="query">The query to execute on local store.</param>
        /// <returns>A task that will return with results when the query finishes.</returns>
        public override Task<JToken> ReadAsync(MobileServiceTableQueryDescription query)
        {
            if (query == null)
            {
                throw new ArgumentNullException("query");
            }

            this.EnsureInitialized();

            var formatter = new SqlQueryFormatter(query);
            string sql = formatter.FormatSelect();

            IList<JObject> rows = this.ExecuteQuery(query.TableName, sql, formatter.Parameters);
            JToken result = new JArray(rows.ToArray());

            if (query.IncludeTotalCount)
            {
                sql = formatter.FormatSelectCount();
                IList<JObject> countRows = this.ExecuteQuery(query.TableName, sql, formatter.Parameters);
                long count = countRows[0].Value<long>("count");
                result = new JObject() 
                { 
                    { "results", result },
                    { "count", count}
                };
            }

            return Task.FromResult(result);
        }