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);
 }
Example #2
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 MobileServiceSyncTable(string tableName, MobileServiceTableKind Kind, MobileServiceClient client)
        {
            Debug.Assert(tableName != null);
            Debug.Assert(client != null);

            this.MobileServiceClient = client;
            this.TableName = tableName;
            this.Kind = Kind;
            this.syncContext = (MobileServiceSyncContext)client.SyncContext;
            this.SupportedOptions = MobileServiceRemoteTableOptions.All;
        }
        public MobileServiceSyncTable(string tableName, MobileServiceTableKind Kind, MobileServiceClient client)
        {
            Debug.Assert(tableName != null);
            Debug.Assert(client != null);

            this.MobileServiceClient = client;
            this.TableName           = tableName;
            this.Kind             = Kind;
            this.syncContext      = (MobileServiceSyncContext)client.SyncContext;
            this.SupportedOptions = MobileServiceRemoteTableOptions.All;
        }
 public IncrementalPullStrategy(MobileServiceTable table,
                                MobileServiceTableQueryDescription query,
                                string queryId,
                                MobileServiceSyncSettingsManager settings,
                                PullCursor cursor,
                                MobileServiceRemoteTableOptions options)
     : base(query, cursor, options)
 {
     this.table          = table;
     this.originalFilter = query.Filter;
     this.queryId        = queryId;
     this.settings       = settings;
     this.ordered        = options.HasFlag(MobileServiceRemoteTableOptions.OrderBy);
 }
 public IncrementalPullStrategy(MobileServiceTable table,
                                MobileServiceTableQueryDescription query,
                                string queryId,
                                MobileServiceSyncSettingsManager settings,
                                PullCursor cursor,
                                MobileServiceRemoteTableOptions options)
     : base(query, cursor, options)
 {
     this.table = table;
     this.originalFilter = query.Filter;
     this.queryId = queryId;
     this.settings = settings;
     this.ordered = options.HasFlag(MobileServiceRemoteTableOptions.OrderBy);
 }
Example #7
0
        // mongo doesn't support skip and top yet it generates next links with top and skip
        private bool IsNextLinkValid(Uri link, MobileServiceRemoteTableOptions options)
        {
            if (link == null)
            {
                return(false);
            }

            IDictionary <string, string> parameters = HttpUtility.ParseQueryString(link.Query);

            bool isValid = ValidateOption(options, parameters, ODataOptions.Top, MobileServiceRemoteTableOptions.Top) &&
                           ValidateOption(options, parameters, ODataOptions.Skip, MobileServiceRemoteTableOptions.Skip) &&
                           ValidateOption(options, parameters, ODataOptions.OrderBy, MobileServiceRemoteTableOptions.OrderBy);

            return(isValid);
        }
 public PullAction(MobileServiceTable table,
                   MobileServiceTableKind tableKind,
                   MobileServiceSyncContext context,
                   string queryId,
                   MobileServiceTableQueryDescription query,
                   IDictionary<string, string> parameters,
                   IEnumerable<string> relatedTables,
                   OperationQueue operationQueue,
                   MobileServiceSyncSettingsManager settings,
                   IMobileServiceLocalStore store,
                   MobileServiceRemoteTableOptions options,
                   MobileServiceObjectReader reader,
                   CancellationToken cancellationToken)
     : base(table, tableKind, queryId, query, relatedTables, context, operationQueue, settings, store, cancellationToken)
 {
     this.options = options;
     this.parameters = parameters;
     this.cursor = new PullCursor(query);
     this.Reader = reader ?? new MobileServiceObjectReader();
 }
 public PullAction(MobileServiceTable table,
                   MobileServiceTableKind tableKind,
                   MobileServiceSyncContext context,
                   string queryId,
                   MobileServiceTableQueryDescription query,
                   IDictionary <string, string> parameters,
                   IEnumerable <string> relatedTables,
                   OperationQueue operationQueue,
                   MobileServiceSyncSettingsManager settings,
                   IMobileServiceLocalStore store,
                   MobileServiceRemoteTableOptions options,
                   MobileServiceObjectReader reader,
                   CancellationToken cancellationToken)
     : base(table, tableKind, queryId, query, relatedTables, context, operationQueue, settings, store, cancellationToken)
 {
     this.options    = options;
     this.parameters = parameters;
     this.cursor     = new PullCursor(query);
     this.Reader     = reader ?? new MobileServiceObjectReader();
 }
Example #10
0
        private static bool ValidateOption(MobileServiceRemoteTableOptions validOptions, IDictionary <string, string> parameters, string optionKey, MobileServiceRemoteTableOptions option)
        {
            bool hasInvalidOption = parameters.ContainsKey(optionKey) && !validOptions.HasFlag(option);

            return(!hasInvalidOption);
        }
        /// <summary>
        /// Pulls all items that match the given query from the associated remote table.
        /// </summary>
        /// <param name="tableName">The name of table to pull</param>
        /// <param name="tableKind">The kind of table</param>
        /// <param name="queryId">A string that uniquely identifies this query and is used to keep track of its sync state.</param>
        /// <param name="query">An OData query that determines which items to 
        /// pull from the remote table.</param>
        /// <param name="options">An instance of <see cref="MobileServiceRemoteTableOptions"/></param>
        /// <param name="parameters">A dictionary of user-defined parameters and values to include in 
        /// the request URI query string.</param>
        /// <param name="relatedTables">
        /// List of tables that may have related records that need to be push before this table is pulled down.
        /// When no table is specified, all tables are considered related.
        /// </param>
        /// <param name="reader">An instance of <see cref="MobileServiceObjectReader"/></param>
        /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken"/> token to observe
        /// </param>
        /// <param name="pullOptions">
        /// PullOptions that determine how to pull data from the remote table
        /// </param>
        /// <returns>
        /// A task that completes when pull operation has finished.
        /// </returns>
        public async Task PullAsync(string tableName, MobileServiceTableKind tableKind, string queryId, string query, MobileServiceRemoteTableOptions options, IDictionary<string, string> parameters, IEnumerable<string> relatedTables, MobileServiceObjectReader reader, CancellationToken cancellationToken, PullOptions pullOptions)
        {
            await this.EnsureInitializedAsync();

            if (parameters != null)
            {
                if (parameters.Keys.Any(k => k.Equals(MobileServiceTable.IncludeDeletedParameterName, StringComparison.OrdinalIgnoreCase)))
                {
                    throw new ArgumentException("The key '{0}' is reserved and cannot be specified as a query parameter.".FormatInvariant(MobileServiceTable.IncludeDeletedParameterName));
                }
            }

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

            // local schema should be same as remote schema otherwise push can't function
            if (queryDescription.Selection.Any() || queryDescription.Projections.Any())
            {
                throw new ArgumentException("Pull query with select clause is not supported.", "query");
            }

            bool isIncrementalSync = !String.IsNullOrEmpty(queryId);
            if (isIncrementalSync)
            {
                if (queryDescription.Ordering.Any())
                {
                    throw new ArgumentException("Incremental pull query must not have orderby clause.", "query");
                }
                if (queryDescription.Top.HasValue || queryDescription.Skip.HasValue)
                {
                    throw new ArgumentException("Incremental pull query must not have skip or top specified.", "query");
                }
            }

            if (!options.HasFlag(MobileServiceRemoteTableOptions.OrderBy) && queryDescription.Ordering.Any())
            {
                throw new ArgumentException("The supported table options does not include orderby.", "query");
            }

            if (!options.HasFlag(MobileServiceRemoteTableOptions.Skip) && queryDescription.Skip.HasValue)
            {
                throw new ArgumentException("The supported table options does not include skip.", "query");
            }

            if (!options.HasFlag(MobileServiceRemoteTableOptions.Top) && queryDescription.Top.HasValue)
            {
                throw new ArgumentException("The supported table options does not include top.", "query");
            }

            // let us not burden the server to calculate the count when we don't need it for pull
            queryDescription.IncludeTotalCount = false;

            using (var store = StoreChangeTrackerFactory.CreateTrackedStore(this.Store, StoreOperationSource.ServerPull, this.storeTrackingOptions, this.client.EventManager, this.settings))
            {
                var action = new PullAction(table, tableKind, this, queryId, queryDescription, parameters, relatedTables,
                    this.opQueue, this.settings, store, options, pullOptions, reader, cancellationToken);
                await this.ExecuteSyncAction(action);
            }
        }
        private static async Task TestIncrementalPull(MobileServiceLocalStoreMock store, MobileServiceRemoteTableOptions options, params string[] expectedUris)
        {
            var hijack = new TestHttpHandler();
            hijack.AddResponseContent(@"[{""id"":""abc"",""String"":""Hey"", ""__updatedAt"": ""2001-02-03T00:00:00.0000000+00:00""},
                                        {""id"":""def"",""String"":""World"", ""__updatedAt"": ""2001-02-03T00:03:00.0000000+07:00""}]"); // for pull
            hijack.AddResponseContent(@"[]");


            store.TableMap[MobileServiceLocalSystemTables.Config]["systemProperties|stringId_test_table"] = new JObject
            {
                { MobileServiceSystemColumns.Id, "systemProperties|stringId_test_table" },
                { "value", "1" }
            };

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

            IMobileServiceSyncTable<ToDoWithStringId> table = service.GetSyncTable<ToDoWithStringId>();
            table.SupportedOptions = options;
            var query = table.Where(t => t.String == "world")
                             .WithParameters(new Dictionary<string, string>() { { "param1", "val1" } })
                             .IncludeTotalCount();

            await table.PullAsync("incquery", query, cancellationToken: CancellationToken.None);

            AssertEx.MatchUris(hijack.Requests, expectedUris);
        }
 private static async Task TestPullAsyncIncrementalWithOptions(MobileServiceRemoteTableOptions options, params string[] uris)
 {
     var store = new MobileServiceLocalStoreMock();
     var settings = new MobileServiceSyncSettingsManager(store);
     await settings.SetDeltaTokenAsync("stringId_test_table", "incquery", new DateTime(2001, 02, 01, 0, 0, 0, DateTimeKind.Utc));
     await TestIncrementalPull(store, options, uris);
 }
        /// <summary>
        /// Pulls all items that match the given query from the associated remote table.
        /// </summary>
        /// <param name="tableName">The name of table to pull</param>
        /// <param name="tableKind">The kind of table</param>
        /// <param name="queryId">A string that uniquely identifies this query and is used to keep track of its sync state.</param>
        /// <param name="query">An OData query that determines which items to
        /// pull from the remote table.</param>
        /// <param name="options">An instance of <see cref="MobileServiceRemoteTableOptions"/></param>
        /// <param name="parameters">A dictionary of user-defined parameters and values to include in
        /// the request URI query string.</param>
        /// <param name="relatedTables">
        /// List of tables that may have related records that need to be push before this table is pulled down.
        /// When no table is specified, all tables are considered related.
        /// </param>
        /// <param name="reader">An instance of <see cref="MobileServiceObjectReader"/></param>
        /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken"/> token to observe
        /// </param>
        /// <param name="pullOptions">
        /// PullOptions that determine how to pull data from the remote table
        /// </param>
        /// <returns>
        /// A task that completes when pull operation has finished.
        /// </returns>
        public async Task PullAsync(string tableName, MobileServiceTableKind tableKind, string queryId, string query, MobileServiceRemoteTableOptions options, IDictionary <string, string> parameters, IEnumerable <string> relatedTables, MobileServiceObjectReader reader, CancellationToken cancellationToken, PullOptions pullOptions)
        {
            await this.EnsureInitializedAsync();

            if (parameters != null)
            {
                if (parameters.Keys.Any(k => k.Equals(MobileServiceTable.IncludeDeletedParameterName, StringComparison.OrdinalIgnoreCase)))
                {
                    throw new ArgumentException("The key '{0}' is reserved and cannot be specified as a query parameter.".FormatInvariant(MobileServiceTable.IncludeDeletedParameterName));
                }
            }

            var table = await this.GetTable(tableName);

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

            // local schema should be same as remote schema otherwise push can't function
            if (queryDescription.Selection.Any() || queryDescription.Projections.Any())
            {
                throw new ArgumentException("Pull query with select clause is not supported.", "query");
            }

            bool isIncrementalSync = !String.IsNullOrEmpty(queryId);

            if (isIncrementalSync)
            {
                if (queryDescription.Ordering.Any())
                {
                    throw new ArgumentException("Incremental pull query must not have orderby clause.", "query");
                }
                if (queryDescription.Top.HasValue || queryDescription.Skip.HasValue)
                {
                    throw new ArgumentException("Incremental pull query must not have skip or top specified.", "query");
                }
            }

            if (!options.HasFlag(MobileServiceRemoteTableOptions.OrderBy) && queryDescription.Ordering.Any())
            {
                throw new ArgumentException("The supported table options does not include orderby.", "query");
            }

            if (!options.HasFlag(MobileServiceRemoteTableOptions.Skip) && queryDescription.Skip.HasValue)
            {
                throw new ArgumentException("The supported table options does not include skip.", "query");
            }

            if (!options.HasFlag(MobileServiceRemoteTableOptions.Top) && queryDescription.Top.HasValue)
            {
                throw new ArgumentException("The supported table options does not include top.", "query");
            }

            // let us not burden the server to calculate the count when we don't need it for pull
            queryDescription.IncludeTotalCount = false;

            using (var store = StoreChangeTrackerFactory.CreateTrackedStore(this.Store, StoreOperationSource.ServerPull, this.storeTrackingOptions, this.client.EventManager, this.settings))
            {
                var action = new PullAction(table, tableKind, this, queryId, queryDescription, parameters, relatedTables,
                                            this.opQueue, this.settings, store, options, pullOptions, reader, cancellationToken);
                await this.ExecuteSyncAction(action);
            }
        }
 private static bool ValidateOption(MobileServiceRemoteTableOptions validOptions, IDictionary<string, string> parameters, string optionKey, MobileServiceRemoteTableOptions option)
 {
     bool hasInvalidOption = parameters.ContainsKey(optionKey) && !validOptions.HasFlag(option);
     return !hasInvalidOption;
 }
        // mongo doesn't support skip and top yet it generates next links with top and skip
        private bool IsNextLinkValid(Uri link, MobileServiceRemoteTableOptions options)
        {
            if (link == null)
            {
                return false;
            }

            IDictionary<string, string> parameters = HttpUtility.ParseQueryString(link.Query);

            bool isValid = ValidateOption(options, parameters, ODataOptions.Top, MobileServiceRemoteTableOptions.Top) &&
                           ValidateOption(options, parameters, ODataOptions.Skip, MobileServiceRemoteTableOptions.Skip) &&
                           ValidateOption(options, parameters, ODataOptions.OrderBy, MobileServiceRemoteTableOptions.OrderBy);

            return isValid;
        }
        /// <summary>
        /// Pulls all items that match the given query from the associated remote table.
        /// </summary>
        /// <param name="tableName">The name of table to pull</param>
        /// <param name="tableKind">The kind of table</param>
        /// <param name="queryId">A string that uniquely identifies this query and is used to keep track of its sync state.</param>
        /// <param name="query">An OData query that determines which items to
        /// pull from the remote table.</param>
        /// <param name="options">An instance of <see cref="MobileServiceRemoteTableOptions"/></param>
        /// <param name="parameters">A dictionary of user-defined parameters and values to include in
        /// the request URI query string.</param>
        /// <param name="relatedTables">
        /// List of tables that may have related records that need to be push before this table is pulled down.
        /// When no table is specified, all tables are considered related.
        /// </param>
        /// <param name="reader">An instance of <see cref="MobileServiceObjectReader"/></param>
        /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken"/> token to observe
        /// </param>
        /// <returns>
        /// A task that completes when pull operation has finished.
        /// </returns>
        public async Task PullAsync(string tableName, MobileServiceTableKind tableKind, string queryId, string query, MobileServiceRemoteTableOptions options, IDictionary <string, string> parameters, IEnumerable <string> relatedTables, MobileServiceObjectReader reader, CancellationToken cancellationToken)
        {
            await this.EnsureInitializedAsync();

            if (parameters != null)
            {
                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);


            // local schema should be same as remote schema otherwise push can't function
            if (queryDescription.Selection.Any() || queryDescription.Projections.Any())
            {
                throw new ArgumentException(Resources.MobileServiceSyncTable_PullWithSelectNotSupported, "query");
            }

            bool isIncrementalSync = !String.IsNullOrEmpty(queryId);

            if (isIncrementalSync)
            {
                if (queryDescription.Ordering.Any())
                {
                    throw new ArgumentException(Resources.MobileServiceSyncTable_IncrementalPullWithOrderNotAllowed, "query");
                }
                if (queryDescription.Top.HasValue || queryDescription.Skip.HasValue)
                {
                    throw new ArgumentException(Resources.MobileServiceSyncTable_IncrementalPullWithSkipTopNotSupported, "query");
                }
            }

            if (!options.HasFlag(MobileServiceRemoteTableOptions.OrderBy) && queryDescription.Ordering.Any())
            {
                throw new ArgumentException(Resources.MobileServiceSyncTable_OrderByNotAllowed, "query");
            }

            if (!options.HasFlag(MobileServiceRemoteTableOptions.Skip) && queryDescription.Skip.HasValue)
            {
                throw new ArgumentException(Resources.MobileServiceSyncTable_SkipNotAllowed, "query");
            }

            if (!options.HasFlag(MobileServiceRemoteTableOptions.Top) && queryDescription.Top.HasValue)
            {
                throw new ArgumentException(Resources.MobileServiceSyncTable_TopNotAllowed, "query");
            }

            // let us not burden the server to calculate the count when we don't need it for pull
            queryDescription.IncludeTotalCount = false;

            var action = new PullAction(table, tableKind, this, queryId, queryDescription, parameters, relatedTables, this.opQueue, this.settings, this.Store, options, reader, cancellationToken);

            await this.ExecuteSyncAction(action);
        }
        private static async Task TestIncrementalPull(MobileServiceLocalStoreMock store, MobileServiceRemoteTableOptions options, params string[] expectedTableUriComponents)
        {
            var hijack = new TestHttpHandler();
            hijack.AddResponseContent(@"[{""id"":""abc"",""String"":""Hey"", ""updatedAt"": ""2001-02-03T00:00:00.0000000+00:00""},
                                        {""id"":""def"",""String"":""World"", ""updatedAt"": ""2001-02-03T00:03:00.0000000+07:00""}]"); // for pull
            hijack.AddResponseContent(@"[]");

            IMobileServiceClient service = new MobileServiceClient(MobileAppUriValidator.DummyMobileApp, hijack);
            MobileAppUriValidator mobileAppUriValidator = new MobileAppUriValidator(service);
            await service.SyncContext.InitializeAsync(store, new MobileServiceSyncHandler());

            IMobileServiceSyncTable<ToDoWithStringId> table = service.GetSyncTable<ToDoWithStringId>();
            table.SupportedOptions = options;
            var query = table.Where(t => t.String == "world")
                             .WithParameters(new Dictionary<string, string>() { { "param1", "val1" } })
                             .IncludeTotalCount();

            await table.PullAsync("incquery", query, cancellationToken: CancellationToken.None);

            var expectedTableUris =
                expectedTableUriComponents.Select(
                    expectedTableUriComponent => mobileAppUriValidator.GetTableUri(expectedTableUriComponent)).ToArray();

            AssertEx.MatchUris(hijack.Requests, expectedTableUris);
        }