From bb1ed14a4a005596e8d4483a6277d299e45c7f8b Mon Sep 17 00:00:00 2001 From: Sergey Galuzo Date: Mon, 31 Aug 2026 16:35:34 -0700 Subject: [PATCH 1/4] Fix delete by url --- .../Resources/Delete/DeletionService.cs | 26 ++++++-- .../Rest/Reindex/ReindexTests.cs | 66 +++++++++++++++++++ 2 files changed, 86 insertions(+), 6 deletions(-) diff --git a/src/Microsoft.Health.Fhir.Shared.Core/Features/Resources/Delete/DeletionService.cs b/src/Microsoft.Health.Fhir.Shared.Core/Features/Resources/Delete/DeletionService.cs index a3b527a1f8..879d701d05 100644 --- a/src/Microsoft.Health.Fhir.Shared.Core/Features/Resources/Delete/DeletionService.cs +++ b/src/Microsoft.Health.Fhir.Shared.Core/Features/Resources/Delete/DeletionService.cs @@ -109,6 +109,16 @@ public async Task DeleteAsync(DeleteResourceRequest request, Cancel switch (request.DeleteOperation) { case DeleteOperation.SoftDelete: + if (key.ResourceType == KnownResourceTypes.SearchParameter + //// we should skip direct status updates when context does not contain pending + && (_contextAccessor.RequestContext == null + || _contextAccessor.RequestContext.Properties == null + || !_contextAccessor.RequestContext.Properties.ContainsKey(SearchParameterRequestContextPropertyNames.PendingStatus))) + { + await DeleteSearchParameter(fhirDataStore, key, false, cancellationToken); + break; + } + ResourceWrapper deletedWrapper = CreateSoftDeletedWrapper(key.ResourceType, request.ResourceKey.Id); bool keepHistory = await _conformanceProvider.Value.CanKeepHistory(key.ResourceType, cancellationToken); @@ -120,12 +130,7 @@ public async Task DeleteAsync(DeleteResourceRequest request, Cancel case DeleteOperation.HardDelete: if (key.ResourceType == KnownResourceTypes.SearchParameter) { - var resourceWrapper = await fhirDataStore.GetAsync(key, cancellationToken); - if (resourceWrapper != null && !resourceWrapper.IsDeleted) - { - await _retryPolicy.ExecuteAsync(async () => await _searchParameterOperations.DeleteSearchParameterAsync(resourceWrapper.RawResource, cancellationToken, ignoreSearchParameterNotSupportedException: true, isHardDelete: true)); - } - + await DeleteSearchParameter(fhirDataStore, key, true, cancellationToken); break; } @@ -141,6 +146,15 @@ public async Task DeleteAsync(DeleteResourceRequest request, Cancel return new ResourceKey(key.ResourceType, key.Id, version); } + private async Task DeleteSearchParameter(IFhirDataStore dataStore, ResourceKey key, bool isHardDelete, CancellationToken cancellationToken) + { + var resourceWrapper = await dataStore.GetAsync(key, cancellationToken); + if (resourceWrapper != null && !resourceWrapper.IsDeleted) + { + await _retryPolicy.ExecuteAsync(async () => await _searchParameterOperations.DeleteSearchParameterAsync(resourceWrapper.RawResource, cancellationToken, ignoreSearchParameterNotSupportedException: true, isHardDelete: isHardDelete)); + } + } + public async Task> DeleteMultipleAsync(ConditionalDeleteResourceRequest request, CancellationToken cancellationToken, IList excludedResourceTypes = null) { return await DeleteMultipleAsyncInternal(request, MaxParallelThreads, excludedResourceTypes, null, cancellationToken); diff --git a/test/Microsoft.Health.Fhir.Shared.Tests.E2E/Rest/Reindex/ReindexTests.cs b/test/Microsoft.Health.Fhir.Shared.Tests.E2E/Rest/Reindex/ReindexTests.cs index ff5fc66c8c..7acd5b53e4 100644 --- a/test/Microsoft.Health.Fhir.Shared.Tests.E2E/Rest/Reindex/ReindexTests.cs +++ b/test/Microsoft.Health.Fhir.Shared.Tests.E2E/Rest/Reindex/ReindexTests.cs @@ -1100,6 +1100,72 @@ public async Task GivenSearchParamDelete_ThenConflictWhenReindexAndSuccessOnNext Assert.Equal(hardDelete ? HttpStatusCode.NotFound : HttpStatusCode.Gone, notFoundEx.StatusCode); } + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task GivenSearchParamConditionalDeleteByUrl_ThenSuccessAndDeletedAfterReindex(bool hardDelete) + { + var code = $"conditional-delete-by-url-{(hardDelete ? "hard" : "soft")}"; + var searchParam = CreatePersonSearchParam(code, $"http://reindex/{code}"); + var create = await _fixture.TestFhirClient.UpdateAsync(searchParam); + Assert.True(create.StatusCode == HttpStatusCode.OK || create.StatusCode == HttpStatusCode.Created); + Assert.Equal(code, create.Resource.Id); + + var delete = await _fixture.TestFhirClient.DeleteAsync($"SearchParameter?url={searchParam.Url}&hardDelete={hardDelete}"); + Assert.Equal(HttpStatusCode.NoContent, delete.StatusCode); + + var resource = await _fixture.TestFhirClient.ReadAsync($"SearchParameter/{code}"); + Assert.NotNull(resource?.Resource); + + var reindex = await _fixture.TestFhirClient.PostReindexJobAsync(new Parameters { Parameter = [] }); + Assert.Equal(HttpStatusCode.Created, reindex.reponse.Response.StatusCode); + var reindexStatus = await WaitForJobCompletionAsync(reindex.uri, TimeSpan.FromSeconds(300)); + Assert.Equal(OperationStatus.Completed, reindexStatus.Status); + + var notFoundEx = await Assert.ThrowsAsync(async () => await _fixture.TestFhirClient.ReadAsync($"SearchParameter/{code}")); + Assert.Equal(hardDelete ? HttpStatusCode.NotFound : HttpStatusCode.Gone, notFoundEx.StatusCode); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task GivenSearchParamConditionalDeleteByMultipleUrlsWithCount_ThenSuccessAndDeletedAfterReindex(bool hardDelete) + { + var code1 = $"conditional-delete-by-url-1-{(hardDelete ? "hard" : "soft")}"; + var searchParam1 = CreatePersonSearchParam(code1, $"http://reindex/{code1}"); + var create1 = await _fixture.TestFhirClient.UpdateAsync(searchParam1); + Assert.True(create1.StatusCode == HttpStatusCode.OK || create1.StatusCode == HttpStatusCode.Created); + Assert.Equal(code1, create1.Resource.Id); + + var code2 = $"conditional-delete-by-url-2-{(hardDelete ? "hard" : "soft")}"; + var searchParam2 = CreatePersonSearchParam(code2, $"http://reindex/{code2}"); + var create2 = await _fixture.TestFhirClient.UpdateAsync(searchParam2); + Assert.True(create2.StatusCode == HttpStatusCode.OK || create2.StatusCode == HttpStatusCode.Created); + Assert.Equal(code2, create2.Resource.Id); + + var delete = await _fixture.TestFhirClient.DeleteAsync($"SearchParameter?url={searchParam1.Url},{searchParam2.Url}&_count=2&hardDelete={hardDelete}"); + Assert.Equal(HttpStatusCode.NoContent, delete.StatusCode); + + var resource1 = await _fixture.TestFhirClient.ReadAsync($"SearchParameter/{code1}"); + Assert.NotNull(resource1?.Resource); + + var resource2 = await _fixture.TestFhirClient.ReadAsync($"SearchParameter/{code2}"); + Assert.NotNull(resource2?.Resource); + + var reindex = await _fixture.TestFhirClient.PostReindexJobAsync(new Parameters { Parameter = [] }); + Assert.Equal(HttpStatusCode.Created, reindex.reponse.Response.StatusCode); + var reindexStatus = await WaitForJobCompletionAsync(reindex.uri, TimeSpan.FromSeconds(300)); + Assert.Equal(OperationStatus.Completed, reindexStatus.Status); + + var expectedStatusCode = hardDelete ? HttpStatusCode.NotFound : HttpStatusCode.Gone; + + var notFoundEx1 = await Assert.ThrowsAsync(async () => await _fixture.TestFhirClient.ReadAsync($"SearchParameter/{code1}")); + Assert.Equal(expectedStatusCode, notFoundEx1.StatusCode); + + var notFoundEx2 = await Assert.ThrowsAsync(async () => await _fixture.TestFhirClient.ReadAsync($"SearchParameter/{code2}")); + Assert.Equal(expectedStatusCode, notFoundEx2.StatusCode); + } + [Theory] [InlineData(true, false)] [InlineData(true, true)] From dc41c999cadba27814ec91b594c52d2f8798819f Mon Sep 17 00:00:00 2001 From: Sergey Galuzo Date: Mon, 31 Aug 2026 16:43:10 -0700 Subject: [PATCH 2/4] Fix comment --- .../Features/Resources/Delete/DeletionService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.Health.Fhir.Shared.Core/Features/Resources/Delete/DeletionService.cs b/src/Microsoft.Health.Fhir.Shared.Core/Features/Resources/Delete/DeletionService.cs index 879d701d05..a0a7b74fbd 100644 --- a/src/Microsoft.Health.Fhir.Shared.Core/Features/Resources/Delete/DeletionService.cs +++ b/src/Microsoft.Health.Fhir.Shared.Core/Features/Resources/Delete/DeletionService.cs @@ -110,7 +110,7 @@ public async Task DeleteAsync(DeleteResourceRequest request, Cancel { case DeleteOperation.SoftDelete: if (key.ResourceType == KnownResourceTypes.SearchParameter - //// we should skip direct status updates when context does not contain pending + //// we should skip direct status updates when context contains pending status && (_contextAccessor.RequestContext == null || _contextAccessor.RequestContext.Properties == null || !_contextAccessor.RequestContext.Properties.ContainsKey(SearchParameterRequestContextPropertyNames.PendingStatus))) From 8b182f961a12ead15bcaddef13844e8cb59598ef Mon Sep 17 00:00:00 2001 From: Sergey Galuzo Date: Mon, 31 Aug 2026 16:56:26 -0700 Subject: [PATCH 3/4] Removed ignore flag and renamed delete --- .../Parameters/ISearchParameterOperations.cs | 4 ++-- .../Parameters/SearchParameterOperations.cs | 15 ++++++--------- .../Registry/ISearchParameterStatusManager.cs | 2 +- .../Registry/SearchParameterStatusManager.cs | 2 +- .../Reindex/ReindexOrchestratorJobTests.cs | 11 ----------- .../SearchParameterStateUpdateHandlerTests.cs | 6 +++--- .../Resources/Delete/DeletionServiceTests.cs | 4 ++-- .../Features/Resources/Delete/DeletionService.cs | 4 ++-- .../Search/FailingSearchParameterStatusManager.cs | 2 +- 9 files changed, 18 insertions(+), 32 deletions(-) diff --git a/src/Microsoft.Health.Fhir.Core/Features/Search/Parameters/ISearchParameterOperations.cs b/src/Microsoft.Health.Fhir.Core/Features/Search/Parameters/ISearchParameterOperations.cs index 11dbd57673..c315d67712 100644 --- a/src/Microsoft.Health.Fhir.Core/Features/Search/Parameters/ISearchParameterOperations.cs +++ b/src/Microsoft.Health.Fhir.Core/Features/Search/Parameters/ISearchParameterOperations.cs @@ -17,11 +17,11 @@ public interface ISearchParameterOperations { DateTimeOffset SearchParamLastUpdated { get; } - Task DeleteSearchParameterAsync(RawResource searchParamResource, CancellationToken cancellationToken, bool ignoreSearchParameterNotSupportedException = false, bool isHardDelete = false); + Task MarkSearchParameterForDeletionAsync(RawResource searchParamResource, CancellationToken cancellationToken, bool isHardDelete = false); Task ValidateSearchParameterAsync(ITypedElement searchParam, CancellationToken cancellationToken, DateTimeOffset? lastUpdated = null); - Task UpdateSearchParameterStatusAsync(IReadOnlyCollection searchParameterUris, SearchParameterStatus status, CancellationToken cancellationToken, bool ignoreSearchParameterNotSupportedException = false); + Task UpdateSearchParameterStatusAsync(IReadOnlyCollection searchParameterUris, SearchParameterStatus status, CancellationToken cancellationToken); /// /// This method should be called to get any updates to search param cache diff --git a/src/Microsoft.Health.Fhir.Core/Features/Search/Parameters/SearchParameterOperations.cs b/src/Microsoft.Health.Fhir.Core/Features/Search/Parameters/SearchParameterOperations.cs index 463115f292..e61dbcb741 100644 --- a/src/Microsoft.Health.Fhir.Core/Features/Search/Parameters/SearchParameterOperations.cs +++ b/src/Microsoft.Health.Fhir.Core/Features/Search/Parameters/SearchParameterOperations.cs @@ -162,25 +162,22 @@ public async Task ValidateSearchParameterAsync(ITypedElement sea } /// - /// Marks the Search Parameter as PendingDelete or PendingHardDelete. This is only used by DeletionService.cs and will be removed when refactoring is done - /// to allow deletion service to properly handle Hard deletions for Search Parameters (e.g. allow reindex prior to removing resource from DB). - /// !!! This method has incorrect name. It does not delete search parameter, it just updates its status. + /// Marks the Search Parameter as PendingDelete or PendingHardDelete /// /// Search Parameter to update to Pending Delete status. /// Cancellation Token - /// The value indicating whether to ignore SearchParameterNotSupportedException. /// True for hard delete (PendingHardDelete), false for soft delete (PendingDelete). - public async Task DeleteSearchParameterAsync(RawResource searchParamResource, CancellationToken cancellationToken, bool ignoreSearchParameterNotSupportedException = false, bool isHardDelete = false) + public async Task MarkSearchParameterForDeletionAsync(RawResource searchParamResource, CancellationToken cancellationToken, bool isHardDelete = false) { var searchParam = _modelInfoProvider.ToTypedElement(searchParamResource); var searchParameterUrl = searchParam.GetStringScalar("url"); try { - _logger.LogInformation("DeleteSearchParameterAsync: Refreshing cache"); + _logger.LogInformation("MarkSearchParameterForDeletionAsync: Refreshing cache"); await GetAndApplySearchParameterUpdates(cancellationToken); var status = isHardDelete ? SearchParameterStatus.PendingHardDelete : SearchParameterStatus.PendingDelete; - _logger.LogInformation("DeleteSearchParameterAsync: Deleting the search parameter '{Url}' with status {Status}", searchParameterUrl, status); + _logger.LogInformation("MarkSearchParameterForDeletionAsync: Marking the search parameter '{Url}' with status {Status}", searchParameterUrl, status); await _searchParameterStatusManager.UpdateSearchParameterStatusAsync(new[] { searchParameterUrl }, status, cancellationToken, lastUpdated: SearchParamLastUpdated); } catch (FhirException fex) @@ -206,9 +203,9 @@ public async Task DeleteSearchParameterAsync(RawResource searchParamResource, Ca } } - public async Task UpdateSearchParameterStatusAsync(IReadOnlyCollection searchParameterUris, SearchParameterStatus status, CancellationToken cancellationToken, bool ignoreSearchParameterNotSupportedException = false) + public async Task UpdateSearchParameterStatusAsync(IReadOnlyCollection searchParameterUris, SearchParameterStatus status, CancellationToken cancellationToken) { - await _searchParameterStatusManager.UpdateSearchParameterStatusAsync(searchParameterUris, status, cancellationToken, ignoreSearchParameterNotSupportedException); + await _searchParameterStatusManager.UpdateSearchParameterStatusAsync(searchParameterUris, status, cancellationToken); } /// diff --git a/src/Microsoft.Health.Fhir.Core/Features/Search/Registry/ISearchParameterStatusManager.cs b/src/Microsoft.Health.Fhir.Core/Features/Search/Registry/ISearchParameterStatusManager.cs index ef219434d2..6e6179a334 100644 --- a/src/Microsoft.Health.Fhir.Core/Features/Search/Registry/ISearchParameterStatusManager.cs +++ b/src/Microsoft.Health.Fhir.Core/Features/Search/Registry/ISearchParameterStatusManager.cs @@ -21,7 +21,7 @@ public interface ISearchParameterStatusManager Task HandleAsync(SearchParameterDefinitionManagerInitialized notification, CancellationToken cancellationToken); - Task UpdateSearchParameterStatusAsync(IReadOnlyCollection searchParameterUris, SearchParameterStatus status, CancellationToken cancellationToken, bool ignoreSearchParameterNotSupportedException = false, long? reindexId = null, DateTimeOffset? lastUpdated = null); + Task UpdateSearchParameterStatusAsync(IReadOnlyCollection searchParameterUris, SearchParameterStatus status, CancellationToken cancellationToken, long? reindexId = null, DateTimeOffset? lastUpdated = null); Task CheckCacheConsistencyAsync(DateTime updateEventsSince, DateTime activeHostsSince, CancellationToken cancellationToken); diff --git a/src/Microsoft.Health.Fhir.Core/Features/Search/Registry/SearchParameterStatusManager.cs b/src/Microsoft.Health.Fhir.Core/Features/Search/Registry/SearchParameterStatusManager.cs index 463c33f449..7a7411a780 100644 --- a/src/Microsoft.Health.Fhir.Core/Features/Search/Registry/SearchParameterStatusManager.cs +++ b/src/Microsoft.Health.Fhir.Core/Features/Search/Registry/SearchParameterStatusManager.cs @@ -136,7 +136,7 @@ public async Task HandleAsync(SearchParameterDefinitionManagerInitialized notifi await EnsureInitializedAsync(cancellationToken); } - public async Task UpdateSearchParameterStatusAsync(IReadOnlyCollection searchParameterUris, SearchParameterStatus status, CancellationToken cancellationToken, bool ignoreSearchParameterNotSupportedException = false, long? reindexId = null, DateTimeOffset? lastUpdated = null) + public async Task UpdateSearchParameterStatusAsync(IReadOnlyCollection searchParameterUris, SearchParameterStatus status, CancellationToken cancellationToken, long? reindexId = null, DateTimeOffset? lastUpdated = null) { EnsureArg.IsNotNull(searchParameterUris); diff --git a/src/Microsoft.Health.Fhir.Shared.Core.UnitTests/Features/Operations/Reindex/ReindexOrchestratorJobTests.cs b/src/Microsoft.Health.Fhir.Shared.Core.UnitTests/Features/Operations/Reindex/ReindexOrchestratorJobTests.cs index c98e3fa103..c82a009913 100644 --- a/src/Microsoft.Health.Fhir.Shared.Core.UnitTests/Features/Operations/Reindex/ReindexOrchestratorJobTests.cs +++ b/src/Microsoft.Health.Fhir.Shared.Core.UnitTests/Features/Operations/Reindex/ReindexOrchestratorJobTests.cs @@ -987,7 +987,6 @@ await _searchParameterStatusManager.Received().UpdateSearchParameterStatusAsync( Arg.Is>(l => l.Contains(searchParam.Url.ToString())), SearchParameterStatus.Disabled, Arg.Any(), - Arg.Any(), Arg.Any(), Arg.Any()); } @@ -1038,7 +1037,6 @@ await _searchParameterStatusManager.Received(1).UpdateSearchParameterStatusAsync urls.Contains(orphanCustom.Url.OriginalString)), SearchParameterStatus.Deleted, Arg.Any(), - Arg.Any(), Arg.Is(id => id == jobInfo.Id), Arg.Any()); } @@ -1088,7 +1086,6 @@ await _searchParameterStatusManager.DidNotReceive().UpdateSearchParameterStatusA Arg.Any>(), SearchParameterStatus.Deleted, Arg.Any(), - Arg.Any(), Arg.Is(id => id == jobInfo.Id), Arg.Any()); } @@ -1125,7 +1122,6 @@ await _searchParameterStatusManager.DidNotReceive().UpdateSearchParameterStatusA Arg.Any>(), SearchParameterStatus.Deleted, Arg.Any(), - Arg.Any(), Arg.Is(id => id == jobInfo.Id), Arg.Any()); } @@ -1177,7 +1173,6 @@ await _searchParameterStatusManager.Received().UpdateSearchParameterStatusAsync( Arg.Is>(l => l.Contains(searchParam.Url.ToString())), SearchParameterStatus.Deleted, Arg.Any(), - Arg.Any(), Arg.Any()); } @@ -1828,7 +1823,6 @@ await _searchParameterStatusManager.Received().UpdateSearchParameterStatusAsync( l.Contains(patientBirthdateParam.Url.ToString())), SearchParameterStatus.Enabled, Arg.Any(), - false, Arg.Is(id => id == jobInfo.Id)); receivedCall = true; @@ -2064,7 +2058,6 @@ await _searchParameterStatusManager.Received().UpdateSearchParameterStatusAsync( Arg.Any>(), SearchParameterStatus.Enabled, Arg.Any(), - false, Arg.Is(id => id == jobInfo.Id)); } @@ -2233,13 +2226,11 @@ await _searchParameterStatusManager.Received().UpdateSearchParameterStatusAsync( Arg.Is>(l => l.Contains(searchParamLowercase.Url.ToString())), SearchParameterStatus.Enabled, Arg.Any(), - false, Arg.Is(id => id == jobInfo.Id)); await _searchParameterStatusManager.Received().UpdateSearchParameterStatusAsync( Arg.Is>(l => l.Contains(searchParamMixedCase.Url.ToString())), SearchParameterStatus.Deleted, Arg.Any(), - false, Arg.Is(id => id == jobInfo.Id)); } @@ -2314,14 +2305,12 @@ await _searchParameterStatusManager.Received().UpdateSearchParameterStatusAsync( Arg.Is>(l => l.Contains(searchParam1.Url.ToString())), SearchParameterStatus.Disabled, Arg.Any(), - false, Arg.Is(id => id == jobInfo.Id)); await _searchParameterStatusManager.Received().UpdateSearchParameterStatusAsync( Arg.Is>(l => l.Contains(searchParam2.Url.ToString())), SearchParameterStatus.Disabled, Arg.Any(), - false, Arg.Is(id => id == jobInfo.Id)); } diff --git a/src/Microsoft.Health.Fhir.Shared.Core.UnitTests/Features/Operations/SearchParameterState/SearchParameterStateUpdateHandlerTests.cs b/src/Microsoft.Health.Fhir.Shared.Core.UnitTests/Features/Operations/SearchParameterState/SearchParameterStateUpdateHandlerTests.cs index 481de80068..8eaa3acf15 100644 --- a/src/Microsoft.Health.Fhir.Shared.Core.UnitTests/Features/Operations/SearchParameterState/SearchParameterStateUpdateHandlerTests.cs +++ b/src/Microsoft.Health.Fhir.Shared.Core.UnitTests/Features/Operations/SearchParameterState/SearchParameterStateUpdateHandlerTests.cs @@ -111,7 +111,7 @@ public SearchParameterStateUpdateHandlerTests() _cancellationToken = CancellationToken.None; _authorizationService.CheckAccess(DataActions.SearchParameter, _cancellationToken).Returns(DataActions.SearchParameter); - _searchParameterOperations.UpdateSearchParameterStatusAsync(Arg.Any>(), Arg.Any(), Arg.Any(), Arg.Any()).Returns(Task.CompletedTask); + _searchParameterOperations.UpdateSearchParameterStatusAsync(Arg.Any>(), Arg.Any(), Arg.Any()).Returns(Task.CompletedTask); var searchParamDefinitionStore = new List { @@ -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>(x => x.Count == 1 && x.First() == ResourceId), SearchParameterStatus.Supported, Arg.Any(), false); + await _searchParameterOperations.Received(1).UpdateSearchParameterStatusAsync(Arg.Is>(x => x.Count == 1 && x.First() == ResourceId), SearchParameterStatus.Supported, Arg.Any()); } [Fact] @@ -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>(x => x.Count == 1 && x.First() == ResourceId), SearchParameterStatus.PendingDisable, Arg.Any(), false); + await _searchParameterOperations.Received(1).UpdateSearchParameterStatusAsync(Arg.Is>(x => x.Count == 1 && x.First() == ResourceId), SearchParameterStatus.PendingDisable, Arg.Any()); } [Fact] diff --git a/src/Microsoft.Health.Fhir.Shared.Core.UnitTests/Features/Resources/Delete/DeletionServiceTests.cs b/src/Microsoft.Health.Fhir.Shared.Core.UnitTests/Features/Resources/Delete/DeletionServiceTests.cs index 79a66e189e..2da40e8fb0 100644 --- a/src/Microsoft.Health.Fhir.Shared.Core.UnitTests/Features/Resources/Delete/DeletionServiceTests.cs +++ b/src/Microsoft.Health.Fhir.Shared.Core.UnitTests/Features/Resources/Delete/DeletionServiceTests.cs @@ -202,7 +202,7 @@ public async Task GivenSearchParameterDelete_WhenConcurrencyConflictOccurs_ThenR var attemptCount = 0; _searchParameterOperations - .DeleteSearchParameterAsync(Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) + .MarkSearchParameterForDeletionAsync(Arg.Any(), Arg.Any(), Arg.Any()) .Returns(callInfo => { attemptCount++; @@ -263,7 +263,7 @@ public async Task GivenSearchParameterDelete_WhenConcurrencyConflictExhaustsRetr _dataStoreFactory.GetScopedDataStore().Returns(scopedDataStore); _searchParameterOperations - .DeleteSearchParameterAsync(Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) + .MarkSearchParameterForDeletionAsync(Arg.Any(), Arg.Any(), Arg.Any()) .Returns(_ => throw new BadRequestException(Core.Resources.SearchParameterConcurrencyConflict)); var exception = await Assert.ThrowsAsync>>(async () => diff --git a/src/Microsoft.Health.Fhir.Shared.Core/Features/Resources/Delete/DeletionService.cs b/src/Microsoft.Health.Fhir.Shared.Core/Features/Resources/Delete/DeletionService.cs index a0a7b74fbd..55a724eeb8 100644 --- a/src/Microsoft.Health.Fhir.Shared.Core/Features/Resources/Delete/DeletionService.cs +++ b/src/Microsoft.Health.Fhir.Shared.Core/Features/Resources/Delete/DeletionService.cs @@ -151,7 +151,7 @@ private async Task DeleteSearchParameter(IFhirDataStore dataStore, ResourceKey k var resourceWrapper = await dataStore.GetAsync(key, cancellationToken); if (resourceWrapper != null && !resourceWrapper.IsDeleted) { - await _retryPolicy.ExecuteAsync(async () => await _searchParameterOperations.DeleteSearchParameterAsync(resourceWrapper.RawResource, cancellationToken, ignoreSearchParameterNotSupportedException: true, isHardDelete: isHardDelete)); + await _retryPolicy.ExecuteAsync(async () => await _searchParameterOperations.MarkSearchParameterForDeletionAsync(resourceWrapper.RawResource, cancellationToken, isHardDelete: isHardDelete)); } } @@ -723,7 +723,7 @@ private async Task DeleteSearchParameterWithLockAsync(SearchResultEntry item, bo await SearchParameterRetry.ExecuteAsync( async () => { - await _searchParameterOperations.DeleteSearchParameterAsync(item.Resource.RawResource, cancellationToken, ignoreSearchParameterNotSupportedException: true, isHardDelete: isHardDelete); + await _searchParameterOperations.MarkSearchParameterForDeletionAsync(item.Resource.RawResource, cancellationToken, isHardDelete: isHardDelete); }, "Deletion"); } diff --git a/test/Microsoft.Health.Fhir.Shared.Tests.E2E/Rest/Search/FailingSearchParameterStatusManager.cs b/test/Microsoft.Health.Fhir.Shared.Tests.E2E/Rest/Search/FailingSearchParameterStatusManager.cs index 5476977d3a..00f74d56f7 100644 --- a/test/Microsoft.Health.Fhir.Shared.Tests.E2E/Rest/Search/FailingSearchParameterStatusManager.cs +++ b/test/Microsoft.Health.Fhir.Shared.Tests.E2E/Rest/Search/FailingSearchParameterStatusManager.cs @@ -31,7 +31,7 @@ public Task HandleAsync(SearchParameterDefinitionManagerInitialized notification public Task> GetAllSearchParameterStatus(CancellationToken cancellationToken) => Task.FromResult>(Array.Empty()); - public Task UpdateSearchParameterStatusAsync(IReadOnlyCollection searchParameterUris, SearchParameterStatus status, CancellationToken cancellationToken, bool ignoreSearchParameterNotSupportedException = false, long? reindexId = null, DateTimeOffset? lastUpdated = null) => Task.CompletedTask; + public Task UpdateSearchParameterStatusAsync(IReadOnlyCollection searchParameterUris, SearchParameterStatus status, CancellationToken cancellationToken, long? reindexId = null, DateTimeOffset? lastUpdated = null) => Task.CompletedTask; public Task CheckCacheConsistencyAsync(DateTime updateEventsSince, DateTime activeHostsSince, CancellationToken cancellationToken) => Task.FromResult(new CacheConsistencyResult()); From 3e7f2352ac194ca41eecddac7076aa5ecc2749d6 Mon Sep 17 00:00:00 2001 From: Sergey Galuzo Date: Tue, 1 Sep 2026 09:05:26 -0700 Subject: [PATCH 4/4] Use context correctly --- .../Features/Resources/Delete/DeletionService.cs | 5 +++-- .../Persistence/FhirStorageTestsFixture.cs | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.Health.Fhir.Shared.Core/Features/Resources/Delete/DeletionService.cs b/src/Microsoft.Health.Fhir.Shared.Core/Features/Resources/Delete/DeletionService.cs index 55a724eeb8..4bc99a1d08 100644 --- a/src/Microsoft.Health.Fhir.Shared.Core/Features/Resources/Delete/DeletionService.cs +++ b/src/Microsoft.Health.Fhir.Shared.Core/Features/Resources/Delete/DeletionService.cs @@ -20,6 +20,7 @@ using Microsoft.Extensions.Options; using Microsoft.Health.Abstractions.Exceptions; using Microsoft.Health.Core.Features.Audit; +using Microsoft.Health.Core.Features.Context; using Microsoft.Health.Fhir.Core.Configs; using Microsoft.Health.Fhir.Core.Exceptions; using Microsoft.Health.Fhir.Core.Extensions; @@ -47,7 +48,7 @@ public class DeletionService : IDeletionService, IDisposable private readonly IScopeProvider _searchServiceFactory; private readonly ResourceIdProvider _resourceIdProvider; private readonly AsyncRetryPolicy _retryPolicy; - private readonly FhirRequestContextAccessor _contextAccessor; + private readonly RequestContextAccessor _contextAccessor; private readonly IAuditLogger _auditLogger; private readonly CoreFeatureConfiguration _configuration; private readonly IFhirRuntimeConfiguration _fhirRuntimeConfiguration; @@ -65,7 +66,7 @@ public DeletionService( IDeletionServiceDataStoreFactory dataStoreFactory, IScopeProvider searchServiceFactory, ResourceIdProvider resourceIdProvider, - FhirRequestContextAccessor contextAccessor, + RequestContextAccessor contextAccessor, IAuditLogger auditLogger, IOptions configuration, IFhirRuntimeConfiguration fhirRuntimeConfiguration, diff --git a/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Persistence/FhirStorageTestsFixture.cs b/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Persistence/FhirStorageTestsFixture.cs index f35083cbf6..189c20bf5c 100644 --- a/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Persistence/FhirStorageTestsFixture.cs +++ b/test/Microsoft.Health.Fhir.Shared.Tests.Integration/Persistence/FhirStorageTestsFixture.cs @@ -354,7 +354,7 @@ public async Task InitializeAsync() deletionServiceDataStoreFactory, SearchService.CreateMockScopeProvider(), _resourceIdProvider, - new FhirRequestContextAccessor(), + FhirRequestContextAccessor, auditLogger, new OptionsWrapper(coreFeatureConfiguration), _fhirRuntimeConfiguration,