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 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); }
// 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(); }
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); }