public static async Task ShouldCallElasticSearchClient()
        {
            var spyLog = new SpyLog();

            var mockConnection = Substitute.For <IElasticConnection>();

            mockConnection.Index.Returns("SearchIndex");
            mockConnection.Options.Returns(new ElasticConnectionOptions());
            mockConnection.Timeout.Returns(TimeSpan.FromSeconds(10));

            var request = new SearchRequest {
                IndexType = "abc123", Size = 2112
            };
            var token = new CancellationToken();

            var processor = new ElasticRequestProcessor(mockConnection, mapping, spyLog, retryPolicy);

            await processor.SearchAsync(request, token);

#pragma warning disable 4014 // Remove this and await the SearchAsync below once NSubstitute 1.8.3 available
            mockConnection.Received(1).SearchAsync(
#pragma warning restore 4014
                @"{""size"":2112,""timeout"":""10s""}",
                request,
                token,
                spyLog
                );
        }
Пример #2
0
        private async Task <object> ExecuteAsync(ElasticTranslateResult translation, CancellationToken cancellationToken = default(CancellationToken))
        {
            Log.Debug(null, null, "Executing query against document '{0}'", translation.SearchRequest.IndexType);

            try
            {
                ElasticResponse response;
                if (translation.SearchRequest.Query == ConstantCriteria.False)
                {
                    response = new ElasticResponse();
                }
                else
                {
                    response = await requestProcessor.SearchAsync(translation.SearchRequest, cancellationToken).ConfigureAwait(false);

                    if (response == null)
                    {
                        throw new InvalidOperationException("No HTTP response received.");
                    }
                }

                var result = translation.Materializer.Materialize(response);
                response.hits?.hits?.Clear(); // Clear list aggressively
                return(result);
            }
            catch (AggregateException ex)
            {
                ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
                return(null);  // Never called, as the above code re-throws
            }
        }
Пример #3
0
        /// <inheritdoc/>
        public async Task <object> ExecuteAsync(Expression expression, CancellationToken cancellationToken = default(CancellationToken))
        {
            Argument.EnsureNotNull("expression", expression);

            var translation = ElasticQueryTranslator.Translate(Mapping, expression);

            Log.Debug(null, null, "Executing query against document '{0}'", translation.SearchRequest.DocumentType);

            try
            {
                ElasticResponse response;
                if (translation.SearchRequest.Filter == ConstantCriteria.False)
                {
                    response = new ElasticResponse();
                }
                else
                {
                    response = await requestProcessor.SearchAsync(translation.SearchRequest, cancellationToken);

                    if (response == null)
                    {
                        throw new InvalidOperationException("No HTTP response received.");
                    }
                }

                return(translation.Materializer.Materialize(response));
            }
            catch (AggregateException ex)
            {
                ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
                return(null);  // Never called, as the above code re-throws
            }
        }
Пример #4
0
        private object ExecuteInternal(Expression expression)
        {
            var translation = ElasticQueryTranslator.Translate(Mapping, Prefix, expression);
            var elementType = TypeHelper.GetSequenceElementType(expression.Type);

            Log.Debug(null, null, "Executing query against type {0}", elementType);

            try
            {
                ElasticResponse response;
                if (translation.SearchRequest.Filter == ConstantCriteria.False)
                {
                    response = new ElasticResponse();
                }
                else
                {
                    response = AsyncHelper.RunSync(() => requestProcessor.SearchAsync(translation.SearchRequest));
                    if (response == null)
                    {
                        throw new InvalidOperationException("No HTTP response received.");
                    }
                }

                return(translation.Materializer.Materialize(response));
            }
            catch (AggregateException ex)
            {
                ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
                return(null);  // Never called, as the above code re-throws
            }
        }
        public static async void SearchAsyncThrowsTaskCancelledExceptionWithSubsequentlyCancelledCancellationToken()
        {
            var request = new SearchRequest {
                IndexType = "docType"
            };
            var processor = new ElasticRequestProcessor(connection, mapping, log, retryPolicy);

            var ex = await Record.ExceptionAsync(() => processor.SearchAsync(request, new CancellationTokenSource(500).Token));

            Assert.IsType <TaskCanceledException>(ex);
        }
        public static async void SearchAsyncThrowsTaskCancelledExceptionWithAlreadyCancelledCancellationToken()
        {
            var request = new SearchRequest {
                DocumentType = "docType"
            };
            var processor = new ElasticRequestProcessor(connection, mapping, log, retryPolicy);

            var ex = await Record.ExceptionAsync(() => processor.SearchAsync(request, new CancellationToken(true)));

            Assert.IsType <TaskCanceledException>(ex);
        }
Пример #7
0
        public static async Task NoAuthorizationWithEmptyUserName()
        {
            var messageHandler  = new SpyMessageHandler();
            var localConnection = new ElasticConnection(messageHandler, new Uri("http://localhost"));
            var processor       = new ElasticRequestProcessor(localConnection, mapping, log, retryPolicy);
            var request         = new ElasticSearchRequest {
                Type = "docType"
            };

            await processor.SearchAsync(request);

            Assert.Null(messageHandler.Request.Headers.Authorization);
        }
Пример #8
0
        /// <inheritdoc/>
        public async Task <object> ExecuteAsync(Expression expression, CancellationToken cancellationToken = default(CancellationToken))
        {
            Argument.EnsureNotNull(nameof(expression), expression);

            var translation = ElasticQueryTranslator.Translate(Mapping, expression);

            Log.Debug(null, null, "Executing query against document '{0}'", translation.SearchRequest.DocumentType);

            try
            {
                ElasticResponse response;
                if (translation.SearchRequest.Filter == ConstantCriteria.False)
                {
                    response = new ElasticResponse();
                }
                else
                {
                    response = await requestProcessor.SearchAsync(translation.SearchRequest, cancellationToken);

                    if (response == null)
                    {
                        throw new InvalidOperationException("No HTTP response received.");
                    }
                }

                var result = translation.Materializer.Materialize(response);

                if (response.hits != null)
                {
                    var hits = response.hits.hits;
                    if (hits != null && hits.Capacity > 4096)
                    {
                        // If the response has a large number of hits then the List<T> instance might end up allocating an array on the large object heap.
                        // This means that the elements in that array can survive multiple rounds of garbage collection, even if they are not actually
                        // reachable anymore.
                        // Clearing out the collection after we materialized the result means the elements can be freed up.
                        hits.Clear();
                    }
                }

                return(result);
            }
            catch (AggregateException ex)
            {
                ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
                return(null);  // Never called, as the above code re-throws
            }
        }
Пример #9
0
        public static void NonSuccessfulHttpRequestThrows()
        {
            var messageHandler = new SpyMessageHandler();

            messageHandler.Response.StatusCode = HttpStatusCode.NotFound;
            var localConnection = new ElasticConnection(messageHandler, new Uri("http://localhost"), "myUser", "myPass");
            var processor       = new ElasticRequestProcessor(localConnection, mapping, log, retryPolicy);
            var request         = new ElasticSearchRequest {
                Type = "docType"
            };

            var ex = Record.Exception(() => processor.SearchAsync(request).GetAwaiter().GetResult());

            Assert.IsType <HttpRequestException>(ex);
            Assert.Equal("Response status code does not indicate success: 404 (Not Found).", ex.Message);
        }
        public static async void NonSuccessfulHttpRequestThrows()
        {
            var messageHandler = new SpyMessageHandler {
                Response = { StatusCode = HttpStatusCode.NotFound }
            };
            var localConnection = new ElasticConnection(messageHandler, new Uri("http://localhost"), "myUser", "myPass");
            var processor       = new ElasticRequestProcessor(localConnection, mapping, log, retryPolicy);
            var request         = new SearchRequest {
                DocumentType = "docType"
            };

            var ex = await Record.ExceptionAsync(() => processor.SearchAsync(request, CancellationToken.None));

            Assert.IsType <HttpRequestException>(ex);
            Assert.Equal("Response status code does not indicate success: 404 (Not Found).", ex.Message);
        }
Пример #11
0
        public static async Task ForcesBasicAuthorizationWhenProvidedWithUsernameAndPassword()
        {
            var messageHandler  = new SpyMessageHandler();
            var localConnection = new ElasticConnection(messageHandler, new Uri("http://localhost"), "myUser", "myPass");
            var processor       = new ElasticRequestProcessor(localConnection, mapping, log, retryPolicy);
            var request         = new ElasticSearchRequest {
                Type = "docType"
            };

            await processor.SearchAsync(request);

            var auth = messageHandler.Request.Headers.Authorization;

            Assert.NotNull(auth);
            Assert.Equal("Basic", auth.Scheme);
            Assert.Equal("myUser:myPass", Encoding.ASCII.GetString(Convert.FromBase64String(auth.Parameter)));
        }
        public static async void SearchAsyncCapturesRequestInfoOnFailure()
        {
            var spyLog           = new SpyLog();
            var brokenConnection = new ElasticConnection(new Uri("http://localhost:12"), index: "MyIndex");
            var processor        = new ElasticRequestProcessor(brokenConnection, mapping, spyLog, new RetryPolicy(spyLog, 100, 1));
            var searchRequest    = new SearchRequest {
                IndexType = "docType"
            };
            var formatter = new SearchRequestFormatter(brokenConnection, mapping, searchRequest);

            var ex = await Record.ExceptionAsync(() => processor.SearchAsync(searchRequest, CancellationToken.None));

            Assert.IsType <RetryFailedException>(ex);
            var retryLogEntry = Assert.Single(spyLog.Entries, s => s.AdditionalInfo.ContainsKey("category") && s.AdditionalInfo["category"].Equals("retry"));

            Assert.Equal("MyIndex", retryLogEntry.AdditionalInfo["index"]);
            Assert.Equal(brokenConnection.GetSearchUri(searchRequest), retryLogEntry.AdditionalInfo["uri"]);
            Assert.Equal(formatter.Body, retryLogEntry.AdditionalInfo["query"]);
        }
Пример #13
0
        public static async Task LogsDebugMessagesDuringExecution()
        {
            var responseString = BuildResponseString(2, 1, 1, 0.3141, "testIndex", "testType", "testId");
            var messageHandler = new SpyMessageHandler();
            var log            = new SpyLog();

            messageHandler.Response.Content = new StringContent(responseString);
            var localConnection = new ElasticConnection(messageHandler, new Uri("http://localhost"), "myUser", "myPass", index: "SearchIndex");
            var processor       = new ElasticRequestProcessor(localConnection, mapping, log, retryPolicy);
            var request         = new ElasticSearchRequest {
                Type = "abc123", Size = 2112
            };

            await processor.SearchAsync(request);

            Assert.Equal(4, log.Messages.Count);
            Assert.Equal(@"[VERBOSE] Request: POST http://localhost/SearchIndex/abc123/_search", log.Messages[0]);
            Assert.Equal(@"[VERBOSE] Body: {""size"":2112,""timeout"":""10s""}", log.Messages[1]);
            Assert.True(new Regex(@"\[VERBOSE\] Response: 200 OK \(in \d+ms\)").Match(log.Messages[2]).Success);
            Assert.True(new Regex(@"\[VERBOSE\] De-serialized \d+ bytes into 1 hits in \d+ms").Match(log.Messages[3]).Success);
        }
        public static async Task LogsDebugMessagesDuringExecution()
        {
            var responseString = BuildResponseString(2, 1, 1, 0.3141, "testIndex", "testType", "testId");
            var messageHandler = new SpyMessageHandler();
            var spyLog         = new SpyLog();

            messageHandler.Response.Content = new StringContent(responseString);
            var localConnection = new ElasticConnection(messageHandler, new Uri("http://localhost"), "myUser", "myPass", "SearchIndex");
            var processor       = new ElasticRequestProcessor(localConnection, mapping, spyLog, retryPolicy);
            var request         = new SearchRequest {
                DocumentType = "abc123", Size = 2112
            };

            await processor.SearchAsync(request, CancellationToken.None);

            Assert.Equal(4, spyLog.Entries.Count);
            Assert.Equal(@"Request: POST http://localhost/SearchIndex/abc123/_search", spyLog.Entries[0].Message);
            Assert.Equal(@"Body:" + '\n' + @"{""size"":2112,""timeout"":""10s""}", spyLog.Entries[1].Message);
            Assert.True(new Regex(@"Response: 200 OK \(in \d+ms\)").Match(spyLog.Entries[2].Message).Success);
            Assert.True(new Regex(@"Deserialized \d+ bytes into 1 hits in \d+ms").Match(spyLog.Entries[3].Message).Success);
        }