Skip to content

Commit

Permalink
Added in check for reindex job in progress. Added unit test to verify. (
Browse files Browse the repository at this point in the history
  • Loading branch information
PTaladay authored Feb 7, 2025
1 parent f3ce53c commit f52ab01
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 7 deletions.
9 changes: 9 additions & 0 deletions src/Microsoft.Health.Fhir.Core/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/Microsoft.Health.Fhir.Core/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -757,4 +757,7 @@
<data name="OperationFailedForCustomerManagedKey" xml:space="preserve">
<value>Error occurred during an operation that is dependent on the customer-managed key. Use https://go.microsoft.com/fwlink/?linkid=2300268 to troubleshoot the issue.</value>
</data>
<data name="ReindexRunning" xml:space="preserve">
<value>A Reindex job is currenlty active. Search parameters cannot be updated until Reindex is complete.</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Microsoft.Health.Core.Features.Security.Authorization;
using Microsoft.Health.Extensions.DependencyInjection;
using Microsoft.Health.Fhir.Core.Configs;
using Microsoft.Health.Fhir.Core.Extensions;
using Microsoft.Health.Fhir.Core.Features.Audit;
using Microsoft.Health.Fhir.Core.Features.Definition;
using Microsoft.Health.Fhir.Core.Features.Operations;
using Microsoft.Health.Fhir.Core.Features.Operations.SearchParameterState;
using Microsoft.Health.Fhir.Core.Features.Search;
using Microsoft.Health.Fhir.Core.Features.Search.Parameters;
Expand Down Expand Up @@ -60,13 +62,17 @@ public class SearchParameterStateUpdateHandlerTests
private readonly ILogger<SearchParameterStatusManager> _logger = Substitute.For<ILogger<SearchParameterStatusManager>>();
private readonly ILogger<SearchParameterStateUpdateHandler> _logger2 = Substitute.For<ILogger<SearchParameterStateUpdateHandler>>();
private readonly IAuditLogger _auditLogger = Substitute.For<IAuditLogger>();
private readonly Func<IScoped<IFhirOperationDataStore>> _fhirOperationDataStoreFactory;
private readonly IFhirOperationDataStore _fhirOperationDataStore = Substitute.For<IFhirOperationDataStore>();

public SearchParameterStateUpdateHandlerTests()
{
_searchParameterDefinitionManager = Substitute.For<SearchParameterDefinitionManager>(ModelInfoProvider.Instance, _mediator, _searchService.CreateMockScopeProvider(), NullLogger<SearchParameterDefinitionManager>.Instance);
_fhirOperationDataStore.CheckActiveReindexJobsAsync(CancellationToken.None).Returns((false, string.Empty));
_fhirOperationDataStoreFactory = () => _fhirOperationDataStore.CreateMockScope();

_searchParameterStatusManager = new SearchParameterStatusManager(_searchParameterStatusDataStore, _searchParameterDefinitionManager, _searchParameterSupportResolver, _mediator, _logger);
_searchParameterStateUpdateHandler = new SearchParameterStateUpdateHandler(_authorizationService, _searchParameterStatusManager, _logger2, _auditLogger);
_searchParameterStateUpdateHandler = new SearchParameterStateUpdateHandler(_authorizationService, _searchParameterStatusManager, _logger2, _auditLogger, _fhirOperationDataStoreFactory);
_cancellationToken = CancellationToken.None;

_authorizationService.CheckAccess(DataActions.SearchParameter, _cancellationToken).Returns(DataActions.SearchParameter);
Expand Down Expand Up @@ -269,7 +275,7 @@ public async Task GivenARequestToUpdateSearchParameterStatus_WhenRequestIsValied
await _searchParameterDefinitionManager.EnsureInitializedAsync(CancellationToken.None);

var loggers = CreateTestAuditLogger();
var searchParameterStateUpdateHandler = new SearchParameterStateUpdateHandler(_authorizationService, _searchParameterStatusManager, _logger2, loggers.auditLogger);
var searchParameterStateUpdateHandler = new SearchParameterStateUpdateHandler(_authorizationService, _searchParameterStatusManager, _logger2, loggers.auditLogger, _fhirOperationDataStoreFactory);
List<Tuple<Uri, SearchParameterStatus>> updates = new List<Tuple<Uri, SearchParameterStatus>>()
{
new Tuple<Uri, SearchParameterStatus>(new Uri(ResourceId), SearchParameterStatus.Disabled),
Expand All @@ -291,6 +297,32 @@ public async Task GivenARequestToUpdateSearchParameterStatus_WhenRequestIsValied
Assert.Contains("Status=PendingDisable", loggers.logger.LogRecords[0].State.ToString());
}

[Fact]
public async Task GivenARequestToUpdateSearchParameterStatus_WhenAReindexJobIsRunning_ThenAnOperationOutcomeIsReturnedIndicatingUpdatesAreNotAllowed()
{
List<Tuple<Uri, SearchParameterStatus>> updates = new List<Tuple<Uri, SearchParameterStatus>>()
{
new Tuple<Uri, SearchParameterStatus>(new Uri(NotFoundResource), SearchParameterStatus.Supported),
};

IFhirOperationDataStore fhirOperationDataStore = Substitute.For<IFhirOperationDataStore>();
fhirOperationDataStore.CheckActiveReindexJobsAsync(CancellationToken.None).Returns((true, string.Empty));
Func<IScoped<IFhirOperationDataStore>> fhirOperationDataStoreFactory = () => fhirOperationDataStore.CreateMockScope();

SearchParameterStateUpdateHandler searchParameterStateUpdateHandler = new SearchParameterStateUpdateHandler(_authorizationService, _searchParameterStatusManager, _logger2, _auditLogger, fhirOperationDataStoreFactory);
SearchParameterStateUpdateResponse response = await searchParameterStateUpdateHandler.Handle(new SearchParameterStateUpdateRequest(updates), default);

Assert.NotNull(response);
Assert.NotNull(response.UpdateStatus);

var unwrappedResponse = response.UpdateStatus.ToPoco<Hl7.Fhir.Model.Bundle>();
var resourceResponse = (OperationOutcome)unwrappedResponse.Entry[0].Resource;
var issue = resourceResponse.Issue[0];
Assert.True(issue.Details.Text == Fhir.Core.Resources.ReindexRunning);
Assert.True(issue.Severity == OperationOutcome.IssueSeverity.Error);
Assert.True(issue.Code == OperationOutcome.IssueType.Conflict);
}

private (IAuditLogger auditLogger, TestLogger logger) CreateTestAuditLogger()
{
IOptions<SecurityConfiguration> optionsConfig = Substitute.For<IOptions<SecurityConfiguration>>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@
using Microsoft.Health.Core;
using Microsoft.Health.Core.Features.Audit;
using Microsoft.Health.Core.Features.Security.Authorization;
using Microsoft.Health.Extensions.DependencyInjection;
using Microsoft.Health.Fhir.Core.Exceptions;
using Microsoft.Health.Fhir.Core.Extensions;
using Microsoft.Health.Fhir.Core.Features.Audit;
using Microsoft.Health.Fhir.Core.Features.Search.Parameters;
using Microsoft.Health.Fhir.Core.Features.Search.Registry;
using Microsoft.Health.Fhir.Core.Features.Security;
using Microsoft.Health.Fhir.Core.Messages.SearchParameterState;
Expand All @@ -34,18 +36,21 @@ public class SearchParameterStateUpdateHandler : IRequestHandler<SearchParameter
private IReadOnlyCollection<ResourceSearchParameterStatus> _resourceSearchParameterStatus = null;
private readonly ILogger<SearchParameterStateUpdateHandler> _logger;
private readonly IAuditLogger _auditLogger;
private readonly Func<IScoped<IFhirOperationDataStore>> _fhirOperationDataStoreFactory;

public SearchParameterStateUpdateHandler(IAuthorizationService<DataActions> authorizationService, SearchParameterStatusManager searchParameterStatusManager, ILogger<SearchParameterStateUpdateHandler> logger, IAuditLogger auditLogger)
public SearchParameterStateUpdateHandler(IAuthorizationService<DataActions> authorizationService, SearchParameterStatusManager searchParameterStatusManager, ILogger<SearchParameterStateUpdateHandler> logger, IAuditLogger auditLogger, Func<IScoped<IFhirOperationDataStore>> fhirOperationDataStoreFactory)
{
EnsureArg.IsNotNull(authorizationService, nameof(authorizationService));
EnsureArg.IsNotNull(searchParameterStatusManager, nameof(searchParameterStatusManager));
EnsureArg.IsNotNull(logger, nameof(logger));
EnsureArg.IsNotNull(auditLogger, nameof(auditLogger));
EnsureArg.IsNotNull(fhirOperationDataStoreFactory, nameof(fhirOperationDataStoreFactory));

_authorizationService = authorizationService;
_searchParameterStatusManager = searchParameterStatusManager;
_logger = logger;
_auditLogger = auditLogger;
_fhirOperationDataStoreFactory = fhirOperationDataStoreFactory;
}

public async Task<SearchParameterStateUpdateResponse> Handle(SearchParameterStateUpdateRequest request, CancellationToken cancellationToken)
Expand All @@ -59,6 +64,12 @@ public async Task<SearchParameterStateUpdateResponse> Handle(SearchParameterStat

_resourceSearchParameterStatus = await _searchParameterStatusManager.GetAllSearchParameterStatus(cancellationToken);
Dictionary<SearchParameterStatus, List<string>> searchParametersToUpdate = ParseRequestForUpdate(request, out List<OperationOutcomeIssue> invalidSearchParameters);

if (await IsReindexRunning(cancellationToken))
{
return CreateBundleResponse(new Dictionary<SearchParameterStatus, List<string>>() { }, new List<OperationOutcomeIssue>() { }, true);
}

foreach (var statusGroup in searchParametersToUpdate)
{
await _searchParameterStatusManager.UpdateSearchParameterStatusAsync(statusGroup.Value, statusGroup.Key, cancellationToken);
Expand Down Expand Up @@ -115,7 +126,7 @@ private Dictionary<SearchParameterStatus, List<string>> ParseRequestForUpdate(Se
return searchParametersToUpdate;
}

private SearchParameterStateUpdateResponse CreateBundleResponse(Dictionary<SearchParameterStatus, List<string>> searchParametersToUpdate, List<OperationOutcomeIssue> invalidSearchParameters)
private SearchParameterStateUpdateResponse CreateBundleResponse(Dictionary<SearchParameterStatus, List<string>> searchParametersToUpdate, List<OperationOutcomeIssue> invalidSearchParameters, bool isReindexRunning = false)
{
// Create the bundle from the result.
var bundle = new Bundle();
Expand Down Expand Up @@ -182,10 +193,33 @@ private SearchParameterStateUpdateResponse CreateBundleResponse(Dictionary<Searc
}

bundle.Entry.Add(
new Bundle.EntryComponent
new Bundle.EntryComponent
{
Resource = succeededResults,
});
}

if (isReindexRunning)
{
bundle.Entry.Add(
new Bundle.EntryComponent
{
Resource = new OperationOutcome
{
Resource = succeededResults,
});
Issue = new List<OperationOutcome.IssueComponent>
{
new OperationOutcome.IssueComponent
{
Severity = OperationOutcome.IssueSeverity.Error,
Code = OperationOutcome.IssueType.Conflict,
Details = new CodeableConcept
{
Text = Core.Resources.ReindexRunning,
},
},
},
},
});
}

bundle.Type = Bundle.BundleType.BatchResponse;
Expand All @@ -197,5 +231,20 @@ private SearchParameterStateUpdateResponse CreateBundleResponse(Dictionary<Searc

return new SearchParameterStateUpdateResponse(bundle.ToResourceElement());
}

private async Task<bool> IsReindexRunning(CancellationToken cancellationToken)
{
// check if reindex job is running
using (IScoped<IFhirOperationDataStore> fhirOperationDataStore = _fhirOperationDataStoreFactory())
{
(var activeReindexJobs, var reindexJobId) = await fhirOperationDataStore.Value.CheckActiveReindexJobsAsync(cancellationToken);
if (activeReindexJobs)
{
return true;
}
}

return false;
}
}
}

0 comments on commit f52ab01

Please sign in to comment.