diff --git a/sdk/keyvault/Azure.Security.KeyVault.Certificates/src/Azure.Security.KeyVault.Certificates.csproj b/sdk/keyvault/Azure.Security.KeyVault.Certificates/src/Azure.Security.KeyVault.Certificates.csproj index 03c6659be8cef..65f8c989bfcac 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Certificates/src/Azure.Security.KeyVault.Certificates.csproj +++ b/sdk/keyvault/Azure.Security.KeyVault.Certificates/src/Azure.Security.KeyVault.Certificates.csproj @@ -35,6 +35,8 @@ + + diff --git a/sdk/keyvault/Azure.Security.KeyVault.Certificates/src/DeleteCertificateOperation.cs b/sdk/keyvault/Azure.Security.KeyVault.Certificates/src/DeleteCertificateOperation.cs index 74344bde665cc..1cd4775954997 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Certificates/src/DeleteCertificateOperation.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Certificates/src/DeleteCertificateOperation.cs @@ -2,35 +2,39 @@ // Licensed under the MIT License. using System; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Azure.Core; -using Azure.Core.Pipeline; namespace Azure.Security.KeyVault.Certificates { /// /// A long-running operation for or . /// - public class DeleteCertificateOperation : Operation + public class DeleteCertificateOperation : Operation, IOperation { private static readonly TimeSpan s_defaultPollingInterval = TimeSpan.FromSeconds(2); private readonly KeyVaultPipeline _pipeline; + private readonly OperationInternal _operationInternal; private readonly DeletedCertificate _value; - private Response _response; - private bool _completed; internal DeleteCertificateOperation(KeyVaultPipeline pipeline, Response response) { _pipeline = pipeline; _value = response.Value ?? throw new InvalidOperationException("The response does not contain a value."); - _response = response.GetRawResponse(); + _operationInternal = new(_pipeline.Diagnostics, this, response.GetRawResponse(), nameof(DeleteCertificateOperation), new[] + { + new KeyValuePair("secret", _value.Name), // Retained for backward compatibility. + new KeyValuePair("certificate", _value.Name), + }); - // The recoveryId is only returned if soft-delete is enabled. + // The recoveryId is only returned if soft delete is enabled. if (_value.RecoveryId is null) { - _completed = true; + // If soft delete is not enabled, deleting is immediate so set success accordingly. + _operationInternal.SetState(OperationState.Success(response.GetRawResponse())); } } @@ -50,33 +54,20 @@ protected DeleteCertificateOperation() {} public override DeletedCertificate Value => _value; /// - public override bool HasCompleted => _completed; + public override bool HasCompleted => _operationInternal.HasCompleted; /// public override bool HasValue => true; /// - public override Response GetRawResponse() => _response; + public override Response GetRawResponse() => _operationInternal.RawResponse; /// public override Response UpdateStatus(CancellationToken cancellationToken = default) { - if (!_completed) + if (!HasCompleted) { - using DiagnosticScope scope = _pipeline.CreateScope($"{nameof(DeleteCertificateOperation)}.{nameof(UpdateStatus)}"); - scope.AddAttribute("secret", _value.Name); - scope.Start(); - - try - { - _response = _pipeline.GetResponse(RequestMethod.Get, cancellationToken, CertificateClient.DeletedCertificatesPath, _value.Name); - _completed = CheckCompleted(_response); - } - catch (Exception e) - { - scope.Failed(e); - throw; - } + return _operationInternal.UpdateStatus(cancellationToken); } return GetRawResponse(); @@ -85,22 +76,9 @@ public override Response UpdateStatus(CancellationToken cancellationToken = defa /// public override async ValueTask UpdateStatusAsync(CancellationToken cancellationToken = default) { - if (!_completed) + if (!HasCompleted) { - using DiagnosticScope scope = _pipeline.CreateScope($"{nameof(DeleteCertificateOperation)}.{nameof(UpdateStatus)}"); - scope.AddAttribute("secret", _value.Name); - scope.Start(); - - try - { - _response = await _pipeline.GetResponseAsync(RequestMethod.Get, cancellationToken, CertificateClient.DeletedCertificatesPath, _value.Name).ConfigureAwait(false); - _completed = await CheckCompletedAsync(_response).ConfigureAwait(false); - } - catch (Exception e) - { - scope.Failed(e); - throw; - } + return await _operationInternal.UpdateStatusAsync(cancellationToken).ConfigureAwait(false); } return GetRawResponse(); @@ -114,34 +92,27 @@ public override ValueTask> WaitForCompletionAsync(C public override ValueTask> WaitForCompletionAsync(TimeSpan pollingInterval, CancellationToken cancellationToken) => this.DefaultWaitForCompletionAsync(pollingInterval, cancellationToken); - private async ValueTask CheckCompletedAsync(Response response) + async ValueTask IOperation.UpdateStateAsync(bool async, CancellationToken cancellationToken) { - switch (response.Status) - { - case 200: - case 403: // Access denied but proof the certificate was deleted. - return true; - - case 404: - return false; + Response response = async + ? await _pipeline.GetResponseAsync(RequestMethod.Get, cancellationToken, CertificateClient.DeletedCertificatesPath, _value.Name).ConfigureAwait(false) + : _pipeline.GetResponse(RequestMethod.Get, cancellationToken, CertificateClient.DeletedCertificatesPath, _value.Name); - default: - throw await _pipeline.Diagnostics.CreateRequestFailedExceptionAsync(response).ConfigureAwait(false); - } - } - private bool CheckCompleted(Response response) - { switch (response.Status) { case 200: case 403: // Access denied but proof the certificate was deleted. - return true; + return OperationState.Success(response); case 404: - return false; + return OperationState.Pending(response); default: - throw _pipeline.Diagnostics.CreateRequestFailedException(response); + RequestFailedException ex = async + ? await _pipeline.Diagnostics.CreateRequestFailedExceptionAsync(response).ConfigureAwait(false) + : _pipeline.Diagnostics.CreateRequestFailedException(response); + + return OperationState.Failure(response, ex); } } } diff --git a/sdk/keyvault/Azure.Security.KeyVault.Certificates/src/RecoverDeletedCertificateOperation.cs b/sdk/keyvault/Azure.Security.KeyVault.Certificates/src/RecoverDeletedCertificateOperation.cs index 80c40e27f37a9..5414ca1d3822f 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Certificates/src/RecoverDeletedCertificateOperation.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Certificates/src/RecoverDeletedCertificateOperation.cs @@ -2,30 +2,33 @@ // Licensed under the MIT License. using System; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Azure.Core; -using Azure.Core.Pipeline; namespace Azure.Security.KeyVault.Certificates { /// /// A long-running operation for or . /// - public class RecoverDeletedCertificateOperation : Operation + public class RecoverDeletedCertificateOperation : Operation, IOperation { private static readonly TimeSpan s_defaultPollingInterval = TimeSpan.FromSeconds(2); private readonly KeyVaultPipeline _pipeline; + private readonly OperationInternal _operationInternal; private readonly KeyVaultCertificateWithPolicy _value; - private Response _response; - private bool _completed; internal RecoverDeletedCertificateOperation(KeyVaultPipeline pipeline, Response response) { _pipeline = pipeline; _value = response.Value ?? throw new InvalidOperationException("The response does not contain a value."); - _response = response.GetRawResponse(); + _operationInternal = new(_pipeline.Diagnostics, this, response.GetRawResponse(), nameof(RecoverDeletedCertificateOperation), new[] + { + new KeyValuePair("secret", _value.Name), // Retained for backward compatibility. + new KeyValuePair("certificate", _value.Name), + }); } /// Initializes a new instance of for mocking. @@ -44,33 +47,20 @@ protected RecoverDeletedCertificateOperation() {} public override KeyVaultCertificateWithPolicy Value => _value; /// - public override bool HasCompleted => _completed; + public override bool HasCompleted => _operationInternal.HasCompleted; /// public override bool HasValue => true; /// - public override Response GetRawResponse() => _response; + public override Response GetRawResponse() => _operationInternal.RawResponse; /// public override Response UpdateStatus(CancellationToken cancellationToken = default) { - if (!_completed) + if (!HasCompleted) { - using DiagnosticScope scope = _pipeline.CreateScope($"{nameof(RecoverDeletedCertificateOperation)}.{nameof(UpdateStatus)}"); - scope.AddAttribute("secret", _value.Name); - scope.Start(); - - try - { - _response = _pipeline.GetResponse(RequestMethod.Get, cancellationToken, CertificateClient.CertificatesPath, _value.Name, "/", _value.Properties.Version); - _completed = CheckCompleted(_response); - } - catch (Exception e) - { - scope.Failed(e); - throw; - } + return _operationInternal.UpdateStatus(cancellationToken); } return GetRawResponse(); @@ -79,22 +69,9 @@ public override Response UpdateStatus(CancellationToken cancellationToken = defa /// public override async ValueTask UpdateStatusAsync(CancellationToken cancellationToken = default) { - if (!_completed) + if (!HasCompleted) { - using DiagnosticScope scope = _pipeline.CreateScope($"{nameof(RecoverDeletedCertificateOperation)}.{nameof(UpdateStatus)}"); - scope.AddAttribute("secret", _value.Name); - scope.Start(); - - try - { - _response = await _pipeline.GetResponseAsync(RequestMethod.Get, cancellationToken, CertificateClient.CertificatesPath, _value.Name, "/", _value.Properties.Version).ConfigureAwait(false); - _completed = await CheckCompletedAsync(_response).ConfigureAwait(false); - } - catch (Exception e) - { - scope.Failed(e); - throw; - } + return await _operationInternal.UpdateStatusAsync(cancellationToken).ConfigureAwait(false); } return GetRawResponse(); @@ -108,34 +85,27 @@ public override ValueTask> WaitForComple public override ValueTask> WaitForCompletionAsync(TimeSpan pollingInterval, CancellationToken cancellationToken) => this.DefaultWaitForCompletionAsync(pollingInterval, cancellationToken); - private async ValueTask CheckCompletedAsync(Response response) + async ValueTask IOperation.UpdateStateAsync(bool async, CancellationToken cancellationToken) { - switch (response.Status) - { - case 200: - case 403: // Access denied but proof the certificate was recovered. - return true; - - case 404: - return false; + Response response = async + ? await _pipeline.GetResponseAsync(RequestMethod.Get, cancellationToken, CertificateClient.CertificatesPath, _value.Name, "/", _value.Properties.Version).ConfigureAwait(false) + : _pipeline.GetResponse(RequestMethod.Get, cancellationToken, CertificateClient.CertificatesPath, _value.Name, "/", _value.Properties.Version); - default: - throw await _pipeline.Diagnostics.CreateRequestFailedExceptionAsync(response).ConfigureAwait(false); - } - } - private bool CheckCompleted(Response response) - { switch (response.Status) { case 200: case 403: // Access denied but proof the certificate was recovered. - return true; + return OperationState.Success(response); case 404: - return false; + return OperationState.Pending(response); default: - throw _pipeline.Diagnostics.CreateRequestFailedException(response); + RequestFailedException ex = async + ? await _pipeline.Diagnostics.CreateRequestFailedExceptionAsync(response).ConfigureAwait(false) + : _pipeline.Diagnostics.CreateRequestFailedException(response); + + return OperationState.Failure(response, ex); } } } diff --git a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Azure.Security.KeyVault.Keys.csproj b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Azure.Security.KeyVault.Keys.csproj index bd3b0e7fb47da..f50eee3c3ce39 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Azure.Security.KeyVault.Keys.csproj +++ b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Azure.Security.KeyVault.Keys.csproj @@ -38,6 +38,8 @@ + + diff --git a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/DeleteKeyOperation.cs b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/DeleteKeyOperation.cs index 15cf60e342f45..582b86c6add8d 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/DeleteKeyOperation.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/DeleteKeyOperation.cs @@ -2,35 +2,39 @@ // Licensed under the MIT License. using System; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Azure.Core; -using Azure.Core.Pipeline; namespace Azure.Security.KeyVault.Keys { /// /// A long-running operation for or . /// - public class DeleteKeyOperation : Operation + public class DeleteKeyOperation : Operation, IOperation { private static readonly TimeSpan s_defaultPollingInterval = TimeSpan.FromSeconds(2); private readonly KeyVaultPipeline _pipeline; + private readonly OperationInternal _operationInternal; private readonly DeletedKey _value; - private Response _response; - private bool _completed; internal DeleteKeyOperation(KeyVaultPipeline pipeline, Response response) { _pipeline = pipeline; _value = response.Value ?? throw new InvalidOperationException("The response does not contain a value."); - _response = response.GetRawResponse(); + _operationInternal = new(_pipeline.Diagnostics, this, response.GetRawResponse(), nameof(DeleteKeyOperation), new[] + { + new KeyValuePair("secret", _value.Name), // Retained for backward compatibility. + new KeyValuePair("key", _value.Name), + }); - // The recoveryId is only returned if soft-delete is enabled. + // The recoveryId is only returned if soft delete is enabled. if (_value.RecoveryId is null) { - _completed = true; + // If soft delete is not enabled, deleting is immediate so set success accordingly. + _operationInternal.SetState(OperationState.Success(response.GetRawResponse())); } } @@ -50,33 +54,20 @@ protected DeleteKeyOperation() {} public override DeletedKey Value => _value; /// - public override bool HasCompleted => _completed; + public override bool HasCompleted => _operationInternal.HasCompleted; /// public override bool HasValue => true; /// - public override Response GetRawResponse() => _response; + public override Response GetRawResponse() => _operationInternal.RawResponse; /// public override Response UpdateStatus(CancellationToken cancellationToken = default) { - if (!_completed) + if (!HasCompleted) { - using DiagnosticScope scope = _pipeline.CreateScope($"{nameof(DeleteKeyOperation)}.{nameof(UpdateStatus)}"); - scope.AddAttribute("secret", _value.Name); - scope.Start(); - - try - { - _response = _pipeline.GetResponse(RequestMethod.Get, cancellationToken, KeyClient.DeletedKeysPath, _value.Name); - _completed = CheckCompleted(_response); - } - catch (Exception e) - { - scope.Failed(e); - throw; - } + return _operationInternal.UpdateStatus(cancellationToken); } return GetRawResponse(); @@ -85,22 +76,9 @@ public override Response UpdateStatus(CancellationToken cancellationToken = defa /// public override async ValueTask UpdateStatusAsync(CancellationToken cancellationToken = default) { - if (!_completed) + if (!HasCompleted) { - using DiagnosticScope scope = _pipeline.CreateScope($"{nameof(DeleteKeyOperation)}.{nameof(UpdateStatus)}"); - scope.AddAttribute("secret", _value.Name); - scope.Start(); - - try - { - _response = await _pipeline.GetResponseAsync(RequestMethod.Get, cancellationToken, KeyClient.DeletedKeysPath, _value.Name).ConfigureAwait(false); - _completed = await CheckCompletedAsync(_response).ConfigureAwait(false); - } - catch (Exception e) - { - scope.Failed(e); - throw; - } + return await _operationInternal.UpdateStatusAsync(cancellationToken).ConfigureAwait(false); } return GetRawResponse(); @@ -114,34 +92,27 @@ public override ValueTask> WaitForCompletionAsync(Cancellat public override ValueTask> WaitForCompletionAsync(TimeSpan pollingInterval, CancellationToken cancellationToken) => this.DefaultWaitForCompletionAsync(pollingInterval, cancellationToken); - private async ValueTask CheckCompletedAsync(Response response) + async ValueTask IOperation.UpdateStateAsync(bool async, CancellationToken cancellationToken) { - switch (response.Status) - { - case 200: - case 403: // Access denied but proof the key was deleted. - return true; - - case 404: - return false; + Response response = async + ? await _pipeline.GetResponseAsync(RequestMethod.Get, cancellationToken, KeyClient.DeletedKeysPath, _value.Name).ConfigureAwait(false) + : _pipeline.GetResponse(RequestMethod.Get, cancellationToken, KeyClient.DeletedKeysPath, _value.Name); - default: - throw await _pipeline.Diagnostics.CreateRequestFailedExceptionAsync(response).ConfigureAwait(false); - } - } - private bool CheckCompleted(Response response) - { switch (response.Status) { case 200: case 403: // Access denied but proof the key was deleted. - return true; + return OperationState.Success(response); case 404: - return false; + return OperationState.Pending(response); default: - throw _pipeline.Diagnostics.CreateRequestFailedException(response); + RequestFailedException ex = async + ? await _pipeline.Diagnostics.CreateRequestFailedExceptionAsync(response).ConfigureAwait(false) + : _pipeline.Diagnostics.CreateRequestFailedException(response); + + return OperationState.Failure(response, ex); } } } diff --git a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/RecoverDeletedKeyOperation.cs b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/RecoverDeletedKeyOperation.cs index 7681dfec62117..7852bffc472e2 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/RecoverDeletedKeyOperation.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/RecoverDeletedKeyOperation.cs @@ -2,30 +2,33 @@ // Licensed under the MIT License. using System; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Azure.Core; -using Azure.Core.Pipeline; namespace Azure.Security.KeyVault.Keys { /// /// A long-running operation for or . /// - public class RecoverDeletedKeyOperation : Operation + public class RecoverDeletedKeyOperation : Operation, IOperation { private static readonly TimeSpan s_defaultPollingInterval = TimeSpan.FromSeconds(2); private readonly KeyVaultPipeline _pipeline; + private readonly OperationInternal _operationInternal; private readonly KeyVaultKey _value; - private Response _response; - private bool _completed; internal RecoverDeletedKeyOperation(KeyVaultPipeline pipeline, Response response) { _pipeline = pipeline; _value = response.Value ?? throw new InvalidOperationException("The response does not contain a value."); - _response = response.GetRawResponse(); + _operationInternal = new(_pipeline.Diagnostics, this, response.GetRawResponse(), nameof(RecoverDeletedKeyOperation), new[] + { + new KeyValuePair("secret", _value.Name), // Retained for backward compatibility. + new KeyValuePair("key", _value.Name), + }); } /// Initializes a new instance of for mocking. @@ -44,33 +47,20 @@ protected RecoverDeletedKeyOperation() {} public override KeyVaultKey Value => _value; /// - public override bool HasCompleted => _completed; + public override bool HasCompleted => _operationInternal.HasCompleted; /// public override bool HasValue => true; /// - public override Response GetRawResponse() => _response; + public override Response GetRawResponse() => _operationInternal.RawResponse; /// public override Response UpdateStatus(CancellationToken cancellationToken = default) { - if (!_completed) + if (!HasCompleted) { - using DiagnosticScope scope = _pipeline.CreateScope($"{nameof(RecoverDeletedKeyOperation)}.{nameof(UpdateStatus)}"); - scope.AddAttribute("secret", _value.Name); - scope.Start(); - - try - { - _response = _pipeline.GetResponse(RequestMethod.Get, cancellationToken, KeyClient.KeysPath, _value.Name, "/", _value.Properties.Version); - _completed = CheckCompleted(_response); - } - catch (Exception e) - { - scope.Failed(e); - throw; - } + return _operationInternal.UpdateStatus(cancellationToken); } return GetRawResponse(); @@ -79,22 +69,9 @@ public override Response UpdateStatus(CancellationToken cancellationToken = defa /// public override async ValueTask UpdateStatusAsync(CancellationToken cancellationToken = default) { - if (!_completed) + if (!HasCompleted) { - using DiagnosticScope scope = _pipeline.CreateScope($"{nameof(RecoverDeletedKeyOperation)}.{nameof(UpdateStatus)}"); - scope.AddAttribute("secret", _value.Name); - scope.Start(); - - try - { - _response = await _pipeline.GetResponseAsync(RequestMethod.Get, cancellationToken, KeyClient.KeysPath, _value.Name, "/", _value.Properties.Version).ConfigureAwait(false); - _completed = await CheckCompletedAsync(_response).ConfigureAwait(false); - } - catch (Exception e) - { - scope.Failed(e); - throw; - } + return await _operationInternal.UpdateStatusAsync(cancellationToken).ConfigureAwait(false); } return GetRawResponse(); @@ -108,34 +85,27 @@ public override ValueTask> WaitForCompletionAsync(Cancella public override ValueTask> WaitForCompletionAsync(TimeSpan pollingInterval, CancellationToken cancellationToken) => this.DefaultWaitForCompletionAsync(pollingInterval, cancellationToken); - private async ValueTask CheckCompletedAsync(Response response) + async ValueTask IOperation.UpdateStateAsync(bool async, CancellationToken cancellationToken) { - switch (response.Status) - { - case 200: - case 403: // Access denied but proof the key was recovered. - return true; - - case 404: - return false; + Response response = async + ? await _pipeline.GetResponseAsync(RequestMethod.Get, cancellationToken, KeyClient.KeysPath, _value.Name, "/", _value.Properties.Version).ConfigureAwait(false) + : _pipeline.GetResponse(RequestMethod.Get, cancellationToken, KeyClient.KeysPath, _value.Name, "/", _value.Properties.Version); - default: - throw await _pipeline.Diagnostics.CreateRequestFailedExceptionAsync(response).ConfigureAwait(false); - } - } - private bool CheckCompleted(Response response) - { switch (response.Status) { case 200: case 403: // Access denied but proof the key was recovered. - return true; + return OperationState.Success(response); case 404: - return false; + return OperationState.Pending(response); default: - throw _pipeline.Diagnostics.CreateRequestFailedException(response); + RequestFailedException ex = async + ? await _pipeline.Diagnostics.CreateRequestFailedExceptionAsync(response).ConfigureAwait(false) + : _pipeline.Diagnostics.CreateRequestFailedException(response); + + return OperationState.Failure(response, ex); } } } diff --git a/sdk/keyvault/Azure.Security.KeyVault.Secrets/src/RecoverDeletedSecretOperation.cs b/sdk/keyvault/Azure.Security.KeyVault.Secrets/src/RecoverDeletedSecretOperation.cs index f10b37da0581b..ee6a832a53e4a 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Secrets/src/RecoverDeletedSecretOperation.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Secrets/src/RecoverDeletedSecretOperation.cs @@ -90,7 +90,7 @@ async ValueTask IOperation.UpdateStateAsync(bool async, Cancella switch (response.Status) { case 200: - case 403: // Access denied but proof the secret was deleted. + case 403: // Access denied but proof the secret was recovered. return OperationState.Success(response); case 404: