diff --git a/src/Microsoft.Health.Fhir.Shared.Api.UnitTests/Features/Formatters/HttpContextExtensionsTests.cs b/src/Microsoft.Health.Fhir.Shared.Api.UnitTests/Features/Formatters/HttpContextExtensionsTests.cs index addc0d3032..988cd679ff 100644 --- a/src/Microsoft.Health.Fhir.Shared.Api.UnitTests/Features/Formatters/HttpContextExtensionsTests.cs +++ b/src/Microsoft.Health.Fhir.Shared.Api.UnitTests/Features/Formatters/HttpContextExtensionsTests.cs @@ -21,7 +21,7 @@ public HttpContextExtensionsTests() } [Fact] - public void GivenARequestWithSummaryType_WhenSerializingTheResponse_ThenTheCorrectSummeryTypeIsApplied() + public void GivenARequestWithSummaryType_WhenSerializingTheResponse_ThenTheCorrectSummaryTypeIsApplied() { var context = new DefaultHttpContext(); context.Request.QueryString = QueryString.Create("_summary", "text"); @@ -32,7 +32,7 @@ public void GivenARequestWithSummaryType_WhenSerializingTheResponse_ThenTheCorre } [Fact] - public void GivenARequestWithCapsSummaryType_WhenSerializingTheResponse_ThenTheCorrectSummeryTypeIsApplied() + public void GivenARequestWithCapsSummaryType_WhenSerializingTheResponse_ThenTheCorrectSummaryTypeIsApplied() { var context = new DefaultHttpContext(); context.Request.QueryString = QueryString.Create("_SUMMARY", "DATA"); @@ -42,6 +42,17 @@ public void GivenARequestWithCapsSummaryType_WhenSerializingTheResponse_ThenTheC Assert.Equal(SummaryType.Data, summary); } + [Fact] + public void GivenARequestWithCountType_WhenSerializingTheResponse_ThenTheCorrectSummaryTypeIsApplied() + { + var context = new DefaultHttpContext(); + context.Request.QueryString = QueryString.Create("_count", "0"); + + var summary = context.GetSummaryTypeOrDefault(); + + Assert.Equal(SummaryType.Count, summary); + } + [Fact] public void GivenARequestWithUnknownSummaryType_WhenSerializingTheResponse_DefaultSummaryReturned() { diff --git a/src/Microsoft.Health.Fhir.Shared.Api/Features/Formatters/HttpContextExtensions.cs b/src/Microsoft.Health.Fhir.Shared.Api/Features/Formatters/HttpContextExtensions.cs index 23cbd1d2fa..4e3871f3aa 100644 --- a/src/Microsoft.Health.Fhir.Shared.Api/Features/Formatters/HttpContextExtensions.cs +++ b/src/Microsoft.Health.Fhir.Shared.Api/Features/Formatters/HttpContextExtensions.cs @@ -25,6 +25,16 @@ public static SummaryType GetSummaryTypeOrDefault(this HttpContext context) { return summary; } + else if (string.IsNullOrWhiteSpace(query)) + { + var result = context.Request.Query[KnownQueryParameterNames.Count].FirstOrDefault(); + + if (!string.IsNullOrWhiteSpace(result) && int.TryParse(result, out var count) && count == 0 && + (context.Response.StatusCode == (int)HttpStatusCode.OK || context.Response.StatusCode == (int)HttpStatusCode.Created)) + { + return Hl7.Fhir.Rest.SummaryType.Count; + } + } return SummaryType.False; } diff --git a/src/Microsoft.Health.Fhir.Shared.Core.UnitTests/Features/Search/SearchOptionsFactoryTests.cs b/src/Microsoft.Health.Fhir.Shared.Core.UnitTests/Features/Search/SearchOptionsFactoryTests.cs index 41dfd22282..8a8465ce6e 100644 --- a/src/Microsoft.Health.Fhir.Shared.Core.UnitTests/Features/Search/SearchOptionsFactoryTests.cs +++ b/src/Microsoft.Health.Fhir.Shared.Core.UnitTests/Features/Search/SearchOptionsFactoryTests.cs @@ -51,7 +51,7 @@ public partial class SearchOptionsFactoryTests public SearchOptionsFactoryTests() { var searchParameterDefinitionManager = Substitute.For(); - _resourceTypeSearchParameterInfo = new SearchParameter { Name = SearchParameterNames.ResourceType, Code = SearchParameterNames.ResourceType, Type = SearchParamType.String }.ToInfo(); + _resourceTypeSearchParameterInfo = new SearchParameter { Name = SearchParameterNames.ResourceType, Code = SearchParameterNames.ResourceType, Type = SearchParamType.String, Url = SearchParameterNames.ResourceTypeUri.AbsoluteUri }.ToInfo(); _lastUpdatedSearchParameterInfo = new SearchParameter { Name = SearchParameterNames.LastUpdated, Code = SearchParameterNames.LastUpdated, Type = SearchParamType.String }.ToInfo(); searchParameterDefinitionManager.GetSearchParameter(Arg.Any(), Arg.Any()).Throws(ci => new SearchParameterNotSupportedException(ci.ArgAt(0), ci.ArgAt(1))); searchParameterDefinitionManager.GetSearchParameter(Arg.Any(), SearchParameterNames.ResourceType).Returns(_resourceTypeSearchParameterInfo); @@ -110,6 +110,39 @@ public void GivenACount_WhenCreated_ThenCorrectMaxItemCountShouldBeSet() Assert.Equal(5, options.MaxItemCount); } + [Fact] + public void GivenACountWithValueZero_WhenCreated_ThenCorrectMaxItemCountShouldBeSet() + { + const ResourceType resourceType = ResourceType.Encounter; + var queryParameters = new[] + { + Tuple.Create("_count", "0"), + }; + + SearchOptions options = CreateSearchOptions( + resourceType: resourceType.ToString(), + queryParameters: queryParameters); + + Assert.NotNull(options); + Assert.True(options.CountOnly); + } + + [Theory] + [InlineData("a")] + [InlineData("1.1")] + public void GivenACountWithInvalidValue_WhenCreated_ThenExceptionShouldBeThrown(string value) + { + const ResourceType resourceType = ResourceType.Encounter; + var queryParameters = new[] + { + Tuple.Create("_count", value), + }; + + Assert.Throws(() => CreateSearchOptions( + resourceType: resourceType.ToString(), + queryParameters: queryParameters)); + } + [Fact] public void GivenNoneOfTheSearchParamIsSupported_WhenCreated_ThenCorrectExpressionShouldBeGenerated() { diff --git a/src/Microsoft.Health.Fhir.Shared.Core/Features/Search/SearchOptionsFactory.cs b/src/Microsoft.Health.Fhir.Shared.Core/Features/Search/SearchOptionsFactory.cs index ddd4e186d7..206f976c82 100644 --- a/src/Microsoft.Health.Fhir.Shared.Core/Features/Search/SearchOptionsFactory.cs +++ b/src/Microsoft.Health.Fhir.Shared.Core/Features/Search/SearchOptionsFactory.cs @@ -184,6 +184,17 @@ public SearchOptions Create( throw new BadRequestException(string.Format(Core.Resources.InvalidTotalParameter, query.Item2, SupportedTotalTypes)); } } + else if (query.Item1 == KnownQueryParameterNames.Count && Convert.ToInt32(query.Item2) == 0) + { + try + { + searchParams.Add(KnownQueryParameterNames.Summary, SummaryType.Count.ToString()); + } + catch (Exception ex) + { + throw new BadRequestException(ex.Message); + } + } else { // Parse the search parameters. diff --git a/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Persistence/SqlCustomQueryTests.cs b/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Persistence/SqlCustomQueryTests.cs index 54f692dae9..4ce17230dc 100644 --- a/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Persistence/SqlCustomQueryTests.cs +++ b/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Persistence/SqlCustomQueryTests.cs @@ -49,16 +49,13 @@ public async Task GivenASqlQuery_IfAStoredProcExistsWithMatchingHash_ThenStoredP // set the wait time to 1 second CustomQueries.WaitTime = 1; - // Create a query with a known hash var queryParameters = new[] { - Tuple.Create("participating-organization.identifier", $"TAB%7C910000290"), - Tuple.Create(SearchParameterNames.Include, $"OrganizationAffiliation:location"), - Tuple.Create("participating-organization.type", $"practice"), + Tuple.Create("_count", "5"), }; // Query before adding an sproc to the database - await _fixture.SearchService.SearchAsync(KnownResourceTypes.OrganizationAffiliation, queryParameters, CancellationToken.None); + await _fixture.SearchService.SearchAsync(KnownResourceTypes.Patient, queryParameters, CancellationToken.None); var hash = _fixture.SqlQueryHashCalculator.MostRecentSqlHash; @@ -75,7 +72,7 @@ public async Task GivenASqlQuery_IfAStoredProcExistsWithMatchingHash_ThenStoredP while (sw.Elapsed.TotalSeconds < 10) // previous single try after 1.1 sec delay was not reliable. { await Task.Delay(300); - await _fixture.SearchService.SearchAsync(KnownResourceTypes.OrganizationAffiliation, queryParameters, CancellationToken.None); + await _fixture.SearchService.SearchAsync(KnownResourceTypes.Patient, queryParameters, CancellationToken.None); Assert.Equal(hash, _fixture.SqlQueryHashCalculator.MostRecentSqlHash); if (await CheckIfSprocUsed(hash)) { @@ -120,7 +117,7 @@ private void AddSproc(string hash) { _fixture.SqlHelper.ExecuteSqlCmd( "CREATE OR ALTER PROCEDURE [dbo].[CustomQuery_" + hash + "]\r\n" + - "(@p0 SmallInt, @p1 SmallInt, @p2 SmallInt, @p3 SmallInt, @p4 NVarChar(256), @p5 SmallInt, @p6 VarChar(256), @p7 Int, @p8 SmallInt, @p9 Int)\r\n" + + "(@p0 SmallInt=1, @p1 SmallInt=1, @p2 SmallInt=1, @p3 SmallInt=1, @p4 NVarChar(256)='code', @p5 SmallInt=1, @p6 VarChar(256)='code', @p7 Int=1, @p8 SmallInt=1, @p9 Int=1)\r\n" + "AS\r\n" + "BEGIN\r\n" + "WITH cte0 AS\r\n(\r\n" +