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);
        }
Example #2
0
        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);
        }
Example #5
0
        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);
        }
Example #6
0
        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);
            }
        }
Example #7
0
        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);
        }
Example #8
0
        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);
        }
Example #9
0
        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);
        }
Example #10
0
        /// <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);
            }
        }
Example #11
0
 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.");
 }
Example #12
0
        /// <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));
                }
            }
        }
Example #13
0
        /// <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));
        }