public async Task GetContinuousChangesAsync_WithIdsFilter() { using var httpTest = new HttpTest(); // Arrange var tokenSource = new CancellationTokenSource(); var docId = SetFeedResponse(httpTest); httpTest.RespondWithJson(new { ok = true }); var filter = ChangesFeedFilter.DocumentIds(new[] { docId }); // Act await foreach (var change in _rebels.GetContinuousChangesAsync(null, filter, tokenSource.Token)) { Assert.Equal(docId, change.Id); tokenSource.Cancel(); } // Assert httpTest .ShouldHaveCalled("http://localhost/rebels/_changes") .WithQueryParamValue("feed", "continuous") .WithQueryParamValue("filter", "_doc_ids") .WithJsonBody <ChangesFeedFilterDocuments>(f => f.DocumentIds.Contains(docId)) .WithVerb(HttpMethod.Post); }
public async Task Changes() { using (var client = new CouchClient("http://localhost:5984")) { IEnumerable <string> dbs = await client.GetDatabasesNamesAsync().ConfigureAwait(false); CouchDatabase <Rebel> rebels = client.GetDatabase <Rebel>(); if (dbs.Contains(rebels.Database)) { await client.DeleteDatabaseAsync <Rebel>().ConfigureAwait(false); } rebels = await client.CreateDatabaseAsync <Rebel>().ConfigureAwait(false); Rebel luke = await rebels.CreateAsync(new Rebel { Name = "Luke", Age = 19 }).ConfigureAwait(false); Assert.Equal("Luke", luke.Name); var options = new ChangesFeedOptions { IncludeDocs = true }; var filter = ChangesFeedFilter.Selector <Rebel>(r => r.Name == "Luke" && r.Age == 19); var changesResult = await rebels.GetChangesAsync(options, filter); Assert.NotEmpty(changesResult.Results); Assert.Equal(changesResult.Results[0].Id, luke.Id); await client.DeleteDatabaseAsync <Rebel>().ConfigureAwait(false); } }
public async Task GetContinuousChangesAsync_WithSelectorFilter() { using var httpTest = new HttpTest(); // Arrange var tokenSource = new CancellationTokenSource(); var docId = SetFeedResponse(httpTest); httpTest.RespondWithJson(new { ok = true }); var filter = ChangesFeedFilter.Selector <Rebel>(rebel => rebel.Id == docId); // Act await foreach (var change in _rebels.GetContinuousChangesAsync(null, filter, tokenSource.Token)) { Assert.Equal(docId, change.Id); tokenSource.Cancel(); } // Assert httpTest .ShouldHaveCalled("http://localhost/rebels/_changes") .WithQueryParamValue("feed", "continuous") .WithQueryParamValue("filter", "_selector") .WithContentType("application/json") .With(call => call.RequestBody == $"{{\"selector\":{{\"_id\":\"{docId}\"}}}}") .WithVerb(HttpMethod.Post); }
public async Task GetContinuousChangesAsync_WithViewFilter() { using var httpTest = new HttpTest(); // Arrange var tokenSource = new CancellationTokenSource(); var docId = SetFeedResponse(httpTest); httpTest.RespondWithJson(new { ok = true }); var view = Guid.NewGuid().ToString(); var filter = ChangesFeedFilter.View(view); // Act await foreach (var change in _rebels.GetContinuousChangesAsync(null, filter, tokenSource.Token)) { Assert.Equal(docId, change.Id); tokenSource.Cancel(); } // Assert httpTest .ShouldHaveCalled("http://localhost/rebels/_changes") .WithQueryParamValue("feed", "continuous") .WithQueryParamValue("filter", "_view") .WithQueryParamValue("view", view) .WithVerb(HttpMethod.Get); }
public async Task GetChangesAsync_WithDesignFilter() { using var httpTest = new HttpTest(); // Arrange SetFeedResponse(httpTest); httpTest.RespondWithJson(new { ok = true }); var filter = ChangesFeedFilter.Design(); // Act var newR = await _rebels.GetChangesAsync(null, filter); // Assert httpTest .ShouldHaveCalled("http://localhost/rebels/_changes") .WithQueryParamValue("filter", "_design") .WithVerb(HttpMethod.Get); }
public async Task ContinuousChanges() { using (var client = new CouchClient("http://localhost:5984")) { IEnumerable <string> dbs = await client.GetDatabasesNamesAsync().ConfigureAwait(false); CouchDatabase <Rebel> rebels = client.GetDatabase <Rebel>(); if (dbs.Contains(rebels.Database)) { await client.DeleteDatabaseAsync <Rebel>().ConfigureAwait(false); } rebels = await client.CreateDatabaseAsync <Rebel>().ConfigureAwait(false); Rebel luke = await rebels.CreateAsync(new Rebel { Name = "Luke", Age = 19 }).ConfigureAwait(false); Assert.Equal("Luke", luke.Name); luke.Surname = "Vader"; _ = Task.Run(async() => { await Task.Delay(2000); await rebels.CreateOrUpdateAsync(luke); }); var ids = new[] { luke.Id }; var option = new ChangesFeedOptions { Since = "now" }; var filter = ChangesFeedFilter.DocumentIds(ids); using var cancelSource = new CancellationTokenSource(); await foreach (var _ in rebels.GetContinuousChangesAsync(option, filter, cancelSource.Token)) { cancelSource.Cancel(); } await client.DeleteDatabaseAsync <Rebel>().ConfigureAwait(false); } }
public async Task GetChangesAsync_WithViewFilter() { using var httpTest = new HttpTest(); // Arrange SetFeedResponse(httpTest); httpTest.RespondWithJson(new { ok = true }); var view = Guid.NewGuid().ToString(); var filter = ChangesFeedFilter.View(view); // Act var newR = await _rebels.GetChangesAsync(null, filter); // Assert httpTest .ShouldHaveCalled("http://localhost/rebels/_changes") .WithQueryParamValue("filter", "_view") .WithQueryParamValue("view", view) .WithVerb(HttpMethod.Get); }
public async Task GetChangesAsync_WithSelectorFilter() { using var httpTest = new HttpTest(); // Arrange SetFeedResponse(httpTest); httpTest.RespondWithJson(new { ok = true }); var docId = Guid.NewGuid().ToString(); var filter = ChangesFeedFilter.Selector <Rebel>(rebel => rebel.Id == docId); // Act var newR = await _rebels.GetChangesAsync(null, filter); // Assert httpTest .ShouldHaveCalled("http://localhost/rebels/_changes") .WithQueryParamValue("filter", "_selector") .WithContentType("application/json") .With(call => call.RequestBody == $"{{\"selector\":{{\"_id\":\"{docId}\"}}}}") .WithVerb(HttpMethod.Post); }
public async Task GetChangesAsync_WithIdsFilter() { using var httpTest = new HttpTest(); // Arrange SetFeedResponse(httpTest); httpTest.RespondWithJson(new { ok = true }); var docId = Guid.NewGuid().ToString(); var filter = ChangesFeedFilter.DocumentIds(new[] { docId }); // Act var newR = await _rebels.GetChangesAsync(null, filter); // Assert httpTest .ShouldHaveCalled("http://localhost/rebels/_changes") .WithQueryParamValue("filter", "_doc_ids") .WithJsonBody <ChangesFeedFilterDocuments>(f => f.DocumentIds.Contains(docId)) .WithVerb(HttpMethod.Post); }
/// <inheritdoc /> public async IAsyncEnumerable <ChangesFeedResponseResult <TSource> > GetContinuousChangesAsync(ChangesFeedOptions options, ChangesFeedFilter filter, [EnumeratorCancellation] CancellationToken cancellationToken) { var infiniteTimeout = TimeSpan.FromMilliseconds(Timeout.Infinite); IFlurlRequest request = NewRequest() .WithTimeout(infiniteTimeout) .AppendPathSegment("_changes") .SetQueryParam("feed", "continuous"); if (options != null) { request = request.ApplyQueryParametersOptions(options); } await using Stream stream = filter == null ? await request.GetStreamAsync(cancellationToken, HttpCompletionOption.ResponseHeadersRead) .ConfigureAwait(false) : await request.QueryContinuousWithFilterAsync <TSource>(_queryProvider, filter, cancellationToken) .ConfigureAwait(false); await foreach (var line in stream.ReadLinesAsync(cancellationToken)) { if (string.IsNullOrEmpty(line)) { continue; } ChangesFeedResponseResult <TSource> result = JsonConvert.DeserializeObject <ChangesFeedResponseResult <TSource> >(line); yield return(result); } }
private async Task <Stream> QueryContinuousWithFilterAsync(IFlurlRequest request, ChangesFeedFilter filter, CancellationToken cancellationToken) { if (filter is DocumentIdsChangesFeedFilter documentIdsFilter) { return(await request .SetQueryParam("filter", "_doc_ids") .PostJsonStreamAsync(new ChangesFeedFilterDocuments(documentIdsFilter.Value), cancellationToken, HttpCompletionOption.ResponseHeadersRead) .ConfigureAwait(false)); } if (filter is SelectorChangesFeedFilter <TSource> selectorFilter) { MethodCallExpression whereExpression = Expression.Call(typeof(Queryable), nameof(Queryable.Where), new[] { typeof(TSource) }, Expression.Constant(Array.Empty <TSource>().AsQueryable()), selectorFilter.Value); var jsonSelector = new QueryTranslator(_settings).Translate(whereExpression); return(await request .WithHeader("Content-Type", "application/json") .SetQueryParam("filter", "_selector") .PostStringStreamAsync(jsonSelector, cancellationToken, HttpCompletionOption.ResponseHeadersRead) .ConfigureAwait(false)); } if (filter is DesignChangesFeedFilter) { return(await request .SetQueryParam("filter", "_design") .GetStreamAsync(cancellationToken, HttpCompletionOption.ResponseHeadersRead) .ConfigureAwait(false)); } if (filter is ViewChangesFeedFilter viewFilter) { return(await request .SetQueryParam("filter", "_view") .SetQueryParam("view", viewFilter.Value) .GetStreamAsync(cancellationToken, HttpCompletionOption.ResponseHeadersRead) .ConfigureAwait(false)); } throw new InvalidOperationException($"Filter of type {filter.GetType().Name} not supported."); }
/// <summary> /// Returns changes as they happen. A continuous feed stays open and connected to the database until explicitly closed. /// </summary> /// <remarks> /// To stop receiving changes call <c>Cancel()</c> on the <c>CancellationTokenSource</c> used to create the <c>CancellationToken</c>. /// </remarks> /// <param name="options">Options to apply to the request.</param> /// <param name="filter">A filter to apply to the result.</param> /// <param name="cancellationToken">A cancellation token to stop receiving changes.</param> /// <returns></returns> public async IAsyncEnumerable <ChangesFeedResponseResult <TSource> > GetContinuousChangesAsync(ChangesFeedOptions options, ChangesFeedFilter filter, [EnumeratorCancellation] CancellationToken cancellationToken) { var infiniteTimeout = TimeSpan.FromMilliseconds(Timeout.Infinite); IFlurlRequest request = NewRequest() .WithTimeout(infiniteTimeout) .AppendPathSegment("_changes") .SetQueryParam("feed", "continuous"); SetChangesFeedOptions(request, options); await using Stream stream = filter == null ? await request.GetStreamAsync(cancellationToken, HttpCompletionOption.ResponseHeadersRead) .ConfigureAwait(false) : await QueryContinuousWithFilterAsync(request, filter, cancellationToken) .ConfigureAwait(false); using var reader = new StreamReader(stream); while (!cancellationToken.IsCancellationRequested && !reader.EndOfStream) { if (cancellationToken.IsCancellationRequested) { continue; } var line = await reader.ReadLineAsync().ConfigureAwait(false); if (!string.IsNullOrEmpty(line)) { yield return(JsonConvert.DeserializeObject <ChangesFeedResponseResult <TSource> >(line)); } } }
/// <summary> /// Returns a sorted list of changes made to documents in the database. /// </summary> /// <remarks> /// Only the most recent change for a given document is guaranteed to be provided. /// </remarks> /// <param name="options">Options to apply to the request.</param> /// <param name="filter">A filter to apply to the result.</param> /// <returns></returns> public async Task <ChangesFeedResponse <TSource> > GetChangesAsync(ChangesFeedOptions options = null, ChangesFeedFilter filter = null) { IFlurlRequest request = NewRequest() .AppendPathSegment("_changes"); if (options?.LongPoll == true) { _ = request.SetQueryParam("feed", "longpoll"); } SetChangesFeedOptions(request, options); return(filter == null ? await request.GetJsonAsync <ChangesFeedResponse <TSource> >() .ConfigureAwait(false) : await QueryWithFilterAsync(request, filter) .ConfigureAwait(false)); }