diff --git a/eng/Packages.Data.props b/eng/Packages.Data.props
index 4ed63e712b75..0b9123b4f4e1 100644
--- a/eng/Packages.Data.props
+++ b/eng/Packages.Data.props
@@ -174,7 +174,7 @@
All should have PrivateAssets="All" set so they don't become package dependencies
-->
-
+
diff --git a/eng/emitter-package.json b/eng/emitter-package.json
index 8612559055f9..8f3d5c1efa5d 100644
--- a/eng/emitter-package.json
+++ b/eng/emitter-package.json
@@ -1,6 +1,6 @@
{
"main": "dist/src/index.js",
"dependencies": {
- "@azure-tools/typespec-csharp": "0.2.0-beta.20230815.1"
+ "@azure-tools/typespec-csharp": "0.2.0-beta.20230817.1"
}
}
diff --git a/sdk/confidentialledger/Azure.Security.ConfidentialLedger/CHANGELOG.md b/sdk/confidentialledger/Azure.Security.ConfidentialLedger/CHANGELOG.md
index f29226a512f2..4a961f96fb47 100644
--- a/sdk/confidentialledger/Azure.Security.ConfidentialLedger/CHANGELOG.md
+++ b/sdk/confidentialledger/Azure.Security.ConfidentialLedger/CHANGELOG.md
@@ -1,6 +1,12 @@
# Release History
-## 1.2.0-beta.1 (Unreleased)
+## 1.2.0 (2023-08-03)
+
+### Bugs Fixed
+
+- Service calls that result in a `HttpStatusCode.NotFound` status will now be retried by default. This is to handle scenarios where there is an unexpected loss of session stickiness when the connected node changes and transactions have not been fully replicated.
+
+## 1.2.0-beta.1 (2022-11-09)
### Features Added
diff --git a/sdk/confidentialledger/Azure.Security.ConfidentialLedger/assets.json b/sdk/confidentialledger/Azure.Security.ConfidentialLedger/assets.json
index 445e1f9bf331..f19574db14f6 100644
--- a/sdk/confidentialledger/Azure.Security.ConfidentialLedger/assets.json
+++ b/sdk/confidentialledger/Azure.Security.ConfidentialLedger/assets.json
@@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "net",
"TagPrefix": "net/confidentialledger/Azure.Security.ConfidentialLedger",
- "Tag": "net/confidentialledger/Azure.Security.ConfidentialLedger_48488df791"
+ "Tag": "net/confidentialledger/Azure.Security.ConfidentialLedger_5657482b45"
}
diff --git a/sdk/confidentialledger/Azure.Security.ConfidentialLedger/src/Azure.Security.ConfidentialLedger.csproj b/sdk/confidentialledger/Azure.Security.ConfidentialLedger/src/Azure.Security.ConfidentialLedger.csproj
index 150fe921ab55..8fc08c597c37 100644
--- a/sdk/confidentialledger/Azure.Security.ConfidentialLedger/src/Azure.Security.ConfidentialLedger.csproj
+++ b/sdk/confidentialledger/Azure.Security.ConfidentialLedger/src/Azure.Security.ConfidentialLedger.csproj
@@ -2,7 +2,7 @@
Client SDK for the Azure Confidential Ledger service
Azure Confidential Ledger
- 1.2.0-beta.1
+ 1.2.0
1.1.0
Azure ConfidentialLedger
diff --git a/sdk/confidentialledger/Azure.Security.ConfidentialLedger/src/ConfidentialLedgerClient.cs b/sdk/confidentialledger/Azure.Security.ConfidentialLedger/src/ConfidentialLedgerClient.cs
index 4244c91a866b..f3237af5160f 100644
--- a/sdk/confidentialledger/Azure.Security.ConfidentialLedger/src/ConfidentialLedgerClient.cs
+++ b/sdk/confidentialledger/Azure.Security.ConfidentialLedger/src/ConfidentialLedgerClient.cs
@@ -78,11 +78,19 @@ internal ConfidentialLedgerClient(Uri ledgerEndpoint, TokenCredential credential
Array.Empty() :
new HttpPipelinePolicy[] { new BearerTokenAuthenticationPolicy(_tokenCredential, AuthorizationScopes) },
transportOptions,
- new ResponseClassifier());
+ new ConfidentialLedgerResponseClassifier());
_ledgerEndpoint = ledgerEndpoint;
_apiVersion = actualOptions.Version;
}
+ internal class ConfidentialLedgerResponseClassifier : ResponseClassifier
+ {
+ public override bool IsRetriableResponse(HttpMessage message)
+ {
+ return base.IsRetriableResponse(message) || message.Response.Status == 404;
+ }
+ }
+
/// Posts a new entry to the ledger. A collection id may optionally be specified.
///
/// Below is the JSON schema for the request and response payloads.
diff --git a/sdk/core/Azure.Core/perf/Serializations/JsonBenchmark.cs b/sdk/core/Azure.Core/perf/Serializations/JsonBenchmark.cs
index 3bf0044b1ae2..d3f64eb3c2b0 100644
--- a/sdk/core/Azure.Core/perf/Serializations/JsonBenchmark.cs
+++ b/sdk/core/Azure.Core/perf/Serializations/JsonBenchmark.cs
@@ -123,11 +123,23 @@ public void Serialize_PublicInterface()
[Benchmark]
[BenchmarkCategory("Internal")]
- public T Deserialize_Internal()
+ public T Deserialize_Internal_JsonElement()
{
return Deserialize(_jsonDocument.RootElement);
}
+ protected virtual T Deserialize(BinaryData data)
+ {
+ return Deserialize(JsonDocument.Parse(data).RootElement);
+ }
+
+ [Benchmark]
+ [BenchmarkCategory("Internal")]
+ public T Deserialize_Internal_BinaryData()
+ {
+ return Deserialize(_data);
+ }
+
[Benchmark]
[BenchmarkCategory("Cast")]
public T Deserialize_ExplicitCast()
@@ -189,5 +201,21 @@ public void JsonDocumentFromBinaryData()
{
using var doc = JsonDocument.Parse(_data);
}
+
+ protected virtual void ModifyValues(T model) { }
+
+ [Benchmark]
+ [BenchmarkCategory("Usage")]
+ public void EndToEndUseCase()
+ {
+ // Instantiate an input model
+ T model = ModelSerializer.Deserialize(_data);
+
+ // Set properties on it
+ ModifyValues(model);
+
+ // Send it over the wire - serialize
+ RequestContent content = CastToRequestContent();
+ }
}
}
diff --git a/sdk/core/Azure.Core/perf/Serializations/SimplePatchModelBenchmark.cs b/sdk/core/Azure.Core/perf/Serializations/SimplePatchModelBenchmark.cs
new file mode 100644
index 000000000000..34e74d3ba48c
--- /dev/null
+++ b/sdk/core/Azure.Core/perf/Serializations/SimplePatchModelBenchmark.cs
@@ -0,0 +1,39 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.IO;
+using System.Text.Json;
+using Azure.Core.Tests.PatchModels;
+
+namespace Azure.Core.Perf.Serializations
+{
+ public class SimplePatchModelBenchmark : JsonBenchmark
+ {
+ protected override SimplePatchModel CastFromResponse() => (SimplePatchModel)_response;
+
+ protected override RequestContent CastToRequestContent() => _model;
+
+ protected override SimplePatchModel Deserialize(JsonElement jsonElement)
+ {
+ using Stream stream = new MemoryStream();
+ using Utf8JsonWriter writer = new(stream);
+ jsonElement.WriteTo(writer);
+ writer.Flush();
+ stream.Position = 0;
+ return Deserialize(BinaryData.FromStream(stream));
+ }
+
+ protected override SimplePatchModel Deserialize(BinaryData data) => SimplePatchModel.Deserialize(data);
+
+ protected override void Serialize(Utf8JsonWriter writer) => _model.Serialize(writer);
+
+ protected override string JsonFileName => "SimplePatchModel.json";
+
+ protected override void ModifyValues(SimplePatchModel model)
+ {
+ model.Name = "xyz";
+ model.Count = 2;
+ }
+ }
+}
diff --git a/sdk/core/Azure.Core/perf/Serializations/SimpleStandardModelBenchmark.cs b/sdk/core/Azure.Core/perf/Serializations/SimpleStandardModelBenchmark.cs
new file mode 100644
index 000000000000..2c00accde5b0
--- /dev/null
+++ b/sdk/core/Azure.Core/perf/Serializations/SimpleStandardModelBenchmark.cs
@@ -0,0 +1,30 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System.Text.Json;
+using Azure.Core.Tests.PatchModels;
+
+namespace Azure.Core.Perf.Serializations
+{
+ public class SimpleStandardModelBenchmark : JsonBenchmark
+ {
+ protected override SimpleStandardModel CastFromResponse() => (SimpleStandardModel)_response;
+
+ protected override RequestContent CastToRequestContent() => _model;
+
+ protected override SimpleStandardModel Deserialize(JsonElement jsonElement)
+ {
+ return SimpleStandardModel.DeserializeSimpleStandardModel(jsonElement, new("J"));
+ }
+
+ protected override void Serialize(Utf8JsonWriter writer) => _model.Serialize(writer);
+
+ protected override string JsonFileName => "SimplePatchModel.json";
+
+ protected override void ModifyValues(SimpleStandardModel model)
+ {
+ model.Name = "xyz";
+ model.Count = 2;
+ }
+ }
+}
diff --git a/sdk/core/Azure.Core/src/DynamicData/DynamicData.cs b/sdk/core/Azure.Core/src/DynamicData/DynamicData.cs
index 03e2ced1e97b..1a978bcf7a0f 100644
--- a/sdk/core/Azure.Core/src/DynamicData/DynamicData.cs
+++ b/sdk/core/Azure.Core/src/DynamicData/DynamicData.cs
@@ -147,12 +147,12 @@ private IEnumerable GetEnumerable()
if (_options.PropertyNameFormat == JsonPropertyNames.UseExact ||
_element.TryGetProperty(name, out MutableJsonElement _))
{
- _element = _element.SetProperty(name, value);
+ SetPropertyInternal(name, value);
return null;
}
// The dynamic content has a specified property name format.
- _element = _element.SetProperty(FormatPropertyName(name), value);
+ SetPropertyInternal(FormatPropertyName(name), value);
// Binding machinery expects the call site signature to return an object
return null;
@@ -166,11 +166,8 @@ private IEnumerable GetEnumerable()
_ => false
};
- private object ConvertType(object value)
- {
- byte[] bytes = JsonSerializer.SerializeToUtf8Bytes(value, _serializerOptions);
- return JsonDocument.Parse(bytes);
- }
+ private JsonElement ConvertType(object value) =>
+ MutableJsonElement.SerializeToJsonElement(value, _serializerOptions);
private object? SetViaIndexer(object index, object value)
{
@@ -179,17 +176,147 @@ private object ConvertType(object value)
switch (index)
{
case string propertyName:
- _element = _element.SetProperty(propertyName, value);
+ SetPropertyInternal(propertyName, value);
return null;
case int arrayIndex:
MutableJsonElement element = _element.GetIndexElement(arrayIndex);
- element.Set(value);
+ SetInternal(ref element, value);
return new DynamicData(element, _options);
}
throw new InvalidOperationException($"Tried to access indexer with an unsupported index type: {index}");
}
+ private void SetPropertyInternal(string name, object value)
+ {
+ switch (value)
+ {
+ case bool b:
+ _element = _element.SetProperty(name, b);
+ break;
+ case string s:
+ _element = _element.SetProperty(name, s);
+ break;
+ case byte b:
+ _element = _element.SetProperty(name, b);
+ break;
+ case sbyte sb:
+ _element = _element.SetProperty(name, sb);
+ break;
+ case short sh:
+ _element = _element.SetProperty(name, sh);
+ break;
+ case ushort us:
+ _element = _element.SetProperty(name, us);
+ break;
+ case int i:
+ _element = _element.SetProperty(name, i);
+ break;
+ case uint u:
+ _element = _element.SetProperty(name, u);
+ break;
+ case long l:
+ _element = _element.SetProperty(name, l);
+ break;
+ case ulong ul:
+ _element = _element.SetProperty(name, ul);
+ break;
+ case float f:
+ _element = _element.SetProperty(name, f);
+ break;
+ case double d:
+ _element = _element.SetProperty(name, d);
+ break;
+ case decimal d:
+ _element = _element.SetProperty(name, d);
+ break;
+ case DateTime d:
+ _element = _element.SetProperty(name, d);
+ break;
+ case DateTimeOffset d:
+ _element = _element.SetProperty(name, d);
+ break;
+ case Guid g:
+ _element = _element.SetProperty(name, g);
+ break;
+ case null:
+ _element = _element.SetPropertyNull(name);
+ break;
+ case JsonElement e:
+ _element = _element.SetProperty(name, e);
+ break;
+ default:
+ JsonElement element = ConvertType(value);
+ _element = _element.SetProperty(name, element);
+ break;
+ }
+ }
+
+ private void SetInternal(ref MutableJsonElement element, object value)
+ {
+ switch (value)
+ {
+ case bool b:
+ element.Set(b);
+ break;
+ case string s:
+ element.Set(s);
+ break;
+ case byte b:
+ element.Set(b);
+ break;
+ case sbyte sb:
+ element.Set(sb);
+ break;
+ case short sh:
+ element.Set(sh);
+ break;
+ case ushort us:
+ element.Set(us);
+ break;
+ case int i:
+ element.Set(i);
+ break;
+ case uint u:
+ element.Set(u);
+ break;
+ case long l:
+ element.Set(l);
+ break;
+ case ulong ul:
+ element.Set(ul);
+ break;
+ case float f:
+ element.Set(f);
+ break;
+ case double d:
+ element.Set(d);
+ break;
+ case decimal d:
+ element.Set(d);
+ break;
+ case DateTime d:
+ element.Set(d);
+ break;
+ case DateTimeOffset d:
+ element.Set(d);
+ break;
+ case Guid g:
+ element.Set(g);
+ break;
+ case null:
+ element.SetNull();
+ break;
+ case JsonElement e:
+ element.Set(e);
+ break;
+ default:
+ JsonElement jsonElement = ConvertType(value);
+ element.Set(jsonElement);
+ break;
+ }
+ }
+
private T? ConvertTo()
{
JsonElement element = _element.GetJsonElement();
diff --git a/sdk/core/Azure.Core/src/Shared/MutableJsonChange.cs b/sdk/core/Azure.Core/src/Shared/MutableJsonChange.cs
index 2d781ad4f4f0..b51cdf5eede9 100644
--- a/sdk/core/Azure.Core/src/Shared/MutableJsonChange.cs
+++ b/sdk/core/Azure.Core/src/Shared/MutableJsonChange.cs
@@ -2,41 +2,23 @@
// Licensed under the MIT License.
using System;
-using System.Buffers;
using System.Text.Json;
-#nullable enable
-
namespace Azure.Core.Json
{
internal struct MutableJsonChange
{
- private JsonElement? _serializedValue;
- private readonly JsonSerializerOptions _serializerOptions;
-
public MutableJsonChange(string path,
int index,
object? value,
- JsonSerializerOptions options,
MutableJsonChangeKind changeKind,
string? addedPropertyName)
{
Path = path;
Index = index;
Value = value;
- _serializerOptions = options;
ChangeKind = changeKind;
AddedPropertyName = addedPropertyName;
-
- if (value is JsonElement element)
- {
- _serializedValue = element;
- }
-
- if (value is JsonDocument doc)
- {
- _serializedValue = doc.RootElement;
- }
}
public string Path { get; }
@@ -49,18 +31,63 @@ public MutableJsonChange(string path,
public MutableJsonChangeKind ChangeKind { get; }
- public JsonValueKind ValueKind => GetSerializedValue().ValueKind;
+ public readonly JsonValueKind ValueKind => Value switch
+ {
+ null => JsonValueKind.Null,
+ bool b => b ? JsonValueKind.True : JsonValueKind.False,
+ string => JsonValueKind.String,
+ DateTime => JsonValueKind.String,
+ DateTimeOffset => JsonValueKind.String,
+ Guid => JsonValueKind.String,
+ byte => JsonValueKind.Number,
+ sbyte => JsonValueKind.Number,
+ short => JsonValueKind.Number,
+ ushort => JsonValueKind.Number,
+ int => JsonValueKind.Number,
+ uint => JsonValueKind.Number,
+ long => JsonValueKind.Number,
+ ulong => JsonValueKind.Number,
+ float => JsonValueKind.Number,
+ double => JsonValueKind.Number,
+ decimal => JsonValueKind.Number,
+ JsonElement e => e.ValueKind,
+ _ => throw new InvalidOperationException($"Unrecognized change type '{Value.GetType()}'.")
+ };
+
+ internal readonly void EnsureString()
+ {
+ if (ValueKind != JsonValueKind.String)
+ {
+ throw new InvalidOperationException($"Expected a 'String' kind but was '{ValueKind}'.");
+ }
+ }
- internal JsonElement GetSerializedValue()
+ internal readonly void EnsureNumber()
{
- if (_serializedValue != null)
+ if (ValueKind != JsonValueKind.Number)
{
- return _serializedValue.Value;
+ throw new InvalidOperationException($"Expected a 'Number' kind but was '{ValueKind}'.");
}
+ }
- byte[] bytes = JsonSerializer.SerializeToUtf8Bytes(Value, _serializerOptions);
- _serializedValue = JsonDocument.Parse(bytes).RootElement;
- return _serializedValue.Value;
+ internal readonly void EnsureArray()
+ {
+ if (ValueKind != JsonValueKind.Array)
+ {
+ throw new InvalidOperationException($"Expected an 'Array' kind but was '{ValueKind}'.");
+ }
+ }
+
+ internal readonly int GetArrayLength()
+ {
+ EnsureArray();
+
+ if (Value is JsonElement e)
+ {
+ return e.GetArrayLength();
+ }
+
+ throw new InvalidOperationException($"Expected an 'Array' kind but was '{ValueKind}'.");
}
internal bool IsDescendant(string path)
@@ -112,7 +139,12 @@ internal bool IsGreaterThan(ReadOnlySpan otherPath)
internal string AsString()
{
- return GetSerializedValue().ToString() ?? "null";
+ if (Value is null)
+ {
+ return "null";
+ }
+
+ return Value.ToString()!;
}
public override string ToString()
diff --git a/sdk/core/Azure.Core/src/Shared/MutableJsonDocument.ChangeTracker.cs b/sdk/core/Azure.Core/src/Shared/MutableJsonDocument.ChangeTracker.cs
index 6e08419ac9ac..0e971ad32dbb 100644
--- a/sdk/core/Azure.Core/src/Shared/MutableJsonDocument.ChangeTracker.cs
+++ b/sdk/core/Azure.Core/src/Shared/MutableJsonDocument.ChangeTracker.cs
@@ -4,9 +4,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
-using System.Text.Json;
-
-#nullable enable
namespace Azure.Core.Json
{
@@ -14,13 +11,7 @@ internal partial class MutableJsonDocument
{
internal class ChangeTracker
{
- public ChangeTracker(JsonSerializerOptions options)
- {
- _options = options;
- }
-
private List? _changes;
- private JsonSerializerOptions _options;
internal const char Delimiter = (char)1;
@@ -102,7 +93,7 @@ internal int AddChange(string path, object? value, MutableJsonChangeKind changeK
int index = _changes.Count;
- _changes.Add(new MutableJsonChange(path, index, value, _options, changeKind, addedPropertyName));
+ _changes.Add(new MutableJsonChange(path, index, value, changeKind, addedPropertyName));
return index;
}
diff --git a/sdk/core/Azure.Core/src/Shared/MutableJsonDocument.cs b/sdk/core/Azure.Core/src/Shared/MutableJsonDocument.cs
index bb4f539dcb32..2cdc0639041d 100644
--- a/sdk/core/Azure.Core/src/Shared/MutableJsonDocument.cs
+++ b/sdk/core/Azure.Core/src/Shared/MutableJsonDocument.cs
@@ -8,8 +8,6 @@
using System.Text.Json;
using System.Text.Json.Serialization;
-#nullable enable
-
namespace Azure.Core.Json
{
///
@@ -18,21 +16,15 @@ namespace Azure.Core.Json
[JsonConverter(typeof(MutableJsonDocumentConverter))]
internal sealed partial class MutableJsonDocument : IDisposable
{
- private static readonly ReadOnlyMemory _emptyJson = "{}"u8.ToArray();
- public static ReadOnlyMemory EmptyJson => _emptyJson;
-
private readonly ReadOnlyMemory _original;
private readonly JsonDocument _originalDocument;
- private readonly JsonSerializerOptions _serializerOptions;
- internal JsonSerializerOptions SerializerOptions { get => _serializerOptions; }
-
private ChangeTracker? _changeTracker;
internal ChangeTracker Changes
{
get
{
- _changeTracker ??= new ChangeTracker(SerializerOptions);
+ _changeTracker ??= new();
return _changeTracker;
}
}
@@ -208,7 +200,6 @@ private MutableJsonDocument(JsonDocument document, ReadOnlyMemory utf8Json
{
_originalDocument = document;
_original = utf8Json;
- _serializerOptions = serializerOptions ?? new JsonSerializerOptions();
}
private class MutableJsonDocumentConverter : JsonConverter
diff --git a/sdk/core/Azure.Core/src/Shared/MutableJsonElement.ArrayEnumerator.cs b/sdk/core/Azure.Core/src/Shared/MutableJsonElement.ArrayEnumerator.cs
index aecd0a1e516a..0c580cd94487 100644
--- a/sdk/core/Azure.Core/src/Shared/MutableJsonElement.ArrayEnumerator.cs
+++ b/sdk/core/Azure.Core/src/Shared/MutableJsonElement.ArrayEnumerator.cs
@@ -16,7 +16,7 @@ internal partial struct MutableJsonElement
/// An enumerable and enumerator for the contents of a mutable JSON array.
///
[DebuggerDisplay("{Current,nq}")]
- public struct ArrayEnumerator : IEnumerable, IEnumerator
+ internal struct ArrayEnumerator : IEnumerable, IEnumerator
{
private readonly MutableJsonElement _element;
private readonly int _length;
diff --git a/sdk/core/Azure.Core/src/Shared/MutableJsonElement.ObjectEnumerator.cs b/sdk/core/Azure.Core/src/Shared/MutableJsonElement.ObjectEnumerator.cs
index 201f8440ad84..46468e4b9bf6 100644
--- a/sdk/core/Azure.Core/src/Shared/MutableJsonElement.ObjectEnumerator.cs
+++ b/sdk/core/Azure.Core/src/Shared/MutableJsonElement.ObjectEnumerator.cs
@@ -20,7 +20,7 @@ internal readonly partial struct MutableJsonElement
/// An enumerable and enumerator for the properties of a JSON object.
///
[DebuggerDisplay("{Current,nq}")]
- public struct ObjectEnumerator : IEnumerable<(string Name, MutableJsonElement Value)>, IEnumerator<(string Name, MutableJsonElement Value)>
+ internal struct ObjectEnumerator : IEnumerable<(string Name, MutableJsonElement Value)>, IEnumerator<(string Name, MutableJsonElement Value)>
{
private readonly MutableJsonElement _target;
private JsonElement.ObjectEnumerator _enumerator;
diff --git a/sdk/core/Azure.Core/src/Shared/MutableJsonElement.WriteTo.cs b/sdk/core/Azure.Core/src/Shared/MutableJsonElement.WriteTo.cs
index 75ae5a7d61d9..eb0453552cde 100644
--- a/sdk/core/Azure.Core/src/Shared/MutableJsonElement.WriteTo.cs
+++ b/sdk/core/Azure.Core/src/Shared/MutableJsonElement.WriteTo.cs
@@ -5,17 +5,10 @@
using System.Collections.Generic;
using System.Text.Json;
-#nullable enable
-
namespace Azure.Core.Json
{
internal partial struct MutableJsonElement
{
- internal void WriteTo(Utf8JsonWriter writer)
- {
- WriteElement(_path, _highWaterMark, _element, writer);
- }
-
internal void WriteTo(Utf8JsonWriter writer, string format)
{
switch (format)
@@ -31,38 +24,25 @@ internal void WriteTo(Utf8JsonWriter writer, string format)
break;
}
}
+ internal void WriteTo(Utf8JsonWriter writer)
+ {
+ WriteElement(_path, _highWaterMark, _element, writer);
+ }
private void WriteElement(string path, int highWaterMark, JsonElement element, Utf8JsonWriter writer)
{
if (Changes.TryGetChange(path, highWaterMark, out MutableJsonChange change))
{
- switch (change.Value)
+ if (change.Value is JsonElement changeElement)
{
- case int i:
- writer.WriteNumberValue(i);
- return;
- case long l:
- writer.WriteNumberValue(l);
- return;
- case double d:
- writer.WriteNumberValue(d);
- return;
- case float f:
- writer.WriteNumberValue(f);
- return;
- case bool b:
- writer.WriteBooleanValue(b);
- return;
- case null:
- writer.WriteNullValue();
- return;
- default:
- break;
-
- // Note: string is not included to let JsonElement handle escaping.
+ element = changeElement;
+ }
+ else
+ {
+ WritePrimitiveChange(change, writer);
+ return;
}
- element = change.GetSerializedValue();
highWaterMark = change.Index;
}
@@ -107,7 +87,14 @@ private void WriteObject(string path, int highWaterMark, JsonElement element, Ut
string propertyPath = MutableJsonDocument.ChangeTracker.PushProperty(path, propertyName);
writer.WritePropertyName(propertyName);
- WriteElement(propertyPath, highWaterMark, property.GetSerializedValue(), writer);
+ if (property.Value is JsonElement changeElement)
+ {
+ WriteElement(propertyPath, highWaterMark, changeElement, writer);
+ }
+ else
+ {
+ WritePrimitiveChange(property, writer);
+ }
}
writer.WriteEndObject();
@@ -126,5 +113,64 @@ private void WriteArray(string path, int highWaterMark, JsonElement element, Utf
writer.WriteEndArray();
}
+ private void WritePrimitiveChange(MutableJsonChange change, Utf8JsonWriter writer)
+ {
+ switch (change.Value)
+ {
+ case bool b:
+ writer.WriteBooleanValue(b);
+ return;
+ case byte b:
+ writer.WriteNumberValue(b);
+ return;
+ case sbyte sb:
+ writer.WriteNumberValue(sb);
+ return;
+ case short sh:
+ writer.WriteNumberValue(sh);
+ return;
+ case ushort us:
+ writer.WriteNumberValue(us);
+ return;
+ case int i:
+ writer.WriteNumberValue(i);
+ return;
+ case uint u:
+ writer.WriteNumberValue(u);
+ return;
+ case long l:
+ writer.WriteNumberValue(l);
+ return;
+ case ulong ul:
+ writer.WriteNumberValue(ul);
+ return;
+ case float f:
+ writer.WriteNumberValue(f);
+ return;
+ case double d:
+ writer.WriteNumberValue(d);
+ return;
+ case decimal d:
+ writer.WriteNumberValue(d);
+ return;
+ case string s:
+ writer.WriteStringValue(s);
+ return;
+ case DateTime d:
+ writer.WriteStringValue(d);
+ return;
+ case DateTimeOffset d:
+ writer.WriteStringValue(d);
+ return;
+ case Guid g:
+ writer.WriteStringValue(g);
+ return;
+ case null:
+ writer.WriteNullValue();
+ return;
+ default:
+ throw new InvalidOperationException($"Unrecognized change type '{change.Value.GetType()}'.");
+ }
+ }
}
}
diff --git a/sdk/core/Azure.Core/src/Shared/MutableJsonElement.cs b/sdk/core/Azure.Core/src/Shared/MutableJsonElement.cs
index c0ec625e63ed..0212d85d81d2 100644
--- a/sdk/core/Azure.Core/src/Shared/MutableJsonElement.cs
+++ b/sdk/core/Azure.Core/src/Shared/MutableJsonElement.cs
@@ -9,8 +9,6 @@
using System.Text.Json;
using System.Text.Json.Serialization;
-#nullable enable
-
namespace Azure.Core.Json
{
///
@@ -119,7 +117,7 @@ public bool TryGetProperty(ReadOnlySpan name, out MutableJsonElement value
return false;
}
- value = new MutableJsonElement(_root, change.GetSerializedValue(), GetString(path, 0, pathLength), change.Index);
+ value = new MutableJsonElement(_root, SerializeToJsonElement(change.Value), GetString(path, 0, pathLength), change.Index);
return true;
}
@@ -159,7 +157,7 @@ public int GetArrayLength()
if (Changes.TryGetChange(_path, _highWaterMark, out MutableJsonChange change))
{
- return change.GetSerializedValue().GetArrayLength();
+ return change.GetArrayLength();
}
return _element.GetArrayLength();
@@ -174,7 +172,7 @@ internal MutableJsonElement GetIndexElement(int index)
string path = MutableJsonDocument.ChangeTracker.PushIndex(_path, index);
if (Changes.TryGetChange(path, _highWaterMark, out MutableJsonChange change))
{
- return new MutableJsonElement(_root, change.GetSerializedValue(), path, change.Index);
+ return new MutableJsonElement(_root, SerializeToJsonElement(change.Value), path, change.Index);
}
return new MutableJsonElement(_root, _element[index], path, _highWaterMark);
@@ -194,6 +192,8 @@ public bool TryGetDouble(out double value)
if (Changes.TryGetChange(_path, _highWaterMark, out MutableJsonChange change))
{
+ change.EnsureNumber();
+
switch (change.Value)
{
case double d:
@@ -205,7 +205,8 @@ public bool TryGetDouble(out double value)
value = default;
return false;
default:
- return change.GetSerializedValue().TryGetDouble(out value);
+ value = checked((double)change.Value);
+ return true;
}
}
@@ -247,6 +248,8 @@ public bool TryGetInt32(out int value)
if (Changes.TryGetChange(_path, _highWaterMark, out MutableJsonChange change))
{
+ change.EnsureNumber();
+
switch (change.Value)
{
case int i:
@@ -258,7 +261,8 @@ public bool TryGetInt32(out int value)
value = default;
return false;
default:
- return change.GetSerializedValue().TryGetInt32(out value);
+ value = checked((int)change.Value);
+ return true;
}
}
@@ -295,6 +299,8 @@ public bool TryGetInt64(out long value)
if (Changes.TryGetChange(_path, _highWaterMark, out MutableJsonChange change))
{
+ change.EnsureNumber();
+
switch (change.Value)
{
case long l:
@@ -306,7 +312,8 @@ public bool TryGetInt64(out long value)
value = default;
return false;
default:
- return change.GetSerializedValue().TryGetInt64(out value);
+ value = checked((long)change.Value);
+ return true;
}
}
@@ -343,6 +350,8 @@ public bool TryGetSingle(out float value)
if (Changes.TryGetChange(_path, _highWaterMark, out MutableJsonChange change))
{
+ change.EnsureNumber();
+
switch (change.Value)
{
case float f:
@@ -354,7 +363,8 @@ public bool TryGetSingle(out float value)
value = default;
return false;
default:
- return change.GetSerializedValue().TryGetSingle(out value);
+ value = checked((float)change.Value);
+ return true;
}
}
@@ -388,6 +398,8 @@ public float GetSingle()
if (Changes.TryGetChange(_path, _highWaterMark, out MutableJsonChange change))
{
+ change.EnsureString();
+
switch (change.Value)
{
case string s:
@@ -397,11 +409,6 @@ public float GetSingle()
case null:
return null;
default:
- JsonElement el = change.GetSerializedValue();
- if (el.ValueKind == JsonValueKind.String)
- {
- return el.GetString();
- }
throw new InvalidOperationException($"Element at '{_path}' is not a string.");
}
}
@@ -437,6 +444,8 @@ public bool TryGetByte(out byte value)
if (Changes.TryGetChange(_path, _highWaterMark, out MutableJsonChange change))
{
+ change.EnsureNumber();
+
switch (change.Value)
{
case byte b:
@@ -448,7 +457,8 @@ public bool TryGetByte(out byte value)
value = default;
return false;
default:
- return change.GetSerializedValue().TryGetByte(out value);
+ value = checked((byte)change.Value);
+ return true;
}
}
@@ -471,18 +481,24 @@ public bool TryGetDateTime(out DateTime value)
if (Changes.TryGetChange(_path, _highWaterMark, out MutableJsonChange change))
{
+ change.EnsureString();
+
switch (change.Value)
{
case DateTime d:
value = d;
return true;
+ case DateTimeOffset:
+ case string:
+ SerializeToJsonElement(change.Value).TryGetDateTime(out value);
+ return true;
case JsonElement element:
return element.TryGetDateTime(out value);
case null:
value = default;
return false;
default:
- return change.GetSerializedValue().TryGetDateTime(out value);
+ throw new InvalidOperationException($"Element {change.Value} cannot be converted to DateTime.");
}
}
@@ -505,19 +521,24 @@ public bool TryGetDateTimeOffset(out DateTimeOffset value)
if (Changes.TryGetChange(_path, _highWaterMark, out MutableJsonChange change))
{
+ change.EnsureString();
+
switch (change.Value)
{
case DateTimeOffset o:
value = o;
return true;
- ;
+ case DateTime:
+ case string:
+ SerializeToJsonElement(change.Value).TryGetDateTimeOffset(out value);
+ return true;
case JsonElement element:
return element.TryGetDateTimeOffset(out value);
case null:
value = default;
return false;
default:
- return change.GetSerializedValue().TryGetDateTimeOffset(out value);
+ throw new InvalidOperationException($"Element {change.Value} cannot be converted to DateTimeOffset.");
}
}
@@ -540,6 +561,8 @@ public bool TryGetDecimal(out decimal value)
if (Changes.TryGetChange(_path, _highWaterMark, out MutableJsonChange change))
{
+ change.EnsureNumber();
+
switch (change.Value)
{
case decimal d:
@@ -551,7 +574,8 @@ public bool TryGetDecimal(out decimal value)
value = default;
return false;
default:
- return change.GetSerializedValue().TryGetDecimal(out value);
+ value = checked((decimal)change.Value);
+ return true;
}
}
@@ -574,18 +598,23 @@ public bool TryGetGuid(out Guid value)
if (Changes.TryGetChange(_path, _highWaterMark, out MutableJsonChange change))
{
+ change.EnsureString();
+
switch (change.Value)
{
case Guid g:
value = g;
return true;
+ case string:
+ SerializeToJsonElement(change.Value).TryGetGuid(out value);
+ return true;
case JsonElement element:
return element.TryGetGuid(out value);
case null:
value = default;
return false;
default:
- return change.GetSerializedValue().TryGetGuid(out value);
+ throw new InvalidOperationException($"Element {change.Value} cannot be converted to Guid.");
}
}
@@ -608,6 +637,8 @@ public bool TryGetInt16(out short value)
if (Changes.TryGetChange(_path, _highWaterMark, out MutableJsonChange change))
{
+ change.EnsureNumber();
+
switch (change.Value)
{
case short s:
@@ -619,7 +650,8 @@ public bool TryGetInt16(out short value)
value = default;
return false;
default:
- return change.GetSerializedValue().TryGetInt16(out value);
+ value = checked((short)change.Value);
+ return true;
}
}
@@ -642,6 +674,8 @@ public bool TryGetSByte(out sbyte value)
if (Changes.TryGetChange(_path, _highWaterMark, out MutableJsonChange change))
{
+ change.EnsureNumber();
+
switch (change.Value)
{
case sbyte b:
@@ -653,7 +687,8 @@ public bool TryGetSByte(out sbyte value)
value = default;
return false;
default:
- return change.GetSerializedValue().TryGetSByte(out value);
+ value = checked((sbyte)change.Value);
+ return true;
}
}
@@ -676,6 +711,8 @@ public bool TryGetUInt16(out ushort value)
if (Changes.TryGetChange(_path, _highWaterMark, out MutableJsonChange change))
{
+ change.EnsureNumber();
+
switch (change.Value)
{
case ushort u:
@@ -687,7 +724,8 @@ public bool TryGetUInt16(out ushort value)
value = default;
return false;
default:
- return change.GetSerializedValue().TryGetUInt16(out value);
+ value = checked((ushort)change.Value);
+ return true;
}
}
@@ -710,6 +748,8 @@ public bool TryGetUInt32(out uint value)
if (Changes.TryGetChange(_path, _highWaterMark, out MutableJsonChange change))
{
+ change.EnsureNumber();
+
switch (change.Value)
{
case uint d:
@@ -721,7 +761,8 @@ public bool TryGetUInt32(out uint value)
value = default;
return false;
default:
- return change.GetSerializedValue().TryGetUInt32(out value);
+ value = checked((uint)change.Value);
+ return true;
}
}
@@ -744,6 +785,8 @@ public bool TryGetUInt64(out ulong value)
if (Changes.TryGetChange(_path, _highWaterMark, out MutableJsonChange change))
{
+ change.EnsureNumber();
+
switch (change.Value)
{
case ulong u:
@@ -755,7 +798,8 @@ public bool TryGetUInt64(out ulong value)
value = default;
return false;
default:
- return change.GetSerializedValue().TryGetUInt64(out value);
+ value = checked((ulong)change.Value);
+ return true;
}
}
@@ -796,34 +840,6 @@ public ObjectEnumerator EnumerateObject()
return new ObjectEnumerator(this);
}
- ///
- /// Set the value of the property with the specified name to the passed-in value. If the property is not already present, it will be created.
- ///
- ///
- /// The value to assign to the element.
- public MutableJsonElement SetProperty(string name, object value)
- {
- if (TryGetProperty(name, out MutableJsonElement element))
- {
- element.Set(value);
- return this;
- }
-
-#if !NET6_0_OR_GREATER
- // Earlier versions of JsonSerializer.Serialize include "RootElement"
- // as a property when called on JsonDocument.
- if (value is JsonDocument doc)
- {
- value = doc.RootElement;
- }
-#endif
-
- // It is a new property.
- string path = MutableJsonDocument.ChangeTracker.PushProperty(_path, name);
- Changes.AddChange(path, GetSerializedValue(value), MutableJsonChangeKind.PropertyAddition, name);
- return this;
- }
-
///
/// Remove the property with the specified name from the current MutableJsonElement.
///
@@ -855,6 +871,19 @@ public void Set(double value)
Changes.AddChange(_path, value);
}
+ public MutableJsonElement SetProperty(string name, double value)
+ {
+ if (TryGetProperty(name, out MutableJsonElement element))
+ {
+ element.Set(value);
+ return this;
+ }
+
+ string path = MutableJsonDocument.ChangeTracker.PushProperty(_path, name);
+ Changes.AddChange(path, value, MutableJsonChangeKind.PropertyAddition, name);
+ return this;
+ }
+
///
/// Sets the value of this element to the passed-in value.
///
@@ -865,6 +894,18 @@ public void Set(int value)
Changes.AddChange(_path, value);
}
+ public MutableJsonElement SetProperty(string name, int value)
+ {
+ if (TryGetProperty(name, out MutableJsonElement element))
+ {
+ element.Set(value);
+ return this;
+ }
+
+ string path = MutableJsonDocument.ChangeTracker.PushProperty(_path, name);
+ Changes.AddChange(path, value, MutableJsonChangeKind.PropertyAddition, name);
+ return this;
+ }
///
/// Sets the value of this element to the passed-in value.
@@ -877,6 +918,19 @@ public void Set(long value)
Changes.AddChange(_path, value);
}
+ public MutableJsonElement SetProperty(string name, long value)
+ {
+ if (TryGetProperty(name, out MutableJsonElement element))
+ {
+ element.Set(value);
+ return this;
+ }
+
+ string path = MutableJsonDocument.ChangeTracker.PushProperty(_path, name);
+ Changes.AddChange(path, value, MutableJsonChangeKind.PropertyAddition, name);
+ return this;
+ }
+
///
/// Sets the value of this element to the passed-in value.
///
@@ -887,6 +941,18 @@ public void Set(float value)
Changes.AddChange(_path, value);
}
+ public MutableJsonElement SetProperty(string name, float value)
+ {
+ if (TryGetProperty(name, out MutableJsonElement element))
+ {
+ element.Set(value);
+ return this;
+ }
+
+ string path = MutableJsonDocument.ChangeTracker.PushProperty(_path, name);
+ Changes.AddChange(path, value, MutableJsonChangeKind.PropertyAddition, name);
+ return this;
+ }
///
/// Sets the value of this element to the passed-in value.
@@ -899,6 +965,39 @@ public void Set(string value)
Changes.AddChange(_path, value);
}
+ public MutableJsonElement SetProperty(string name, string value)
+ {
+ if (TryGetProperty(name, out MutableJsonElement element))
+ {
+ element.Set(value);
+ return this;
+ }
+
+ string path = MutableJsonDocument.ChangeTracker.PushProperty(_path, name);
+ Changes.AddChange(path, value, MutableJsonChangeKind.PropertyAddition, name);
+ return this;
+ }
+
+ public void SetNull()
+ {
+ EnsureValid();
+
+ Changes.AddChange(_path, null);
+ }
+
+ public MutableJsonElement SetPropertyNull(string name)
+ {
+ if (TryGetProperty(name, out MutableJsonElement element))
+ {
+ element.SetNull();
+ return this;
+ }
+
+ string path = MutableJsonDocument.ChangeTracker.PushProperty(_path, name);
+ Changes.AddChange(path, null, MutableJsonChangeKind.PropertyAddition, name);
+ return this;
+ }
+
///
/// Sets the value of this element to the passed-in value.
///
@@ -910,6 +1009,19 @@ public void Set(bool value)
Changes.AddChange(_path, value);
}
+ public MutableJsonElement SetProperty(string name, bool value)
+ {
+ if (TryGetProperty(name, out MutableJsonElement element))
+ {
+ element.Set(value);
+ return this;
+ }
+
+ string path = MutableJsonDocument.ChangeTracker.PushProperty(_path, name);
+ Changes.AddChange(path, value, MutableJsonChangeKind.PropertyAddition, name);
+ return this;
+ }
+
///
/// Sets the value of this element to the passed-in value.
///
@@ -921,6 +1033,19 @@ public void Set(byte value)
Changes.AddChange(_path, value);
}
+ public MutableJsonElement SetProperty(string name, byte value)
+ {
+ if (TryGetProperty(name, out MutableJsonElement element))
+ {
+ element.Set(value);
+ return this;
+ }
+
+ string path = MutableJsonDocument.ChangeTracker.PushProperty(_path, name);
+ Changes.AddChange(path, value, MutableJsonChangeKind.PropertyAddition, name);
+ return this;
+ }
+
///
/// Sets the value of this element to the passed-in value.
///
@@ -932,6 +1057,19 @@ public void Set(sbyte value)
Changes.AddChange(_path, value);
}
+ public MutableJsonElement SetProperty(string name, sbyte value)
+ {
+ if (TryGetProperty(name, out MutableJsonElement element))
+ {
+ element.Set(value);
+ return this;
+ }
+
+ string path = MutableJsonDocument.ChangeTracker.PushProperty(_path, name);
+ Changes.AddChange(path, value, MutableJsonChangeKind.PropertyAddition, name);
+ return this;
+ }
+
///
/// Sets the value of this element to the passed-in value.
///
@@ -942,6 +1080,18 @@ public void Set(short value)
Changes.AddChange(_path, value);
}
+ public MutableJsonElement SetProperty(string name, short value)
+ {
+ if (TryGetProperty(name, out MutableJsonElement element))
+ {
+ element.Set(value);
+ return this;
+ }
+
+ string path = MutableJsonDocument.ChangeTracker.PushProperty(_path, name);
+ Changes.AddChange(path, value, MutableJsonChangeKind.PropertyAddition, name);
+ return this;
+ }
///
/// Sets the value of this element to the passed-in value.
@@ -954,6 +1104,19 @@ public void Set(ushort value)
Changes.AddChange(_path, value);
}
+ public MutableJsonElement SetProperty(string name, ushort value)
+ {
+ if (TryGetProperty(name, out MutableJsonElement element))
+ {
+ element.Set(value);
+ return this;
+ }
+
+ string path = MutableJsonDocument.ChangeTracker.PushProperty(_path, name);
+ Changes.AddChange(path, value, MutableJsonChangeKind.PropertyAddition, name);
+ return this;
+ }
+
///
/// Sets the value of this element to the passed-in value.
///
@@ -965,6 +1128,19 @@ public void Set(uint value)
Changes.AddChange(_path, value);
}
+ public MutableJsonElement SetProperty(string name, uint value)
+ {
+ if (TryGetProperty(name, out MutableJsonElement element))
+ {
+ element.Set(value);
+ return this;
+ }
+
+ string path = MutableJsonDocument.ChangeTracker.PushProperty(_path, name);
+ Changes.AddChange(path, value, MutableJsonChangeKind.PropertyAddition, name);
+ return this;
+ }
+
///
/// Sets the value of this element to the passed-in value.
///
@@ -976,6 +1152,19 @@ public void Set(ulong value)
Changes.AddChange(_path, value);
}
+ public MutableJsonElement SetProperty(string name, ulong value)
+ {
+ if (TryGetProperty(name, out MutableJsonElement element))
+ {
+ element.Set(value);
+ return this;
+ }
+
+ string path = MutableJsonDocument.ChangeTracker.PushProperty(_path, name);
+ Changes.AddChange(path, value, MutableJsonChangeKind.PropertyAddition, name);
+ return this;
+ }
+
///
/// Sets the value of this element to the passed-in value.
///
@@ -987,6 +1176,19 @@ public void Set(decimal value)
Changes.AddChange(_path, value);
}
+ public MutableJsonElement SetProperty(string name, decimal value)
+ {
+ if (TryGetProperty(name, out MutableJsonElement element))
+ {
+ element.Set(value);
+ return this;
+ }
+
+ string path = MutableJsonDocument.ChangeTracker.PushProperty(_path, name);
+ Changes.AddChange(path, value, MutableJsonChangeKind.PropertyAddition, name);
+ return this;
+ }
+
///
/// Sets the value of this element to the passed-in value.
///
@@ -997,6 +1199,18 @@ public void Set(Guid value)
Changes.AddChange(_path, value);
}
+ public MutableJsonElement SetProperty(string name, Guid value)
+ {
+ if (TryGetProperty(name, out MutableJsonElement element))
+ {
+ element.Set(value);
+ return this;
+ }
+
+ string path = MutableJsonDocument.ChangeTracker.PushProperty(_path, name);
+ Changes.AddChange(path, value, MutableJsonChangeKind.PropertyAddition, name);
+ return this;
+ }
///
/// Sets the value of this element to the passed-in value.
@@ -1009,6 +1223,19 @@ public void Set(DateTime value)
Changes.AddChange(_path, value);
}
+ public MutableJsonElement SetProperty(string name, DateTime value)
+ {
+ if (TryGetProperty(name, out MutableJsonElement element))
+ {
+ element.Set(value);
+ return this;
+ }
+
+ string path = MutableJsonDocument.ChangeTracker.PushProperty(_path, name);
+ Changes.AddChange(path, value, MutableJsonChangeKind.PropertyAddition, name);
+ return this;
+ }
+
///
/// Sets the value of this element to the passed-in value.
///
@@ -1020,98 +1247,41 @@ public void Set(DateTimeOffset value)
Changes.AddChange(_path, value);
}
+ public MutableJsonElement SetProperty(string name, DateTimeOffset value)
+ {
+ if (TryGetProperty(name, out MutableJsonElement element))
+ {
+ element.Set(value);
+ return this;
+ }
+
+ string path = MutableJsonDocument.ChangeTracker.PushProperty(_path, name);
+ Changes.AddChange(path, value, MutableJsonChangeKind.PropertyAddition, name);
+ return this;
+ }
+
///
/// Sets the value of this element to the passed-in value.
///
/// The value to assign to the element.
- public void Set(object value)
+ public void Set(JsonElement value)
{
EnsureValid();
- switch (value)
- {
- case bool b:
- Set(b);
- break;
- case string s:
- Set(s);
- break;
- case byte b:
- Set(b);
- break;
- case sbyte sb:
- Set(sb);
- break;
- case short sh:
- Set(sh);
- break;
- case ushort us:
- Set(us);
- break;
- case int i:
- Set(i);
- break;
- case uint u:
- Set(u);
- break;
- case long l:
- Set(l);
- break;
- case ulong ul:
- Set(ul);
- break;
- case float f:
- Set(f);
- break;
- case double d:
- Set(d);
- break;
- case decimal d:
- Set(d);
- break;
- case DateTime d:
- Set(d);
- break;
- case DateTimeOffset d:
- Set(d);
- break;
- case Guid g:
- Set(g);
- break;
- case null:
- Changes.AddChange(_path, null);
- break;
- default:
- Changes.AddChange(_path, GetSerializedValue(value));
- break;
- }
- }
-
- private object GetSerializedValue(object value)
- {
- if (value is JsonDocument doc)
- {
- return doc.RootElement;
- }
-
- if (value is JsonElement element)
- {
- return element;
- }
-
- if (value is MutableJsonDocument mjd)
- {
- mjd.RootElement.EnsureValid();
- }
-
- if (value is MutableJsonElement mje)
- {
- mje.EnsureValid();
- }
-
- // If it's not a special type, we'll serialize it on assignment.
- byte[] bytes = JsonSerializer.SerializeToUtf8Bytes(value, _root.SerializerOptions);
- return JsonDocument.Parse(bytes).RootElement;
+ Changes.AddChange(_path, value);
+ }
+
+ public MutableJsonElement SetProperty(string name, JsonElement value)
+ {
+ if (TryGetProperty(name, out MutableJsonElement element))
+ {
+ element.Set(value);
+ return this;
+ }
+
+ string path = MutableJsonDocument.ChangeTracker.PushProperty(_path, name);
+ Changes.AddChange(path, value, MutableJsonChangeKind.PropertyAddition, name);
+ return this;
}
///
@@ -1133,20 +1303,40 @@ public override string ToString()
return _element.ToString() ?? "null";
}
+ internal static JsonElement SerializeToJsonElement(object? value, JsonSerializerOptions? options = default)
+ {
+ byte[] bytes = JsonSerializer.SerializeToUtf8Bytes(value, options);
+ return ParseFromBytes(bytes);
+ }
+
+ private static JsonElement ParseFromBytes(byte[] bytes)
+ {
+ // Most JsonDocument.Parse calls return an array that is backed by one or more
+ // ArrayPool arrays. Those arrays are not returned until the instance is disposed.
+ // This workaround allows us to dispose the JsonDocument so that we don't leak
+ // ArrayPool arrays.
+#if NET6_0_OR_GREATER
+ Utf8JsonReader reader = new(bytes);
+ return JsonElement.ParseValue(ref reader);
+#else
+ using JsonDocument doc = JsonDocument.Parse(bytes);
+ return doc.RootElement.Clone();
+#endif
+ }
+
internal JsonElement GetJsonElement()
{
EnsureValid();
if (Changes.TryGetChange(_path, _highWaterMark, out MutableJsonChange change))
{
- return change.GetSerializedValue();
+ return SerializeToJsonElement(change.Value);
}
// Account for changes to descendants of this element as well
if (Changes.DescendantChanged(_path, _highWaterMark))
{
- JsonDocument document = JsonDocument.Parse(GetRawBytes());
- return document.RootElement;
+ return ParseFromBytes(GetRawBytes());
}
return _element;
diff --git a/sdk/core/Azure.Core/tests/DynamicData/MutableJsonDocumentChangeTrackerTests.cs b/sdk/core/Azure.Core/tests/DynamicData/MutableJsonDocumentChangeTrackerTests.cs
index 0ccf4bf9b15a..814c30c4c7c7 100644
--- a/sdk/core/Azure.Core/tests/DynamicData/MutableJsonDocumentChangeTrackerTests.cs
+++ b/sdk/core/Azure.Core/tests/DynamicData/MutableJsonDocumentChangeTrackerTests.cs
@@ -3,9 +3,6 @@
using System;
using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text.Json;
using Azure.Core.Json;
using NUnit.Framework;
@@ -40,7 +37,7 @@ public void CanCompareChangesByPath()
[Test]
public void CanGetSortedChanges()
{
- MutableJsonDocument.ChangeTracker changeTracker = new(null);
+ MutableJsonDocument.ChangeTracker changeTracker = new();
char delimiter = MutableJsonDocument.ChangeTracker.Delimiter;
changeTracker.AddChange("a", 1);
@@ -69,7 +66,7 @@ public void CanGetSortedChanges()
#region Helpers
private MutableJsonChange CreateChange(string name)
{
- return new MutableJsonChange(name, -1, null, null, MutableJsonChangeKind.PropertyUpdate, null);
+ return new MutableJsonChange(name, -1, null, MutableJsonChangeKind.PropertyUpdate, null);
}
#endregion
}
diff --git a/sdk/core/Azure.Core/tests/DynamicData/MutableJsonDocumentTests.cs b/sdk/core/Azure.Core/tests/DynamicData/MutableJsonDocumentTests.cs
index 5f0e56548736..bc1496c34bc5 100644
--- a/sdk/core/Azure.Core/tests/DynamicData/MutableJsonDocumentTests.cs
+++ b/sdk/core/Azure.Core/tests/DynamicData/MutableJsonDocumentTests.cs
@@ -5,6 +5,7 @@
using System.IO;
using System.Text.Json;
using Azure.Core.Json;
+using Azure.Core.Serialization;
using NUnit.Framework;
namespace Azure.Core.Tests
@@ -165,61 +166,6 @@ public void JsonWithDelimiterIsInvalidJson()
Assert.IsTrue(threwJsonException);
}
- [Test]
- public void CanSetPropertyToMutableJsonDocument()
- {
- string json = """
- {
- "Foo" : "Hello"
- }
- """;
-
- MutableJsonDocument mdoc = MutableJsonDocument.Parse(json);
- MutableJsonDocument childMDoc = MutableJsonDocument.Parse("""{ "Baz": "hi" }""");
-
- mdoc.RootElement.GetProperty("Foo").Set(childMDoc);
-
- Assert.AreEqual("hi", mdoc.RootElement.GetProperty("Foo").GetProperty("Baz").GetString());
-
- string expected = """
- {
- "Foo" : {
- "Baz" : "hi"
- }
- }
- """;
-
- ValidateWriteTo(expected, mdoc);
- }
-
- [Test]
- public void CanSetPropertyToJsonDocument()
- {
- string json = """
- {
- "Foo" : "Hello"
- }
- """;
-
- MutableJsonDocument mdoc = MutableJsonDocument.Parse(json);
-
- JsonDocument doc = JsonDocument.Parse("""{ "Baz": "hi" }""");
-
- mdoc.RootElement.GetProperty("Foo").Set(doc);
-
- Assert.AreEqual("hi", mdoc.RootElement.GetProperty("Foo").GetProperty("Baz").GetString());
-
- string expected = """
- {
- "Foo" : {
- "Baz" : "hi"
- }
- }
- """;
-
- ValidateWriteTo(expected, mdoc);
- }
-
[Test]
public void CanAddPropertyToRootObject()
{
@@ -303,63 +249,6 @@ public void CanAddPropertyToObject()
ValidateWriteTo(expected, mdoc);
}
- [Test]
- public void CanAddMutableJsonDocumentProperty()
- {
- string json = """
- {
- "Foo" : "Hello"
- }
- """;
-
- MutableJsonDocument mdoc = MutableJsonDocument.Parse(json);
- MutableJsonDocument anotherMDoc = MutableJsonDocument.Parse("""{ "Baz": "hi" }""");
-
- mdoc.RootElement.SetProperty("A", anotherMDoc);
-
- Assert.AreEqual("hi", mdoc.RootElement.GetProperty("A").GetProperty("Baz").GetString());
-
- string expected = """
- {
- "Foo" : "Hello",
- "A" : {
- "Baz" : "hi"
- }
- }
- """;
-
- ValidateWriteTo(expected, mdoc);
- }
-
- [Test]
- public void CanAddJsonDocumentProperty()
- {
- string json = """
- {
- "Foo" : "Hello"
- }
- """;
-
- MutableJsonDocument mdoc = MutableJsonDocument.Parse(json);
-
- JsonDocument doc = JsonDocument.Parse("""{ "Baz": "hi" }""");
-
- mdoc.RootElement.SetProperty("A", doc);
-
- Assert.AreEqual("hi", mdoc.RootElement.GetProperty("A").GetProperty("Baz").GetString());
-
- string expected = """
- {
- "Foo" : "Hello",
- "A" : {
- "Baz" : "hi"
- }
- }
- """;
-
- ValidateWriteTo(expected, mdoc);
- }
-
[Test]
public void CanRemovePropertyFromRootObject()
{
@@ -451,9 +340,10 @@ public void CanReplaceObjectWithAnonymousType()
}
""";
- MutableJsonDocument mdoc = MutableJsonDocument.Parse(json);
+ using MutableJsonDocument mdoc = MutableJsonDocument.Parse(json);
- mdoc.RootElement.GetProperty("Baz").Set(new { B = 5.5 });
+ JsonElement element = MutableJsonElement.SerializeToJsonElement(new { B = 5.5 });
+ mdoc.RootElement.GetProperty("Baz").Set(element);
// Assert:
@@ -611,67 +501,6 @@ public void CanChangeArrayElementType()
ValidateWriteTo(expected, mdoc);
}
- [Test]
- public void ChangeToDocumentAppearsInElementReference()
- {
- // This tests reference semantics.
-
- string json = """[ { "Foo" : 4 } ]""";
-
- MutableJsonDocument mdoc = MutableJsonDocument.Parse(json);
-
- // a is a reference to the 0th element; a's path is "0"
- MutableJsonElement a = mdoc.RootElement.GetIndexElement(0);
-
- // resets json to equivalent of "[ 5 ]"
- mdoc.RootElement.GetIndexElement(0).Set(5);
-
- Assert.AreEqual(5, mdoc.RootElement.GetIndexElement(0).GetInt32());
-
- // a's path is "0" so a.GetInt32() should return 5.
- Assert.AreEqual(5, a.GetInt32());
-
- // The following should throw because json[0] is now 5 and not an object.
- Assert.Throws(() => a.GetProperty("Foo").Set(6));
-
- Assert.AreEqual(5, mdoc.RootElement.GetIndexElement(0).GetInt32());
-
- // Setting json[0] back to 'a' makes it 5 again.
- mdoc.RootElement.GetIndexElement(0).Set(a);
-
- Assert.AreEqual(5, mdoc.RootElement.GetIndexElement(0).GetInt32());
-
- // Type round-trips correctly.
- BinaryData buffer = GetWriteToBuffer(mdoc);
- JsonDocument doc = JsonDocument.Parse(buffer);
-
- Assert.AreEqual(5, doc.RootElement[0].GetInt32());
-
- string expected = """[ 5 ]""";
- ValidateWriteTo(expected, mdoc);
- }
-
- [Test]
- public void ChangeToChildDocumentDoesNotAppearInParentDocument()
- {
- // This tests value semantics.
-
- MutableJsonDocument mdoc = MutableJsonDocument.Parse("{}");
- MutableJsonDocument child = MutableJsonDocument.Parse("{}");
-
- mdoc.RootElement.SetProperty("a", child);
- Assert.AreEqual("""{"a":{}}""", mdoc.RootElement.ToString());
- Assert.AreEqual("""{}""", child.RootElement.ToString());
-
- child.RootElement.SetProperty("b", 2);
-
- Assert.AreEqual("""{"a":{}}""", mdoc.RootElement.ToString());
- Assert.AreEqual("""{"b":2}""", child.RootElement.ToString());
-
- ValidateWriteTo("""{"a":{}}""", mdoc);
- ValidateWriteTo("""{"b":2}""", child);
- }
-
[Test]
public void ChangeToAddedElementReferenceAppearsInDocument()
{
@@ -700,244 +529,6 @@ public void ChangeToAddedElementReferenceAppearsInDocument()
ValidateWriteTo(expected, mdoc);
}
- [Test]
- public void ChangeToElementReferenceAppearsInDocument()
- {
- // This tests reference semantics.
-
- string json = """[ { "Foo" : 4 } ]""";
-
- MutableJsonDocument mdoc = MutableJsonDocument.Parse(json);
-
- // a is a reference to the 0th element; a's path is "0"
- MutableJsonElement a = mdoc.RootElement.GetIndexElement(0);
-
- a.GetProperty("Foo").Set(new
- {
- Bar = 5
- });
-
- Assert.AreEqual(5, a.GetProperty("Foo").GetProperty("Bar").GetInt32());
- Assert.AreEqual(5, mdoc.RootElement.GetIndexElement(0).GetProperty("Foo").GetProperty("Bar").GetInt32());
-
- string expected = """
- [ {
- "Foo" : {
- "Bar" : 5
- }
- } ]
- """;
-
- ValidateWriteTo(expected, mdoc);
- }
-
- [Test]
- public void CanInvalidateElement()
- {
- string json = """
- [{
- "Foo" : {
- "A": 6
- }
- }]
- """;
-
- MutableJsonDocument mdoc = MutableJsonDocument.Parse(json);
-
- // a's path points to "0.Foo.A"
- var a = mdoc.RootElement.GetIndexElement(0).GetProperty("Foo").GetProperty("A");
-
- // resets json to equivalent of "[ 5 ]"
- mdoc.RootElement.GetIndexElement(0).Set(5);
-
- Assert.AreEqual(5, mdoc.RootElement.GetIndexElement(0).GetInt32());
-
- // a's path points to "0.Foo.A" so a.GetInt32() should throw since this
- // in an invalid path.
- Assert.Throws(() => a.GetInt32());
-
- // Setting json[0] to a should throw as well, as the element doesn't point
- // to a valid path in the mutated JSON tree.
- Assert.Throws(() => mdoc.RootElement.GetIndexElement(0).Set(a));
-
- // 3. Type round-trips correctly.
- BinaryData buffer = GetWriteToBuffer(mdoc);
- JsonDocument doc = JsonDocument.Parse(buffer);
-
- Assert.AreEqual(5, doc.RootElement[0].GetInt32());
-
- string expected = """[ 5 ]""";
- ValidateWriteTo(expected, mdoc);
- }
-
- [Test]
- public void CanAccessPropertyInChangedStructure()
- {
- string json = """
- [ {
- "foo" : {
- "a": 6
- }
- } ]
- """;
-
- MutableJsonDocument mdoc = MutableJsonDocument.Parse(json);
-
- // a's path points to "0.foo.a"
- MutableJsonElement a = mdoc.RootElement.GetIndexElement(0).GetProperty("foo").GetProperty("a");
-
- // resets json to equivalent of "[ 5 ]"
- mdoc.RootElement.GetIndexElement(0).Set(5);
-
- Assert.AreEqual(5, mdoc.RootElement.GetIndexElement(0).GetInt32());
-
- // a's path points to "0.foo.a" so a.GetInt32() should throws since this now an invalid path.
- Assert.Throws(() => a.GetInt32());
-
- // Since a is invalid now, we can't set it on mdoc.
- Assert.Throws(() => mdoc.RootElement.GetIndexElement(0).Set(a));
-
- // Reset json[0] to an object
- mdoc.RootElement.GetIndexElement(0).Set(new
- {
- foo = new
- {
- a = 7
- }
- });
-
- // We should be able to get the value of 0.foo.a without being tripped up by earlier changes.
- int aValue = mdoc.RootElement.GetIndexElement(0).GetProperty("foo").GetProperty("a").GetInt32();
- Assert.AreEqual(7, aValue);
-
- // 3. Type round-trips correctly.
- BinaryData buffer = GetWriteToBuffer(mdoc);
- JsonDocument doc = JsonDocument.Parse(buffer);
-
- string expected = """
- [ {
- "foo" : {
- "a": 7
- }
- } ]
- """;
-
- ValidateWriteTo(expected, mdoc);
- }
-
- [Test]
- public void CanAccessChangesInDifferentBranches()
- {
- string json = """
- [ {
- "Foo" : {
- "A": 6
- }
- },
- {
- "Bar" : "hi"
- }]
- """;
-
- MutableJsonDocument mdoc = MutableJsonDocument.Parse(json);
-
- // resets json to equivalent of "[ 5, ... ]"
- mdoc.RootElement.GetIndexElement(0).Set(5);
-
- Assert.AreEqual(5, mdoc.RootElement.GetIndexElement(0).GetInt32());
- Assert.AreEqual("hi", mdoc.RootElement.GetIndexElement(1).GetProperty("Bar").GetString());
-
- // Make a structural change to json[0] but not json[1]
- mdoc.RootElement.GetIndexElement(0).Set(new
- {
- Foo = new
- {
- A = 7
- }
- });
-
- // We should be able to get the value of A without being tripped up by earlier changes.
- // We should also be able to get the value of json[1] without it having been invalidated.
- Assert.AreEqual(7, mdoc.RootElement.GetIndexElement(0).GetProperty("Foo").GetProperty("A").GetInt32());
- Assert.AreEqual("hi", mdoc.RootElement.GetIndexElement(1).GetProperty("Bar").GetString());
-
- // Now change json[1]
- mdoc.RootElement.GetIndexElement(1).GetProperty("Bar").Set("new");
- Assert.AreEqual("new", mdoc.RootElement.GetIndexElement(1).GetProperty("Bar").GetString());
-
- // 3. Type round-trips correctly.
- string expected = """
- [ {
- "Foo" : {
- "A": 7
- }
- },
- {
- "Bar" : "new"
- }]
- """;
-
- ValidateWriteTo(expected, mdoc);
- }
-
- [Test]
- public void PriorChangeToReplacedPropertyIsIgnored()
- {
- string json = """
- {
- "ArrayProperty": [
- {
- "Foo" : {
- "A": 6
- }
- }
- ],
- "Bar" : "hi"
- }
- """;
-
- MutableJsonDocument mdoc = MutableJsonDocument.Parse(json);
-
- mdoc.RootElement.GetProperty("ArrayProperty").GetIndexElement(0).GetProperty("Foo").GetProperty("A").Set(8);
-
- Assert.AreEqual(8, mdoc.RootElement.GetProperty("ArrayProperty").GetIndexElement(0).GetProperty("Foo").GetProperty("A").GetInt32());
-
- // resets json to equivalent of "[ 5 ]"
- mdoc.RootElement.GetProperty("ArrayProperty").GetIndexElement(0).Set(5);
-
- Assert.AreEqual(5, mdoc.RootElement.GetProperty("ArrayProperty").GetIndexElement(0).GetInt32());
-
- // Reset json[0] to an object
- mdoc.RootElement.GetProperty("ArrayProperty").GetIndexElement(0).Set(new
- {
- Foo = new
- {
- A = 7
- }
- });
-
- // We should be able to get the value of A without being tripped up
- // by earlier changes.
- int aValue = mdoc.RootElement.GetProperty("ArrayProperty").GetIndexElement(0).GetProperty("Foo").GetProperty("A").GetInt32();
- Assert.AreEqual(7, aValue);
-
- // 3. Type round-trips correctly.
- string expected = """
- {
- "ArrayProperty": [
- {
- "Foo" : {
- "A": 7
- }
- }
- ],
- "Bar" : "hi"
- }
- """;
-
- ValidateWriteTo(expected, mdoc);
- }
-
[Test]
public void CanSetProperty_StringToNumber()
{
@@ -991,14 +582,15 @@ public void CanSetProperty_StringToObject()
{
string json = """{ "Foo" : "hi" }""";
- MutableJsonDocument mdoc = MutableJsonDocument.Parse(json);
+ using MutableJsonDocument mdoc = MutableJsonDocument.Parse(json);
Assert.AreEqual("hi", mdoc.RootElement.GetProperty("Foo").GetString());
- mdoc.RootElement.GetProperty("Foo").Set(new
+ JsonElement element = MutableJsonElement.SerializeToJsonElement(new
{
Bar = 6
});
+ mdoc.RootElement.GetProperty("Foo").Set(element);
Assert.AreEqual(6, mdoc.RootElement.GetProperty("Foo").GetProperty("Bar").GetInt32());
@@ -1017,11 +609,12 @@ public void CanSetProperty_StringToArray()
{
string json = """[ { "Foo" : "hi" } ]""";
- MutableJsonDocument mdoc = MutableJsonDocument.Parse(json);
+ using MutableJsonDocument mdoc = MutableJsonDocument.Parse(json);
Assert.AreEqual("hi", mdoc.RootElement.GetIndexElement(0).GetProperty("Foo").GetString());
- mdoc.RootElement.GetIndexElement(0).GetProperty("Foo").Set(new int[] { 1, 2, 3 });
+ JsonElement element = MutableJsonElement.SerializeToJsonElement(new int[] { 1, 2, 3 });
+ mdoc.RootElement.GetIndexElement(0).GetProperty("Foo").Set(element);
Assert.AreEqual(1, mdoc.RootElement.GetIndexElement(0).GetProperty("Foo").GetIndexElement(0).GetInt32());
Assert.AreEqual(2, mdoc.RootElement.GetIndexElement(0).GetProperty("Foo").GetIndexElement(1).GetInt32());
diff --git a/sdk/core/Azure.Core/tests/DynamicData/MutableJsonDocumentWriteToTests.cs b/sdk/core/Azure.Core/tests/DynamicData/MutableJsonDocumentWriteToTests.cs
index 9febd58087a9..edff85842b5a 100644
--- a/sdk/core/Azure.Core/tests/DynamicData/MutableJsonDocumentWriteToTests.cs
+++ b/sdk/core/Azure.Core/tests/DynamicData/MutableJsonDocumentWriteToTests.cs
@@ -47,10 +47,22 @@ public void CanWriteDateTimeAppConfigValue()
"""u8;
BinaryData data = new(json.ToArray());
- MutableJsonDocument mdoc = MutableJsonDocument.Parse(data);
+ using MutableJsonDocument mdoc = MutableJsonDocument.Parse(data);
mdoc.RootElement.GetProperty("foo").Set("hi");
MutableJsonDocumentTests.ValidateWriteTo(data, mdoc);
+
+ ReadOnlySpan json2 = """
+ {
+ "foo": "hi",
+ "last_modified":"2023-03-23T16:35:35+00:00"
+ }
+ """u8;
+ BinaryData data2 = new(json2.ToArray());
+ mdoc.RootElement.GetProperty("last_modified").Set("2023-03-23T16:35:35+00:00");
+
+ MutableJsonDocumentTests.ValidateWriteTo(data2, mdoc);
+ MutableJsonDocumentTests.ValidateWriteTo(data2.ToString(), mdoc);
}
[Test]
@@ -321,7 +333,7 @@ public void CanWriteObjectWithChangesAndAdditions()
}
""";
- MutableJsonDocument mdoc = MutableJsonDocument.Parse(json);
+ using MutableJsonDocument mdoc = MutableJsonDocument.Parse(json);
mdoc.RootElement.GetProperty("Bar").SetProperty("c", "new");
mdoc.RootElement.GetProperty("Bar").GetProperty("a").Set(1.2);
@@ -339,7 +351,8 @@ public void CanWriteObjectWithChangesAndAdditions()
MutableJsonDocumentTests.ValidateWriteTo(expected, mdoc);
- mdoc.RootElement.SetProperty("Baz", new int[] { 1, 2, 3 });
+ JsonElement element = MutableJsonElement.SerializeToJsonElement(new int[] { 1, 2, 3 });
+ mdoc.RootElement.SetProperty("Baz", element);
expected = """
{
@@ -396,8 +409,7 @@ public void CanWriteChangesInterleavedAcrossBranches()
""";
MutableJsonDocumentTests.ValidateWriteTo(expected, mdoc);
-
- var value = new
+ JsonElement element = MutableJsonElement.SerializeToJsonElement(new
{
Foo = new
{
@@ -408,9 +420,9 @@ public void CanWriteChangesInterleavedAcrossBranches()
b = 4,
c = "new"
}
- };
+ });
- mdoc.RootElement.Set(value);
+ mdoc.RootElement.Set(element);
expected = """
{
@@ -437,7 +449,7 @@ public void CanWriteArrayWithChangedElements()
}
""";
- MutableJsonDocument mdoc = MutableJsonDocument.Parse(json);
+ using MutableJsonDocument mdoc = MutableJsonDocument.Parse(json);
mdoc.RootElement.GetProperty("Bar").GetIndexElement(0).Set(2);
mdoc.RootElement.GetProperty("Bar").GetIndexElement(1).Set(4);
@@ -452,7 +464,8 @@ public void CanWriteArrayWithChangedElements()
MutableJsonDocumentTests.ValidateWriteTo(expected, mdoc);
- mdoc.RootElement.GetProperty("Bar").Set(new int[] { 0, 1, 2, 3 });
+ JsonElement element = MutableJsonElement.SerializeToJsonElement(new int[] { 0, 1, 2, 3 });
+ mdoc.RootElement.GetProperty("Bar").Set(element);
mdoc.RootElement.GetProperty("Bar").GetIndexElement(3).Set(4);
expected = """
@@ -535,8 +548,9 @@ public void WriteToBehaviorMatchesJsonDocument(dynamic json)
// Mutate a value
string name = mdoc.RootElement.EnumerateObject().First().Name;
- var value = mdoc.RootElement.EnumerateObject().First().Value;
- mdoc.RootElement.GetProperty(name).Set(value);
+ MutableJsonElement value = mdoc.RootElement.EnumerateObject().First().Value;
+ JsonElement element = MutableJsonElement.SerializeToJsonElement(value);
+ mdoc.RootElement.GetProperty(name).Set(element);
// Validate after changes.
MutableJsonDocumentTests.ValidateWriteTo(json, mdoc);
@@ -567,24 +581,26 @@ public void CanWriteByte()
}
[TestCaseSource(nameof(NumberValues))]
- public void CanWriteNumber(string serializedX, T x, T y, T z)
+ public void CanWriteNumber(string serializedX, T x, T y, T z,
+ Action set,
+ Func setProperty)
{
string json = $"{{\"foo\" : {serializedX}}}";
// Get from parsed JSON
- MutableJsonDocument mdoc = MutableJsonDocument.Parse(json);
+ using MutableJsonDocument mdoc = MutableJsonDocument.Parse(json);
// Get from parsed JSON
Assert.AreEqual($"{x}", mdoc.RootElement.GetProperty("foo").ToString());
MutableJsonDocumentTests.ValidateWriteTo(json, mdoc);
// Get from assigned existing value
- mdoc.RootElement.GetProperty("foo").Set(y);
+ set(mdoc, "foo", y);
Assert.AreEqual($"{y}", mdoc.RootElement.GetProperty("foo").ToString());
MutableJsonDocumentTests.ValidateWriteTo($"{{\"foo\" : {y}}}", mdoc);
// Get from added value
- mdoc.RootElement.SetProperty("bar", z);
+ setProperty(mdoc, "bar", z);
Assert.AreEqual($"{z}", mdoc.RootElement.GetProperty("bar").ToString());
MutableJsonDocumentTests.ValidateWriteTo($"{{\"foo\":{y},\"bar\":{z}}}", mdoc);
}
@@ -615,6 +631,7 @@ public void CanWriteGuid()
}
[Test]
+ [Ignore("Investigating possible issue in Utf8JsonWriter.")]
public void CanWriteDateTime()
{
DateTime dateTime = DateTime.Parse("2023-05-07T21:04:45.1657010-07:00");
@@ -643,9 +660,10 @@ public void CanWriteDateTime()
}
[Test]
+ [Ignore("Investigating possible issue in Utf8JsonWriter.")]
public void CanWriteDateTimeOffset()
{
- DateTimeOffset dateTime = DateTimeOffset.Now;
+ DateTimeOffset dateTime = DateTimeOffset.Parse("2023-08-17T10:36:42.5482841+07:00");
string dateTimeString = MutableJsonElementTests.FormatDateTimeOffset(dateTime);
string json = $"{{\"foo\" : \"{dateTimeString}\"}}";
@@ -656,14 +674,14 @@ public void CanWriteDateTimeOffset()
MutableJsonDocumentTests.ValidateWriteTo(json, mdoc);
// Get from assigned existing value
- DateTimeOffset fooValue = DateTimeOffset.Now.AddDays(1);
+ DateTimeOffset fooValue = dateTime.AddDays(1);
string fooString = MutableJsonElementTests.FormatDateTimeOffset(fooValue);
mdoc.RootElement.GetProperty("foo").Set(fooValue);
Assert.AreEqual(fooString, mdoc.RootElement.GetProperty("foo").ToString());
MutableJsonDocumentTests.ValidateWriteTo($"{{\"foo\" : \"{fooString}\"}}", mdoc);
// Get from added value
- DateTimeOffset barValue = DateTimeOffset.Now.AddDays(2);
+ DateTimeOffset barValue = dateTime.AddDays(2);
string barString = MutableJsonElementTests.FormatDateTimeOffset(barValue);
mdoc.RootElement.SetProperty("bar", barValue);
Assert.AreEqual(barString, mdoc.RootElement.GetProperty("bar").ToString());
@@ -862,11 +880,12 @@ public void CanWritePatchInterleaveParentAndChildChanges()
}
}
""";
- MutableJsonDocument mdoc = MutableJsonDocument.Parse(json);
+ using MutableJsonDocument mdoc = MutableJsonDocument.Parse(json);
mdoc.RootElement.GetProperty("a").GetProperty("aa").Set(3);
mdoc.RootElement.GetProperty("b").GetProperty("ba").Set("3");
- mdoc.RootElement.GetProperty("b").Set(new { ba = "3", bb = "4" });
+ JsonElement element = MutableJsonElement.SerializeToJsonElement(new { ba = "3", bb = "4" });
+ mdoc.RootElement.GetProperty("b").Set(element);
mdoc.RootElement.GetProperty("a").GetProperty("ab").Set(4);
mdoc.RootElement.GetProperty("b").GetProperty("ba").Set("5");
mdoc.RootElement.GetProperty("a").GetProperty("aa").Set(5);
@@ -1022,10 +1041,11 @@ public void CanWritePatchForArraysAtDifferentLevels()
}
""";
- MutableJsonDocument mdoc = MutableJsonDocument.Parse(json);
+ using MutableJsonDocument mdoc = MutableJsonDocument.Parse(json);
mdoc.RootElement.GetProperty("a").GetProperty("aa").GetIndexElement(0).Set(2);
mdoc.RootElement.GetProperty("b").GetProperty("bb").GetProperty("bbb").GetIndexElement(1).Set(true);
- mdoc.RootElement.GetProperty("c").GetIndexElement(1).Set(new { cd = "cd" });
+ JsonElement element = MutableJsonElement.SerializeToJsonElement(new { cd = "cd" });
+ mdoc.RootElement.GetProperty("c").GetIndexElement(1).Set(element);
ValidatePatch("""
{
@@ -1063,11 +1083,12 @@ public void CanWritePatchInterleaveArrayChanges()
}
""";
- MutableJsonDocument mdoc = MutableJsonDocument.Parse(json);
+ using MutableJsonDocument mdoc = MutableJsonDocument.Parse(json);
mdoc.RootElement.GetProperty("a").GetProperty("aa").GetIndexElement(0).Set(2);
mdoc.RootElement.GetProperty("b").GetProperty("bb").GetProperty("bbb").GetIndexElement(1).Set(true);
mdoc.RootElement.GetProperty("a").GetProperty("aa").GetIndexElement(0).Set(3);
- mdoc.RootElement.GetProperty("c").GetIndexElement(1).Set(new { cd = "cd" });
+ JsonElement element = MutableJsonElement.SerializeToJsonElement(new { cd = "cd" });
+ mdoc.RootElement.GetProperty("c").GetIndexElement(1).Set(element);
mdoc.RootElement.GetProperty("a").GetProperty("aa").GetIndexElement(1).Set(4);
ValidatePatch("""
@@ -1109,8 +1130,10 @@ public void CanWritePatchInterleaveParentAndChildArrayChanges()
MutableJsonDocument mdoc = MutableJsonDocument.Parse(json);
mdoc.RootElement.GetProperty("a").GetProperty("aa").GetIndexElement(0).Set(2);
mdoc.RootElement.GetProperty("b").GetProperty("bb").GetProperty("bbb").GetIndexElement(1).Set(true);
- mdoc.RootElement.GetProperty("a").GetProperty("aa").Set(new int[] { 2, 3 });
- mdoc.RootElement.GetProperty("c").GetIndexElement(1).Set(new { cd = "cd" });
+ JsonElement element = MutableJsonElement.SerializeToJsonElement(new int[] { 2, 3 });
+ mdoc.RootElement.GetProperty("a").GetProperty("aa").Set(element);
+ element = MutableJsonElement.SerializeToJsonElement(new { cd = "cd" });
+ mdoc.RootElement.GetProperty("c").GetIndexElement(1).Set(element);
mdoc.RootElement.GetProperty("a").GetProperty("aa").GetIndexElement(1).Set(4);
ValidatePatch("""
@@ -1144,9 +1167,9 @@ public void CanWritePatchReplaceObject()
}
}
""";
- MutableJsonDocument mdoc = MutableJsonDocument.Parse(json);
-
- mdoc.RootElement.GetProperty("a").Set(new { aa = 3, ab = 4 });
+ using MutableJsonDocument mdoc = MutableJsonDocument.Parse(json);
+ JsonElement element = MutableJsonElement.SerializeToJsonElement(new { aa = 3, ab = 4 });
+ mdoc.RootElement.GetProperty("a").Set(element);
ValidatePatch("""
{
@@ -1173,9 +1196,9 @@ public void CanWritePatchReplaceObject_Deletions()
}
}
""";
- MutableJsonDocument mdoc = MutableJsonDocument.Parse(json);
-
- mdoc.RootElement.GetProperty("a").Set(new { ac = 3 });
+ using MutableJsonDocument mdoc = MutableJsonDocument.Parse(json);
+ JsonElement element = MutableJsonElement.SerializeToJsonElement(new { ac = 3 });
+ mdoc.RootElement.GetProperty("a").Set(element);
ValidatePatch("""
{
@@ -1257,9 +1280,10 @@ public void CanWritePatchAddObject()
}
}
""";
- MutableJsonDocument mdoc = MutableJsonDocument.Parse(json);
+ using MutableJsonDocument mdoc = MutableJsonDocument.Parse(json);
- mdoc.RootElement.SetProperty("c", new { ca = true, cb = false });
+ JsonElement element = MutableJsonElement.SerializeToJsonElement(new { ca = true, cb = false });
+ mdoc.RootElement.SetProperty("c", element);
ValidatePatch("""
{
@@ -1473,12 +1497,13 @@ public void CanWritePatchRfc7396SecondExample()
"content": "This will be unchanged"
}
""";
- MutableJsonDocument mdoc = MutableJsonDocument.Parse(json);
+ using MutableJsonDocument mdoc = MutableJsonDocument.Parse(json);
mdoc.RootElement.GetProperty("title").Set("Hello!");
mdoc.RootElement.SetProperty("phoneNumber", "+01-123-456-7890");
mdoc.RootElement.GetProperty("author").RemoveProperty("familyName");
- mdoc.RootElement.SetProperty("tags", new string[] { "example" });
+ JsonElement element = MutableJsonElement.SerializeToJsonElement(new string[] { "example" });
+ mdoc.RootElement.SetProperty("tags", element);
ValidatePatch("""
{
@@ -1658,19 +1683,41 @@ public static IEnumerable
+
+ Always
+
Always
diff --git a/sdk/core/Azure.Core/tests/common/TestData/SimplePatchModel.json b/sdk/core/Azure.Core/tests/common/TestData/SimplePatchModel.json
new file mode 100644
index 000000000000..3bb777e63da3
--- /dev/null
+++ b/sdk/core/Azure.Core/tests/common/TestData/SimplePatchModel.json
@@ -0,0 +1,5 @@
+{
+ "name": "abc",
+ "count": 1,
+ "updatedOn": "2023-10-19T10:19:10.0190001Z"
+}
diff --git a/sdk/core/Azure.Core/tests/public/PatchModels/ParentPatchModel.cs b/sdk/core/Azure.Core/tests/public/PatchModels/ParentPatchModel.cs
index edeafab91c3c..dca72f9ae0c4 100644
--- a/sdk/core/Azure.Core/tests/public/PatchModels/ParentPatchModel.cs
+++ b/sdk/core/Azure.Core/tests/public/PatchModels/ParentPatchModel.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT License.
using Azure.Core.Json;
+using Azure.Core.Serialization;
namespace Azure.Core.Tests.PatchModels
{
@@ -58,7 +59,7 @@ public ChildPatchModel Child
{
if (!_element.TryGetProperty("child", out MutableJsonElement element))
{
- _element.SetProperty("child", new { });
+ _element.SetProperty("child", DynamicData.SerializeToJsonElement(new { }));
element = _element.GetProperty("child");
}
diff --git a/sdk/core/Azure.Core/tests/public/PatchModels/PatchModelTests.cs b/sdk/core/Azure.Core/tests/public/PatchModels/PatchModelTests.cs
index 47dc0f96c310..bfc6eaf7c4bb 100644
--- a/sdk/core/Azure.Core/tests/public/PatchModels/PatchModelTests.cs
+++ b/sdk/core/Azure.Core/tests/public/PatchModels/PatchModelTests.cs
@@ -62,6 +62,98 @@ public void CanRoundTripSimpleModel()
ValidatePatch("""{"count":2, "name":"xyz"}""", model);
}
+ #region Standard model
+
+ [Test]
+ public void CanSetIntProperty()
+ {
+ JsonDocument doc = JsonDocument.Parse("""
+ {
+ "name": "abc",
+ "count": 1,
+ "updatedOn": "2023-10-19T10:19:10.0190001Z"
+ }
+ """);
+ SimpleStandardModel model = SimpleStandardModel.DeserializeSimpleStandardModel(doc.RootElement, new ModelSerializerOptions("J"));
+ model.Count = 2;
+
+ ValidateSerialize("""
+ {
+ "name": "abc",
+ "count": 2,
+ "updatedOn": "2023-10-19T10:19:10.0190001Z"
+ }
+ """, model);
+ }
+
+ [Test]
+ public void CanSetStringProperty()
+ {
+ JsonDocument doc = JsonDocument.Parse("""
+ {
+ "name": "abc",
+ "count": 1,
+ "updatedOn": "2023-10-19T10:19:10.0190001Z"
+ }
+ """);
+ SimpleStandardModel model = SimpleStandardModel.DeserializeSimpleStandardModel(doc.RootElement, new ModelSerializerOptions("J"));
+ model.Name = "xyz";
+
+ ValidateSerialize("""
+ {
+ "name": "xyz",
+ "count": 1,
+ "updatedOn": "2023-10-19T10:19:10.0190001Z"
+ }
+ """, model);
+ }
+
+ [Test]
+ public void CanSetDateTimeProperty()
+ {
+ DateTimeOffset updateTime = DateTimeOffset.Parse("2023-10-20T10:20:10.0190001Z");
+ JsonDocument doc = JsonDocument.Parse("""
+ {
+ "name": "abc",
+ "count": 1,
+ "updatedOn": "2023-10-19T10:19:10.0190001Z"
+ }
+ """);
+
+ SimpleStandardModel model = SimpleStandardModel.DeserializeSimpleStandardModel(doc.RootElement, new ModelSerializerOptions("J"));
+ model.UpdatedOn = updateTime;
+
+ ValidateSerialize("""
+ {
+ "name": "abc",
+ "count": 1,
+ "updatedOn": "2023-10-20T10:20:10.0190001Z"
+ }
+ """, model);
+ }
+
+ [Test]
+ public void CanRoundTripSimpleStandardModel()
+ {
+ BinaryData json = BinaryData.FromString("""
+ {
+ "name": "abc",
+ "count": 1
+ }
+ """);
+
+ SimplePatchModel model = ModelSerializer.Deserialize(json);
+
+ Assert.AreEqual(1, model.Count);
+ Assert.AreEqual("abc", model.Name);
+
+ model.Name = "xyz";
+ model.Count = 2;
+
+ ValidatePatch("""{"count":2, "name":"xyz"}""", model);
+ }
+ #endregion standard model
+
[Test]
public void CanPatchNestedModel()
{
diff --git a/sdk/core/Azure.Core/tests/public/PatchModels/SimplePatchModel.Serialization.cs b/sdk/core/Azure.Core/tests/public/PatchModels/SimplePatchModel.Serialization.cs
index bc278c559567..ffa78f9c896c 100644
--- a/sdk/core/Azure.Core/tests/public/PatchModels/SimplePatchModel.Serialization.cs
+++ b/sdk/core/Azure.Core/tests/public/PatchModels/SimplePatchModel.Serialization.cs
@@ -8,7 +8,7 @@
namespace Azure.Core.Tests.PatchModels
{
- public partial class SimplePatchModel : IModelJsonSerializable
+ public partial class SimplePatchModel : IModelJsonSerializable, IUtf8JsonSerializable
{
SimplePatchModel IModelJsonSerializable.Deserialize(ref Utf8JsonReader reader, ModelSerializerOptions options)
{
@@ -62,11 +62,20 @@ BinaryData IModelSerializable.Serialize(ModelSerializerOptions
return ModelSerializer.SerializeCore(this, options);
}
+ public static implicit operator RequestContent(SimplePatchModel model)
+ => RequestContent.Create(model, ModelSerializerOptions.DefaultWireOptions);
+
public static explicit operator SimplePatchModel(Response response)
{
Argument.AssertNotNull(response, nameof(response));
return Deserialize(response.Content, ModelSerializerOptions.DefaultWireOptions);
}
+
+ void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => ((IModelJsonSerializable)this).Serialize(writer, ModelSerializerOptions.DefaultWireOptions);
+
+ // TODO: Temp for pef tests
+ public void Serialize(Utf8JsonWriter writer) => ((IUtf8JsonSerializable)this).Write(writer);
+ public static SimplePatchModel Deserialize(BinaryData data) => Deserialize(data, ModelSerializerOptions.DefaultWireOptions);
}
}
diff --git a/sdk/core/Azure.Core/tests/public/PatchModels/SimplePatchModel.cs b/sdk/core/Azure.Core/tests/public/PatchModels/SimplePatchModel.cs
index 74a329c5a2a1..8ab9b18bee7e 100644
--- a/sdk/core/Azure.Core/tests/public/PatchModels/SimplePatchModel.cs
+++ b/sdk/core/Azure.Core/tests/public/PatchModels/SimplePatchModel.cs
@@ -60,7 +60,17 @@ public int? Count
}
return null;
}
- set => _element.SetProperty("count", value);
+ set
+ {
+ if (value == null)
+ {
+ _element.SetPropertyNull("count");
+ }
+ else
+ {
+ _element.SetProperty("count", value.Value);
+ }
+ }
}
///
@@ -76,7 +86,17 @@ public DateTimeOffset? UpdatedOn
}
return null;
}
- set => _element.SetProperty("updatedOn", value);
+ set
+ {
+ if (value == null)
+ {
+ _element.SetPropertyNull("updatedOn");
+ }
+ else
+ {
+ _element.SetProperty("updatedOn", value.Value);
+ }
+ }
}
#pragma warning restore AZC0020 // Avoid using banned types in libraries
}
diff --git a/sdk/core/Azure.Core/tests/public/PatchModels/SimpleStandardModel.Serialization.cs b/sdk/core/Azure.Core/tests/public/PatchModels/SimpleStandardModel.Serialization.cs
new file mode 100644
index 000000000000..7d4fe4396b63
--- /dev/null
+++ b/sdk/core/Azure.Core/tests/public/PatchModels/SimpleStandardModel.Serialization.cs
@@ -0,0 +1,112 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Text.Json;
+using Azure.Core.Serialization;
+
+namespace Azure.Core.Tests.PatchModels
+{
+ public partial class SimpleStandardModel : IModelJsonSerializable, IUtf8JsonSerializable
+ {
+ public static SimpleStandardModel DeserializeSimpleStandardModel(JsonElement element, ModelSerializerOptions options)
+ {
+ options ??= ModelSerializerOptions.DefaultWireOptions;
+
+ if (element.ValueKind == JsonValueKind.Null)
+ {
+ return null;
+ }
+ Optional name = default;
+ Optional count = default;
+ Optional updatedOn = default;
+
+ foreach (var property in element.EnumerateObject())
+ {
+ if (property.NameEquals("name"u8))
+ {
+ name = property.Value.GetString();
+ continue;
+ }
+ if (property.NameEquals("count"u8))
+ {
+ count = property.Value.GetInt32();
+ continue;
+ }
+ if (property.NameEquals("updatedOn"u8))
+ {
+ updatedOn = property.Value.GetDateTimeOffset();
+ continue;
+ }
+ }
+ return new SimpleStandardModel(name, count, updatedOn);
+ }
+
+ SimpleStandardModel IModelJsonSerializable.Deserialize(ref Utf8JsonReader reader, ModelSerializerOptions options)
+ {
+ PatchModelHelper.ValidateFormat(this, options.Format);
+
+ using var doc = JsonDocument.ParseValue(ref reader);
+ return DeserializeSimpleStandardModel(doc.RootElement, options);
+ }
+
+ SimpleStandardModel IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options)
+ {
+ PatchModelHelper.ValidateFormat(this, options.Format);
+
+ JsonDocument doc = JsonDocument.Parse(data);
+ return DeserializeSimpleStandardModel(doc.RootElement, options);
+ }
+
+ private void Serialize(Utf8JsonWriter writer, ModelSerializerOptions options)
+ {
+ writer.WriteStartObject();
+ if (Optional.IsDefined(Name))
+ {
+ writer.WritePropertyName("name"u8);
+ writer.WriteStringValue(Name);
+ }
+ if (Optional.IsDefined(Count))
+ {
+ writer.WritePropertyName("count"u8);
+ writer.WriteNumberValue(Count.Value);
+ }
+ if (Optional.IsDefined(UpdatedOn))
+ {
+ writer.WritePropertyName("updatedOn"u8);
+ writer.WriteStringValue(UpdatedOn.Value.UtcDateTime.ToString("O"));
+ }
+ writer.WriteEndObject();
+ }
+
+ void IModelJsonSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options)
+ {
+ PatchModelHelper.ValidateFormat(this, options.Format);
+
+ Serialize(writer, options);
+ }
+
+ BinaryData IModelSerializable.Serialize(ModelSerializerOptions options)
+ {
+ PatchModelHelper.ValidateFormat(this, options.Format);
+
+ return ModelSerializer.SerializeCore(this, options);
+ }
+
+ public static implicit operator RequestContent(SimpleStandardModel model)
+ => RequestContent.Create(model, ModelSerializerOptions.DefaultWireOptions);
+
+ public static explicit operator SimpleStandardModel(Response response)
+ {
+ Argument.AssertNotNull(response, nameof(response));
+
+ using JsonDocument jsonDocument = JsonDocument.Parse(response.ContentStream);
+ return DeserializeSimpleStandardModel(jsonDocument.RootElement, ModelSerializerOptions.DefaultWireOptions);
+ }
+
+ void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => ((IModelJsonSerializable)this).Serialize(writer, ModelSerializerOptions.DefaultWireOptions);
+
+ // TODO: Temp for pef tests
+ public void Serialize(Utf8JsonWriter writer) => ((IUtf8JsonSerializable)this).Write(writer);
+ }
+}
diff --git a/sdk/core/Azure.Core/tests/public/PatchModels/SimpleStandardModel.cs b/sdk/core/Azure.Core/tests/public/PatchModels/SimpleStandardModel.cs
new file mode 100644
index 000000000000..e374790584ed
--- /dev/null
+++ b/sdk/core/Azure.Core/tests/public/PatchModels/SimpleStandardModel.cs
@@ -0,0 +1,25 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+
+namespace Azure.Core.Tests.PatchModels
+{
+ public partial class SimpleStandardModel
+ {
+ public SimpleStandardModel() { }
+
+ internal SimpleStandardModel(string name, int count, DateTimeOffset updatedOn)
+ {
+ Name = name;
+ Count = count;
+ UpdatedOn = updatedOn;
+ }
+
+ public string Name { get; set; }
+
+ public int? Count { get; set; }
+
+ public DateTimeOffset? UpdatedOn { get; set; }
+ }
+}
diff --git a/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/tests/EventHubEndToEndTests.cs b/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/tests/EventHubEndToEndTests.cs
index 68d81103e6f3..d921d59c0fef 100644
--- a/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/tests/EventHubEndToEndTests.cs
+++ b/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/tests/EventHubEndToEndTests.cs
@@ -31,7 +31,8 @@ public class EventHubEndToEndTests : WebJobsEventHubTestBase
{
private static readonly TimeSpan NoEventReadTimeout = TimeSpan.FromSeconds(5);
- private static EventWaitHandle _eventWait;
+ private static EventWaitHandle _eventWait1;
+ private static EventWaitHandle _eventWait2;
private static List _results;
private static DateTimeOffset _initialOffsetEnqueuedTimeUTC;
@@ -39,13 +40,15 @@ public class EventHubEndToEndTests : WebJobsEventHubTestBase
public void SetUp()
{
_results = new List();
- _eventWait = new ManualResetEvent(initialState: false);
+ _eventWait1 = new ManualResetEvent(initialState: false);
+ _eventWait2 = new ManualResetEvent(initialState: false);
}
[TearDown]
public void TearDown()
{
- _eventWait.Dispose();
+ _eventWait1.Dispose();
+ _eventWait2.Dispose();
}
[Test]
@@ -56,7 +59,7 @@ public async Task EventHub_PocoBinding()
{
await jobHost.CallAsync(nameof(EventHubTestBindToPocoJobs.SendEvent_TestHub));
- bool result = _eventWait.WaitOne(Timeout);
+ bool result = _eventWait1.WaitOne(Timeout);
Assert.True(result);
}
@@ -75,7 +78,7 @@ public async Task EventHub_StringBinding()
{
await jobHost.CallAsync(nameof(EventHubTestBindToStringJobs.SendEvent_TestHub), new { input = "data" });
- bool result = _eventWait.WaitOne(Timeout);
+ bool result = _eventWait1.WaitOne(Timeout);
Assert.True(result);
var logs = host.GetTestLoggerProvider().GetAllLogMessages().Select(p => p.FormattedMessage);
@@ -94,7 +97,7 @@ public async Task EventHub_SingleDispatch()
{
await jobHost.CallAsync(nameof(EventHubTestSingleDispatchJobs.SendEvent_TestHub), new { input = "data" });
- bool result = _eventWait.WaitOne(Timeout);
+ bool result = _eventWait1.WaitOne(Timeout);
Assert.True(result);
await StopWithDrainAsync(host);
@@ -108,11 +111,11 @@ public async Task EventHub_SingleDispatch_Dispose()
{
await using var producer = new EventHubProducerClient(EventHubsTestEnvironment.Instance.EventHubsConnectionString, _eventHubScope.EventHubName);
await producer.SendAsync(new EventData[] { new EventData(new BinaryData("data")) });
- var (_, host) = BuildHost(ConfigureTestEventHub);
+ var (jobHost, _) = BuildHost(ConfigureTestEventHub);
- bool result = _eventWait.WaitOne(Timeout);
- Assert.True(result);
- host.Dispose();
+ Assert.True(_eventWait1.WaitOne(Timeout));
+ jobHost.Dispose();
+ Assert.True(_eventWait2.WaitOne(Timeout));
}
[Test]
@@ -120,11 +123,11 @@ public async Task EventHub_SingleDispatch_StopWithoutDrain()
{
await using var producer = new EventHubProducerClient(EventHubsTestEnvironment.Instance.EventHubsConnectionString, _eventHubScope.EventHubName);
await producer.SendAsync(new EventData[] { new EventData(new BinaryData("data")) });
- var (_, host) = BuildHost(ConfigureTestEventHub);
+ var (jobHost, _) = BuildHost(ConfigureTestEventHub);
- bool result = _eventWait.WaitOne(Timeout);
- Assert.True(result);
- await host.StopAsync();
+ Assert.True(_eventWait1.WaitOne(Timeout));
+ await jobHost.StopAsync();
+ Assert.True(_eventWait2.WaitOne(Timeout));
}
[Test]
@@ -146,7 +149,7 @@ public async Task EventHub_SingleDispatch_ConsumerGroup()
{
await jobHost.CallAsync(nameof(EventHubTestSingleDispatchWithConsumerGroupJobs.SendEvent_TestHub));
- bool result = _eventWait.WaitOne(Timeout);
+ bool result = _eventWait1.WaitOne(Timeout);
Assert.True(result);
}
}
@@ -159,7 +162,7 @@ public async Task EventHub_SingleDispatch_BinaryData()
{
await jobHost.CallAsync(nameof(EventHubTestSingleDispatchJobsBinaryData.SendEvent_TestHub), new { input = "data" });
- bool result = _eventWait.WaitOne(Timeout);
+ bool result = _eventWait1.WaitOne(Timeout);
Assert.True(result);
await StopWithDrainAsync(host);
@@ -176,7 +179,7 @@ public async Task EventHub_ProducerClient()
{
await jobHost.CallAsync(nameof(EventHubTestClientDispatch.SendEvents));
- bool result = _eventWait.WaitOne(Timeout);
+ bool result = _eventWait1.WaitOne(Timeout);
Assert.True(result);
}
}
@@ -189,7 +192,7 @@ public async Task EventHub_Collector()
{
await jobHost.CallAsync(nameof(EventHubTestCollectorDispatch.SendEvents));
- bool result = _eventWait.WaitOne(Timeout);
+ bool result = _eventWait1.WaitOne(Timeout);
Assert.True(result);
}
}
@@ -202,7 +205,7 @@ public async Task EventHub_CollectorPartitionKey()
{
await jobHost.CallAsync(nameof(EventHubTestCollectorDispatch.SendEventsWithKey));
- bool result = _eventWait.WaitOne(Timeout);
+ bool result = _eventWait1.WaitOne(Timeout);
Assert.True(result);
}
}
@@ -320,7 +323,7 @@ public async Task AssertCanSendReceiveMessage(Action hostConfigura
{
await jobHost.CallAsync(nameof(EventHubTestSingleDispatchJobWithConnection.SendEvent_TestHub), new { input = "data" });
- bool result = _eventWait.WaitOne(Timeout);
+ bool result = _eventWait1.WaitOne(Timeout);
Assert.True(result);
}
}
@@ -334,7 +337,7 @@ public async Task EventHub_MultipleDispatch()
int numEvents = 5;
await jobHost.CallAsync(nameof(EventHubTestMultipleDispatchJobs.SendEvents_TestHub), new { numEvents = numEvents, input = "data" });
- bool result = _eventWait.WaitOne(Timeout);
+ bool result = _eventWait1.WaitOne(Timeout);
Assert.True(result);
await StopWithDrainAsync(host);
@@ -352,7 +355,7 @@ public async Task EventHub_MultipleDispatch_BinaryData()
int numEvents = 5;
await jobHost.CallAsync(nameof(EventHubTestMultipleDispatchJobsBinaryData.SendEvents_TestHub), new { numEvents = numEvents, input = "data" });
- bool result = _eventWait.WaitOne(Timeout);
+ bool result = _eventWait1.WaitOne(Timeout);
Assert.True(result);
await StopWithDrainAsync(host);
@@ -389,7 +392,7 @@ public async Task EventHub_MultipleDispatch_MinBatchSize()
int numEvents = 5;
await jobHost.CallAsync(nameof(EventHubTestMultipleDispatchMinBatchSizeJobs.SendEvents_TestHub), new { numEvents = numEvents, input = "data" });
- bool result = _eventWait.WaitOne(Timeout);
+ bool result = _eventWait1.WaitOne(Timeout);
Assert.True(result);
await StopWithDrainAsync(host);
@@ -403,23 +406,23 @@ public async Task EventHub_MultipleDispatch_Dispose()
{
await using var producer = new EventHubProducerClient(EventHubsTestEnvironment.Instance.EventHubsConnectionString, _eventHubScope.EventHubName);
await producer.SendAsync(new EventData[] { new EventData(new BinaryData("data")) });
- var (_, host) = BuildHost();
+ var (jobHost, _) = BuildHost();
- bool result = _eventWait.WaitOne(Timeout);
- Assert.True(result);
- host.Dispose();
- }
+ Assert.True(_eventWait1.WaitOne(Timeout));
+ jobHost.Dispose();
+ Assert.True(_eventWait2.WaitOne(Timeout));
+ }
[Test]
public async Task EventHub_MultipleDispatch_StopWithoutDrain()
{
await using var producer = new EventHubProducerClient(EventHubsTestEnvironment.Instance.EventHubsConnectionString, _eventHubScope.EventHubName);
await producer.SendAsync(new EventData[] { new EventData(new BinaryData("data")) });
- var (_, host) = BuildHost();
+ var (jobHost, _) = BuildHost();
- bool result = _eventWait.WaitOne(Timeout);
- Assert.True(result);
- await host.StopAsync();
+ Assert.True(_eventWait1.WaitOne(Timeout));
+ await jobHost.StopAsync();
+ Assert.True(_eventWait2.WaitOne(Timeout));
}
[Test]
@@ -429,7 +432,7 @@ public async Task EventHub_PartitionKey()
using (jobHost)
{
await jobHost.CallAsync(nameof(EventHubPartitionKeyTestJobs.SendEvents_TestHub), new { input = "data" });
- bool result = _eventWait.WaitOne(Timeout);
+ bool result = _eventWait1.WaitOne(Timeout);
Assert.True(result);
}
@@ -455,7 +458,7 @@ public async Task EventHub_InitialOffsetFromStart()
});
using (jobHost)
{
- bool result = _eventWait.WaitOne(Timeout);
+ bool result = _eventWait1.WaitOne(Timeout);
Assert.True(result);
}
}
@@ -482,7 +485,7 @@ public async Task EventHub_InitialOffsetFromEnd()
using (jobHost)
{
// We don't expect to get signaled as there should be no messages received with a FromEnd initial offset
- bool result = _eventWait.WaitOne(NoEventReadTimeout);
+ bool result = _eventWait1.WaitOne(NoEventReadTimeout);
Assert.False(result, "An event was received while none were expected.");
// send events which should be received. To ensure that the test is
@@ -499,7 +502,7 @@ public async Task EventHub_InitialOffsetFromEnd()
}
});
- result = _eventWait.WaitOne(Timeout);
+ result = _eventWait1.WaitOne(Timeout);
cts.Cancel();
try { await sendTask; } catch { /* Ignore, we're not testing sends */ }
@@ -549,7 +552,7 @@ public async Task EventHub_InitialOffsetFromEnqueuedTime()
});
using (jobHost)
{
- bool result = _eventWait.WaitOne(Timeout);
+ bool result = _eventWait1.WaitOne(Timeout);
Assert.True(result);
}
}
@@ -640,7 +643,7 @@ public static void ProcessSingleEvent([EventHubTrigger(TestHubName, Connection =
Assert.AreNotEqual(default(LastEnqueuedEventProperties), triggerPartitionContext.ReadLastEnqueuedEventProperties());
Assert.True(triggerPartitionContext.IsCheckpointingAfterInvocation);
- _eventWait.Set();
+ _eventWait1.Set();
}
}
@@ -648,10 +651,13 @@ public class EventHubTestSingleDispatchJobs_Dispose
{
public static async Task SendEvent_TestHub([EventHubTrigger(TestHubName, Connection = TestHubName)] string evt, CancellationToken cancellationToken)
{
- _eventWait.Set();
- // wait a small amount of time for the host to call dispose
- await Task.Delay(3000, CancellationToken.None);
- Assert.IsTrue(cancellationToken.IsCancellationRequested);
+ _eventWait1.Set();
+ // wait for the host to call dispose
+ while (!cancellationToken.IsCancellationRequested)
+ {
+ await Task.Delay(100, CancellationToken.None);
+ }
+ _eventWait2.Set();
}
}
@@ -659,10 +665,13 @@ public class EventHubTestMultipleDispatchJobs_Dispose
{
public static async Task SendEvent_TestHub([EventHubTrigger(TestHubName, Connection = TestHubName)] string[] evt, CancellationToken cancellationToken)
{
- _eventWait.Set();
- // wait a small amount of time for the host to call dispose
- await Task.Delay(3000, CancellationToken.None);
- Assert.IsTrue(cancellationToken.IsCancellationRequested);
+ _eventWait1.Set();
+ // wait for the host to call dispose
+ while (!cancellationToken.IsCancellationRequested)
+ {
+ await Task.Delay(1000, CancellationToken.None);
+ }
+ _eventWait2.Set();
}
}
@@ -693,7 +702,7 @@ public static void ProcessSingleEvent([EventHubTrigger(TestHubName, Connection =
Assert.AreEqual(eventData.PartitionKey, s_partitionKey);
}
- _eventWait.Set();
+ _eventWait1.Set();
}
}
@@ -710,7 +719,7 @@ await producer.SendAsync(new[]
public static void ProcessSingleEvent([EventHubTrigger(TestHubName, Connection = TestHubName)] EventData eventData)
{
Assert.AreEqual(eventData.EventBody.ToString(), "Event 1");
- _eventWait.Set();
+ _eventWait1.Set();
}
}
@@ -725,7 +734,7 @@ public static void ProcessSingleEvent([EventHubTrigger(TestHubName, Connection =
{
Assert.AreEqual(evt, nameof(EventHubTestSingleDispatchWithConsumerGroupJobs));
- _eventWait.Set();
+ _eventWait1.Set();
}
}
@@ -741,7 +750,7 @@ public static void ProcessSingleEvent([EventHubTrigger(TestHubName, Connection =
IDictionary systemProperties)
{
Assert.AreEqual("data", evt.ToString());
- _eventWait.Set();
+ _eventWait1.Set();
}
}
@@ -757,7 +766,7 @@ public static void BindToPoco([EventHubTrigger(TestHubName, Connection = TestHub
Assert.AreEqual(input.Value, "data");
Assert.AreEqual(input.Name, "foo");
logger.LogInformation($"PocoValues(foo,data)");
- _eventWait.Set();
+ _eventWait1.Set();
}
}
@@ -771,7 +780,7 @@ public static void SendEvent_TestHub(string input, [EventHub(TestHubName, Connec
public static void BindToString([EventHubTrigger(TestHubName, Connection = TestHubName)] string input, ILogger logger)
{
logger.LogInformation($"Input({input})");
- _eventWait.Set();
+ _eventWait1.Set();
}
}
@@ -817,7 +826,7 @@ public static void ProcessMultipleEvents([EventHubTrigger(TestHubName, Connectio
if (s_processedEventCount == s_eventCount)
{
_results.AddRange(events);
- _eventWait.Set();
+ _eventWait1.Set();
}
}
}
@@ -850,7 +859,7 @@ public static void ProcessMultipleEventsBinaryData([EventHubTrigger(TestHubName,
// filter for the ID the current test is using
if (s_processedEventCount == s_eventCount)
{
- _eventWait.Set();
+ _eventWait1.Set();
}
}
}
@@ -915,7 +924,7 @@ public static void ProcessMultipleEvents([EventHubTrigger(TestHubName, Connectio
if (s_processedEventCount >= s_eventCount)
{
- _eventWait.Set();
+ _eventWait1.Set();
}
}
}
@@ -963,7 +972,7 @@ public static void ProcessMultiplePartitionEvents([EventHubTrigger(TestHubName,
if (_results.Count > 0)
{
- _eventWait.Set();
+ _eventWait1.Set();
}
}
}
@@ -982,7 +991,7 @@ public static void ProcessSingleEvent([EventHubTrigger(TestHubName, Connection =
Assert.AreEqual("value1", properties["TestProp1"]);
Assert.AreEqual("value2", properties["TestProp2"]);
- _eventWait.Set();
+ _eventWait1.Set();
}
}
public class TestPoco
@@ -997,7 +1006,7 @@ public static void ProcessSingleEvent([EventHubTrigger(TestHubName, Connection =
string partitionKey, DateTime enqueuedTimeUtc, IDictionary properties,
IDictionary systemProperties)
{
- _eventWait.Set();
+ _eventWait1.Set();
}
}
@@ -1022,7 +1031,7 @@ public static void ProcessMultipleEvents([EventHubTrigger(TestHubName, Connectio
{
Assert.GreaterOrEqual(DateTimeOffset.Parse(result), earliestAllowedOffset);
}
- _eventWait.Set();
+ _eventWait1.Set();
}
}
}
diff --git a/sdk/identity/Azure.Identity/samples/TokenCache.md b/sdk/identity/Azure.Identity/samples/TokenCache.md
index fdfc23181024..586599d94120 100644
--- a/sdk/identity/Azure.Identity/samples/TokenCache.md
+++ b/sdk/identity/Azure.Identity/samples/TokenCache.md
@@ -18,6 +18,10 @@ The in-memory token cache provided by the Azure Identity library is thread-safe.
**Note:** When Azure Identity library credentials are used with Azure service libraries (for example, Azure Blob Storage), the in-memory token caching is active in the `HttpPipeline` layer as well. All `TokenCredential` implementations are supported there, including custom implementations external to the Azure Identity library.
+#### Disable caching
+
+As there are many levels of cache, it's not possible to disable in-memory caching. However, the in-memory cache may be cleared by creating a new credential instance.
+
## Persistent token caching
*Persistent disk token caching* is an opt-in feature in the Azure Identity library. The feature allows apps to cache access tokens in an encrypted, persistent storage mechanism. As indicated in the following table, the storage mechanism differs across operating systems.
diff --git a/sdk/storage/Azure.Storage.Blobs/src/AssemblyInfo.cs b/sdk/storage/Azure.Storage.Blobs/src/AssemblyInfo.cs
index 014bf8d8af38..eac6cd30ae46 100644
--- a/sdk/storage/Azure.Storage.Blobs/src/AssemblyInfo.cs
+++ b/sdk/storage/Azure.Storage.Blobs/src/AssemblyInfo.cs
@@ -8,5 +8,11 @@
"012ea67d2479751a0b8c087a4185541b851bd8b16f8d91b840e51b1cb0ba6fe647997e57429265" +
"e85ef62d565db50a69ae1647d54d7bd855e4db3d8a91510e5bcbd0edfbbecaa20a7bd9ae74593d" +
"aa7b11b4")]
+[assembly: InternalsVisibleTo("Azure.Storage.DataMovement.Blobs.Tests, PublicKey=" +
+ "0024000004800000940000000602000000240000525341310004000001000100d15ddcb2968829" +
+ "5338af4b7686603fe614abd555e09efba8fb88ee09e1f7b1ccaeed2e8f823fa9eef3fdd60217fc" +
+ "012ea67d2479751a0b8c087a4185541b851bd8b16f8d91b840e51b1cb0ba6fe647997e57429265" +
+ "e85ef62d565db50a69ae1647d54d7bd855e4db3d8a91510e5bcbd0edfbbecaa20a7bd9ae74593d" +
+ "aa7b11b4")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
-[assembly: Azure.Core.AzureResourceProviderNamespace("Microsoft.Storage")]
\ No newline at end of file
+[assembly: Azure.Core.AzureResourceProviderNamespace("Microsoft.Storage")]
diff --git a/sdk/storage/Azure.Storage.Common/tests/Shared/RandomExtensions.cs b/sdk/storage/Azure.Storage.Common/tests/Shared/RandomExtensions.cs
index ad9bd93b27ae..56154bd06db3 100644
--- a/sdk/storage/Azure.Storage.Common/tests/Shared/RandomExtensions.cs
+++ b/sdk/storage/Azure.Storage.Common/tests/Shared/RandomExtensions.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT License.
using System;
+using System.Text;
namespace Azure.Storage.Tests
{
@@ -18,5 +19,19 @@ public static string NextString(this Random random, int length)
}
return new string(buffer);
}
+
+ public static Span NextBytesInline(this Random random, int length)
+ {
+ var buffer = new byte[length];
+ random.NextBytes(buffer);
+ return new Span(buffer);
+ }
+
+ public static string NextBase64(this Random random, int length)
+ {
+ var buffer = new byte[length];
+ random.NextBytes(buffer);
+ return Convert.ToBase64String(buffer);
+ }
}
}
diff --git a/sdk/storage/Azure.Storage.DataMovement.Blobs/api/Azure.Storage.DataMovement.Blobs.net6.0.cs b/sdk/storage/Azure.Storage.DataMovement.Blobs/api/Azure.Storage.DataMovement.Blobs.net6.0.cs
index 34885273cd8c..aaf9d8a9a6ec 100644
--- a/sdk/storage/Azure.Storage.DataMovement.Blobs/api/Azure.Storage.DataMovement.Blobs.net6.0.cs
+++ b/sdk/storage/Azure.Storage.DataMovement.Blobs/api/Azure.Storage.DataMovement.Blobs.net6.0.cs
@@ -44,6 +44,12 @@ public BlobContainerClientTransferOptions() { }
public partial class BlobsStorageResourceProvider : Azure.Storage.DataMovement.StorageResourceProvider
{
public BlobsStorageResourceProvider() { }
+ public BlobsStorageResourceProvider(Azure.AzureSasCredential credential) { }
+ public BlobsStorageResourceProvider(Azure.Core.TokenCredential credential) { }
+ public BlobsStorageResourceProvider(Azure.Storage.DataMovement.Blobs.BlobsStorageResourceProvider.GetAzureSasCredential getAzureSasCredentialAsync) { }
+ public BlobsStorageResourceProvider(Azure.Storage.DataMovement.Blobs.BlobsStorageResourceProvider.GetStorageSharedKeyCredential getStorageSharedKeyCredentialAsync) { }
+ public BlobsStorageResourceProvider(Azure.Storage.DataMovement.Blobs.BlobsStorageResourceProvider.GetTokenCredential getTokenCredentialAsync) { }
+ public BlobsStorageResourceProvider(Azure.Storage.StorageSharedKeyCredential credential) { }
protected override string TypeId { get { throw null; } }
public Azure.Storage.DataMovement.StorageResource FromBlob(string blobUri, Azure.Storage.DataMovement.Blobs.BlobStorageResourceOptions options = null) { throw null; }
public Azure.Storage.DataMovement.StorageResource FromClient(Azure.Storage.Blobs.BlobContainerClient client, Azure.Storage.DataMovement.Blobs.BlobStorageResourceContainerOptions options = null) { throw null; }
@@ -53,6 +59,9 @@ public BlobsStorageResourceProvider() { }
public Azure.Storage.DataMovement.StorageResource FromContainer(string containerUri, Azure.Storage.DataMovement.Blobs.BlobStorageResourceContainerOptions options = null) { throw null; }
protected override System.Threading.Tasks.Task FromDestinationAsync(Azure.Storage.DataMovement.DataTransferProperties properties, System.Threading.CancellationToken cancellationToken) { throw null; }
protected override System.Threading.Tasks.Task FromSourceAsync(Azure.Storage.DataMovement.DataTransferProperties properties, System.Threading.CancellationToken cancellationToken) { throw null; }
+ public delegate Azure.AzureSasCredential GetAzureSasCredential(string uri, bool readOnly);
+ public delegate Azure.Storage.StorageSharedKeyCredential GetStorageSharedKeyCredential(string uri, bool readOnly);
+ public delegate Azure.Core.TokenCredential GetTokenCredential(string uri, bool readOnly);
}
public partial class BlobStorageResourceContainer : Azure.Storage.DataMovement.StorageResourceContainer
{
diff --git a/sdk/storage/Azure.Storage.DataMovement.Blobs/api/Azure.Storage.DataMovement.Blobs.netstandard2.0.cs b/sdk/storage/Azure.Storage.DataMovement.Blobs/api/Azure.Storage.DataMovement.Blobs.netstandard2.0.cs
index 34885273cd8c..aaf9d8a9a6ec 100644
--- a/sdk/storage/Azure.Storage.DataMovement.Blobs/api/Azure.Storage.DataMovement.Blobs.netstandard2.0.cs
+++ b/sdk/storage/Azure.Storage.DataMovement.Blobs/api/Azure.Storage.DataMovement.Blobs.netstandard2.0.cs
@@ -44,6 +44,12 @@ public BlobContainerClientTransferOptions() { }
public partial class BlobsStorageResourceProvider : Azure.Storage.DataMovement.StorageResourceProvider
{
public BlobsStorageResourceProvider() { }
+ public BlobsStorageResourceProvider(Azure.AzureSasCredential credential) { }
+ public BlobsStorageResourceProvider(Azure.Core.TokenCredential credential) { }
+ public BlobsStorageResourceProvider(Azure.Storage.DataMovement.Blobs.BlobsStorageResourceProvider.GetAzureSasCredential getAzureSasCredentialAsync) { }
+ public BlobsStorageResourceProvider(Azure.Storage.DataMovement.Blobs.BlobsStorageResourceProvider.GetStorageSharedKeyCredential getStorageSharedKeyCredentialAsync) { }
+ public BlobsStorageResourceProvider(Azure.Storage.DataMovement.Blobs.BlobsStorageResourceProvider.GetTokenCredential getTokenCredentialAsync) { }
+ public BlobsStorageResourceProvider(Azure.Storage.StorageSharedKeyCredential credential) { }
protected override string TypeId { get { throw null; } }
public Azure.Storage.DataMovement.StorageResource FromBlob(string blobUri, Azure.Storage.DataMovement.Blobs.BlobStorageResourceOptions options = null) { throw null; }
public Azure.Storage.DataMovement.StorageResource FromClient(Azure.Storage.Blobs.BlobContainerClient client, Azure.Storage.DataMovement.Blobs.BlobStorageResourceContainerOptions options = null) { throw null; }
@@ -53,6 +59,9 @@ public BlobsStorageResourceProvider() { }
public Azure.Storage.DataMovement.StorageResource FromContainer(string containerUri, Azure.Storage.DataMovement.Blobs.BlobStorageResourceContainerOptions options = null) { throw null; }
protected override System.Threading.Tasks.Task FromDestinationAsync(Azure.Storage.DataMovement.DataTransferProperties properties, System.Threading.CancellationToken cancellationToken) { throw null; }
protected override System.Threading.Tasks.Task FromSourceAsync(Azure.Storage.DataMovement.DataTransferProperties properties, System.Threading.CancellationToken cancellationToken) { throw null; }
+ public delegate Azure.AzureSasCredential GetAzureSasCredential(string uri, bool readOnly);
+ public delegate Azure.Storage.StorageSharedKeyCredential GetStorageSharedKeyCredential(string uri, bool readOnly);
+ public delegate Azure.Core.TokenCredential GetTokenCredential(string uri, bool readOnly);
}
public partial class BlobStorageResourceContainer : Azure.Storage.DataMovement.StorageResourceContainer
{
diff --git a/sdk/storage/Azure.Storage.DataMovement.Blobs/src/BlobStorageResourceContainer.cs b/sdk/storage/Azure.Storage.DataMovement.Blobs/src/BlobStorageResourceContainer.cs
index 436a4f58ea36..bc35d81002e9 100644
--- a/sdk/storage/Azure.Storage.DataMovement.Blobs/src/BlobStorageResourceContainer.cs
+++ b/sdk/storage/Azure.Storage.DataMovement.Blobs/src/BlobStorageResourceContainer.cs
@@ -18,11 +18,11 @@ namespace Azure.Storage.DataMovement.Blobs
///
public class BlobStorageResourceContainer : StorageResourceContainer
{
- private BlobContainerClient _blobContainerClient;
- private string _directoryPrefix;
+ internal BlobContainerClient BlobContainerClient { get; }
+ internal string DirectoryPrefix { get; }
private BlobStorageResourceContainerOptions _options;
- private bool IsDirectory => _directoryPrefix != null;
+ private bool IsDirectory => DirectoryPrefix != null;
///
/// The constructor to create an instance of the BlobStorageResourceContainer.
@@ -34,16 +34,16 @@ public class BlobStorageResourceContainer : StorageResourceContainer
/// Options for the storage resource. See .
public BlobStorageResourceContainer(BlobContainerClient blobContainerClient, BlobStorageResourceContainerOptions options = default)
{
- _blobContainerClient = blobContainerClient;
+ BlobContainerClient = blobContainerClient;
_options = options;
- _directoryPrefix = _options?.BlobDirectoryPrefix;
+ DirectoryPrefix = _options?.BlobDirectoryPrefix;
- Uri = _directoryPrefix != null
- ? new BlobUriBuilder(_blobContainerClient.Uri)
+ Uri = DirectoryPrefix != null
+ ? new BlobUriBuilder(BlobContainerClient.Uri)
{
- BlobName = _directoryPrefix,
+ BlobName = DirectoryPrefix,
}.ToUri()
- : _blobContainerClient.Uri;
+ : BlobContainerClient.Uri;
}
///
@@ -55,7 +55,7 @@ public BlobStorageResourceContainer(BlobContainerClient blobContainerClient, Blo
/// Gets the path of the storage resource.
/// Return empty string since we are using the root of the container.
///
- public override string Path => _directoryPrefix ?? string.Empty;
+ public override string Path => DirectoryPrefix ?? string.Empty;
///
/// Gets the URL of the storage resource.
@@ -89,7 +89,7 @@ private StorageResourceItem GetBlobAsStorageResource(
// Recreate the blobName using the existing parent directory path
if (type == BlobType.Append)
{
- AppendBlobClient client = _blobContainerClient.GetAppendBlobClient(blobName);
+ AppendBlobClient client = BlobContainerClient.GetAppendBlobClient(blobName);
return new AppendBlobStorageResource(
client,
length,
@@ -98,7 +98,7 @@ private StorageResourceItem GetBlobAsStorageResource(
}
else if (type == BlobType.Page)
{
- PageBlobClient client = _blobContainerClient.GetPageBlobClient(blobName);
+ PageBlobClient client = BlobContainerClient.GetPageBlobClient(blobName);
return new PageBlobStorageResource(
client,
length,
@@ -107,7 +107,7 @@ private StorageResourceItem GetBlobAsStorageResource(
}
else // (type == BlobType.Block)
{
- BlockBlobClient client = _blobContainerClient.GetBlockBlobClient(blobName);
+ BlockBlobClient client = BlobContainerClient.GetBlockBlobClient(blobName);
return new BlockBlobStorageResource(
client,
length,
@@ -125,8 +125,8 @@ private StorageResourceItem GetBlobAsStorageResource(
protected override async IAsyncEnumerable GetStorageResourcesAsync(
[EnumeratorCancellation] CancellationToken cancellationToken = default)
{
- AsyncPageable pages = _blobContainerClient.GetBlobsAsync(
- prefix: _directoryPrefix,
+ AsyncPageable pages = BlobContainerClient.GetBlobsAsync(
+ prefix: DirectoryPrefix,
cancellationToken: cancellationToken);
await foreach (BlobItem blobItem in pages.ConfigureAwait(false))
{
@@ -320,7 +320,7 @@ await checkpointer.GetBlobContainerOptionsAsync(
private string ApplyOptionalPrefix(string path)
=> IsDirectory
- ? string.Join("/", _directoryPrefix, path)
+ ? string.Join("/", DirectoryPrefix, path)
: path;
}
}
diff --git a/sdk/storage/Azure.Storage.DataMovement.Blobs/src/BlobsStorageResourceProvider.cs b/sdk/storage/Azure.Storage.DataMovement.Blobs/src/BlobsStorageResourceProvider.cs
index 68e1cdbeafd2..37654eda7d11 100644
--- a/sdk/storage/Azure.Storage.DataMovement.Blobs/src/BlobsStorageResourceProvider.cs
+++ b/sdk/storage/Azure.Storage.DataMovement.Blobs/src/BlobsStorageResourceProvider.cs
@@ -15,6 +15,39 @@ namespace Azure.Storage.DataMovement.Blobs
///
public class BlobsStorageResourceProvider : StorageResourceProvider
{
+ ///
+ /// Delegate for fetching a shared key credential for a given URI.
+ ///
+ ///
+ /// URI of resource to fetch credential for.
+ ///
+ ///
+ /// Whether the permission can be read-only.
+ ///
+ public delegate StorageSharedKeyCredential GetStorageSharedKeyCredential(string uri, bool readOnly);
+
+ ///
+ /// Delegate for fetching a token credential for a given URI.
+ ///
+ ///
+ /// URI of resource to fetch credential for.
+ ///
+ ///
+ /// Whether the permission can be read-only.
+ ///
+ public delegate TokenCredential GetTokenCredential(string uri, bool readOnly);
+
+ ///
+ /// Delegate for fetching a SAS credential for a given URI.
+ ///
+ ///
+ /// URI of resource to fetch credential for.
+ ///
+ ///
+ /// Whether the permission can be read-only.
+ ///
+ public delegate AzureSasCredential GetAzureSasCredential(string uri, bool readOnly);
+
private enum ResourceType
{
Unknown = 0,
@@ -24,14 +57,167 @@ private enum ResourceType
BlobContainer = 4,
}
+ private enum CredentialType
+ {
+ None = 0,
+ SharedKey = 1,
+ Token = 2,
+ Sas = 4
+ }
+
///
protected override string TypeId => "blob";
+ private readonly CredentialType _credentialType;
+ private readonly GetStorageSharedKeyCredential _getStorageSharedKeyCredential;
+ private readonly GetTokenCredential _getTokenCredential;
+ private readonly GetAzureSasCredential _getAzureSasCredential;
+
///
/// Default constrctor.
///
public BlobsStorageResourceProvider()
{
+ _credentialType = CredentialType.None;
+ }
+
+ ///
+ ///
+ /// Constructs this provider to use the given credential when making a new Blob Storage
+ /// .
+ ///
+ ///
+ /// This instance will use the given when constructing the underlying
+ /// Azure.Storage.Blobs client, e.g. .
+ /// The credential will only be used when the provider needs to construct a client in the first place. It will
+ /// not be used when creating a from a pre-existing client, e.g.
+ /// .
+ ///
+ ///
+ ///
+ /// Shared key credential to use when constructing resources.
+ ///
+ public BlobsStorageResourceProvider(StorageSharedKeyCredential credential)
+ {
+ _credentialType = CredentialType.SharedKey;
+ _getStorageSharedKeyCredential = (_, _) => credential;
+ }
+
+ ///
+ ///
+ /// Constructs this provider to use the given credential when making a new Blob Storage
+ /// .
+ ///
+ ///
+ /// This instance will use the given when constructing the underlying
+ /// Azure.Storage.Blobs client, e.g. .
+ /// The credential will only be used when the provider needs to construct a client in the first place. It will
+ /// not be used when creating a from a pre-existing client, e.g.
+ /// .
+ ///
+ ///
+ ///
+ /// Token credential to use when constructing resources.
+ ///
+ public BlobsStorageResourceProvider(TokenCredential credential)
+ {
+ _credentialType = CredentialType.Token;
+ _getTokenCredential = (_, _) => credential;
+ }
+
+ ///
+ ///
+ /// Constructs this provider to use the given credential when making a new Blob Storage
+ /// .
+ ///
+ ///
+ /// This instance will use the given when constructing the underlying
+ /// Azure.Storage.Blobs client, e.g. .
+ /// The credential will only be used when the provider needs to construct a client in the first place. It will
+ /// not be used when creating a from a pre-existing client, e.g.
+ /// .
+ /// Additionally, if the given target blob resource already has a SAS token in the URI, that token will be
+ /// preferred over this credential.
+ ///
+ ///
+ ///
+ /// SAS credential to use when constructing resources.
+ ///
+ public BlobsStorageResourceProvider(AzureSasCredential credential)
+ {
+ _credentialType = CredentialType.Sas;
+ _getAzureSasCredential = (_, _) => credential;
+ }
+
+ ///
+ ///
+ /// Constructs this provider to use the given delegate for acquiring a credential when making a new Blob
+ /// Storage .
+ ///
+ ///
+ /// This instance will use the given to fetch a credential
+ /// when constructing the underlying Azure.Storage.Blobs client, e.g.
+ /// .
+ /// The delegate will only be used when the provider needs to construct a client in the first place. It will
+ /// not be used when creating a from a pre-existing client, e.g.
+ /// .
+ ///
+ ///
+ ///
+ /// Delegate for acquiring a credential.
+ ///
+ public BlobsStorageResourceProvider(GetStorageSharedKeyCredential getStorageSharedKeyCredentialAsync)
+ {
+ _credentialType = CredentialType.SharedKey;
+ _getStorageSharedKeyCredential = getStorageSharedKeyCredentialAsync;
+ }
+
+ ///
+ ///
+ /// Constructs this provider to use the given delegate for acquiring a credential when making a new Blob
+ /// Storage .
+ ///
+ ///
+ /// This instance will use the given to fetch a credential
+ /// when constructing the underlying Azure.Storage.Blobs client, e.g.
+ /// .
+ /// The delegate will only be used when the provider needs to construct a client in the first place. It will
+ /// not be used when creating a from a pre-existing client, e.g.
+ /// .
+ ///
+ ///
+ ///
+ /// Delegate for acquiring a credential.
+ ///
+ public BlobsStorageResourceProvider(GetTokenCredential getTokenCredentialAsync)
+ {
+ _credentialType = CredentialType.SharedKey;
+ _getTokenCredential = getTokenCredentialAsync;
+ }
+
+ ///
+ ///
+ /// Constructs this provider to use the given delegate for acquiring a credential when making a new Blob
+ /// Storage .
+ ///
+ ///
+ /// This instance will use the given to fetch a credential
+ /// when constructing the underlying Azure.Storage.Blobs client, e.g.
+ /// .
+ /// The delegate will only be used when the provider needs to construct a client in the first place. It will
+ /// not be used when creating a from a pre-existing client, e.g.
+ /// .
+ /// Additionally, if the given target blob resource already has a SAS token in the URI, that token will be
+ /// preferred over this delegate.
+ ///
+ ///
+ ///
+ /// Delegate for acquiring a credential.
+ ///
+ public BlobsStorageResourceProvider(GetAzureSasCredential getAzureSasCredentialAsync)
+ {
+ _credentialType = CredentialType.SharedKey;
+ _getAzureSasCredential = getAzureSasCredentialAsync;
}
#region Abstract Class Implementation
@@ -49,6 +235,7 @@ private async Task FromTransferPropertiesAsync(
CancellationToken cancellationToken)
{
ResourceType type = GetType(getSource ? properties.SourceTypeId : properties.DestinationTypeId, properties.IsContainer);
+ string uri = getSource ? properties.SourcePath : properties.DestinationPath;
IBlobResourceRehydrator rehydrator = type switch
{
ResourceType.BlockBlob => new BlockBlobResourceRehydrator(),
@@ -57,7 +244,33 @@ private async Task FromTransferPropertiesAsync(
ResourceType.BlobContainer => new BlobContainerResourceRehydrator(),
_ => throw BadResourceTypeException(type)
};
- return await rehydrator.RehydrateAsync(properties, getSource, cancellationToken).ConfigureAwait(false);
+ return _credentialType switch
+ {
+ CredentialType.None => await rehydrator.RehydrateAsync(
+ properties,
+ getSource,
+ cancellationToken)
+ .ConfigureAwait(false),
+ CredentialType.SharedKey => await rehydrator.RehydrateAsync(
+ properties,
+ getSource,
+ _getStorageSharedKeyCredential(uri, getSource),
+ cancellationToken)
+ .ConfigureAwait(false),
+ CredentialType.Token => await rehydrator.RehydrateAsync(
+ properties,
+ getSource,
+ _getTokenCredential(uri, getSource),
+ cancellationToken)
+ .ConfigureAwait(false),
+ CredentialType.Sas => await rehydrator.RehydrateAsync(
+ properties,
+ getSource,
+ _getAzureSasCredential(uri, getSource),
+ cancellationToken)
+ .ConfigureAwait(false),
+ _ => throw BadCredentialTypeException(_credentialType),
+ };
}
///
@@ -94,7 +307,15 @@ internal async Task FromDestinationInternalHookAsync(
///
public StorageResource FromContainer(string containerUri, BlobStorageResourceContainerOptions options = default)
{
- return new BlobStorageResourceContainer(new BlobContainerClient(new Uri(containerUri)), options);
+ BlobContainerClient client = _credentialType switch
+ {
+ CredentialType.None => new BlobContainerClient(new Uri(containerUri)),
+ CredentialType.SharedKey => new BlobContainerClient(new Uri(containerUri), _getStorageSharedKeyCredential(containerUri, false)),
+ CredentialType.Token => new BlobContainerClient(new Uri(containerUri), _getTokenCredential(containerUri, false)),
+ CredentialType.Sas => new BlobContainerClient(new Uri(containerUri), _getAzureSasCredential(containerUri, false)),
+ _ => throw BadCredentialTypeException(_credentialType),
+ };
+ return new BlobStorageResourceContainer(client, options);
}
///
@@ -124,17 +345,49 @@ public StorageResource FromBlob(string blobUri, BlobStorageResourceOptions optio
{
if (options is BlockBlobStorageResourceOptions)
{
- return new BlockBlobStorageResource(new BlockBlobClient(new Uri(blobUri)), options as BlockBlobStorageResourceOptions);
+ BlockBlobClient blockClient = _credentialType switch
+ {
+ CredentialType.None => new BlockBlobClient(new Uri(blobUri)),
+ CredentialType.SharedKey => new BlockBlobClient(new Uri(blobUri), _getStorageSharedKeyCredential(blobUri, false)),
+ CredentialType.Token => new BlockBlobClient(new Uri(blobUri), _getTokenCredential(blobUri, false)),
+ CredentialType.Sas => new BlockBlobClient(new Uri(blobUri), _getAzureSasCredential(blobUri, false)),
+ _ => throw BadCredentialTypeException(_credentialType),
+ };
+ return new BlockBlobStorageResource(blockClient, options as BlockBlobStorageResourceOptions);
}
if (options is PageBlobStorageResourceOptions)
{
- return new PageBlobStorageResource(new PageBlobClient(new Uri(blobUri)), options as PageBlobStorageResourceOptions);
+ PageBlobClient pageClient = _credentialType switch
+ {
+ CredentialType.None => new PageBlobClient(new Uri(blobUri)),
+ CredentialType.SharedKey => new PageBlobClient(new Uri(blobUri), _getStorageSharedKeyCredential(blobUri, false)),
+ CredentialType.Token => new PageBlobClient(new Uri(blobUri), _getTokenCredential(blobUri, false)),
+ CredentialType.Sas => new PageBlobClient(new Uri(blobUri), _getAzureSasCredential(blobUri, false)),
+ _ => throw BadCredentialTypeException(_credentialType),
+ };
+ return new PageBlobStorageResource(pageClient, options as PageBlobStorageResourceOptions);
}
if (options is AppendBlobStorageResourceOptions)
{
- return new AppendBlobStorageResource(new AppendBlobClient(new Uri(blobUri)), options as AppendBlobStorageResourceOptions);
+ AppendBlobClient appendClient = _credentialType switch
+ {
+ CredentialType.None => new AppendBlobClient(new Uri(blobUri)),
+ CredentialType.SharedKey => new AppendBlobClient(new Uri(blobUri), _getStorageSharedKeyCredential(blobUri, false)),
+ CredentialType.Token => new AppendBlobClient(new Uri(blobUri), _getTokenCredential(blobUri, false)),
+ CredentialType.Sas => new AppendBlobClient(new Uri(blobUri), _getAzureSasCredential(blobUri, false)),
+ _ => throw BadCredentialTypeException(_credentialType),
+ };
+ return new AppendBlobStorageResource(appendClient, options as AppendBlobStorageResourceOptions);
}
- return new BlockBlobStorageResource(new BlockBlobClient(new Uri(blobUri)), new BlockBlobStorageResourceOptions(options));
+ BlockBlobClient client = _credentialType switch
+ {
+ CredentialType.None => new BlockBlobClient(new Uri(blobUri)),
+ CredentialType.SharedKey => new BlockBlobClient(new Uri(blobUri), _getStorageSharedKeyCredential(blobUri, false)),
+ CredentialType.Token => new BlockBlobClient(new Uri(blobUri), _getTokenCredential(blobUri, false)),
+ CredentialType.Sas => new BlockBlobClient(new Uri(blobUri), _getAzureSasCredential(blobUri, false)),
+ _ => throw BadCredentialTypeException(_credentialType),
+ };
+ return new BlockBlobStorageResource(client, options as BlockBlobStorageResourceOptions);
}
#endregion
@@ -253,26 +506,29 @@ public async Task RehydrateAsync(
=> await BlobStorageResourceContainer.RehydrateResourceAsync(properties, isSource, cancellationToken)
.ConfigureAwait(false);
- public Task RehydrateAsync(
+ public async Task RehydrateAsync(
DataTransferProperties properties,
bool isSource,
StorageSharedKeyCredential credential,
CancellationToken cancellationToken)
- => throw new NotImplementedException("Creds not yet suppored on rehydration.");
+ => await BlobStorageResourceContainer.RehydrateResourceAsync(properties, isSource, credential, cancellationToken)
+ .ConfigureAwait(false);
- public Task RehydrateAsync(
+ public async Task RehydrateAsync(
DataTransferProperties properties,
bool isSource,
TokenCredential credential,
CancellationToken cancellationToken)
- => throw new NotImplementedException("Creds not yet suppored on rehydration.");
+ => await BlobStorageResourceContainer.RehydrateResourceAsync(properties, isSource, credential, cancellationToken)
+ .ConfigureAwait(false);
- public Task RehydrateAsync(
+ public async Task RehydrateAsync(
DataTransferProperties properties,
bool isSource,
AzureSasCredential credential,
CancellationToken cancellationToken)
- => throw new NotImplementedException("Creds not yet suppored on rehydration.");
+ => await BlobStorageResourceContainer.RehydrateResourceAsync(properties, isSource, credential, cancellationToken)
+ .ConfigureAwait(false);
}
private class BlockBlobResourceRehydrator : IBlobResourceRehydrator
@@ -284,26 +540,29 @@ public async Task RehydrateAsync(
=> await BlockBlobStorageResource.RehydrateResourceAsync(properties, isSource, cancellationToken)
.ConfigureAwait(false);
- public Task RehydrateAsync(
+ public async Task RehydrateAsync(
DataTransferProperties properties,
bool isSource,
StorageSharedKeyCredential credential,
CancellationToken cancellationToken)
- => throw new NotImplementedException("Creds not yet suppored on rehydration.");
+ => await BlockBlobStorageResource.RehydrateResourceAsync(properties, isSource, credential, cancellationToken)
+ .ConfigureAwait(false);
- public Task RehydrateAsync(
+ public async Task RehydrateAsync(
DataTransferProperties properties,
bool isSource,
TokenCredential credential,
CancellationToken cancellationToken)
- => throw new NotImplementedException("Creds not yet suppored on rehydration.");
+ => await BlockBlobStorageResource.RehydrateResourceAsync(properties, isSource, credential, cancellationToken)
+ .ConfigureAwait(false);
- public Task RehydrateAsync(
+ public async Task RehydrateAsync(
DataTransferProperties properties,
bool isSource,
AzureSasCredential credential,
CancellationToken cancellationToken)
- => throw new NotImplementedException("Creds not yet suppored on rehydration.");
+ => await BlockBlobStorageResource.RehydrateResourceAsync(properties, isSource, credential, cancellationToken)
+ .ConfigureAwait(false);
}
private class PageBlobResourceRehydrator : IBlobResourceRehydrator
@@ -315,26 +574,29 @@ public async Task RehydrateAsync(
=> await PageBlobStorageResource.RehydrateResourceAsync(properties, isSource, cancellationToken)
.ConfigureAwait(false);
- public Task RehydrateAsync(
+ public async Task RehydrateAsync(
DataTransferProperties properties,
bool isSource,
StorageSharedKeyCredential credential,
CancellationToken cancellationToken)
- => throw new NotImplementedException("Creds not yet suppored on rehydration.");
+ => await PageBlobStorageResource.RehydrateResourceAsync(properties, isSource, credential, cancellationToken)
+ .ConfigureAwait(false);
- public Task RehydrateAsync(
+ public async Task RehydrateAsync(
DataTransferProperties properties,
bool isSource,
TokenCredential credential,
CancellationToken cancellationToken)
- => throw new NotImplementedException("Creds not yet suppored on rehydration.");
+ => await PageBlobStorageResource.RehydrateResourceAsync(properties, isSource, credential, cancellationToken)
+ .ConfigureAwait(false);
- public Task RehydrateAsync(
+ public async Task RehydrateAsync(
DataTransferProperties properties,
bool isSource,
AzureSasCredential credential,
CancellationToken cancellationToken)
- => throw new NotImplementedException("Creds not yet suppored on rehydration.");
+ => await PageBlobStorageResource.RehydrateResourceAsync(properties, isSource, credential, cancellationToken)
+ .ConfigureAwait(false);
}
private class AppendBlobResourceRehydrator : IBlobResourceRehydrator
@@ -346,26 +608,29 @@ public async Task RehydrateAsync(
=> await AppendBlobStorageResource.RehydrateResourceAsync(properties, isSource, cancellationToken)
.ConfigureAwait(false);
- public Task RehydrateAsync(
+ public async Task RehydrateAsync(
DataTransferProperties properties,
bool isSource,
StorageSharedKeyCredential credential,
CancellationToken cancellationToken)
- => throw new NotImplementedException("Creds not yet suppored on rehydration.");
+ => await AppendBlobStorageResource.RehydrateResourceAsync(properties, isSource, credential, cancellationToken)
+ .ConfigureAwait(false);
- public Task RehydrateAsync(
+ public async Task RehydrateAsync(
DataTransferProperties properties,
bool isSource,
TokenCredential credential,
CancellationToken cancellationToken)
- => throw new NotImplementedException("Creds not yet suppored on rehydration.");
+ => await AppendBlobStorageResource.RehydrateResourceAsync(properties, isSource, credential, cancellationToken)
+ .ConfigureAwait(false);
- public Task RehydrateAsync(
+ public async Task RehydrateAsync(
DataTransferProperties properties,
bool isSource,
AzureSasCredential credential,
CancellationToken cancellationToken)
- => throw new NotImplementedException("Creds not yet suppored on rehydration.");
+ => await AppendBlobStorageResource.RehydrateResourceAsync(properties, isSource, credential, cancellationToken)
+ .ConfigureAwait(false);
}
#endregion
@@ -381,5 +646,9 @@ private static ResourceType GetType(string typeId, bool isContainer)
private static ArgumentException BadResourceTypeException(ResourceType resourceType)
=> new ArgumentException(
$"No support for resource type {Enum.GetName(typeof(ResourceType), resourceType)}.");
+
+ private static ArgumentException BadCredentialTypeException(CredentialType credentialType)
+ => new ArgumentException(
+ $"No support for credential type {Enum.GetName(typeof(CredentialType), credentialType)}.");
}
}
diff --git a/sdk/storage/Azure.Storage.DataMovement.Blobs/tests/Azure.Storage.DataMovement.Blobs.Tests.csproj b/sdk/storage/Azure.Storage.DataMovement.Blobs/tests/Azure.Storage.DataMovement.Blobs.Tests.csproj
index 71d8b2edac7b..1395f4dd48a2 100644
--- a/sdk/storage/Azure.Storage.DataMovement.Blobs/tests/Azure.Storage.DataMovement.Blobs.Tests.csproj
+++ b/sdk/storage/Azure.Storage.DataMovement.Blobs/tests/Azure.Storage.DataMovement.Blobs.Tests.csproj
@@ -11,6 +11,7 @@
+
diff --git a/sdk/storage/Azure.Storage.DataMovement.Blobs/tests/BlobStorageResourceProviderTests.cs b/sdk/storage/Azure.Storage.DataMovement.Blobs/tests/BlobStorageResourceProviderTests.cs
new file mode 100644
index 000000000000..a10f2d4f0c34
--- /dev/null
+++ b/sdk/storage/Azure.Storage.DataMovement.Blobs/tests/BlobStorageResourceProviderTests.cs
@@ -0,0 +1,169 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Buffers.Text;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Azure.Core;
+using Azure.Storage.Blobs;
+using Azure.Storage.Blobs.Specialized;
+using Azure.Storage.Tests;
+using Moq;
+using NUnit.Framework;
+
+namespace Azure.Storage.DataMovement.Blobs.Tests
+{
+ public class BlobStorageResourceProviderTests
+ {
+ public enum CredType
+ {
+ None,
+ SharedKey,
+ Token,
+ Sas
+ }
+ public enum BlobType
+ {
+ Unspecified,
+ Block,
+ Page,
+ Append
+ }
+
+ private void AssertCredPresent(BlobClientConfiguration clientConfig, CredType credType)
+ {
+ Assert.IsNotNull(clientConfig);
+ switch (credType)
+ {
+ case CredType.None:
+ Assert.IsNull(clientConfig.SharedKeyCredential);
+ Assert.IsNull(clientConfig.TokenCredential);
+ Assert.IsNull(clientConfig.SasCredential);
+ break;
+ case CredType.SharedKey:
+ Assert.IsNotNull(clientConfig.SharedKeyCredential);
+ Assert.IsNull(clientConfig.TokenCredential);
+ Assert.IsNull(clientConfig.SasCredential);
+ break;
+ case CredType.Token:
+ Assert.IsNull(clientConfig.SharedKeyCredential);
+ Assert.IsNotNull(clientConfig.TokenCredential);
+ Assert.IsNull(clientConfig.SasCredential);
+ break;
+ case CredType.Sas:
+ Assert.IsNull(clientConfig.SharedKeyCredential);
+ Assert.IsNull(clientConfig.TokenCredential);
+ Assert.IsNotNull(clientConfig.SasCredential);
+ break;
+ default:
+ throw new ArgumentException("No assertion support for cred type " + credType.ToString());
+ }
+ }
+
+ private void AssertBlobStorageResourceType(
+ StorageResource resource,
+ BlobType blobType,
+ out BlobBaseClient underlyingClient)
+ {
+ Assert.IsNotNull(resource);
+ switch (blobType)
+ {
+ case BlobType.Unspecified:
+ case BlobType.Block:
+ Assert.That(resource, Is.InstanceOf());
+ BlockBlobStorageResource blockResource = resource as BlockBlobStorageResource;
+ Assert.IsNotNull(blockResource.BlobClient);
+ underlyingClient = blockResource.BlobClient;
+ break;
+ case BlobType.Page:
+ Assert.That(resource, Is.InstanceOf());
+ PageBlobStorageResource pageResource = resource as PageBlobStorageResource;
+ Assert.IsNotNull(pageResource.BlobClient);
+ underlyingClient = pageResource.BlobClient;
+ break;
+ case BlobType.Append:
+ Assert.That(resource, Is.InstanceOf());
+ AppendBlobStorageResource appendResource = resource as AppendBlobStorageResource;
+ Assert.IsNotNull(appendResource.BlobClient);
+ underlyingClient = appendResource.BlobClient;
+ break;
+ default:
+ throw new ArgumentException("No assertion support for blob type " + blobType.ToString());
+ }
+ }
+
+ private (Mock SharedKey, Mock Token, Mock Sas) GetMockCreds()
+ {
+ return (
+ new Mock("myaccount", new Random().NextBase64(64)),
+ new Mock(),
+ new Mock("mysignature"));
+ }
+
+ [Test]
+ [Combinatorial]
+ public void FromContainer(
+ [Values(true, false)] bool withPrefix,
+ [Values(CredType.None, CredType.SharedKey, CredType.Token, CredType.Sas)] CredType credType)
+ {
+ const string containerName = "mycontainer";
+ const string prefix = "my/prefix";
+ string uri = $"https://myaccount.blob.core.windows.net/{containerName}" + (withPrefix ? $"/{prefix}" : "");
+ (Mock SharedKey, Mock Token, Mock Sas) mockCreds = GetMockCreds();
+
+ BlobsStorageResourceProvider provider = credType switch
+ {
+ CredType.None => new(),
+ CredType.SharedKey => new(mockCreds.SharedKey.Object),
+ CredType.Token => new(mockCreds.Token.Object),
+ CredType.Sas => new(mockCreds.Sas.Object),
+ _ => throw new ArgumentException("Bad cred type"),
+ };
+ BlobStorageResourceContainer resource = provider.FromContainer(uri) as BlobStorageResourceContainer;
+
+ Assert.IsNotNull(resource);
+ Assert.AreEqual(uri, resource.Uri.ToString());
+ Assert.AreEqual(uri, resource.BlobContainerClient.Uri.ToString());
+ AssertCredPresent(resource.BlobContainerClient.ClientConfiguration, credType);
+ }
+
+ [Test]
+ [Combinatorial]
+ public void FromBlob(
+ [Values(BlobType.Unspecified, BlobType.Block, BlobType.Page, BlobType.Append)] BlobType blobType,
+ [Values(CredType.None, CredType.SharedKey, CredType.Token, CredType.Sas)] CredType credType)
+ {
+ const string containerName = "mycontainer";
+ const string blobName = "my/blob.txt";
+ string uri = $"https://myaccount.blob.core.windows.net/{containerName}/{blobName}";
+ (Mock SharedKey, Mock Token, Mock Sas) mockCreds = GetMockCreds();
+
+ BlobsStorageResourceProvider provider = credType switch
+ {
+ CredType.None => new(),
+ CredType.SharedKey => new(mockCreds.SharedKey.Object),
+ CredType.Token => new(mockCreds.Token.Object),
+ CredType.Sas => new(mockCreds.Sas.Object),
+ _ => throw new ArgumentException("Bad cred type"),
+ };
+
+ StorageResource resource = blobType switch
+ {
+ BlobType.Unspecified => provider.FromBlob(uri),
+ BlobType.Block => provider.FromBlob(uri, new BlockBlobStorageResourceOptions()),
+ BlobType.Page => provider.FromBlob(uri, new PageBlobStorageResourceOptions()),
+ BlobType.Append => provider.FromBlob(uri, new AppendBlobStorageResourceOptions()),
+ _ => throw new ArgumentException("Bad blob type")
+ };
+
+ Assert.IsNotNull(resource);
+ AssertBlobStorageResourceType(resource, blobType, out BlobBaseClient underlyingClient);
+ Assert.AreEqual(uri, resource.Uri.ToString());
+ Assert.AreEqual(uri, underlyingClient.Uri.ToString());
+ AssertCredPresent(underlyingClient.ClientConfiguration, credType);
+ }
+ }
+}
diff --git a/sdk/storage/Azure.Storage.DataMovement.Blobs/tests/StorageResourceEtagManagementTests.cs b/sdk/storage/Azure.Storage.DataMovement.Blobs/tests/StorageResourceEtagManagementTests.cs
index 79c43cb7cb79..9125751550da 100644
--- a/sdk/storage/Azure.Storage.DataMovement.Blobs/tests/StorageResourceEtagManagementTests.cs
+++ b/sdk/storage/Azure.Storage.DataMovement.Blobs/tests/StorageResourceEtagManagementTests.cs
@@ -19,6 +19,8 @@ namespace Azure.Storage.DataMovement.Blobs.Tests
[TestFixture]
public class StorageResourceEtagManagementTests
{
+ private const string ETag = "ETag";
+
[Test]
public async Task BlockBlobMaintainsEtagForDownloads()
{
@@ -29,7 +31,7 @@ public async Task BlockBlobMaintainsEtagForDownloads()
mock.Setup(b => b.GetPropertiesAsync(It.IsAny(), It.IsAny()))
.Returns(Task.FromResult(Response.FromValue(
new BlobProperties(),
- new MockResponse(200).WithHeader(Constants.HeaderNames.ETag, etag.ToString()))));
+ new MockResponse(200).WithHeader(ETag, etag.ToString()))));
mock.Setup(b => b.DownloadStreamingAsync(It.IsAny(), It.IsAny()))
.Returns(Task.FromResult(Response.FromValue(
BlobsModelFactory.BlobDownloadStreamingResult(Stream.Null, new BlobDownloadDetails()),
@@ -59,7 +61,7 @@ public async Task PageBlobBlobMaintainsEtagForDownloads()
mock.Setup(b => b.GetPropertiesAsync(It.IsAny(), It.IsAny()))
.Returns(Task.FromResult(Response.FromValue(
new BlobProperties(),
- new MockResponse(200).WithHeader(Constants.HeaderNames.ETag, etag.ToString()))));
+ new MockResponse(200).WithHeader(ETag, etag.ToString()))));
mock.Setup(b => b.DownloadStreamingAsync(It.IsAny(), It.IsAny()))
.Returns(Task.FromResult(Response.FromValue(
BlobsModelFactory.BlobDownloadStreamingResult(Stream.Null, new BlobDownloadDetails()),
@@ -89,7 +91,7 @@ public async Task AppendBlobMaintainsEtagForDownloads()
mock.Setup(b => b.GetPropertiesAsync(It.IsAny(), It.IsAny()))
.Returns(Task.FromResult(Response.FromValue(
new BlobProperties(),
- new MockResponse(200).WithHeader(Constants.HeaderNames.ETag, etag.ToString()))));
+ new MockResponse(200).WithHeader(ETag, etag.ToString()))));
mock.Setup(b => b.DownloadStreamingAsync(It.IsAny(), It.IsAny()))
.Returns(Task.FromResult(Response.FromValue(
BlobsModelFactory.BlobDownloadStreamingResult(Stream.Null, new BlobDownloadDetails()),
diff --git a/sdk/storage/Azure.Storage.DataMovement/tests/Shared/CheckpointerTesting.cs b/sdk/storage/Azure.Storage.DataMovement/tests/Shared/CheckpointerTesting.cs
index 7168e8bb047d..59f2afbd58df 100644
--- a/sdk/storage/Azure.Storage.DataMovement/tests/Shared/CheckpointerTesting.cs
+++ b/sdk/storage/Azure.Storage.DataMovement/tests/Shared/CheckpointerTesting.cs
@@ -13,6 +13,8 @@ namespace Azure.Storage.DataMovement.Tests
{
internal class CheckpointerTesting
{
+ private const int KB = 1024;
+ private const int MB = 1024 * KB;
internal const string DefaultTransferId =
"c591bacc-5552-4c5c-b068-552685ec5cd5";
internal const long DefaultPartNumber = 5;
@@ -38,7 +40,7 @@ internal static readonly DateTimeOffset DefaultStartTime
internal const JobPartPlanBlockBlobTier DefaultBlockBlobTier = JobPartPlanBlockBlobTier.None;
internal const JobPartPlanPageBlobTier DefaultPageBlobTier = JobPartPlanPageBlobTier.None;
internal const string DefaultCpkScopeInfo = "cpk-scope-info";
- internal const long DefaultBlockSize = 4 * Constants.KB;
+ internal const long DefaultBlockSize = 4 * KB;
internal const byte DefaultS2sInvalidMetadataHandleOption = 0;
internal const byte DefaultChecksumVerificationOption = 0;
internal const JobPartDeleteSnapshotsOption DefaultDeleteSnapshotsOption = JobPartDeleteSnapshotsOption.None;
@@ -171,10 +173,10 @@ internal static async Task AssertJobPlanHeaderAsync(JobPartPlanHeader header, St
originalHeaderStream.Seek(0, SeekOrigin.Begin);
stream.Seek(0, SeekOrigin.Begin);
- for (var i = 0; i < headerSize; i += Constants.DefaultBufferSize * 5 / 2)
+ for (var i = 0; i < headerSize; i += (int)DefaultBlockSize * 5 / 2)
{
var startIndex = i;
- var count = Math.Min(Constants.DefaultBufferSize, (int)(headerSize - startIndex));
+ var count = Math.Min((int)DefaultBlockSize, (int)(headerSize - startIndex));
var buffer = new byte[count];
var actual = new byte[count];