diff --git a/sdk/core/Azure.Core/src/Shared/OperationInternal.cs b/sdk/core/Azure.Core/src/Shared/OperationInternal.cs
index 1d7420fe6913..92f2e102334c 100644
--- a/sdk/core/Azure.Core/src/Shared/OperationInternal.cs
+++ b/sdk/core/Azure.Core/src/Shared/OperationInternal.cs
@@ -78,6 +78,15 @@ public OperationInternal(
_operation = operation;
}
+ ///
+ /// Sets the state immediately.
+ ///
+ /// The used to set and other members.
+ public void SetState(OperationState state)
+ {
+ ApplyStateAsync(false, state.RawResponse, state.HasCompleted, state.HasSucceeded, state.OperationFailedException, throwIfFailed: false).EnsureCompleted();
+ }
+
protected override async ValueTask UpdateStateAsync(bool async, CancellationToken cancellationToken)
{
OperationState state = await _operation.UpdateStateAsync(async, cancellationToken).ConfigureAwait(false);
diff --git a/sdk/core/Azure.Core/src/Shared/OperationInternalBase.cs b/sdk/core/Azure.Core/src/Shared/OperationInternalBase.cs
index 816417adf1f3..5f92b89275c2 100644
--- a/sdk/core/Azure.Core/src/Shared/OperationInternalBase.cs
+++ b/sdk/core/Azure.Core/src/Shared/OperationInternalBase.cs
@@ -179,7 +179,7 @@ private async ValueTask UpdateStatusAsync(bool async, CancellationToke
}
}
- protected async ValueTask ApplyStateAsync(bool async, Response response, bool hasCompleted, bool hasSucceeded, RequestFailedException? requestFailedException)
+ protected async ValueTask ApplyStateAsync(bool async, Response response, bool hasCompleted, bool hasSucceeded, RequestFailedException? requestFailedException, bool throwIfFailed = true)
{
RawResponse = response;
@@ -198,7 +198,13 @@ protected async ValueTask ApplyStateAsync(bool async, Response respons
(async
? await _diagnostics.CreateRequestFailedExceptionAsync(response).ConfigureAwait(false)
: _diagnostics.CreateRequestFailedException(response));
- throw OperationFailedException;
+
+ if (throwIfFailed)
+ {
+ throw OperationFailedException;
+ }
+
+ return response;
}
protected static TimeSpan GetServerDelay(Response response, TimeSpan pollingInterval)
diff --git a/sdk/core/Azure.Core/src/Shared/OperationInternalOfT.cs b/sdk/core/Azure.Core/src/Shared/OperationInternalOfT.cs
index 1e31ddc23e8d..9bc88ec9fb1f 100644
--- a/sdk/core/Azure.Core/src/Shared/OperationInternalOfT.cs
+++ b/sdk/core/Azure.Core/src/Shared/OperationInternalOfT.cs
@@ -168,6 +168,19 @@ public async ValueTask> WaitForCompletionAsync(TimeSpan pollingInter
return Response.FromValue(Value, rawResponse);
}
+ ///
+ /// Sets the state immediately.
+ ///
+ /// The used to set and other members.
+ public void SetState(OperationState state)
+ {
+ if (state.HasCompleted && state.HasSucceeded)
+ {
+ Value = state.Value!;
+ }
+ ApplyStateAsync(false, state.RawResponse, state.HasCompleted, state.HasSucceeded, state.OperationFailedException, throwIfFailed: false).EnsureCompleted();
+ }
+
protected override async ValueTask UpdateStateAsync(bool async, CancellationToken cancellationToken)
{
OperationState state = await _operation.UpdateStateAsync(async, cancellationToken).ConfigureAwait(false);
diff --git a/sdk/core/Azure.Core/tests/OperationInternalTests.cs b/sdk/core/Azure.Core/tests/OperationInternalTests.cs
index 8cf1d67820be..dcd2dacc6035 100644
--- a/sdk/core/Azure.Core/tests/OperationInternalTests.cs
+++ b/sdk/core/Azure.Core/tests/OperationInternalTests.cs
@@ -87,6 +87,68 @@ public void RawResponseInitialization()
}
}
+ [Test]
+ public void SetStateSucceeds()
+ {
+ var operationInternal = CreateOperation(isOfT, UpdateResult.Pending);
+ if (operationInternal is OperationInternal oi)
+ {
+ oi.SetState(OperationState.Success(mockResponse));
+ }
+ else if (operationInternal is OperationInternal oit)
+ {
+ oit.SetState(OperationState.Success(mockResponse, 1));
+ }
+
+ Assert.IsTrue(operationInternal.HasCompleted);
+ if (operationInternal is OperationInternal oit2)
+ {
+ Assert.IsTrue(oit2.HasValue);
+ Assert.AreEqual(1, oit2.Value);
+ }
+ }
+
+ [Test]
+ public void SetStateIsPending()
+ {
+ var operationInternal = CreateOperation(isOfT, UpdateResult.Pending);
+ if (operationInternal is OperationInternal oi)
+ {
+ oi.SetState(OperationState.Pending(mockResponse));
+ }
+ else if (operationInternal is OperationInternal oit)
+ {
+ oit.SetState(OperationState.Pending(mockResponse));
+ }
+
+ Assert.IsFalse(operationInternal.HasCompleted);
+ if (operationInternal is OperationInternal oit2)
+ {
+ Assert.IsFalse(oit2.HasValue);
+ }
+ }
+
+ [Test]
+ public void SetStateFails()
+ {
+ var operationInternal = CreateOperation(isOfT, UpdateResult.Pending);
+ if (operationInternal is OperationInternal oi)
+ {
+ oi.SetState(OperationState.Failure(mockResponse));
+ }
+ else if (operationInternal is OperationInternal oit)
+ {
+ oit.SetState(OperationState.Failure(mockResponse));
+ }
+
+ Assert.IsTrue(operationInternal.HasCompleted);
+ if (operationInternal is OperationInternal oit2)
+ {
+ Assert.IsFalse(oit2.HasValue);
+ Assert.Throws(() => _ = oit2.Value);
+ }
+ }
+
[Test]
public async Task UpdateStatusWhenOperationIsPending([Values(true, false)] bool async)
{
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 03c6659be8ce..65f8c989bfca 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/CertificateProperties.cs b/sdk/keyvault/Azure.Security.KeyVault.Certificates/src/CertificateProperties.cs
index 9e5b5f8b7292..84031a8ca6df 100644
--- a/sdk/keyvault/Azure.Security.KeyVault.Certificates/src/CertificateProperties.cs
+++ b/sdk/keyvault/Azure.Security.KeyVault.Certificates/src/CertificateProperties.cs
@@ -158,18 +158,14 @@ internal void ReadProperty(JsonProperty prop)
}
}
- private void ParseId(Uri idToParse)
+ private void ParseId(Uri id)
{
- // We expect an identifier with either 3 or 4 segments: host + collection + name [+ version]
- if (idToParse.Segments.Length != 3 && idToParse.Segments.Length != 4)
- throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Invalid ObjectIdentifier: {0}. Bad number of segments: {1}", idToParse, idToParse.Segments.Length));
+ KeyVaultIdentifier identifier = KeyVaultIdentifier.ParseWithCollection(id, "certificates");
- if (!string.Equals(idToParse.Segments[1], "certificates" + "/", StringComparison.OrdinalIgnoreCase))
- throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Invalid ObjectIdentifier: {0}. segment [1] should be 'certificates/', found '{1}'", idToParse, idToParse.Segments[1]));
-
- VaultUri = new Uri($"{idToParse.Scheme}://{idToParse.Authority}");
- Name = idToParse.Segments[2].Trim('/');
- Version = (idToParse.Segments.Length == 4) ? idToParse.Segments[3].TrimEnd('/') : null;
+ Id = id;
+ VaultUri = identifier.VaultUri;
+ Name = identifier.Name;
+ Version = identifier.Version;
}
}
}
diff --git a/sdk/keyvault/Azure.Security.KeyVault.Certificates/src/DeleteCertificateOperation.cs b/sdk/keyvault/Azure.Security.KeyVault.Certificates/src/DeleteCertificateOperation.cs
index 74344bde665c..1cd477595499 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 80c40e27f37a..5414ca1d3822 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 bd3b0e7fb47d..f50eee3c3ce3 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 15cf60e342f4..582b86c6add8 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/KeyProperties.cs b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/KeyProperties.cs
index 654f55a072c3..08085b4dd655 100644
--- a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/KeyProperties.cs
+++ b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/KeyProperties.cs
@@ -138,20 +138,15 @@ public KeyProperties(Uri id)
///
/// Parses the key identifier into the , , and of the key.
///
- /// The key vault object identifier.
- internal void ParseId(Uri idToParse)
+ /// The key vault object identifier.
+ internal void ParseId(Uri id)
{
- // We expect an identifier with either 3 or 4 segments: host + collection + name [+ version]
- if (idToParse.Segments.Length != 3 && idToParse.Segments.Length != 4)
- throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Invalid ObjectIdentifier: {0}. Bad number of segments: {1}", idToParse, idToParse.Segments.Length));
+ KeyVaultIdentifier identifier = KeyVaultIdentifier.ParseWithCollection(id, "keys");
- if (!string.Equals(idToParse.Segments[1], "keys" + "/", StringComparison.OrdinalIgnoreCase))
- throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Invalid ObjectIdentifier: {0}. segment [1] should be 'keys/', found '{1}'", idToParse, idToParse.Segments[1]));
-
- Id = idToParse;
- VaultUri = new Uri($"{idToParse.Scheme}://{idToParse.Authority}");
- Name = idToParse.Segments[2].Trim('/');
- Version = (idToParse.Segments.Length == 4) ? idToParse.Segments[3].TrimEnd('/') : null;
+ Id = id;
+ VaultUri = identifier.VaultUri;
+ Name = identifier.Name;
+ Version = identifier.Version;
}
internal void ReadProperty(JsonProperty prop)
diff --git a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/RecoverDeletedKeyOperation.cs b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/RecoverDeletedKeyOperation.cs
index 7681dfec6211..7852bffc472e 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/Azure.Security.KeyVault.Secrets.csproj b/sdk/keyvault/Azure.Security.KeyVault.Secrets/src/Azure.Security.KeyVault.Secrets.csproj
index 0fba7ca7bd07..f04b10d59a74 100644
--- a/sdk/keyvault/Azure.Security.KeyVault.Secrets/src/Azure.Security.KeyVault.Secrets.csproj
+++ b/sdk/keyvault/Azure.Security.KeyVault.Secrets/src/Azure.Security.KeyVault.Secrets.csproj
@@ -1,4 +1,4 @@
-
+
This is the Microsoft Azure Key Vault Secrets client library
@@ -37,6 +37,8 @@
+
+
diff --git a/sdk/keyvault/Azure.Security.KeyVault.Secrets/src/DeleteSecretOperation.cs b/sdk/keyvault/Azure.Security.KeyVault.Secrets/src/DeleteSecretOperation.cs
index 98e42d5e9ae2..b99e1bbe2928 100644
--- a/sdk/keyvault/Azure.Security.KeyVault.Secrets/src/DeleteSecretOperation.cs
+++ b/sdk/keyvault/Azure.Security.KeyVault.Secrets/src/DeleteSecretOperation.cs
@@ -2,35 +2,35 @@
// 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.Secrets
{
///
/// A long-running operation for or .
///
- public class DeleteSecretOperation : Operation
+ public class DeleteSecretOperation : Operation, IOperation
{
private static readonly TimeSpan s_defaultPollingInterval = TimeSpan.FromSeconds(2);
private readonly KeyVaultPipeline _pipeline;
+ private readonly OperationInternal _operationInternal;
private readonly DeletedSecret _value;
- private Response _response;
- private bool _completed;
internal DeleteSecretOperation(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(DeleteSecretOperation), new[] { new KeyValuePair("secret", _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 +50,20 @@ protected DeleteSecretOperation() {}
public override DeletedSecret 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(DeleteSecretOperation)}.{nameof(UpdateStatus)}");
- scope.AddAttribute("secret", _value.Name);
- scope.Start();
-
- try
- {
- _response = _pipeline.GetResponse(RequestMethod.Get, cancellationToken, SecretClient.DeletedSecretsPath, _value.Name);
- _completed = CheckCompleted(_response);
- }
- catch (Exception e)
- {
- scope.Failed(e);
- throw;
- }
+ return _operationInternal.UpdateStatus(cancellationToken);
}
return GetRawResponse();
@@ -85,22 +72,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(DeleteSecretOperation)}.{nameof(UpdateStatus)}");
- scope.AddAttribute("secret", _value.Name);
- scope.Start();
-
- try
- {
- _response = await _pipeline.GetResponseAsync(RequestMethod.Get, cancellationToken, SecretClient.DeletedSecretsPath, _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 +88,27 @@ public override ValueTask> WaitForCompletionAsync(Cancel
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 secret was deleted.
- return true;
-
- case 404:
- return false;
+ Response response = async
+ ? await _pipeline.GetResponseAsync(RequestMethod.Get, cancellationToken, SecretClient.DeletedSecretsPath, _value.Name).ConfigureAwait(false)
+ : _pipeline.GetResponse(RequestMethod.Get, cancellationToken, SecretClient.DeletedSecretsPath, _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 secret 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.Secrets/src/ObjectId.cs b/sdk/keyvault/Azure.Security.KeyVault.Secrets/src/ObjectId.cs
deleted file mode 100644
index 2747a46de33b..000000000000
--- a/sdk/keyvault/Azure.Security.KeyVault.Secrets/src/ObjectId.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License.
-
-using System;
-using System.Globalization;
-
-namespace Azure.Security.KeyVault.Secrets
-{
- internal struct ObjectId
- {
- public Uri Id { get; set; }
-
- public Uri VaultUri { get; set; }
-
- public string Name { get; set; }
-
- public string Version { get; set; }
-
- public void ParseId(string collection, string id) => ParseId(collection, new Uri(id, UriKind.Absolute));
-
- public void ParseId(string collection, Uri id)
- {
- Id = id;
-
- // We expect an identifier with either 3 or 4 segments: host + collection + name [+ version]
- if (Id.Segments.Length != 3 && Id.Segments.Length != 4)
- throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Invalid ObjectIdentifier: {0}. Bad number of segments: {1}", id, Id.Segments.Length));
-
- if (!string.Equals(Id.Segments[1], collection + "/", StringComparison.OrdinalIgnoreCase))
- throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Invalid ObjectIdentifier: {0}. segment [1] should be '{1}/', found '{2}'", id, collection, Id.Segments[1]));
-
- VaultUri = new Uri($"{Id.Scheme}://{Id.Authority}");
- Name = Id.Segments[2].Trim('/');
- Version = (Id.Segments.Length == 4) ? Id.Segments[3].TrimEnd('/') : null;
- }
- }
-}
diff --git a/sdk/keyvault/Azure.Security.KeyVault.Secrets/src/RecoverDeletedSecretOperation.cs b/sdk/keyvault/Azure.Security.KeyVault.Secrets/src/RecoverDeletedSecretOperation.cs
index 26d061a1ca6a..ee6a832a53e4 100644
--- a/sdk/keyvault/Azure.Security.KeyVault.Secrets/src/RecoverDeletedSecretOperation.cs
+++ b/sdk/keyvault/Azure.Security.KeyVault.Secrets/src/RecoverDeletedSecretOperation.cs
@@ -2,30 +2,29 @@
// 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.Secrets
{
///
/// A long-running operation for or .
///
- public class RecoverDeletedSecretOperation : Operation
+ public class RecoverDeletedSecretOperation : Operation, IOperation
{
private static readonly TimeSpan s_defaultPollingInterval = TimeSpan.FromSeconds(2);
private readonly KeyVaultPipeline _pipeline;
+ private readonly OperationInternal _operationInternal;
private readonly SecretProperties _value;
- private Response _response;
- private bool _completed;
internal RecoverDeletedSecretOperation(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(RecoverDeletedSecretOperation), new[] { new KeyValuePair("secret", _value.Name) });
}
/// Initializes a new instance of for mocking.
@@ -44,33 +43,20 @@ protected RecoverDeletedSecretOperation() {}
public override SecretProperties 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(RecoverDeletedSecretOperation)}.{nameof(UpdateStatus)}");
- scope.AddAttribute("secret", _value.Name);
- scope.Start();
-
- try
- {
- _response = _pipeline.GetResponse(RequestMethod.Get, cancellationToken, SecretClient.SecretsPath, _value.Name, "/", _value.Version);
- _completed = CheckCompleted(_response);
- }
- catch (Exception e)
- {
- scope.Failed(e);
- throw;
- }
+ return _operationInternal.UpdateStatus(cancellationToken);
}
return GetRawResponse();
@@ -79,22 +65,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(RecoverDeletedSecretOperation)}.{nameof(UpdateStatus)}");
- scope.AddAttribute("secret", _value.Name);
- scope.Start();
-
- try
- {
- _response = await _pipeline.GetResponseAsync(RequestMethod.Get, cancellationToken, SecretClient.SecretsPath, _value.Name, "/", _value.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 +81,27 @@ public override ValueTask> WaitForCompletionAsync(Can
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 secret was recovered.
- return true;
-
- case 404:
- return false;
+ Response response = async
+ ? await _pipeline.GetResponseAsync(RequestMethod.Get, cancellationToken, SecretClient.SecretsPath, _value.Name, "/", _value.Version).ConfigureAwait(false)
+ : _pipeline.GetResponse(RequestMethod.Get, cancellationToken, SecretClient.SecretsPath, _value.Name, "/", _value.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 secret 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/SecretClient.cs b/sdk/keyvault/Azure.Security.KeyVault.Secrets/src/SecretClient.cs
index 13dbd5ed4099..88cc54903c65 100644
--- a/sdk/keyvault/Azure.Security.KeyVault.Secrets/src/SecretClient.cs
+++ b/sdk/keyvault/Azure.Security.KeyVault.Secrets/src/SecretClient.cs
@@ -159,7 +159,7 @@ public virtual AsyncPageable GetPropertiesOfSecretVersionsAsyn
{
Argument.AssertNotNullOrEmpty(name, nameof(name));
- Uri firstPageUri = new Uri(VaultUri, $"{SecretsPath}{name}/versions?api-version={_pipeline.ApiVersion}");
+ Uri firstPageUri = _pipeline.CreateFirstPageUri($"{SecretsPath}{name}/versions");
return PageResponseEnumerator.CreateAsyncEnumerable(nextLink => _pipeline.GetPageAsync(firstPageUri, nextLink, () => new SecretProperties(), "SecretClient.GetPropertiesOfSecretVersions", cancellationToken));
}
@@ -186,7 +186,7 @@ public virtual Pageable GetPropertiesOfSecretVersions(string n
{
Argument.AssertNotNullOrEmpty(name, nameof(name));
- Uri firstPageUri = new Uri(VaultUri, $"{SecretsPath}{name}/versions?api-version={_pipeline.ApiVersion}");
+ Uri firstPageUri = _pipeline.CreateFirstPageUri($"{SecretsPath}{name}/versions");
return PageResponseEnumerator.CreateEnumerable(nextLink => _pipeline.GetPage(firstPageUri, nextLink, () => new SecretProperties(), "SecretClient.GetPropertiesOfSecretVersions", cancellationToken));
}
@@ -204,7 +204,7 @@ public virtual Pageable GetPropertiesOfSecretVersions(string n
/// The server returned an error. See for details returned from the server.
public virtual AsyncPageable GetPropertiesOfSecretsAsync(CancellationToken cancellationToken = default)
{
- Uri firstPageUri = new Uri(VaultUri, SecretsPath + $"?api-version={_pipeline.ApiVersion}");
+ Uri firstPageUri = _pipeline.CreateFirstPageUri(SecretsPath);
return PageResponseEnumerator.CreateAsyncEnumerable(nextLink => _pipeline.GetPageAsync(firstPageUri, nextLink, () => new SecretProperties(), "SecretClient.GetPropertiesOfSecrets", cancellationToken));
}
@@ -222,7 +222,7 @@ public virtual AsyncPageable GetPropertiesOfSecretsAsync(Cance
/// The server returned an error. See for details returned from the server.
public virtual Pageable GetPropertiesOfSecrets(CancellationToken cancellationToken = default)
{
- Uri firstPageUri = new Uri(VaultUri, SecretsPath + $"?api-version={_pipeline.ApiVersion}");
+ Uri firstPageUri = _pipeline.CreateFirstPageUri(SecretsPath);
return PageResponseEnumerator.CreateEnumerable(nextLink => _pipeline.GetPage(firstPageUri, nextLink, () => new SecretProperties(), "SecretClient.GetPropertiesOfSecrets", cancellationToken));
}
@@ -545,7 +545,7 @@ public virtual Response GetDeletedSecret(string name, Cancellatio
/// The server returned an error. See for details returned from the server.
public virtual AsyncPageable GetDeletedSecretsAsync(CancellationToken cancellationToken = default)
{
- Uri firstPageUri = new Uri(VaultUri, DeletedSecretsPath + $"?api-version={_pipeline.ApiVersion}");
+ Uri firstPageUri = _pipeline.CreateFirstPageUri(DeletedSecretsPath);
return PageResponseEnumerator.CreateAsyncEnumerable(nextLink => _pipeline.GetPageAsync(firstPageUri, nextLink, () => new DeletedSecret(), "SecretClient.GetDeletedSecrets", cancellationToken));
}
@@ -562,7 +562,7 @@ public virtual AsyncPageable GetDeletedSecretsAsync(CancellationT
/// The server returned an error. See for details returned from the server.
public virtual Pageable GetDeletedSecrets(CancellationToken cancellationToken = default)
{
- Uri firstPageUri = new Uri(VaultUri, DeletedSecretsPath + $"?api-version={_pipeline.ApiVersion}");
+ Uri firstPageUri = _pipeline.CreateFirstPageUri(DeletedSecretsPath);
return PageResponseEnumerator.CreateEnumerable(nextLink => _pipeline.GetPage(firstPageUri, nextLink, () => new DeletedSecret(), "SecretClient.GetDeletedSecrets", cancellationToken));
}
diff --git a/sdk/keyvault/Azure.Security.KeyVault.Secrets/src/SecretProperties.cs b/sdk/keyvault/Azure.Security.KeyVault.Secrets/src/SecretProperties.cs
index f8a68384ab70..0aba21069c53 100644
--- a/sdk/keyvault/Azure.Security.KeyVault.Secrets/src/SecretProperties.cs
+++ b/sdk/keyvault/Azure.Security.KeyVault.Secrets/src/SecretProperties.cs
@@ -25,7 +25,6 @@ public class SecretProperties : IJsonDeserializable, IJsonSerializable
private static readonly JsonEncodedText s_attributesPropertyNameBytes = JsonEncodedText.Encode(AttributesPropertyName);
private static readonly JsonEncodedText s_tagsPropertyNameBytes = JsonEncodedText.Encode(TagsPropertyName);
- private ObjectId _identifier;
private SecretAttributes _attributes;
private Dictionary _tags;
private string _keyId;
@@ -44,7 +43,7 @@ public SecretProperties(string name)
{
Argument.AssertNotNullOrEmpty(name, nameof(name));
- _identifier.Name = name;
+ Name = name;
}
///
@@ -56,28 +55,28 @@ public SecretProperties(Uri id)
{
Argument.AssertNotNull(id, nameof(id));
- _identifier.ParseId("secrets", id);
+ ParseId(id);
}
///
/// Gets the secret identifier.
///
- public Uri Id { get => _identifier.Id; internal set => _identifier.Id = value; }
+ public Uri Id { get; internal set; }
///
/// Gets the Key Vault base .
///
- public Uri VaultUri { get => _identifier.VaultUri; internal set => _identifier.VaultUri = value; }
+ public Uri VaultUri { get; internal set; }
///
/// Gets the name of the secret.
///
- public string Name { get => _identifier.Name; internal set => _identifier.Name = value; }
+ public string Name { get; internal set; }
///
/// Gets the version of the secret.
///
- public string Version { get => _identifier.Version; internal set => _identifier.Version = value; }
+ public string Version { get; internal set; }
///
/// Gets or sets the content type of the secret value such as "text/plain" for a password.
@@ -142,6 +141,20 @@ public Uri KeyId
///
public IDictionary Tags => LazyInitializer.EnsureInitialized(ref _tags);
+ ///
+ /// Parses the key identifier into the , , and of the key.
+ ///
+ /// The key vault object identifier.
+ internal void ParseId(Uri id)
+ {
+ KeyVaultIdentifier identifier = KeyVaultIdentifier.ParseWithCollection(id, "secrets");
+
+ Id = id;
+ VaultUri = identifier.VaultUri;
+ Name = identifier.Name;
+ Version = identifier.Version;
+ }
+
internal void ReadProperties(JsonElement json)
{
foreach (JsonProperty prop in json.EnumerateObject())
@@ -155,7 +168,9 @@ internal void ReadProperty(JsonProperty prop)
switch (prop.Name)
{
case IdPropertyName:
- _identifier.ParseId("secrets", prop.Value.GetString());
+ string id = prop.Value.GetString();
+ Id = new Uri(id);
+ ParseId(Id);
break;
case ContentTypePropertyName:
diff --git a/sdk/keyvault/Azure.Security.KeyVault.Shared/src/KeyVaultIdentifier.cs b/sdk/keyvault/Azure.Security.KeyVault.Shared/src/KeyVaultIdentifier.cs
index c1036544012a..19244fb0135b 100644
--- a/sdk/keyvault/Azure.Security.KeyVault.Shared/src/KeyVaultIdentifier.cs
+++ b/sdk/keyvault/Azure.Security.KeyVault.Shared/src/KeyVaultIdentifier.cs
@@ -32,6 +32,17 @@ public static KeyVaultIdentifier Parse(Uri id)
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Invalid ObjectIdentifier: {0}. Bad number of segments: {1}", id, id.Segments.Length));
}
+ public static KeyVaultIdentifier ParseWithCollection(Uri id, string collection)
+ {
+ KeyVaultIdentifier identifier = Parse(id);
+ if (!string.Equals(identifier.Collection, collection, StringComparison.OrdinalIgnoreCase))
+ {
+ throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Invalid ObjectIdentifier: {0}. segment [1] should be '{1}/', found '{2}'", id, collection, identifier.Collection));
+ }
+
+ return identifier;
+ }
+
public static bool TryParse(Uri id, out KeyVaultIdentifier identifier)
{
if (id is null)