diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ad99b47a2..47baa3e6b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ ENHANCEMENTS: * Update the version of `super-linter` used in the `build_validation_develop` workflow to 8.7.0 ([#4957](https://github.com/microsoft/AzureTRE/issues/4957)) BUG FIXES: +* Fix API resource update retries so a successful retry returns the patched resource to the original caller. ([#5025](https://github.com/microsoft/AzureTRE/pull/5025)) * Fix workspace deletion when backup is enabled for the base, unrestricted and airlock-import-review workspaces by adding a `delete_backups_on_uninstall` flag and a pre-teardown backup cleanup (`remove_backup.sh`) that stops protection and either deletes or retains the Recovery Services Vault, so deletion works with Azure secure-by-default soft delete ([#4962](https://github.com/microsoft/AzureTRE/issues/4962)) * Fix Nexus shared service security: fetch admin password from Key Vault at runtime via managed identity (IMDS) instead of embedding it in the VM Run Command script content. Fix `deploy_nexus_container.sh` short-circuit path to fail loudly if the container does not start. (`sonatype-nexus` 3.10.0) ([#4983](https://github.com/microsoft/AzureTRE/pull/4983)) * Fix UI TypeScript deprecation warning by updating `moduleResolution` to `bundler` in `tsconfig.json`. ([#4968](https://github.com/microsoft/AzureTRE/issues/4968)) diff --git a/api_app/_version.py b/api_app/_version.py index 7c4a9591e1..025f4c5d0b 100644 --- a/api_app/_version.py +++ b/api_app/_version.py @@ -1 +1 @@ -__version__ = "0.26.0" +__version__ = "0.26.1" diff --git a/api_app/service_bus/helpers.py b/api_app/service_bus/helpers.py index 56ff47a724..05d32115f2 100644 --- a/api_app/service_bus/helpers.py +++ b/api_app/service_bus/helpers.py @@ -130,7 +130,7 @@ async def try_update_with_retries(num_retries: int, attempt_count: int, resource except CosmosAccessConditionFailedError as e: logger.warning(f"Etag mismatch for {resource_to_update_id}. Retrying.") if attempt_count < num_retries: - await try_update_with_retries( + return await try_update_with_retries( num_retries=num_retries, attempt_count=(attempt_count + 1), resource_repo=resource_repo, diff --git a/api_app/tests_ma/test_service_bus/test_resource_request_sender.py b/api_app/tests_ma/test_service_bus/test_resource_request_sender.py index 5f51770f02..8c31ab17f9 100644 --- a/api_app/tests_ma/test_service_bus/test_resource_request_sender.py +++ b/api_app/tests_ma/test_service_bus/test_resource_request_sender.py @@ -148,6 +148,49 @@ async def test_multi_step_document_sends_first_step( ) +@patch("service_bus.resource_request_sender.ResourceHistoryRepository.create") +@patch("service_bus.resource_request_sender.ResourceRepository.create") +@patch("service_bus.resource_request_sender.ResourceTemplateRepository.create") +async def test_multi_step_document_retries_then_succeeds( + resource_template_repo, + resource_repo, + resource_history_repo, + basic_shared_service, + basic_shared_service_template, + test_user, + multi_step_resource_template, + primary_resource +): + + resource_repo.get_resource_by_id.return_value = basic_shared_service + resource_template_repo.get_template_by_name_and_version.return_value = ( + basic_shared_service_template + ) + updated_resource = basic_shared_service.copy(update={"etag": "updated-etag"}) + resource_repo.patch_resource.side_effect = [ + CosmosAccessConditionFailedError(), + (updated_resource, basic_shared_service_template), + ] + + result = await try_update_with_retries( + num_retries=5, + attempt_count=0, + resource_repo=resource_repo, + resource_template_repo=resource_template_repo, + user=test_user, + resource_to_update_id="resource-id", + template_step=multi_step_resource_template.pipeline.install[0], + resource_history_repo=resource_history_repo, + primary_resource=primary_resource, + primary_parent_workspace=None, + primary_parent_workspace_svc=None + ) + + assert result == updated_resource + assert len(resource_repo.patch_resource.mock_calls) == 2 + assert len(resource_repo.get_resource_by_id.mock_calls) == 2 + + @patch("service_bus.resource_request_sender.ResourceHistoryRepository.create") @patch("service_bus.resource_request_sender.ResourceRepository.create") @patch("service_bus.resource_request_sender.ResourceTemplateRepository.create")