Skip to content

Commit 53fb43b

Browse files
author
Robert Johnson
committed
Refactor to use different internal parameter
1 parent cee984a commit 53fb43b

25 files changed

+182
-53
lines changed

src/Microsoft.Health.Fhir.Api/Features/ActionResults/ResourceActionResult.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public override async Task ExecuteResultAsync(ActionContext context)
6565
}
6666
catch (ObjectDisposedException ode)
6767
{
68-
throw new ServiceUnavailableException(Resources.NotAbleToCreateTheFinalResultsOfAnOperation, ode);
68+
throw new ServiceUnavailableException(Api.Resources.NotAbleToCreateTheFinalResultsOfAnOperation, ode);
6969
}
7070

7171
HttpResponse response = context.HttpContext.Response;

src/Microsoft.Health.Fhir.Api/Features/ActionResults/TooManyRequestsActionResult.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public TooManyRequestsActionResult()
1717
new OperationOutcomeIssue(
1818
OperationOutcomeConstants.IssueSeverity.Error,
1919
OperationOutcomeConstants.IssueType.Throttled,
20-
Resources.TooManyConcurrentRequests),
20+
Api.Resources.TooManyConcurrentRequests),
2121
HttpStatusCode.TooManyRequests)
2222
{
2323
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// -------------------------------------------------------------------------------------------------
2+
// Copyright (c) Microsoft Corporation. All rights reserved.
3+
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
4+
// -------------------------------------------------------------------------------------------------
5+
6+
using System;
7+
using EnsureThat;
8+
using Microsoft.AspNetCore.Http;
9+
using Microsoft.AspNetCore.Mvc.Filters;
10+
using Microsoft.Health.Core.Features.Context;
11+
using Microsoft.Health.Fhir.Api.Features.Headers;
12+
using Microsoft.Health.Fhir.Core.Features.Context;
13+
using Microsoft.Health.Fhir.Core.Registration;
14+
15+
namespace Microsoft.Health.Fhir.Api.Features.Filters
16+
{
17+
/// <summary>
18+
/// Latency over efficiency filter.
19+
/// Adds to FHIR Request Context a flag to optimize query latency over efficiency.
20+
/// </summary>
21+
[AttributeUsage(AttributeTargets.Class)]
22+
public sealed class QueryCacheFilterAttribute : ActionFilterAttribute
23+
{
24+
private readonly RequestContextAccessor<IFhirRequestContext> _fhirRequestContextAccessor;
25+
private readonly IFhirRuntimeConfiguration _runtimeConfiguration;
26+
27+
public QueryCacheFilterAttribute(RequestContextAccessor<IFhirRequestContext> fhirRequestContextAccessor, IFhirRuntimeConfiguration runtimeConfiguration)
28+
{
29+
EnsureArg.IsNotNull(fhirRequestContextAccessor, nameof(fhirRequestContextAccessor));
30+
EnsureArg.IsNotNull(runtimeConfiguration, nameof(runtimeConfiguration));
31+
32+
_fhirRequestContextAccessor = fhirRequestContextAccessor;
33+
_runtimeConfiguration = runtimeConfiguration;
34+
}
35+
36+
public override void OnActionExecuting(ActionExecutingContext context)
37+
{
38+
EnsureArg.IsNotNull(context, nameof(context));
39+
40+
if (_runtimeConfiguration.IsQueryCacheSupported)
41+
{
42+
SetupConditionalRequestWithQueryCache(context.HttpContext, _fhirRequestContextAccessor.RequestContext);
43+
}
44+
45+
base.OnActionExecuting(context);
46+
}
47+
48+
private static void SetupConditionalRequestWithQueryCache(HttpContext context, IFhirRequestContext fhirRequestContext)
49+
{
50+
if (context?.Request?.Headers != null && fhirRequestContext != null)
51+
{
52+
string useQueryCache = context.GetQueryCache();
53+
54+
if (!string.IsNullOrEmpty(useQueryCache))
55+
{
56+
fhirRequestContext.DecorateRequestContextWithQueryCache(useQueryCache);
57+
}
58+
}
59+
}
60+
}
61+
}

src/Microsoft.Health.Fhir.Api/Features/Filters/ValidateAsyncRequestFilterAttribute.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public override void OnActionExecuting(ActionExecutingContext context)
2828
if (!context.HttpContext.Request.Headers.TryGetValue(KnownHeaders.Prefer, out var preferHeaderValue) ||
2929
!string.Equals(preferHeaderValue[0], PreferHeaderExpectedValue, StringComparison.OrdinalIgnoreCase))
3030
{
31-
throw new RequestNotValidException(string.Format(Resources.UnsupportedHeaderValue, preferHeaderValue.FirstOrDefault(), KnownHeaders.Prefer));
31+
throw new RequestNotValidException(string.Format(Api.Resources.UnsupportedHeaderValue, preferHeaderValue.FirstOrDefault(), KnownHeaders.Prefer));
3232
}
3333
}
3434
}

src/Microsoft.Health.Fhir.Api/Features/Filters/ValidateExportRequestFilterAttribute.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public override void OnActionExecuting(ActionExecutingContext context)
6565
acceptHeaderValue.Count != 1 ||
6666
!string.Equals(acceptHeaderValue[0], KnownContentTypes.JsonContentType, StringComparison.OrdinalIgnoreCase))
6767
{
68-
throw new RequestNotValidException(string.Format(Resources.UnsupportedHeaderValue, acceptHeaderValue.FirstOrDefault(), HeaderNames.Accept));
68+
throw new RequestNotValidException(string.Format(Api.Resources.UnsupportedHeaderValue, acceptHeaderValue.FirstOrDefault(), HeaderNames.Accept));
6969
}
7070

7171
if (context.HttpContext.Request.Headers.TryGetValue(PreferHeaderName, out var preferHeaderValues))
@@ -79,7 +79,7 @@ public override void OnActionExecuting(ActionExecutingContext context)
7979
|| (v.Length == 1 && !(requiredHeaderValueFound = string.Equals(v[0], PreferHeaderValueRequired, StringComparison.OrdinalIgnoreCase)))
8080
|| (v.Length == 2 && (!string.Equals(v[0], PreferHeaderValueOptional, StringComparison.OrdinalIgnoreCase) || !Enum.TryParse<SearchParameterHandling>(v[1], true, out _))))
8181
{
82-
throw new RequestNotValidException(string.Format(Resources.UnsupportedHeaderValue, value, PreferHeaderName));
82+
throw new RequestNotValidException(string.Format(Api.Resources.UnsupportedHeaderValue, value, PreferHeaderName));
8383
}
8484
}
8585

@@ -102,14 +102,14 @@ public override void OnActionExecuting(ActionExecutingContext context)
102102
continue;
103103
}
104104

105-
throw new RequestNotValidException(string.Format(Resources.UnsupportedParameter, paramName));
105+
throw new RequestNotValidException(string.Format(Api.Resources.UnsupportedParameter, paramName));
106106
}
107107

108108
if (queryCollection?.Keys != null &&
109109
queryCollection.Keys.Contains(KnownQueryParameterNames.TypeFilter) &&
110110
!queryCollection.Keys.Contains(KnownQueryParameterNames.Type))
111111
{
112-
throw new RequestNotValidException(Resources.TypeFilterWithoutTypeIsUnsupported);
112+
throw new RequestNotValidException(Api.Resources.TypeFilterWithoutTypeIsUnsupported);
113113
}
114114

115115
if (queryCollection.TryGetValue(KnownQueryParameterNames.OutputFormat, out var outputFormats))
@@ -118,7 +118,7 @@ public override void OnActionExecuting(ActionExecutingContext context)
118118
{
119119
if (!(outputFormat == null || SupportedOutputFormats.Contains(outputFormat)))
120120
{
121-
throw new RequestNotValidException(string.Format(Resources.InvalidOutputFormat, outputFormat));
121+
throw new RequestNotValidException(string.Format(Api.Resources.InvalidOutputFormat, outputFormat));
122122
}
123123
}
124124
}

src/Microsoft.Health.Fhir.Api/Features/Filters/ValidateFormatParametersAttribute.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,13 @@ public override async Task OnActionExecutionAsync(ActionExecutingContext context
5151
{
5252
if (!await _parametersValidator.IsFormatSupportedAsync(headerValue[0]))
5353
{
54-
throw new UnsupportedMediaTypeException(string.Format(Resources.UnsupportedHeaderValue, headerValue.FirstOrDefault(), HeaderNames.ContentType));
54+
throw new UnsupportedMediaTypeException(string.Format(Api.Resources.UnsupportedHeaderValue, headerValue.FirstOrDefault(), HeaderNames.ContentType));
5555
}
5656
}
5757
else
5858
{
5959
// If no content type is supplied, then the server should respond with an unsupported media type exception.
60-
throw new UnsupportedMediaTypeException(Resources.ContentTypeHeaderRequired);
60+
throw new UnsupportedMediaTypeException(Api.Resources.ContentTypeHeaderRequired);
6161
}
6262
}
6363
else if (httpContext.Request.Method.Equals(HttpMethod.Patch.Method, StringComparison.OrdinalIgnoreCase))
@@ -66,7 +66,7 @@ public override async Task OnActionExecutionAsync(ActionExecutingContext context
6666
{
6767
if (!await _parametersValidator.IsPatchFormatSupportedAsync(headerValue[0]))
6868
{
69-
throw new UnsupportedMediaTypeException(string.Format(Resources.UnsupportedHeaderValue, headerValue.FirstOrDefault(), HeaderNames.ContentType));
69+
throw new UnsupportedMediaTypeException(string.Format(Api.Resources.UnsupportedHeaderValue, headerValue.FirstOrDefault(), HeaderNames.ContentType));
7070
}
7171
}
7272
}

src/Microsoft.Health.Fhir.Api/Features/Filters/ValidateImportRequestFilterAttribute.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public override void OnActionExecuting(ActionExecutingContext context)
3535
preferHeaderValue.Count != 1 ||
3636
!string.Equals(preferHeaderValue[0], PreferHeaderExpectedValue, StringComparison.OrdinalIgnoreCase))
3737
{
38-
throw new RequestNotValidException(string.Format(Resources.UnsupportedHeaderValue, preferHeaderValue.FirstOrDefault(), PreferHeaderName));
38+
throw new RequestNotValidException(string.Format(Api.Resources.UnsupportedHeaderValue, preferHeaderValue.FirstOrDefault(), PreferHeaderName));
3939
}
4040

4141
if (string.Equals(context.HttpContext.Request.Method, "POST", StringComparison.OrdinalIgnoreCase))
@@ -44,7 +44,7 @@ public override void OnActionExecuting(ActionExecutingContext context)
4444
contentTypeHeaderValue.Count != 1 ||
4545
!contentTypeHeaderValue[0].Contains(ContentTypeHeaderExpectedValue, StringComparison.OrdinalIgnoreCase))
4646
{
47-
throw new RequestNotValidException(string.Format(Resources.UnsupportedHeaderValue, contentTypeHeaderValue.FirstOrDefault(), HeaderNames.ContentType));
47+
throw new RequestNotValidException(string.Format(Api.Resources.UnsupportedHeaderValue, contentTypeHeaderValue.FirstOrDefault(), HeaderNames.ContentType));
4848
}
4949
}
5050
}

src/Microsoft.Health.Fhir.Api/Features/Filters/ValidateParametersResourceAttribute.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,12 @@ public override void OnActionExecuting(ActionExecutingContext context)
2525
context.ActionArguments?.TryGetValue("inputParams", out inputResource);
2626
if (inputResource == null)
2727
{
28-
throw new RequestNotValidException(Resources.MissingInputParams);
28+
throw new RequestNotValidException(Api.Resources.MissingInputParams);
2929
}
3030

3131
if (inputResource is not Parameters)
3232
{
33-
throw new RequestNotValidException(string.Format(Resources.UnsupportedResourceType, inputResource.GetType().ToString()));
33+
throw new RequestNotValidException(string.Format(Api.Resources.UnsupportedResourceType, inputResource.GetType().ToString()));
3434
}
3535
}
3636
}

src/Microsoft.Health.Fhir.Api/Features/Filters/ValidateReindexRequestFilterAttribute.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public override void OnActionExecuting(ActionExecutingContext context)
2828
if (context.HttpContext.Request.Headers.TryGetValue(PreferHeaderName, out var preferHeaderValue) &&
2929
!string.Equals(preferHeaderValue[0], PreferHeaderExpectedValue, StringComparison.OrdinalIgnoreCase))
3030
{
31-
throw new RequestNotValidException(string.Format(Resources.UnsupportedHeaderValue, preferHeaderValue.FirstOrDefault(), PreferHeaderName));
31+
throw new RequestNotValidException(string.Format(Api.Resources.UnsupportedHeaderValue, preferHeaderValue.FirstOrDefault(), PreferHeaderName));
3232
}
3333
}
3434
}

src/Microsoft.Health.Fhir.Shared.Api/Features/Headers/HttpContextExtensions.cs renamed to src/Microsoft.Health.Fhir.Api/Features/Headers/HttpContextExtensions.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,21 @@ public static bool IsLatencyOverEfficiencyEnabled(this HttpContext outerHttpCont
4848
return defaultValue;
4949
}
5050

51+
/// <summary>
52+
/// Retrieves from the HTTP header information on using query caching.
53+
/// </summary>
54+
/// <param name="outerHttpContext">HTTP context</param>
55+
/// <returns>Query cache header value</returns>
56+
public static string GetQueryCache(this HttpContext outerHttpContext)
57+
{
58+
if (outerHttpContext != null && outerHttpContext.Request.Headers.TryGetValue(KnownHeaders.QueryCacheEnabled, out StringValues headerValues))
59+
{
60+
return headerValues.FirstOrDefault();
61+
}
62+
63+
return null;
64+
}
65+
5166
/// <summary>
5267
/// Retrieves from the HTTP header information about the conditional-query processing logic to be adopted.
5368
/// </summary>
@@ -110,5 +125,15 @@ public static bool DecorateRequestContextWithOptimizedConcurrency(this IFhirRequ
110125

111126
return requestContext.Properties.TryAdd(KnownQueryParameterNames.OptimizeConcurrency, true);
112127
}
128+
129+
public static bool DecorateRequestContextWithQueryCache(this IFhirRequestContext requestContext, string value)
130+
{
131+
if (requestContext == null)
132+
{
133+
return false;
134+
}
135+
136+
return requestContext.Properties.TryAdd(KnownQueryParameterNames.QueryCaching, value);
137+
}
113138
}
114139
}

src/Microsoft.Health.Fhir.Api/Features/Operations/Import/InitialImportLockMiddleware.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public sealed class InitialImportLockMiddleware
2626

2727
// hard-coding these to minimize resource consumption for locked message
2828
private const string LockedContentType = "application/json; charset=utf-8";
29-
private static readonly ReadOnlyMemory<byte> _lockedBody = CreateLockedBody(Resources.LockedForInitialImportMode);
29+
private static readonly ReadOnlyMemory<byte> _lockedBody = CreateLockedBody(Api.Resources.LockedForInitialImportMode);
3030

3131
public InitialImportLockMiddleware(
3232
RequestDelegate next,

src/Microsoft.Health.Fhir.Api/Features/Routing/SearchPostReroutingMiddleware.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public async Task Invoke(HttpContext context)
5050
{
5151
context.Response.Clear();
5252
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
53-
await context.Response.WriteAsync(Resources.ContentTypeFormUrlEncodedExpected);
53+
await context.Response.WriteAsync(Api.Resources.ContentTypeFormUrlEncodedExpected);
5454
return;
5555
}
5656
}

src/Microsoft.Health.Fhir.Api/Features/Routing/UrlResolver.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ public Uri ResolveOperationResultUrl(string operationName, string id)
260260
routeName = RouteNames.GetBulkDeleteStatusById;
261261
break;
262262
default:
263-
throw new OperationNotImplementedException(string.Format(Resources.OperationNotImplemented, operationName));
263+
throw new OperationNotImplementedException(string.Format(Api.Resources.OperationNotImplemented, operationName));
264264
}
265265

266266
var routeValues = new RouteValueDictionary()
@@ -317,7 +317,7 @@ public Uri ResolveOperationDefinitionUrl(string operationName)
317317
routeName = RouteNames.SearchParameterStatusOperationDefinition;
318318
break;
319319
default:
320-
throw new OperationNotImplementedException(string.Format(Resources.OperationNotImplemented, operationName));
320+
throw new OperationNotImplementedException(string.Format(Api.Resources.OperationNotImplemented, operationName));
321321
}
322322

323323
return GetRouteUri(

src/Microsoft.Health.Fhir.Api/Features/SMART/SmartClinicalScopesMiddleware.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,14 +152,14 @@ public async Task Invoke(
152152
{
153153
if (authorizationConfiguration.ErrorOnMissingFhirUserClaim)
154154
{
155-
throw new BadHttpRequestException(string.Format(Resources.FhirUserClaimMustBeURL, fhirUser));
155+
throw new BadHttpRequestException(string.Format(Api.Resources.FhirUserClaimMustBeURL, fhirUser));
156156
}
157157
}
158158
catch (ArgumentNullException)
159159
{
160160
if (authorizationConfiguration.ErrorOnMissingFhirUserClaim)
161161
{
162-
throw new BadHttpRequestException(Resources.FhirUserClaimCannotBeNull);
162+
throw new BadHttpRequestException(Api.Resources.FhirUserClaimCannotBeNull);
163163
}
164164
}
165165
}

src/Microsoft.Health.Fhir.Api/Features/Throttling/ThrottlingMiddleware.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public sealed class ThrottlingMiddleware : IAsyncDisposable, IDisposable
4040

4141
// hard-coding these to minimize resource consumption when throttling
4242
private const string ThrottledContentType = "application/json; charset=utf-8";
43-
private static readonly ReadOnlyMemory<byte> _throttledBody = CreateThrottledBody(Resources.TooManyConcurrentRequests);
43+
private static readonly ReadOnlyMemory<byte> _throttledBody = CreateThrottledBody(Api.Resources.TooManyConcurrentRequests);
4444

4545
private readonly RequestDelegate _next;
4646
private readonly ILogger<ThrottlingMiddleware> _logger;
@@ -284,7 +284,7 @@ private async Task Return429(HttpContext context)
284284
{
285285
Interlocked.Increment(ref _currentPeriodRejectedCount);
286286

287-
_logger.LogWarning(Resources.TooManyConcurrentRequests + " Limit is {Limit}. Requests in flight {Requests}", _concurrentRequestLimit, _requestsInFlight);
287+
_logger.LogWarning(Api.Resources.TooManyConcurrentRequests + " Limit is {Limit}. Requests in flight {Requests}", _concurrentRequestLimit, _requestsInFlight);
288288

289289
context.Response.StatusCode = StatusCodes.Status429TooManyRequests;
290290

src/Microsoft.Health.Fhir.Core/Features/KnownHeaders.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,7 @@ public static class KnownHeaders
3434

3535
// #conditionalQueryParallelism - Header used to activate parallel conditional-query processing.
3636
public const string ConditionalQueryProcessingLogic = "x-conditionalquery-processing-logic";
37+
38+
public const string QueryCacheEnabled = "x-ms-query-cache-enabled";
3739
}
3840
}

src/Microsoft.Health.Fhir.Core/Features/KnownQueryParameterNames.cs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,18 @@ public static class KnownQueryParameterNames
6161
public const string Container = "_container";
6262

6363
/// <summary>
64-
/// This settings is currently set by:
65-
/// x-ms-query-latency-over-efficiency - Gen1 and Gen2
66-
/// x-conditionalquery-processing-logic - Gen1 only
67-
/// In Gen1 it is used to hint that the request should run with a max parallel setting.
68-
/// In Gen2 it is used to tell the system to optimize for latency over efficiency by running two queries in parallel (with and without query caching).
64+
/// This setting is currently set by:
65+
/// x-ms-query-latency-over-efficiency
66+
/// x-conditionalquery-processing-logic
67+
/// It is used to hint that the request should run with a max parallel setting.
6968
/// </summary>
7069
public const string OptimizeConcurrency = "_optimizeConcurrency";
7170

71+
/// <summary>
72+
/// This setting is controlled by the x-ms-query-cache-enabled header. It controls whether to use the query cache or not.
73+
/// </summary>
74+
public const string QueryCaching = "_queryCaching";
75+
7276
/// <summary>
7377
/// The anonymization configuration
7478
/// </summary>

src/Microsoft.Health.Fhir.Core/Registration/AzureApiForFhirRuntimeConfiguration.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,7 @@ public class AzureApiForFhirRuntimeConfiguration : IFhirRuntimeConfiguration
2020
public bool IsTransactionSupported => false;
2121

2222
public bool IsLatencyOverEfficiencySupported => true;
23+
24+
public bool IsQueryCacheSupported => false;
2325
}
2426
}

src/Microsoft.Health.Fhir.Core/Registration/AzureHealthDataServicesRuntimeConfiguration.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ public class AzureHealthDataServicesRuntimeConfiguration : IFhirRuntimeConfigura
1919

2020
public bool IsTransactionSupported => true;
2121

22-
public bool IsLatencyOverEfficiencySupported => true;
22+
public bool IsLatencyOverEfficiencySupported => false;
23+
24+
public bool IsQueryCacheSupported => true;
2325
}
2426
}

src/Microsoft.Health.Fhir.Core/Registration/IFhirRuntimeConfiguration.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,10 @@ public interface IFhirRuntimeConfiguration
3333
/// Supports the 'latency-over-efficiency' HTTP header.
3434
/// </summary>
3535
bool IsLatencyOverEfficiencySupported { get; }
36+
37+
/// <summary>
38+
/// Supports the query cache HTTP header.
39+
/// </summary>
40+
bool IsQueryCacheSupported { get; }
3641
}
3742
}

0 commit comments

Comments
 (0)