Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,14 @@
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// -------------------------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using EnsureThat;
using Hl7.Fhir.ElementModel;
using Medino;
using Microsoft.Health.Core.Features.Context;
using Microsoft.Health.Fhir.Core.Exceptions;
using Microsoft.Health.Fhir.Core.Extensions;
using Microsoft.Health.Fhir.Core.Features.Context;
using Microsoft.Health.Fhir.Core.Features.Definition;
using Microsoft.Health.Fhir.Core.Features.Persistence;
using Microsoft.Health.Fhir.Core.Features.Search.Registry;
using Microsoft.Health.Fhir.Core.Features.Validation;
using Microsoft.Health.Fhir.Core.Messages.Delete;
using Microsoft.Health.Fhir.Core.Models;

Expand All @@ -27,34 +19,18 @@ namespace Microsoft.Health.Fhir.Core.Features.Search.Parameters
public class DeleteSearchParameterBehavior<TDeleteResourceRequest, TDeleteResourceResponse> : IPipelineBehavior<TDeleteResourceRequest, TDeleteResourceResponse>
where TDeleteResourceRequest : DeleteResourceRequest, IRequest<TDeleteResourceResponse>
{
private readonly ISearchParameterOperations _searchParameterOperations;
private readonly IFhirDataStore _fhirDataStore;
private readonly ISearchParameterDefinitionManager _searchParameterDefinitionManager;
private readonly ISearchParameterStatusManager _searchParameterStatusManager;
private readonly RequestContextAccessor<IFhirRequestContext> _requestContextAccessor;
private readonly IModelInfoProvider _modelInfoProvider;

public DeleteSearchParameterBehavior(
ISearchParameterOperations searchParameterOperations,
IFhirDataStore fhirDataStore,
ISearchParameterDefinitionManager searchParameterDefinitionManager,
ISearchParameterStatusManager searchParameterStatusManager,
RequestContextAccessor<IFhirRequestContext> requestContextAccessor,
IModelInfoProvider modelInfoProvider)
ISearchParameterDefinitionManager searchParameterDefinitionManager)
{
EnsureArg.IsNotNull(searchParameterOperations, nameof(searchParameterOperations));
EnsureArg.IsNotNull(fhirDataStore, nameof(fhirDataStore));
EnsureArg.IsNotNull(searchParameterDefinitionManager, nameof(searchParameterDefinitionManager));
EnsureArg.IsNotNull(searchParameterStatusManager, nameof(searchParameterStatusManager));
EnsureArg.IsNotNull(requestContextAccessor, nameof(requestContextAccessor));
EnsureArg.IsNotNull(modelInfoProvider, nameof(modelInfoProvider));

_searchParameterOperations = searchParameterOperations;
_fhirDataStore = fhirDataStore;
_searchParameterDefinitionManager = searchParameterDefinitionManager;
_searchParameterStatusManager = searchParameterStatusManager;
_requestContextAccessor = requestContextAccessor;
_modelInfoProvider = modelInfoProvider;
}

public async Task<TDeleteResourceResponse> HandleAsync(TDeleteResourceRequest request, RequestHandlerDelegate<TDeleteResourceResponse> next, CancellationToken cancellationToken)
Expand All @@ -76,54 +52,9 @@ public async Task<TDeleteResourceResponse> HandleAsync(TDeleteResourceRequest re
{
throw new ResourceNotFoundException(string.Format(Core.Resources.ResourceNotFoundById, deleteRequest.ResourceKey.ResourceType, deleteRequest.ResourceKey.Id));
}

// If the search parameter exists and is not already deleted, update status to pending delete
if (!searchParamResource.IsDeleted)
{
var typed = _modelInfoProvider.ToTypedElement(searchParamResource.RawResource);
await QueuePendingDeleteStatusAsync(typed.GetStringScalar("url"), deleteRequest.DeleteOperation, cancellationToken);
}

return await next();
}

return await next();
}

private async Task QueuePendingDeleteStatusAsync(string url, DeleteOperation deleteOperation, CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(url))
{
return;
}

var context = _requestContextAccessor.RequestContext;
if (context == null)
{
return;
}

var lastUpdated = _requestContextAccessor.RequestContext.GetSearchParameterLastUpdated();
if (!lastUpdated.HasValue)
{
await _searchParameterOperations.GetAndApplySearchParameterUpdates(cancellationToken);
lastUpdated = _searchParameterOperations.SearchParamLastUpdated;
}

_searchParameterDefinitionManager.TryGetSearchParameter(url, out var existing);

var status = deleteOperation == DeleteOperation.HardDelete ? SearchParameterStatus.PendingHardDelete : SearchParameterStatus.PendingDelete;

var update = new ResourceSearchParameterStatus
{
Uri = new Uri(url),
Status = status,
LastUpdated = lastUpdated.Value,
IsPartiallySupported = existing?.IsPartiallySupported ?? false,
SortStatus = existing?.SortStatus ?? SortParameterStatus.Disabled,
};

context.Properties[SearchParameterRequestContextPropertyNames.PendingStatus] = update;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ public interface ISearchParameterOperations
{
DateTimeOffset SearchParamLastUpdated { get; }

Task DeleteSearchParameterAsync(RawResource searchParamResource, CancellationToken cancellationToken, bool ignoreSearchParameterNotSupportedException = false, bool isHardDelete = false);
Task DeleteSearchParameterAsync(RawResource searchParamResource, CancellationToken cancellationToken, bool isHardDelete = false);

Task<DateTimeOffset> ValidateSearchParameterAsync(ITypedElement searchParam, CancellationToken cancellationToken, DateTimeOffset? lastUpdated = null);

Task UpdateSearchParameterStatusAsync(IReadOnlyCollection<string> searchParameterUris, SearchParameterStatus status, CancellationToken cancellationToken, bool ignoreSearchParameterNotSupportedException = false);
Task UpdateSearchParameterStatusAsync(IReadOnlyCollection<string> searchParameterUris, SearchParameterStatus status, CancellationToken cancellationToken);

/// <summary>
/// This method should be called to get any updates to search param cache
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,8 @@ public async Task<DateTimeOffset> ValidateSearchParameterAsync(ITypedElement sea
/// </summary>
/// <param name="searchParamResource">Search Parameter to update to Pending Delete status.</param>
/// <param name="cancellationToken">Cancellation Token</param>
/// <param name="ignoreSearchParameterNotSupportedException">The value indicating whether to ignore SearchParameterNotSupportedException.</param>
/// <param name="isHardDelete">True for hard delete (PendingHardDelete), false for soft delete (PendingDelete).</param>
public async Task DeleteSearchParameterAsync(RawResource searchParamResource, CancellationToken cancellationToken, bool ignoreSearchParameterNotSupportedException = false, bool isHardDelete = false)
public async Task DeleteSearchParameterAsync(RawResource searchParamResource, CancellationToken cancellationToken, bool isHardDelete = false)
{
var searchParam = _modelInfoProvider.ToTypedElement(searchParamResource);
var searchParameterUrl = searchParam.GetStringScalar("url");
Expand All @@ -181,7 +180,11 @@ public async Task DeleteSearchParameterAsync(RawResource searchParamResource, Ca
await GetAndApplySearchParameterUpdates(cancellationToken);
var status = isHardDelete ? SearchParameterStatus.PendingHardDelete : SearchParameterStatus.PendingDelete;
_logger.LogInformation("DeleteSearchParameterAsync: Deleting the search parameter '{Url}' with status {Status}", searchParameterUrl, status);
await _searchParameterStatusManager.UpdateSearchParameterStatusAsync(new[] { searchParameterUrl }, status, cancellationToken, lastUpdated: SearchParamLastUpdated);
await _searchParameterStatusManager.UpdateSearchParameterStatusAsync(
new[] { searchParameterUrl },
status,
cancellationToken,
lastUpdated: SearchParamLastUpdated);
}
catch (FhirException fex)
{
Expand All @@ -206,9 +209,9 @@ public async Task DeleteSearchParameterAsync(RawResource searchParamResource, Ca
}
}

public async Task UpdateSearchParameterStatusAsync(IReadOnlyCollection<string> searchParameterUris, SearchParameterStatus status, CancellationToken cancellationToken, bool ignoreSearchParameterNotSupportedException = false)
public async Task UpdateSearchParameterStatusAsync(IReadOnlyCollection<string> searchParameterUris, SearchParameterStatus status, CancellationToken cancellationToken)
{
await _searchParameterStatusManager.UpdateSearchParameterStatusAsync(searchParameterUris, status, cancellationToken, ignoreSearchParameterNotSupportedException);
await _searchParameterStatusManager.UpdateSearchParameterStatusAsync(searchParameterUris, status, cancellationToken);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public interface ISearchParameterStatusManager

Task HandleAsync(SearchParameterDefinitionManagerInitialized notification, CancellationToken cancellationToken);

Task UpdateSearchParameterStatusAsync(IReadOnlyCollection<string> searchParameterUris, SearchParameterStatus status, CancellationToken cancellationToken, bool ignoreSearchParameterNotSupportedException = false, long? reindexId = null, DateTimeOffset? lastUpdated = null);
Task UpdateSearchParameterStatusAsync(IReadOnlyCollection<string> searchParameterUris, SearchParameterStatus status, CancellationToken cancellationToken, long? reindexId = null, DateTimeOffset? lastUpdated = null);

Task<CacheConsistencyResult> CheckCacheConsistencyAsync(DateTime updateEventsSince, DateTime activeHostsSince, CancellationToken cancellationToken);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ public async Task HandleAsync(SearchParameterDefinitionManagerInitialized notifi
await EnsureInitializedAsync(cancellationToken);
}

public async Task UpdateSearchParameterStatusAsync(IReadOnlyCollection<string> searchParameterUris, SearchParameterStatus status, CancellationToken cancellationToken, bool ignoreSearchParameterNotSupportedException = false, long? reindexId = null, DateTimeOffset? lastUpdated = null)
public async Task UpdateSearchParameterStatusAsync(IReadOnlyCollection<string> searchParameterUris, SearchParameterStatus status, CancellationToken cancellationToken, long? reindexId = null, DateTimeOffset? lastUpdated = null)
{
EnsureArg.IsNotNull(searchParameterUris);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -308,22 +308,6 @@ private async Task<UpsertOutcome> InternalUpsertAsync(
{
EnsureArg.IsNotNull(resource, nameof(resource));

// Skip write for SearchParameter in pending delete - just return existing
if (resource.ResourceTypeName == KnownResourceTypes.SearchParameter && resource.IsDeleted)
{
var pending = GetPendingSearchParameterStatus();
if (pending?.Status == SearchParameterStatus.PendingDelete || pending?.Status == SearchParameterStatus.PendingHardDelete)
{
var existing = await GetAsync(new ResourceKey(resource.ResourceTypeName, resource.ResourceId), cancellationToken);
if (existing == null)
{
throw new ResourceNotFoundException(string.Format(Fhir.Core.Resources.ResourceNotFoundById, resource.ResourceTypeName, resource.ResourceId));
}

return new UpsertOutcome(existing, SaveOutcomeType.Updated);
}
}

var cosmosWrapper = new FhirCosmosResourceWrapper(resource);
UpdateSortIndex(cosmosWrapper);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -987,7 +987,6 @@ await _searchParameterStatusManager.Received().UpdateSearchParameterStatusAsync(
Arg.Is<IReadOnlyCollection<string>>(l => l.Contains(searchParam.Url.ToString())),
SearchParameterStatus.Disabled,
Arg.Any<CancellationToken>(),
Arg.Any<bool>(),
Arg.Any<long?>(),
Arg.Any<DateTimeOffset?>());
}
Expand Down Expand Up @@ -1038,7 +1037,6 @@ await _searchParameterStatusManager.Received(1).UpdateSearchParameterStatusAsync
urls.Contains(orphanCustom.Url.OriginalString)),
SearchParameterStatus.Deleted,
Arg.Any<CancellationToken>(),
Arg.Any<bool>(),
Arg.Is<long?>(id => id == jobInfo.Id),
Arg.Any<DateTimeOffset?>());
}
Expand Down Expand Up @@ -1088,7 +1086,6 @@ await _searchParameterStatusManager.DidNotReceive().UpdateSearchParameterStatusA
Arg.Any<IReadOnlyCollection<string>>(),
SearchParameterStatus.Deleted,
Arg.Any<CancellationToken>(),
Arg.Any<bool>(),
Arg.Is<long?>(id => id == jobInfo.Id),
Arg.Any<DateTimeOffset?>());
}
Expand Down Expand Up @@ -1125,7 +1122,6 @@ await _searchParameterStatusManager.DidNotReceive().UpdateSearchParameterStatusA
Arg.Any<IReadOnlyCollection<string>>(),
SearchParameterStatus.Deleted,
Arg.Any<CancellationToken>(),
Arg.Any<bool>(),
Arg.Is<long?>(id => id == jobInfo.Id),
Arg.Any<DateTimeOffset?>());
}
Expand Down Expand Up @@ -1177,7 +1173,6 @@ await _searchParameterStatusManager.Received().UpdateSearchParameterStatusAsync(
Arg.Is<List<string>>(l => l.Contains(searchParam.Url.ToString())),
SearchParameterStatus.Deleted,
Arg.Any<CancellationToken>(),
Arg.Any<bool>(),
Arg.Any<long?>());
}

Expand Down Expand Up @@ -1828,7 +1823,6 @@ await _searchParameterStatusManager.Received().UpdateSearchParameterStatusAsync(
l.Contains(patientBirthdateParam.Url.ToString())),
SearchParameterStatus.Enabled,
Arg.Any<CancellationToken>(),
false,
Arg.Is<long?>(id => id == jobInfo.Id));

receivedCall = true;
Expand Down Expand Up @@ -2064,7 +2058,6 @@ await _searchParameterStatusManager.Received().UpdateSearchParameterStatusAsync(
Arg.Any<IReadOnlyCollection<string>>(),
SearchParameterStatus.Enabled,
Arg.Any<CancellationToken>(),
false,
Arg.Is<long?>(id => id == jobInfo.Id));
}

Expand Down Expand Up @@ -2233,13 +2226,11 @@ await _searchParameterStatusManager.Received().UpdateSearchParameterStatusAsync(
Arg.Is<IReadOnlyCollection<string>>(l => l.Contains(searchParamLowercase.Url.ToString())),
SearchParameterStatus.Enabled,
Arg.Any<CancellationToken>(),
false,
Arg.Is<long?>(id => id == jobInfo.Id));
await _searchParameterStatusManager.Received().UpdateSearchParameterStatusAsync(
Arg.Is<IReadOnlyCollection<string>>(l => l.Contains(searchParamMixedCase.Url.ToString())),
SearchParameterStatus.Deleted,
Arg.Any<CancellationToken>(),
false,
Arg.Is<long?>(id => id == jobInfo.Id));
}

Expand Down Expand Up @@ -2314,14 +2305,12 @@ await _searchParameterStatusManager.Received().UpdateSearchParameterStatusAsync(
Arg.Is<IReadOnlyCollection<string>>(l => l.Contains(searchParam1.Url.ToString())),
SearchParameterStatus.Disabled,
Arg.Any<CancellationToken>(),
false,
Arg.Is<long?>(id => id == jobInfo.Id));

await _searchParameterStatusManager.Received().UpdateSearchParameterStatusAsync(
Arg.Is<IReadOnlyCollection<string>>(l => l.Contains(searchParam2.Url.ToString())),
SearchParameterStatus.Disabled,
Arg.Any<CancellationToken>(),
false,
Arg.Is<long?>(id => id == jobInfo.Id));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ public SearchParameterStateUpdateHandlerTests()
_cancellationToken = CancellationToken.None;

_authorizationService.CheckAccess(DataActions.SearchParameter, _cancellationToken).Returns(DataActions.SearchParameter);
_searchParameterOperations.UpdateSearchParameterStatusAsync(Arg.Any<IReadOnlyCollection<string>>(), Arg.Any<SearchParameterStatus>(), Arg.Any<CancellationToken>(), Arg.Any<bool>()).Returns(Task.CompletedTask);
_searchParameterOperations.UpdateSearchParameterStatusAsync(Arg.Any<IReadOnlyCollection<string>>(), Arg.Any<SearchParameterStatus>(), Arg.Any<CancellationToken>()).Returns(Task.CompletedTask);

var searchParamDefinitionStore = new List<SearchParameterInfo>
{
Expand Down Expand Up @@ -239,7 +239,7 @@ public async Task GivenARequestToUpdateSearchParameterStatus_WhenTheStatusIsEnab
var statusPart = resourceResponse.Parameter[0].Part.Where(p => p.Name == SearchParameterStateProperties.Status).First();
Assert.True(urlPart.Value.ToString() == ResourceId);
Assert.True(statusPart.Value.ToString() == SearchParameterStatus.Supported.ToString());
await _searchParameterOperations.Received(1).UpdateSearchParameterStatusAsync(Arg.Is<IReadOnlyCollection<string>>(x => x.Count == 1 && x.First() == ResourceId), SearchParameterStatus.Supported, Arg.Any<CancellationToken>(), false);
await _searchParameterOperations.Received(1).UpdateSearchParameterStatusAsync(Arg.Is<IReadOnlyCollection<string>>(x => x.Count == 1 && x.First() == ResourceId), SearchParameterStatus.Supported, Arg.Any<CancellationToken>());
}

[Fact]
Expand Down Expand Up @@ -303,7 +303,7 @@ public async Task GivenARequestToUpdateSearchParameterStatus_WhenStatusIsDisable
var statusPart = resourceResponse.Parameter[0].Part.Where(p => p.Name == SearchParameterStateProperties.Status).First();
Assert.True(urlPart.Value.ToString() == ResourceId);
Assert.True(statusPart.Value.ToString() == SearchParameterStatus.PendingDisable.ToString());
await _searchParameterOperations.Received(1).UpdateSearchParameterStatusAsync(Arg.Is<IReadOnlyCollection<string>>(x => x.Count == 1 && x.First() == ResourceId), SearchParameterStatus.PendingDisable, Arg.Any<CancellationToken>(), false);
await _searchParameterOperations.Received(1).UpdateSearchParameterStatusAsync(Arg.Is<IReadOnlyCollection<string>>(x => x.Count == 1 && x.First() == ResourceId), SearchParameterStatus.PendingDisable, Arg.Any<CancellationToken>());
}

[Fact]
Expand Down
Loading
Loading