diff --git a/sdk/core/Azure.Core/Azure.Core.sln b/sdk/core/Azure.Core/Azure.Core.sln index 23ac37baec81..52f5ae08b3c8 100644 --- a/sdk/core/Azure.Core/Azure.Core.sln +++ b/sdk/core/Azure.Core/Azure.Core.sln @@ -45,6 +45,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{13A0 samples\README.md = samples\README.md samples\RequestContext.md = samples\RequestContext.md samples\Response.md = samples\Response.md + samples\Serialization.md = samples\Serialization.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Core.Expressions.DataFactory", "..\Azure.Core.Expressions.DataFactory\src\Azure.Core.Expressions.DataFactory.csproj", "{7723BA5F-0017-45B4-B584-7D11AA069735}" diff --git a/sdk/core/Azure.Core/api/Azure.Core.net461.cs b/sdk/core/Azure.Core/api/Azure.Core.net461.cs index 3f774979daad..7891e8064b98 100644 --- a/sdk/core/Azure.Core/api/Azure.Core.net461.cs +++ b/sdk/core/Azure.Core/api/Azure.Core.net461.cs @@ -538,6 +538,7 @@ protected internal virtual void SetHeader(string name, string value) { } public abstract partial class RequestContent : System.IDisposable { protected RequestContent() { } + public static Azure.Core.RequestContent Create(Azure.Core.SequenceWriter writer) { throw null; } public static Azure.Core.RequestContent Create(Azure.Core.Serialization.DynamicData content) { throw null; } public static Azure.Core.RequestContent Create(System.BinaryData content) { throw null; } public static Azure.Core.RequestContent Create(System.Buffers.ReadOnlySequence bytes) { throw null; } @@ -549,6 +550,7 @@ protected RequestContent() { } public static Azure.Core.RequestContent Create(object serializable, Azure.Core.Serialization.ObjectSerializer? serializer) { throw null; } public static Azure.Core.RequestContent Create(System.ReadOnlyMemory bytes) { throw null; } public static Azure.Core.RequestContent Create(string content) { throw null; } + public static Azure.Core.RequestContent CreatePatch(Azure.Core.Serialization.DynamicData content) { throw null; } public abstract void Dispose(); public static implicit operator Azure.Core.RequestContent (Azure.Core.Serialization.DynamicData content) { throw null; } public static implicit operator Azure.Core.RequestContent (System.BinaryData content) { throw null; } @@ -718,6 +720,18 @@ internal RetryOptions() { } public Azure.Core.RetryMode Mode { get { throw null; } set { } } public System.TimeSpan NetworkTimeout { get { throw null; } set { } } } + public sealed partial class SequenceWriter : System.Buffers.IBufferWriter, System.IDisposable + { + public SequenceWriter(int bufferSize = 4096) { } + public void Advance(int bytesWritten) { } + public void Dispose() { } + public System.Memory GetMemory(int sizeHint = 0) { throw null; } + public System.Buffers.ReadOnlySequence GetReadOnlySequence() { throw null; } + public System.Span GetSpan(int sizeHint = 0) { throw null; } + public bool TryComputeLength(out long length) { throw null; } + public void WriteTo(System.IO.Stream stream, System.Threading.CancellationToken cancellation) { } + public System.Threading.Tasks.Task WriteToAsync(System.IO.Stream stream, System.Threading.CancellationToken cancellation) { throw null; } + } public partial class StatusCodeClassifier : Azure.Core.ResponseClassifier { public StatusCodeClassifier(System.ReadOnlySpan successStatusCodes) { } @@ -1105,10 +1119,24 @@ public void Dispose() { } System.Dynamic.DynamicMetaObject System.Dynamic.IDynamicMetaObjectProvider.GetMetaObject(System.Linq.Expressions.Expression parameter) { throw null; } public override string ToString() { throw null; } } + public partial interface IJsonModelSerializable : Azure.Core.Serialization.IModelSerializable + { + object Deserialize(ref System.Text.Json.Utf8JsonReader reader, Azure.Core.Serialization.ModelSerializerOptions options); + void Serialize(System.Text.Json.Utf8JsonWriter writer, Azure.Core.Serialization.ModelSerializerOptions options); + } public partial interface IMemberNameConverter { string? ConvertMemberName(System.Reflection.MemberInfo member); } + public partial interface IModelSerializable + { + object Deserialize(System.BinaryData data, Azure.Core.Serialization.ModelSerializerOptions options); + System.BinaryData Serialize(Azure.Core.Serialization.ModelSerializerOptions options); + } + public partial interface IXmlModelSerializable : Azure.Core.Serialization.IModelSerializable + { + void Serialize(System.Xml.XmlWriter writer, Azure.Core.Serialization.ModelSerializerOptions options); + } public partial class JsonObjectSerializer : Azure.Core.Serialization.ObjectSerializer, Azure.Core.Serialization.IMemberNameConverter { public JsonObjectSerializer() { } @@ -1127,6 +1155,52 @@ public enum JsonPropertyNames UseExact = 0, CamelCase = 1, } + public partial class ModelJsonConverter : System.Text.Json.Serialization.JsonConverter + { + public ModelJsonConverter() { } + public ModelJsonConverter(Azure.Core.Serialization.ModelSerializerFormat format) { } + public Azure.Core.Serialization.ModelSerializerOptions Options { get { throw null; } } + public override bool CanConvert(System.Type typeToConvert) { throw null; } + public override Azure.Core.Serialization.IJsonModelSerializable Read(ref System.Text.Json.Utf8JsonReader reader, System.Type typeToConvert, System.Text.Json.JsonSerializerOptions options) { throw null; } + public override void Write(System.Text.Json.Utf8JsonWriter writer, Azure.Core.Serialization.IJsonModelSerializable value, System.Text.Json.JsonSerializerOptions options) { } + } + public static partial class ModelSerializer + { + public static object Deserialize(System.BinaryData data, System.Type typeToConvert, Azure.Core.Serialization.ModelSerializerOptions options = default(Azure.Core.Serialization.ModelSerializerOptions)) { throw null; } + public static T Deserialize(System.BinaryData data, Azure.Core.Serialization.ModelSerializerOptions options = default(Azure.Core.Serialization.ModelSerializerOptions)) where T : class, Azure.Core.Serialization.IModelSerializable { throw null; } + public static System.BinaryData Serialize(object model, Azure.Core.Serialization.ModelSerializerOptions options = default(Azure.Core.Serialization.ModelSerializerOptions)) { throw null; } + public static System.BinaryData Serialize(T model, Azure.Core.Serialization.ModelSerializerOptions options = default(Azure.Core.Serialization.ModelSerializerOptions)) where T : Azure.Core.Serialization.IModelSerializable { throw null; } + } + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] + public readonly partial struct ModelSerializerFormat : System.IEquatable + { + private readonly object _dummy; + private readonly int _dummyPrimitive; + public static readonly Azure.Core.Serialization.ModelSerializerFormat Json; + public static readonly Azure.Core.Serialization.ModelSerializerFormat Wire; + public ModelSerializerFormat(string value) { throw null; } + public bool Equals(Azure.Core.Serialization.ModelSerializerFormat other) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override bool Equals(object obj) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override int GetHashCode() { throw null; } + public static bool operator ==(Azure.Core.Serialization.ModelSerializerFormat left, Azure.Core.Serialization.ModelSerializerFormat right) { throw null; } + public static implicit operator string (Azure.Core.Serialization.ModelSerializerFormat value) { throw null; } + public static implicit operator Azure.Core.Serialization.ModelSerializerFormat (string value) { throw null; } + public static bool operator !=(Azure.Core.Serialization.ModelSerializerFormat left, Azure.Core.Serialization.ModelSerializerFormat right) { throw null; } + public override string ToString() { throw null; } + } + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] + public partial struct ModelSerializerOptions + { + private object _dummy; + private int _dummyPrimitive; + public static readonly Azure.Core.Serialization.ModelSerializerOptions AzureServiceDefault; + public ModelSerializerOptions() { throw null; } + public ModelSerializerOptions(Azure.Core.Serialization.ModelSerializerFormat format) { throw null; } + public Azure.Core.Serialization.ModelSerializerFormat Format { get { throw null; } } + public System.Collections.Generic.Dictionary Serializers { get { throw null; } } + } public abstract partial class ObjectSerializer { protected ObjectSerializer() { } diff --git a/sdk/core/Azure.Core/api/Azure.Core.net5.0.cs b/sdk/core/Azure.Core/api/Azure.Core.net5.0.cs index c10168bea2ae..181b7e483365 100644 --- a/sdk/core/Azure.Core/api/Azure.Core.net5.0.cs +++ b/sdk/core/Azure.Core/api/Azure.Core.net5.0.cs @@ -538,6 +538,7 @@ protected internal virtual void SetHeader(string name, string value) { } public abstract partial class RequestContent : System.IDisposable { protected RequestContent() { } + public static Azure.Core.RequestContent Create(Azure.Core.SequenceWriter writer) { throw null; } public static Azure.Core.RequestContent Create(Azure.Core.Serialization.DynamicData content) { throw null; } public static Azure.Core.RequestContent Create(System.BinaryData content) { throw null; } public static Azure.Core.RequestContent Create(System.Buffers.ReadOnlySequence bytes) { throw null; } @@ -549,6 +550,7 @@ protected RequestContent() { } public static Azure.Core.RequestContent Create(object serializable, Azure.Core.Serialization.ObjectSerializer? serializer) { throw null; } public static Azure.Core.RequestContent Create(System.ReadOnlyMemory bytes) { throw null; } public static Azure.Core.RequestContent Create(string content) { throw null; } + public static Azure.Core.RequestContent CreatePatch(Azure.Core.Serialization.DynamicData content) { throw null; } public abstract void Dispose(); public static implicit operator Azure.Core.RequestContent (Azure.Core.Serialization.DynamicData content) { throw null; } public static implicit operator Azure.Core.RequestContent (System.BinaryData content) { throw null; } @@ -718,6 +720,18 @@ internal RetryOptions() { } public Azure.Core.RetryMode Mode { get { throw null; } set { } } public System.TimeSpan NetworkTimeout { get { throw null; } set { } } } + public sealed partial class SequenceWriter : System.Buffers.IBufferWriter, System.IDisposable + { + public SequenceWriter(int bufferSize = 4096) { } + public void Advance(int bytesWritten) { } + public void Dispose() { } + public System.Memory GetMemory(int sizeHint = 0) { throw null; } + public System.Buffers.ReadOnlySequence GetReadOnlySequence() { throw null; } + public System.Span GetSpan(int sizeHint = 0) { throw null; } + public bool TryComputeLength(out long length) { throw null; } + public void WriteTo(System.IO.Stream stream, System.Threading.CancellationToken cancellation) { } + public System.Threading.Tasks.Task WriteToAsync(System.IO.Stream stream, System.Threading.CancellationToken cancellation) { throw null; } + } public partial class StatusCodeClassifier : Azure.Core.ResponseClassifier { public StatusCodeClassifier(System.ReadOnlySpan successStatusCodes) { } @@ -1105,10 +1119,24 @@ public void Dispose() { } System.Dynamic.DynamicMetaObject System.Dynamic.IDynamicMetaObjectProvider.GetMetaObject(System.Linq.Expressions.Expression parameter) { throw null; } public override string ToString() { throw null; } } + public partial interface IJsonModelSerializable : Azure.Core.Serialization.IModelSerializable + { + object Deserialize(ref System.Text.Json.Utf8JsonReader reader, Azure.Core.Serialization.ModelSerializerOptions options); + void Serialize(System.Text.Json.Utf8JsonWriter writer, Azure.Core.Serialization.ModelSerializerOptions options); + } public partial interface IMemberNameConverter { string? ConvertMemberName(System.Reflection.MemberInfo member); } + public partial interface IModelSerializable + { + object Deserialize(System.BinaryData data, Azure.Core.Serialization.ModelSerializerOptions options); + System.BinaryData Serialize(Azure.Core.Serialization.ModelSerializerOptions options); + } + public partial interface IXmlModelSerializable : Azure.Core.Serialization.IModelSerializable + { + void Serialize(System.Xml.XmlWriter writer, Azure.Core.Serialization.ModelSerializerOptions options); + } public partial class JsonObjectSerializer : Azure.Core.Serialization.ObjectSerializer, Azure.Core.Serialization.IMemberNameConverter { public JsonObjectSerializer() { } @@ -1127,6 +1155,52 @@ public enum JsonPropertyNames UseExact = 0, CamelCase = 1, } + public partial class ModelJsonConverter : System.Text.Json.Serialization.JsonConverter + { + public ModelJsonConverter() { } + public ModelJsonConverter(Azure.Core.Serialization.ModelSerializerFormat format) { } + public Azure.Core.Serialization.ModelSerializerOptions Options { get { throw null; } } + public override bool CanConvert(System.Type typeToConvert) { throw null; } + public override Azure.Core.Serialization.IJsonModelSerializable Read(ref System.Text.Json.Utf8JsonReader reader, System.Type typeToConvert, System.Text.Json.JsonSerializerOptions options) { throw null; } + public override void Write(System.Text.Json.Utf8JsonWriter writer, Azure.Core.Serialization.IJsonModelSerializable value, System.Text.Json.JsonSerializerOptions options) { } + } + public static partial class ModelSerializer + { + public static object Deserialize(System.BinaryData data, System.Type typeToConvert, Azure.Core.Serialization.ModelSerializerOptions options = default(Azure.Core.Serialization.ModelSerializerOptions)) { throw null; } + public static T Deserialize(System.BinaryData data, Azure.Core.Serialization.ModelSerializerOptions options = default(Azure.Core.Serialization.ModelSerializerOptions)) where T : class, Azure.Core.Serialization.IModelSerializable { throw null; } + public static System.BinaryData Serialize(object model, Azure.Core.Serialization.ModelSerializerOptions options = default(Azure.Core.Serialization.ModelSerializerOptions)) { throw null; } + public static System.BinaryData Serialize(T model, Azure.Core.Serialization.ModelSerializerOptions options = default(Azure.Core.Serialization.ModelSerializerOptions)) where T : Azure.Core.Serialization.IModelSerializable { throw null; } + } + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] + public readonly partial struct ModelSerializerFormat : System.IEquatable + { + private readonly object _dummy; + private readonly int _dummyPrimitive; + public static readonly Azure.Core.Serialization.ModelSerializerFormat Json; + public static readonly Azure.Core.Serialization.ModelSerializerFormat Wire; + public ModelSerializerFormat(string value) { throw null; } + public bool Equals(Azure.Core.Serialization.ModelSerializerFormat other) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override bool Equals([System.Diagnostics.CodeAnalysis.AllowNullAttribute] object obj) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override int GetHashCode() { throw null; } + public static bool operator ==(Azure.Core.Serialization.ModelSerializerFormat left, Azure.Core.Serialization.ModelSerializerFormat right) { throw null; } + public static implicit operator string (Azure.Core.Serialization.ModelSerializerFormat value) { throw null; } + public static implicit operator Azure.Core.Serialization.ModelSerializerFormat (string value) { throw null; } + public static bool operator !=(Azure.Core.Serialization.ModelSerializerFormat left, Azure.Core.Serialization.ModelSerializerFormat right) { throw null; } + public override string ToString() { throw null; } + } + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] + public partial struct ModelSerializerOptions + { + private object _dummy; + private int _dummyPrimitive; + public static readonly Azure.Core.Serialization.ModelSerializerOptions AzureServiceDefault; + public ModelSerializerOptions() { throw null; } + public ModelSerializerOptions(Azure.Core.Serialization.ModelSerializerFormat format) { throw null; } + public Azure.Core.Serialization.ModelSerializerFormat Format { get { throw null; } } + public System.Collections.Generic.Dictionary Serializers { get { throw null; } } + } public abstract partial class ObjectSerializer { protected ObjectSerializer() { } diff --git a/sdk/core/Azure.Core/api/Azure.Core.net6.0.cs b/sdk/core/Azure.Core/api/Azure.Core.net6.0.cs index c10168bea2ae..181b7e483365 100644 --- a/sdk/core/Azure.Core/api/Azure.Core.net6.0.cs +++ b/sdk/core/Azure.Core/api/Azure.Core.net6.0.cs @@ -538,6 +538,7 @@ protected internal virtual void SetHeader(string name, string value) { } public abstract partial class RequestContent : System.IDisposable { protected RequestContent() { } + public static Azure.Core.RequestContent Create(Azure.Core.SequenceWriter writer) { throw null; } public static Azure.Core.RequestContent Create(Azure.Core.Serialization.DynamicData content) { throw null; } public static Azure.Core.RequestContent Create(System.BinaryData content) { throw null; } public static Azure.Core.RequestContent Create(System.Buffers.ReadOnlySequence bytes) { throw null; } @@ -549,6 +550,7 @@ protected RequestContent() { } public static Azure.Core.RequestContent Create(object serializable, Azure.Core.Serialization.ObjectSerializer? serializer) { throw null; } public static Azure.Core.RequestContent Create(System.ReadOnlyMemory bytes) { throw null; } public static Azure.Core.RequestContent Create(string content) { throw null; } + public static Azure.Core.RequestContent CreatePatch(Azure.Core.Serialization.DynamicData content) { throw null; } public abstract void Dispose(); public static implicit operator Azure.Core.RequestContent (Azure.Core.Serialization.DynamicData content) { throw null; } public static implicit operator Azure.Core.RequestContent (System.BinaryData content) { throw null; } @@ -718,6 +720,18 @@ internal RetryOptions() { } public Azure.Core.RetryMode Mode { get { throw null; } set { } } public System.TimeSpan NetworkTimeout { get { throw null; } set { } } } + public sealed partial class SequenceWriter : System.Buffers.IBufferWriter, System.IDisposable + { + public SequenceWriter(int bufferSize = 4096) { } + public void Advance(int bytesWritten) { } + public void Dispose() { } + public System.Memory GetMemory(int sizeHint = 0) { throw null; } + public System.Buffers.ReadOnlySequence GetReadOnlySequence() { throw null; } + public System.Span GetSpan(int sizeHint = 0) { throw null; } + public bool TryComputeLength(out long length) { throw null; } + public void WriteTo(System.IO.Stream stream, System.Threading.CancellationToken cancellation) { } + public System.Threading.Tasks.Task WriteToAsync(System.IO.Stream stream, System.Threading.CancellationToken cancellation) { throw null; } + } public partial class StatusCodeClassifier : Azure.Core.ResponseClassifier { public StatusCodeClassifier(System.ReadOnlySpan successStatusCodes) { } @@ -1105,10 +1119,24 @@ public void Dispose() { } System.Dynamic.DynamicMetaObject System.Dynamic.IDynamicMetaObjectProvider.GetMetaObject(System.Linq.Expressions.Expression parameter) { throw null; } public override string ToString() { throw null; } } + public partial interface IJsonModelSerializable : Azure.Core.Serialization.IModelSerializable + { + object Deserialize(ref System.Text.Json.Utf8JsonReader reader, Azure.Core.Serialization.ModelSerializerOptions options); + void Serialize(System.Text.Json.Utf8JsonWriter writer, Azure.Core.Serialization.ModelSerializerOptions options); + } public partial interface IMemberNameConverter { string? ConvertMemberName(System.Reflection.MemberInfo member); } + public partial interface IModelSerializable + { + object Deserialize(System.BinaryData data, Azure.Core.Serialization.ModelSerializerOptions options); + System.BinaryData Serialize(Azure.Core.Serialization.ModelSerializerOptions options); + } + public partial interface IXmlModelSerializable : Azure.Core.Serialization.IModelSerializable + { + void Serialize(System.Xml.XmlWriter writer, Azure.Core.Serialization.ModelSerializerOptions options); + } public partial class JsonObjectSerializer : Azure.Core.Serialization.ObjectSerializer, Azure.Core.Serialization.IMemberNameConverter { public JsonObjectSerializer() { } @@ -1127,6 +1155,52 @@ public enum JsonPropertyNames UseExact = 0, CamelCase = 1, } + public partial class ModelJsonConverter : System.Text.Json.Serialization.JsonConverter + { + public ModelJsonConverter() { } + public ModelJsonConverter(Azure.Core.Serialization.ModelSerializerFormat format) { } + public Azure.Core.Serialization.ModelSerializerOptions Options { get { throw null; } } + public override bool CanConvert(System.Type typeToConvert) { throw null; } + public override Azure.Core.Serialization.IJsonModelSerializable Read(ref System.Text.Json.Utf8JsonReader reader, System.Type typeToConvert, System.Text.Json.JsonSerializerOptions options) { throw null; } + public override void Write(System.Text.Json.Utf8JsonWriter writer, Azure.Core.Serialization.IJsonModelSerializable value, System.Text.Json.JsonSerializerOptions options) { } + } + public static partial class ModelSerializer + { + public static object Deserialize(System.BinaryData data, System.Type typeToConvert, Azure.Core.Serialization.ModelSerializerOptions options = default(Azure.Core.Serialization.ModelSerializerOptions)) { throw null; } + public static T Deserialize(System.BinaryData data, Azure.Core.Serialization.ModelSerializerOptions options = default(Azure.Core.Serialization.ModelSerializerOptions)) where T : class, Azure.Core.Serialization.IModelSerializable { throw null; } + public static System.BinaryData Serialize(object model, Azure.Core.Serialization.ModelSerializerOptions options = default(Azure.Core.Serialization.ModelSerializerOptions)) { throw null; } + public static System.BinaryData Serialize(T model, Azure.Core.Serialization.ModelSerializerOptions options = default(Azure.Core.Serialization.ModelSerializerOptions)) where T : Azure.Core.Serialization.IModelSerializable { throw null; } + } + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] + public readonly partial struct ModelSerializerFormat : System.IEquatable + { + private readonly object _dummy; + private readonly int _dummyPrimitive; + public static readonly Azure.Core.Serialization.ModelSerializerFormat Json; + public static readonly Azure.Core.Serialization.ModelSerializerFormat Wire; + public ModelSerializerFormat(string value) { throw null; } + public bool Equals(Azure.Core.Serialization.ModelSerializerFormat other) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override bool Equals([System.Diagnostics.CodeAnalysis.AllowNullAttribute] object obj) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override int GetHashCode() { throw null; } + public static bool operator ==(Azure.Core.Serialization.ModelSerializerFormat left, Azure.Core.Serialization.ModelSerializerFormat right) { throw null; } + public static implicit operator string (Azure.Core.Serialization.ModelSerializerFormat value) { throw null; } + public static implicit operator Azure.Core.Serialization.ModelSerializerFormat (string value) { throw null; } + public static bool operator !=(Azure.Core.Serialization.ModelSerializerFormat left, Azure.Core.Serialization.ModelSerializerFormat right) { throw null; } + public override string ToString() { throw null; } + } + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] + public partial struct ModelSerializerOptions + { + private object _dummy; + private int _dummyPrimitive; + public static readonly Azure.Core.Serialization.ModelSerializerOptions AzureServiceDefault; + public ModelSerializerOptions() { throw null; } + public ModelSerializerOptions(Azure.Core.Serialization.ModelSerializerFormat format) { throw null; } + public Azure.Core.Serialization.ModelSerializerFormat Format { get { throw null; } } + public System.Collections.Generic.Dictionary Serializers { get { throw null; } } + } public abstract partial class ObjectSerializer { protected ObjectSerializer() { } diff --git a/sdk/core/Azure.Core/api/Azure.Core.netcoreapp2.1.cs b/sdk/core/Azure.Core/api/Azure.Core.netcoreapp2.1.cs index 3f774979daad..7891e8064b98 100644 --- a/sdk/core/Azure.Core/api/Azure.Core.netcoreapp2.1.cs +++ b/sdk/core/Azure.Core/api/Azure.Core.netcoreapp2.1.cs @@ -538,6 +538,7 @@ protected internal virtual void SetHeader(string name, string value) { } public abstract partial class RequestContent : System.IDisposable { protected RequestContent() { } + public static Azure.Core.RequestContent Create(Azure.Core.SequenceWriter writer) { throw null; } public static Azure.Core.RequestContent Create(Azure.Core.Serialization.DynamicData content) { throw null; } public static Azure.Core.RequestContent Create(System.BinaryData content) { throw null; } public static Azure.Core.RequestContent Create(System.Buffers.ReadOnlySequence bytes) { throw null; } @@ -549,6 +550,7 @@ protected RequestContent() { } public static Azure.Core.RequestContent Create(object serializable, Azure.Core.Serialization.ObjectSerializer? serializer) { throw null; } public static Azure.Core.RequestContent Create(System.ReadOnlyMemory bytes) { throw null; } public static Azure.Core.RequestContent Create(string content) { throw null; } + public static Azure.Core.RequestContent CreatePatch(Azure.Core.Serialization.DynamicData content) { throw null; } public abstract void Dispose(); public static implicit operator Azure.Core.RequestContent (Azure.Core.Serialization.DynamicData content) { throw null; } public static implicit operator Azure.Core.RequestContent (System.BinaryData content) { throw null; } @@ -718,6 +720,18 @@ internal RetryOptions() { } public Azure.Core.RetryMode Mode { get { throw null; } set { } } public System.TimeSpan NetworkTimeout { get { throw null; } set { } } } + public sealed partial class SequenceWriter : System.Buffers.IBufferWriter, System.IDisposable + { + public SequenceWriter(int bufferSize = 4096) { } + public void Advance(int bytesWritten) { } + public void Dispose() { } + public System.Memory GetMemory(int sizeHint = 0) { throw null; } + public System.Buffers.ReadOnlySequence GetReadOnlySequence() { throw null; } + public System.Span GetSpan(int sizeHint = 0) { throw null; } + public bool TryComputeLength(out long length) { throw null; } + public void WriteTo(System.IO.Stream stream, System.Threading.CancellationToken cancellation) { } + public System.Threading.Tasks.Task WriteToAsync(System.IO.Stream stream, System.Threading.CancellationToken cancellation) { throw null; } + } public partial class StatusCodeClassifier : Azure.Core.ResponseClassifier { public StatusCodeClassifier(System.ReadOnlySpan successStatusCodes) { } @@ -1105,10 +1119,24 @@ public void Dispose() { } System.Dynamic.DynamicMetaObject System.Dynamic.IDynamicMetaObjectProvider.GetMetaObject(System.Linq.Expressions.Expression parameter) { throw null; } public override string ToString() { throw null; } } + public partial interface IJsonModelSerializable : Azure.Core.Serialization.IModelSerializable + { + object Deserialize(ref System.Text.Json.Utf8JsonReader reader, Azure.Core.Serialization.ModelSerializerOptions options); + void Serialize(System.Text.Json.Utf8JsonWriter writer, Azure.Core.Serialization.ModelSerializerOptions options); + } public partial interface IMemberNameConverter { string? ConvertMemberName(System.Reflection.MemberInfo member); } + public partial interface IModelSerializable + { + object Deserialize(System.BinaryData data, Azure.Core.Serialization.ModelSerializerOptions options); + System.BinaryData Serialize(Azure.Core.Serialization.ModelSerializerOptions options); + } + public partial interface IXmlModelSerializable : Azure.Core.Serialization.IModelSerializable + { + void Serialize(System.Xml.XmlWriter writer, Azure.Core.Serialization.ModelSerializerOptions options); + } public partial class JsonObjectSerializer : Azure.Core.Serialization.ObjectSerializer, Azure.Core.Serialization.IMemberNameConverter { public JsonObjectSerializer() { } @@ -1127,6 +1155,52 @@ public enum JsonPropertyNames UseExact = 0, CamelCase = 1, } + public partial class ModelJsonConverter : System.Text.Json.Serialization.JsonConverter + { + public ModelJsonConverter() { } + public ModelJsonConverter(Azure.Core.Serialization.ModelSerializerFormat format) { } + public Azure.Core.Serialization.ModelSerializerOptions Options { get { throw null; } } + public override bool CanConvert(System.Type typeToConvert) { throw null; } + public override Azure.Core.Serialization.IJsonModelSerializable Read(ref System.Text.Json.Utf8JsonReader reader, System.Type typeToConvert, System.Text.Json.JsonSerializerOptions options) { throw null; } + public override void Write(System.Text.Json.Utf8JsonWriter writer, Azure.Core.Serialization.IJsonModelSerializable value, System.Text.Json.JsonSerializerOptions options) { } + } + public static partial class ModelSerializer + { + public static object Deserialize(System.BinaryData data, System.Type typeToConvert, Azure.Core.Serialization.ModelSerializerOptions options = default(Azure.Core.Serialization.ModelSerializerOptions)) { throw null; } + public static T Deserialize(System.BinaryData data, Azure.Core.Serialization.ModelSerializerOptions options = default(Azure.Core.Serialization.ModelSerializerOptions)) where T : class, Azure.Core.Serialization.IModelSerializable { throw null; } + public static System.BinaryData Serialize(object model, Azure.Core.Serialization.ModelSerializerOptions options = default(Azure.Core.Serialization.ModelSerializerOptions)) { throw null; } + public static System.BinaryData Serialize(T model, Azure.Core.Serialization.ModelSerializerOptions options = default(Azure.Core.Serialization.ModelSerializerOptions)) where T : Azure.Core.Serialization.IModelSerializable { throw null; } + } + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] + public readonly partial struct ModelSerializerFormat : System.IEquatable + { + private readonly object _dummy; + private readonly int _dummyPrimitive; + public static readonly Azure.Core.Serialization.ModelSerializerFormat Json; + public static readonly Azure.Core.Serialization.ModelSerializerFormat Wire; + public ModelSerializerFormat(string value) { throw null; } + public bool Equals(Azure.Core.Serialization.ModelSerializerFormat other) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override bool Equals(object obj) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override int GetHashCode() { throw null; } + public static bool operator ==(Azure.Core.Serialization.ModelSerializerFormat left, Azure.Core.Serialization.ModelSerializerFormat right) { throw null; } + public static implicit operator string (Azure.Core.Serialization.ModelSerializerFormat value) { throw null; } + public static implicit operator Azure.Core.Serialization.ModelSerializerFormat (string value) { throw null; } + public static bool operator !=(Azure.Core.Serialization.ModelSerializerFormat left, Azure.Core.Serialization.ModelSerializerFormat right) { throw null; } + public override string ToString() { throw null; } + } + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] + public partial struct ModelSerializerOptions + { + private object _dummy; + private int _dummyPrimitive; + public static readonly Azure.Core.Serialization.ModelSerializerOptions AzureServiceDefault; + public ModelSerializerOptions() { throw null; } + public ModelSerializerOptions(Azure.Core.Serialization.ModelSerializerFormat format) { throw null; } + public Azure.Core.Serialization.ModelSerializerFormat Format { get { throw null; } } + public System.Collections.Generic.Dictionary Serializers { get { throw null; } } + } public abstract partial class ObjectSerializer { protected ObjectSerializer() { } diff --git a/sdk/core/Azure.Core/api/Azure.Core.netstandard2.0.cs b/sdk/core/Azure.Core/api/Azure.Core.netstandard2.0.cs index 3f774979daad..7891e8064b98 100644 --- a/sdk/core/Azure.Core/api/Azure.Core.netstandard2.0.cs +++ b/sdk/core/Azure.Core/api/Azure.Core.netstandard2.0.cs @@ -538,6 +538,7 @@ protected internal virtual void SetHeader(string name, string value) { } public abstract partial class RequestContent : System.IDisposable { protected RequestContent() { } + public static Azure.Core.RequestContent Create(Azure.Core.SequenceWriter writer) { throw null; } public static Azure.Core.RequestContent Create(Azure.Core.Serialization.DynamicData content) { throw null; } public static Azure.Core.RequestContent Create(System.BinaryData content) { throw null; } public static Azure.Core.RequestContent Create(System.Buffers.ReadOnlySequence bytes) { throw null; } @@ -549,6 +550,7 @@ protected RequestContent() { } public static Azure.Core.RequestContent Create(object serializable, Azure.Core.Serialization.ObjectSerializer? serializer) { throw null; } public static Azure.Core.RequestContent Create(System.ReadOnlyMemory bytes) { throw null; } public static Azure.Core.RequestContent Create(string content) { throw null; } + public static Azure.Core.RequestContent CreatePatch(Azure.Core.Serialization.DynamicData content) { throw null; } public abstract void Dispose(); public static implicit operator Azure.Core.RequestContent (Azure.Core.Serialization.DynamicData content) { throw null; } public static implicit operator Azure.Core.RequestContent (System.BinaryData content) { throw null; } @@ -718,6 +720,18 @@ internal RetryOptions() { } public Azure.Core.RetryMode Mode { get { throw null; } set { } } public System.TimeSpan NetworkTimeout { get { throw null; } set { } } } + public sealed partial class SequenceWriter : System.Buffers.IBufferWriter, System.IDisposable + { + public SequenceWriter(int bufferSize = 4096) { } + public void Advance(int bytesWritten) { } + public void Dispose() { } + public System.Memory GetMemory(int sizeHint = 0) { throw null; } + public System.Buffers.ReadOnlySequence GetReadOnlySequence() { throw null; } + public System.Span GetSpan(int sizeHint = 0) { throw null; } + public bool TryComputeLength(out long length) { throw null; } + public void WriteTo(System.IO.Stream stream, System.Threading.CancellationToken cancellation) { } + public System.Threading.Tasks.Task WriteToAsync(System.IO.Stream stream, System.Threading.CancellationToken cancellation) { throw null; } + } public partial class StatusCodeClassifier : Azure.Core.ResponseClassifier { public StatusCodeClassifier(System.ReadOnlySpan successStatusCodes) { } @@ -1105,10 +1119,24 @@ public void Dispose() { } System.Dynamic.DynamicMetaObject System.Dynamic.IDynamicMetaObjectProvider.GetMetaObject(System.Linq.Expressions.Expression parameter) { throw null; } public override string ToString() { throw null; } } + public partial interface IJsonModelSerializable : Azure.Core.Serialization.IModelSerializable + { + object Deserialize(ref System.Text.Json.Utf8JsonReader reader, Azure.Core.Serialization.ModelSerializerOptions options); + void Serialize(System.Text.Json.Utf8JsonWriter writer, Azure.Core.Serialization.ModelSerializerOptions options); + } public partial interface IMemberNameConverter { string? ConvertMemberName(System.Reflection.MemberInfo member); } + public partial interface IModelSerializable + { + object Deserialize(System.BinaryData data, Azure.Core.Serialization.ModelSerializerOptions options); + System.BinaryData Serialize(Azure.Core.Serialization.ModelSerializerOptions options); + } + public partial interface IXmlModelSerializable : Azure.Core.Serialization.IModelSerializable + { + void Serialize(System.Xml.XmlWriter writer, Azure.Core.Serialization.ModelSerializerOptions options); + } public partial class JsonObjectSerializer : Azure.Core.Serialization.ObjectSerializer, Azure.Core.Serialization.IMemberNameConverter { public JsonObjectSerializer() { } @@ -1127,6 +1155,52 @@ public enum JsonPropertyNames UseExact = 0, CamelCase = 1, } + public partial class ModelJsonConverter : System.Text.Json.Serialization.JsonConverter + { + public ModelJsonConverter() { } + public ModelJsonConverter(Azure.Core.Serialization.ModelSerializerFormat format) { } + public Azure.Core.Serialization.ModelSerializerOptions Options { get { throw null; } } + public override bool CanConvert(System.Type typeToConvert) { throw null; } + public override Azure.Core.Serialization.IJsonModelSerializable Read(ref System.Text.Json.Utf8JsonReader reader, System.Type typeToConvert, System.Text.Json.JsonSerializerOptions options) { throw null; } + public override void Write(System.Text.Json.Utf8JsonWriter writer, Azure.Core.Serialization.IJsonModelSerializable value, System.Text.Json.JsonSerializerOptions options) { } + } + public static partial class ModelSerializer + { + public static object Deserialize(System.BinaryData data, System.Type typeToConvert, Azure.Core.Serialization.ModelSerializerOptions options = default(Azure.Core.Serialization.ModelSerializerOptions)) { throw null; } + public static T Deserialize(System.BinaryData data, Azure.Core.Serialization.ModelSerializerOptions options = default(Azure.Core.Serialization.ModelSerializerOptions)) where T : class, Azure.Core.Serialization.IModelSerializable { throw null; } + public static System.BinaryData Serialize(object model, Azure.Core.Serialization.ModelSerializerOptions options = default(Azure.Core.Serialization.ModelSerializerOptions)) { throw null; } + public static System.BinaryData Serialize(T model, Azure.Core.Serialization.ModelSerializerOptions options = default(Azure.Core.Serialization.ModelSerializerOptions)) where T : Azure.Core.Serialization.IModelSerializable { throw null; } + } + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] + public readonly partial struct ModelSerializerFormat : System.IEquatable + { + private readonly object _dummy; + private readonly int _dummyPrimitive; + public static readonly Azure.Core.Serialization.ModelSerializerFormat Json; + public static readonly Azure.Core.Serialization.ModelSerializerFormat Wire; + public ModelSerializerFormat(string value) { throw null; } + public bool Equals(Azure.Core.Serialization.ModelSerializerFormat other) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override bool Equals(object obj) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override int GetHashCode() { throw null; } + public static bool operator ==(Azure.Core.Serialization.ModelSerializerFormat left, Azure.Core.Serialization.ModelSerializerFormat right) { throw null; } + public static implicit operator string (Azure.Core.Serialization.ModelSerializerFormat value) { throw null; } + public static implicit operator Azure.Core.Serialization.ModelSerializerFormat (string value) { throw null; } + public static bool operator !=(Azure.Core.Serialization.ModelSerializerFormat left, Azure.Core.Serialization.ModelSerializerFormat right) { throw null; } + public override string ToString() { throw null; } + } + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] + public partial struct ModelSerializerOptions + { + private object _dummy; + private int _dummyPrimitive; + public static readonly Azure.Core.Serialization.ModelSerializerOptions AzureServiceDefault; + public ModelSerializerOptions() { throw null; } + public ModelSerializerOptions(Azure.Core.Serialization.ModelSerializerFormat format) { throw null; } + public Azure.Core.Serialization.ModelSerializerFormat Format { get { throw null; } } + public System.Collections.Generic.Dictionary Serializers { get { throw null; } } + } public abstract partial class ObjectSerializer { protected ObjectSerializer() { } diff --git a/sdk/core/Azure.Core/perf/Azure.Core.Perf.csproj b/sdk/core/Azure.Core/perf/Azure.Core.Perf.csproj index 724b7428071d..ee8e6110c4a4 100644 --- a/sdk/core/Azure.Core/perf/Azure.Core.Perf.csproj +++ b/sdk/core/Azure.Core/perf/Azure.Core.Perf.csproj @@ -12,6 +12,7 @@ + @@ -21,6 +22,15 @@ + + Always + + + Always + + + Always + Always diff --git a/sdk/core/Azure.Core/perf/Program.cs b/sdk/core/Azure.Core/perf/Program.cs index c630aee8495d..1278af7392f1 100644 --- a/sdk/core/Azure.Core/perf/Program.cs +++ b/sdk/core/Azure.Core/perf/Program.cs @@ -1,9 +1,33 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System.Linq; using System.Reflection; using Azure.Test.Perf; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Running; -await PerfProgram.Main(Assembly.GetExecutingAssembly(), args); -// To run Benchmark.NET benchmarks, comment the line above and uncomment the benchmark test below. -// BenchmarkDotNet.Running.BenchmarkRunner.Run(); +if (!args.Contains("--bm")) + await PerfProgram.Main(Assembly.GetExecutingAssembly(), args); + +// To run Benchmark.NET benchmarks, use the --bm flag. + +// To see the list of benchmarks that can be run +// dotnet run -c Release --framework net6.0 --bm --list flat + +// To run a specific benchmark class +// dotnet run -c Release --framework net6.0 --bm --filter Azure.Core.Perf.SerializationBenchmark* + +// To run a specific benchmark method +// dotnet run -c Release --framework net6.0 --bm --filter *SerializationBenchmark.Deserialize_PublicInterface +// or +// dotnet run -c Release --framework net6.0 --bm --filter Azure.Core.Perf.SerializationBenchmark.Deserialize_PublicInterface + +// To run a specific benchmark class and category +// dotnet run -c Release --framework net6.0 --bm --anyCategories PublicInterface --filter Azure.Core.Perf.SerializationBenchmark* + +var config = ManualConfig.Create(DefaultConfig.Instance); +config.Options = ConfigOptions.JoinSummary | ConfigOptions.StopOnFirstError; +BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args.Where(a => !a.Equals("--bm")).ToArray(), config); + +//BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, new DebugInProcessConfig()); diff --git a/sdk/core/Azure.Core/perf/SerializationBenchmark/AvailabilitySetDataBenchmark.cs b/sdk/core/Azure.Core/perf/SerializationBenchmark/AvailabilitySetDataBenchmark.cs new file mode 100644 index 000000000000..66f0ca2f0185 --- /dev/null +++ b/sdk/core/Azure.Core/perf/SerializationBenchmark/AvailabilitySetDataBenchmark.cs @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Text.Json; +using Azure.Core.Serialization; +using Azure.Core.Tests.Public.ResourceManager.Compute; + +namespace Azure.Core.Perf +{ + public class AvailabilitySetDataBenchmark : SerializationBenchmark + { + protected override AvailabilitySetData Deserialize(JsonElement jsonElement) + { + return AvailabilitySetData.DeserializeAvailabilitySetData(jsonElement); + } + + protected override AvailabilitySetData Deserialize(BinaryData binaryData) + { + return (AvailabilitySetData)((IModelSerializable)_model).Deserialize(binaryData, ModelSerializerOptions.AzureServiceDefault); + } + + protected override void Serialize(Utf8JsonWriter writer) + { + _model.Serialize(writer); + } + + protected override RequestContent CastToRequestContent() + { + return _model; + } + + protected override AvailabilitySetData CastFromResponse() + { + return (AvailabilitySetData)_response; + } + + protected override string JsonFileName => "AvailabilitySetData.json"; + } +} diff --git a/sdk/core/Azure.Core/perf/SerializationBenchmark/BimodalRepro.cs b/sdk/core/Azure.Core/perf/SerializationBenchmark/BimodalRepro.cs new file mode 100644 index 000000000000..99ed14476528 --- /dev/null +++ b/sdk/core/Azure.Core/perf/SerializationBenchmark/BimodalRepro.cs @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using BenchmarkDotNet.Attributes; + +namespace Azure.Core.Perf +{ + [MemoryDiagnoser] + [InProcess] + public class BimodalRepro + { + [Benchmark] + public void Bimodal() + { + using var content = new SequenceWriter(); + } + } +} diff --git a/sdk/core/Azure.Core/perf/SerializationBenchmark/LoadTestPatchModelBenchmark.cs b/sdk/core/Azure.Core/perf/SerializationBenchmark/LoadTestPatchModelBenchmark.cs new file mode 100644 index 000000000000..9c3a639c2b64 --- /dev/null +++ b/sdk/core/Azure.Core/perf/SerializationBenchmark/LoadTestPatchModelBenchmark.cs @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.IO; +using System.Text.Json; +using Azure.Core.Serialization; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Running; + +namespace Azure.Core.Perf +{ + public class LoadTestPatchModelBenchmark : SerializationBenchmark + { + protected override void Serialize(Utf8JsonWriter writer) + { + _model.Serialize(writer); + } + + protected override RequestContent CastToRequestContent() + { + return _model; + } + + protected override Developer.LoadTesting.Models.Test CastFromResponse() + { + return (Developer.LoadTesting.Models.Test)_response; + } + + protected override Developer.LoadTesting.Models.Test Deserialize(JsonElement jsonElement) + { + using MemoryStream stream = new MemoryStream(); + using (Utf8JsonWriter writer = new Utf8JsonWriter(stream)) + { + jsonElement.WriteTo(writer); + } + stream.Position = 0; + BinaryData data = BinaryData.FromStream(stream); + return (Developer.LoadTesting.Models.Test)((IModelSerializable)_model).Deserialize(data, ModelSerializerOptions.AzureServiceDefault); + } + + protected override Developer.LoadTesting.Models.Test Deserialize(BinaryData binaryData) + { + return (Developer.LoadTesting.Models.Test)((IModelSerializable)_model).Deserialize(binaryData, ModelSerializerOptions.AzureServiceDefault); + } + + protected override string JsonFileName => "LoadTestPatchModel.json"; + } +} diff --git a/sdk/core/Azure.Core/perf/SerializationBenchmark/ResourceProviderDataBenchmark.cs b/sdk/core/Azure.Core/perf/SerializationBenchmark/ResourceProviderDataBenchmark.cs new file mode 100644 index 000000000000..f3bba0bd45f7 --- /dev/null +++ b/sdk/core/Azure.Core/perf/SerializationBenchmark/ResourceProviderDataBenchmark.cs @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Text.Json; +using Azure.Core.Serialization; +using Azure.Core.Tests.Public.ResourceManager.Resources; +using BenchmarkDotNet.Attributes; + +namespace Azure.Core.Perf +{ + [Config(typeof(SerializationBenchmarkConfig))] + public class ResourceProviderDataBenchmark : SerializationBenchmark + { + protected override string JsonFileName => "ResourceProviderData.json"; + + protected override ResourceProviderData CastFromResponse() + { + return (ResourceProviderData)_response; + } + + protected override RequestContent CastToRequestContent() + { + return _model; + } + + protected override ResourceProviderData Deserialize(JsonElement jsonElement) + { + return ResourceProviderData.DeserializeResourceProviderData(jsonElement); + } + + protected override ResourceProviderData Deserialize(BinaryData binaryData) + { + return (ResourceProviderData)((IModelSerializable)_model).Deserialize(binaryData, ModelSerializerOptions.AzureServiceDefault); + } + + protected override void Serialize(Utf8JsonWriter writer) + { + _model.Serialize(writer); + } + } +} diff --git a/sdk/core/Azure.Core/perf/SerializationBenchmark/SerializationBenchmark.cs b/sdk/core/Azure.Core/perf/SerializationBenchmark/SerializationBenchmark.cs new file mode 100644 index 000000000000..1fe538a3ac46 --- /dev/null +++ b/sdk/core/Azure.Core/perf/SerializationBenchmark/SerializationBenchmark.cs @@ -0,0 +1,239 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Buffers; +using System.IO; +using System.Reflection; +using System.Text; +using System.Text.Json; +using Azure.Core.Serialization; +using Azure.Core.TestFramework; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Configs; + +namespace Azure.Core.Perf +{ + [MemoryDiagnoser] + [GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)] + public abstract class SerializationBenchmark where T : class, IJsonModelSerializable + { + private string _json; + protected T _model; + protected Response _response; + private ModelSerializerOptions _options; + private BinaryData _data; + private SequenceWriter _content; + private ReadOnlySequence _sequence; + private byte[] _buffer; + + protected abstract T Deserialize(JsonElement jsonElement); + + protected abstract T Deserialize(BinaryData binaryData); + + protected abstract void Serialize(Utf8JsonWriter writer); + + protected abstract RequestContent CastToRequestContent(); + + protected abstract T CastFromResponse(); + + protected abstract string JsonFileName { get; } + + [GlobalSetup] + public void SetUp() + { + _json = File.ReadAllText(Path.Combine(Directory.GetParent(Assembly.GetExecutingAssembly().Location).FullName, "SerializationBenchmark", "TestData", JsonFileName)); + _data = BinaryData.FromString(_json); + _model = ModelSerializer.Deserialize(_data); + _response = new MockResponse(200); + _response.ContentStream = new MemoryStream(Encoding.UTF8.GetBytes(_json)); + _options = ModelSerializerOptions.AzureServiceDefault; + _content = new SequenceWriter(); + using Utf8JsonWriter writer = new Utf8JsonWriter(_content); + _model.Serialize(writer, new ModelSerializerOptions()); + writer.Flush(); + _sequence = _content.GetReadOnlySequence(); + _buffer = new byte[2000]; + } + + [Benchmark] + [BenchmarkCategory("Internal")] + public void Serialize_Internal() + { + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream); + Serialize(writer); + writer.Flush(); + } + + [Benchmark] + [BenchmarkCategory("Cast")] + public void Serialize_ImplicitCast() + { + using var x = CastToRequestContent(); + } + + [Benchmark] + [BenchmarkCategory("Cast")] + public bool Serialize_ImplicitCastWithSerialize() + { + using var x = CastToRequestContent(); + return x.TryComputeLength(out var length); + } + + [Benchmark] + [BenchmarkCategory("Cast")] + public void Serialize_ImplicitCastWithUsage() + { + using var x = CastToRequestContent(); + x.TryComputeLength(out var length); + using var stream = new MemoryStream((int)length); + x.WriteTo(stream, default); + } + + [Benchmark] + [BenchmarkCategory("Cast")] + public RequestContent CreateRequestContent() + { + return RequestContent.Create(_content); + } + + [Benchmark] + [BenchmarkCategory("ModelJsonConverter")] + public string Serialize_ModelJsonConverter() + { + JsonSerializerOptions options = new JsonSerializerOptions(); + options.Converters.Add(new ModelJsonConverter(ModelSerializerFormat.Wire)); + return JsonSerializer.Serialize(_model, options); + } + + [Benchmark] + [BenchmarkCategory("ModelSerializer")] + public BinaryData Serialize_ModelSerializer() + { + return ModelSerializer.Serialize(_model, _options); + } + + [Benchmark] + [BenchmarkCategory("ModelSerializer")] + public BinaryData Serialize_ModelSerializerNonGeneric() + { + return ModelSerializer.Serialize((object)_model, _options); + } + + [Benchmark] + [BenchmarkCategory("PublicInterface")] + public void Serialize_PublicInterface() + { + using var content = new SequenceWriter(); + using var writer = new Utf8JsonWriter(content); + _model.Serialize(writer, _options); + writer.Flush(); + } + + [Benchmark] + [BenchmarkCategory("Internal")] + public T Deserialize_Internal() + { + using JsonDocument doc = JsonDocument.Parse(_json); + return Deserialize(doc.RootElement); + } + + [Benchmark] + [BenchmarkCategory("Internal")] + public T Deserialize_InternalBinaryData() + { + return Deserialize(_data); + } + + [Benchmark] + [BenchmarkCategory("Cast")] + public T Deserialize_ExplicitCast() + { + T result = CastFromResponse(); + _response.ContentStream.Position = 0; //reset for reuse + return result; + } + + [Benchmark] + [BenchmarkCategory("ModelJsonConverter")] + public T Deserialize_ModelJsonConverter() + { + JsonSerializerOptions options = new JsonSerializerOptions(); + options.Converters.Add(new ModelJsonConverter(ModelSerializerFormat.Wire)); + return JsonSerializer.Deserialize(_json, options); + } + + [Benchmark] + [BenchmarkCategory("ModelSerializer")] + public T Deserialize_ModelSerializerFromString() + { + return ModelSerializer.Deserialize(BinaryData.FromString(_json), _options); + } + + [Benchmark] + [BenchmarkCategory("ModelSerializer")] + public T Deserialize_ModelSerializerFromBinaryData() + { + return ModelSerializer.Deserialize(_data, _options); + } + + [Benchmark] + [BenchmarkCategory("ModelSerializer")] + public object Deserialize_ModelSerializerFromBinaryDataNonGeneric() + { + return ModelSerializer.Deserialize(_data, typeof(T), _options); + } + + [Benchmark] + [BenchmarkCategory("PublicInterface")] + public object Deserialize_PublicInterfaceFromString() + { + return _model.Deserialize(BinaryData.FromString(_json), _options); + } + + [Benchmark] + [BenchmarkCategory("PublicInterface")] + public object Deserialize_PublicInterfaceFromBinaryData() + { + return _model.Deserialize(_data, _options); + } + + [Benchmark] + [BenchmarkCategory("PublicInterface")] + public object Deserialize_Utf8JsonReaderFromBinaryData() + { + Utf8JsonReader reader = new Utf8JsonReader(_data); + return _model.Deserialize(ref reader, _options); + } + + [Benchmark] + [BenchmarkCategory("JsonDocument")] + public ReadOnlySequence GetSequence() + { + return _content.GetReadOnlySequence(); + } + + [Benchmark] + [BenchmarkCategory("JsonDocument")] + public void JsonDocumentFromSequence() + { + using var doc = JsonDocument.Parse(_sequence); + } + + [Benchmark] + [BenchmarkCategory("JsonDocument")] + public void JsonDocumentFromReader() + { + Utf8JsonReader reader = new Utf8JsonReader(_data); + using var doc = JsonDocument.ParseValue(ref reader); + } + + [Benchmark] + [BenchmarkCategory("JsonDocument")] + public void JsonDocumentFromBinaryData() + { + using var doc = JsonDocument.Parse(_data); + } + } +} diff --git a/sdk/core/Azure.Core/perf/SerializationBenchmark/SerializationBenchmarkConfig.cs b/sdk/core/Azure.Core/perf/SerializationBenchmark/SerializationBenchmarkConfig.cs new file mode 100644 index 000000000000..36901ef62427 --- /dev/null +++ b/sdk/core/Azure.Core/perf/SerializationBenchmark/SerializationBenchmarkConfig.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using BenchmarkDotNet.Columns; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Reports; +using Perfolizer.Horology; + +namespace Azure.Core.Perf +{ + internal class SerializationBenchmarkConfig : ManualConfig + { + public SerializationBenchmarkConfig() + { + SummaryStyle = SummaryStyle.Default + .WithTimeUnit(TimeUnit.Microsecond) + .WithSizeUnit(SizeUnit.KB); + } + } +} diff --git a/sdk/core/Azure.Core/perf/SerializationBenchmark/TestData/AvailabilitySetData.json b/sdk/core/Azure.Core/perf/SerializationBenchmark/TestData/AvailabilitySetData.json new file mode 100644 index 000000000000..0081d88bbfbe --- /dev/null +++ b/sdk/core/Azure.Core/perf/SerializationBenchmark/TestData/AvailabilitySetData.json @@ -0,0 +1,16 @@ +{ + "name": "testAS-3375", + "id": "/subscriptions/e37510d7-33b6-4676-886f-ee75bcc01871/resourceGroups/testRG-6497/providers/Microsoft.Compute/availabilitySets/testAS-3375", + "type": "Microsoft.Compute/availabilitySets", + "location": "eastus", + "tags": { + "key": "value" + }, + "properties": { + "platformUpdateDomainCount": 5, + "platformFaultDomainCount": 3 + }, + "sku": { + "name": "Classic" + } +} diff --git a/sdk/core/Azure.Core/perf/SerializationBenchmark/TestData/LoadTestPatchModel.json b/sdk/core/Azure.Core/perf/SerializationBenchmark/TestData/LoadTestPatchModel.json new file mode 100644 index 000000000000..7941661f4a1d --- /dev/null +++ b/sdk/core/Azure.Core/perf/SerializationBenchmark/TestData/LoadTestPatchModel.json @@ -0,0 +1,69 @@ +{ + "testId": "12345678-1234-1234-1234-123456789012", + "description": "sample description", + "displayName": "Performance_LoadTest", + "loadTestConfiguration": { + "engineInstances": 6, + "splitAllCSVs": true + }, + "passFailCriteria": { + "passFailMetrics": { + "fefd759d-7fe8-4f83-8b6d-aeebe0f491fe": { + "clientMetric": "response_time_ms", + "aggregate": "percentage", + "condition": ">", + "value": 20, + "action": "continue", + "actualValue": 10, + "result": "passed" + } + } + }, + "createdDateTime": "2021-12-05T16:43:46.072Z", + "createdBy": "user@contoso.com", + "lastModifiedDateTime": "2021-12-05T16:43:46.072Z", + "lastModifiedBy": "user@contoso.com", + "inputArtifacts": { + "configFileInfo": { + "url": "https://dummyurl.com/configresource", + "fileName": "config.yaml", + "fileType": "ADDITIONAL_ARTIFACTS", + "expireDateTime": "2021-12-05T16:43:46.072Z", + "validationStatus": "" + }, + "testScriptFileInfo": { + "url": "https://dummyurl.com/testscriptresource", + "fileName": "sample.jmx", + "fileType": "JMX_FILE", + "expireDateTime": "2021-12-05T16:43:46.072Z", + "validationStatus": "VALIDATION_SUCCESS" + }, + "userPropFileInfo": { + "url": "https://dummyurl.com/userpropresource", + "fileName": "user.properties", + "fileType": "USER_PROPERTIES", + "expireDateTime": "2021-12-05T16:43:46.072Z", + "validationStatus": "" + }, + "inputArtifactsZipFileInfo": { + "url": "https://dummyurl.com/inputartifactzipresource", + "fileName": "inputartifacts.zip", + "fileType": "ADDITIONAL_ARTIFACTS", + "expireDateTime": "2021-12-05T16:43:46.072Z", + "validationStatus": "" + }, + "additionalFileInfo": [] + }, + "secrets": { + "secret1": { + "value": "https://samplevault.vault.azure.net/secrets/samplesecret/f113f91fd4c44a368049849c164db827", + "type": "AKV_SECRET_URI" + } + }, + "environmentVariables": { + "envvar1": "sampletext" + }, + "subnetId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/samplerg/providers/Microsoft.Network/virtualNetworks/samplenetworkresource/subnets/AAAAA0A0A0", + "keyvaultReferenceIdentityType": "UserAssigned", + "keyvaultReferenceIdentityId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/samplerg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/identity1" +} diff --git a/sdk/core/Azure.Core/perf/SerializationBenchmark/TestData/ResourceProviderData.json b/sdk/core/Azure.Core/perf/SerializationBenchmark/TestData/ResourceProviderData.json new file mode 100644 index 000000000000..5e95ddfd70d2 --- /dev/null +++ b/sdk/core/Azure.Core/perf/SerializationBenchmark/TestData/ResourceProviderData.json @@ -0,0 +1,11392 @@ +{ + "id": "/subscriptions/e37510d7-33b6-4676-886f-ee75bcc01871/providers/Microsoft.Network", + "namespace": "Microsoft.Network", + "resourceTypes": [ + { + "resourceType": "virtualNetworks", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2018-01-01", + "2017-11-01", + "2017-10-01", + "2017-09-01", + "2017-08-01", + "2017-06-01", + "2017-04-01", + "2017-03-01", + "2016-12-01", + "2016-11-01", + "2016-10-01", + "2016-09-01", + "2016-08-01", + "2016-07-01", + "2016-06-01", + "2016-03-30", + "2015-06-15", + "2015-05-01-preview", + "2014-12-01-preview" + ], + "defaultApiVersion": "2020-03-01", + "apiProfiles": [ + { + "profileVersion": "2017-03-09-profile", + "apiVersion": "2015-06-15" + }, + { + "profileVersion": "2018-03-01-hybrid", + "apiVersion": "2017-10-01" + }, + { + "profileVersion": "2019-03-01-hybrid", + "apiVersion": "2017-10-01" + } + ], + "locationMappings": [ + { + "location": "East US 2 EUAP", + "type": "EdgeZone", + "extendedLocations": [ + "microsoftrrdclab1", + "microsoftrrdclab3" + ] + }, + { + "location": "West US", + "type": "EdgeZone", + "extendedLocations": [ + "microsoftlosangeles1" + ] + }, + { + "location": "East US 2", + "type": "EdgeZone", + "extendedLocations": [ + "microsoftmiami1" + ] + } + ], + "capabilities": "CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation" + }, + { + "resourceType": "virtualNetworks/taggedTrafficConsumers", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2018-01-01", + "2017-11-01", + "2017-10-01", + "2017-09-01", + "2017-08-01", + "2017-06-01", + "2017-04-01", + "2017-03-01", + "2016-12-01", + "2016-11-01", + "2016-10-01", + "2016-09-01", + "2016-08-01", + "2016-07-01", + "2016-06-01", + "2016-03-30", + "2015-06-15", + "2015-05-01-preview", + "2014-12-01-preview" + ], + "defaultApiVersion": "2020-03-01", + "apiProfiles": [ + { + "profileVersion": "2017-03-09-profile", + "apiVersion": "2015-06-15" + }, + { + "profileVersion": "2018-03-01-hybrid", + "apiVersion": "2017-10-01" + }, + { + "profileVersion": "2019-03-01-hybrid", + "apiVersion": "2017-10-01" + } + ], + "capabilities": "None" + }, + { + "resourceType": "natGateways", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01" + ], + "defaultApiVersion": "2020-03-01", + "zoneMappings": [ + { + "location": "Australia East", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Brazil South", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Canada Central", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Central India", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Central US", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Central US EUAP", + "zones": [ + "1", + "2" + ] + }, + { + "location": "East Asia", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "East US", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "East US 2", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "East US 2 EUAP", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "France Central", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Germany West Central", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Japan East", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Korea Central", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "North Central US", + "zones": [] + }, + { + "location": "North Europe", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Norway East", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Qatar Central", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "South Africa North", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "South Central US", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Southeast Asia", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Sweden Central", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Switzerland North", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "UAE North", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "UK South", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "West Europe", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "West US", + "zones": [] + }, + { + "location": "West US 2", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "West US 3", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Poland Central", + "zones": [ + "2", + "1", + "3" + ] + } + ], + "capabilities": "CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation" + }, + { + "resourceType": "publicIPAddresses", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2018-01-01", + "2017-11-01", + "2017-10-01", + "2017-09-01", + "2017-08-01", + "2017-06-01", + "2017-04-01", + "2017-03-01", + "2016-12-01", + "2016-11-01", + "2016-10-01", + "2016-09-01", + "2016-08-01", + "2016-07-01", + "2016-06-01", + "2016-03-30", + "2015-06-15", + "2015-05-01-preview", + "2014-12-01-preview" + ], + "defaultApiVersion": "2020-03-01", + "apiProfiles": [ + { + "profileVersion": "2017-03-09-profile", + "apiVersion": "2015-06-15" + }, + { + "profileVersion": "2018-03-01-hybrid", + "apiVersion": "2017-10-01" + }, + { + "profileVersion": "2019-03-01-hybrid", + "apiVersion": "2017-10-01" + } + ], + "zoneMappings": [ + { + "location": "Australia East", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Brazil South", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Canada Central", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Central India", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Central US", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Central US EUAP", + "zones": [ + "1", + "2" + ] + }, + { + "location": "East Asia", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "East US", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "East US 2", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "East US 2 EUAP", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "France Central", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Germany West Central", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Japan East", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Korea Central", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "North Central US", + "zones": [] + }, + { + "location": "North Europe", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Norway East", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Qatar Central", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "South Africa North", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "South Central US", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Southeast Asia", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Sweden Central", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Switzerland North", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "UAE North", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "UK South", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "West Europe", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "West US", + "zones": [] + }, + { + "location": "West US 2", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "West US 3", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Poland Central", + "zones": [ + "2", + "1", + "3" + ] + } + ], + "locationMappings": [ + { + "location": "East US 2 EUAP", + "type": "EdgeZone", + "extendedLocations": [ + "microsoftrrdclab1", + "microsoftrrdclab3" + ] + }, + { + "location": "West US", + "type": "EdgeZone", + "extendedLocations": [ + "microsoftlosangeles1" + ] + }, + { + "location": "East US 2", + "type": "EdgeZone", + "extendedLocations": [ + "microsoftmiami1" + ] + } + ], + "capabilities": "CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation" + }, + { + "resourceType": "internalPublicIpAddresses", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01" + ], + "capabilities": "None" + }, + { + "resourceType": "customIpPrefixes", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01" + ], + "defaultApiVersion": "2020-06-01", + "zoneMappings": [ + { + "location": "Australia East", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Brazil South", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Canada Central", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Central India", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Central US", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Central US EUAP", + "zones": [ + "1", + "2" + ] + }, + { + "location": "East Asia", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "East US", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "East US 2", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "East US 2 EUAP", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "France Central", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Germany West Central", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Japan East", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Korea Central", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "North Central US", + "zones": [] + }, + { + "location": "North Europe", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Norway East", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Qatar Central", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "South Africa North", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "South Central US", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Southeast Asia", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Sweden Central", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Switzerland North", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "UAE North", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "UK South", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "West Europe", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "West US", + "zones": [] + }, + { + "location": "West US 2", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "West US 3", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Poland Central", + "zones": [ + "2", + "1", + "3" + ] + } + ], + "locationMappings": [ + { + "location": "East US 2 EUAP", + "type": "EdgeZone", + "extendedLocations": [ + "microsoftrrdclab1", + "microsoftrrdclab3" + ] + }, + { + "location": "West US", + "type": "EdgeZone", + "extendedLocations": [ + "microsoftlosangeles1" + ] + }, + { + "location": "East US 2", + "type": "EdgeZone", + "extendedLocations": [ + "microsoftmiami1" + ] + } + ], + "capabilities": "CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation" + }, + { + "resourceType": "networkInterfaces", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2018-01-01", + "2017-11-01", + "2017-10-01", + "2017-09-01", + "2017-08-01", + "2017-06-01", + "2017-04-01", + "2017-03-01", + "2016-12-01", + "2016-11-01", + "2016-10-01", + "2016-09-01", + "2016-08-01", + "2016-07-01", + "2016-06-01", + "2016-03-30", + "2015-06-15", + "2015-05-01-preview", + "2014-12-01-preview" + ], + "defaultApiVersion": "2020-03-01", + "apiProfiles": [ + { + "profileVersion": "2017-03-09-profile", + "apiVersion": "2015-06-15" + }, + { + "profileVersion": "2018-03-01-hybrid", + "apiVersion": "2017-10-01" + }, + { + "profileVersion": "2019-03-01-hybrid", + "apiVersion": "2017-10-01" + } + ], + "locationMappings": [ + { + "location": "East US 2 EUAP", + "type": "EdgeZone", + "extendedLocations": [ + "microsoftrrdclab1", + "microsoftrrdclab3" + ] + }, + { + "location": "West US", + "type": "EdgeZone", + "extendedLocations": [ + "microsoftlosangeles1" + ] + }, + { + "location": "East US 2", + "type": "EdgeZone", + "extendedLocations": [ + "microsoftmiami1" + ] + } + ], + "capabilities": "CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation" + }, + { + "resourceType": "dscpConfigurations", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01" + ], + "defaultApiVersion": "2020-06-01", + "capabilities": "CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation" + }, + { + "resourceType": "privateEndpoints", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01" + ], + "defaultApiVersion": "2020-03-01", + "locationMappings": [ + { + "location": "East US 2 EUAP", + "type": "EdgeZone", + "extendedLocations": [ + "microsoftrrdclab1", + "microsoftrrdclab3" + ] + }, + { + "location": "West US", + "type": "EdgeZone", + "extendedLocations": [ + "microsoftlosangeles1" + ] + }, + { + "location": "East US 2", + "type": "EdgeZone", + "extendedLocations": [ + "microsoftmiami1" + ] + } + ], + "capabilities": "CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation" + }, + { + "resourceType": "privateEndpoints/privateLinkServiceProxies", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01" + ], + "defaultApiVersion": "2020-03-01", + "capabilities": "None" + }, + { + "resourceType": "privateEndpointRedirectMaps", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01" + ], + "defaultApiVersion": "2020-03-01", + "capabilities": "SupportsTags, SupportsLocation" + }, + { + "resourceType": "loadBalancers", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2018-01-01", + "2017-11-01", + "2017-10-01", + "2017-09-01", + "2017-08-01", + "2017-06-01", + "2017-04-01", + "2017-03-01", + "2016-12-01", + "2016-11-01", + "2016-10-01", + "2016-09-01", + "2016-08-01", + "2016-07-01", + "2016-06-01", + "2016-03-30", + "2015-06-15", + "2015-05-01-preview", + "2014-12-01-preview" + ], + "defaultApiVersion": "2020-03-01", + "apiProfiles": [ + { + "profileVersion": "2017-03-09-profile", + "apiVersion": "2015-06-15" + }, + { + "profileVersion": "2018-03-01-hybrid", + "apiVersion": "2017-10-01" + }, + { + "profileVersion": "2019-03-01-hybrid", + "apiVersion": "2017-10-01" + } + ], + "locationMappings": [ + { + "location": "East US 2 EUAP", + "type": "EdgeZone", + "extendedLocations": [ + "microsoftrrdclab1", + "microsoftrrdclab3" + ] + }, + { + "location": "West US", + "type": "EdgeZone", + "extendedLocations": [ + "microsoftlosangeles1" + ] + }, + { + "location": "East US 2", + "type": "EdgeZone", + "extendedLocations": [ + "microsoftmiami1" + ] + } + ], + "capabilities": "CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation" + }, + { + "resourceType": "networkSecurityGroups", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2018-01-01", + "2017-11-01", + "2017-10-01", + "2017-09-01", + "2017-08-01", + "2017-06-01", + "2017-04-01", + "2017-03-01", + "2016-12-01", + "2016-11-01", + "2016-10-01", + "2016-09-01", + "2016-08-01", + "2016-07-01", + "2016-06-01", + "2016-03-30", + "2015-06-15", + "2015-05-01-preview", + "2014-12-01-preview" + ], + "defaultApiVersion": "2020-03-01", + "apiProfiles": [ + { + "profileVersion": "2017-03-09-profile", + "apiVersion": "2015-06-15" + }, + { + "profileVersion": "2018-03-01-hybrid", + "apiVersion": "2017-10-01" + }, + { + "profileVersion": "2019-03-01-hybrid", + "apiVersion": "2017-10-01" + } + ], + "capabilities": "CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation" + }, + { + "resourceType": "applicationSecurityGroups", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2018-01-01", + "2017-11-01", + "2017-10-01", + "2017-09-01" + ], + "defaultApiVersion": "2020-03-01", + "apiProfiles": [ + { + "profileVersion": "2017-03-09-profile", + "apiVersion": "2017-09-01" + }, + { + "profileVersion": "2018-03-01-hybrid", + "apiVersion": "2017-10-01" + } + ], + "capabilities": "CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation" + }, + { + "resourceType": "serviceEndpointPolicies", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2018-01-01" + ], + "defaultApiVersion": "2020-03-01", + "capabilities": "CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation" + }, + { + "resourceType": "networkIntentPolicies", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01" + ], + "defaultApiVersion": "2020-03-01", + "capabilities": "CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation" + }, + { + "resourceType": "routeTables", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2018-01-01", + "2017-11-01", + "2017-10-01", + "2017-09-01", + "2017-08-01", + "2017-06-01", + "2017-04-01", + "2017-03-01", + "2016-12-01", + "2016-11-01", + "2016-10-01", + "2016-09-01", + "2016-08-01", + "2016-07-01", + "2016-06-01", + "2016-03-30", + "2015-06-15", + "2015-05-01-preview", + "2014-12-01-preview" + ], + "defaultApiVersion": "2020-03-01", + "apiProfiles": [ + { + "profileVersion": "2017-03-09-profile", + "apiVersion": "2015-06-15" + }, + { + "profileVersion": "2018-03-01-hybrid", + "apiVersion": "2017-10-01" + }, + { + "profileVersion": "2019-03-01-hybrid", + "apiVersion": "2017-10-01" + } + ], + "capabilities": "CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation" + }, + { + "resourceType": "publicIPPrefixes", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01" + ], + "defaultApiVersion": "2020-03-01", + "zoneMappings": [ + { + "location": "Australia East", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Brazil South", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Canada Central", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Central India", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Central US", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Central US EUAP", + "zones": [ + "1", + "2" + ] + }, + { + "location": "East Asia", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "East US", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "East US 2", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "East US 2 EUAP", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "France Central", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Germany West Central", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Japan East", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Korea Central", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "North Central US", + "zones": [] + }, + { + "location": "North Europe", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Norway East", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Qatar Central", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "South Africa North", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "South Central US", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Southeast Asia", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Sweden Central", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Switzerland North", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "UAE North", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "UK South", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "West Europe", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "West US", + "zones": [] + }, + { + "location": "West US 2", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "West US 3", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Poland Central", + "zones": [ + "2", + "1", + "3" + ] + } + ], + "locationMappings": [ + { + "location": "East US 2 EUAP", + "type": "EdgeZone", + "extendedLocations": [ + "microsoftrrdclab1", + "microsoftrrdclab3" + ] + }, + { + "location": "West US", + "type": "EdgeZone", + "extendedLocations": [ + "microsoftlosangeles1" + ] + }, + { + "location": "East US 2", + "type": "EdgeZone", + "extendedLocations": [ + "microsoftmiami1" + ] + } + ], + "capabilities": "CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation" + }, + { + "resourceType": "networkWatchers", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2018-01-01", + "2017-11-01", + "2017-10-01", + "2017-09-01", + "2017-08-01", + "2017-06-01", + "2017-04-01", + "2017-03-01", + "2016-12-01", + "2016-11-01", + "2016-10-01", + "2016-09-01", + "2016-08-01", + "2016-07-01", + "2016-06-01", + "2016-03-30", + "2015-06-15", + "2015-05-01-preview", + "2014-12-01-preview" + ], + "defaultApiVersion": "2020-03-01", + "capabilities": "CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation" + }, + { + "resourceType": "networkWatchers/connectionMonitors", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2018-01-01", + "2017-11-01", + "2017-10-01", + "2017-09-01" + ], + "defaultApiVersion": "2020-03-01", + "capabilities": "CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation" + }, + { + "resourceType": "networkWatchers/flowLogs", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2018-01-01", + "2017-11-01", + "2017-10-01", + "2017-09-01" + ], + "defaultApiVersion": "2020-03-01", + "capabilities": "CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation" + }, + { + "resourceType": "networkWatchers/pingMeshes", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2018-01-01", + "2017-11-01", + "2017-10-01", + "2017-09-01" + ], + "defaultApiVersion": "2020-03-01", + "capabilities": "CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation" + }, + { + "resourceType": "virtualNetworkGateways", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2018-01-01", + "2017-11-01", + "2017-10-01", + "2017-09-01", + "2017-08-01", + "2017-06-01", + "2017-04-01", + "2017-03-01", + "2016-12-01", + "2016-11-01", + "2016-10-01", + "2016-09-01", + "2016-08-01", + "2016-07-01", + "2016-06-01", + "2016-03-30", + "2015-06-15", + "2015-05-01-preview", + "2014-12-01-preview" + ], + "defaultApiVersion": "2020-03-01", + "apiProfiles": [ + { + "profileVersion": "2017-03-09-profile", + "apiVersion": "2015-06-15" + }, + { + "profileVersion": "2018-03-01-hybrid", + "apiVersion": "2017-03-01" + }, + { + "profileVersion": "2019-03-01-hybrid", + "apiVersion": "2017-10-01" + } + ], + "locationMappings": [ + { + "location": "East US 2 EUAP", + "type": "EdgeZone", + "extendedLocations": [ + "microsoftrrdclab1", + "microsoftrrdclab3" + ] + }, + { + "location": "West US", + "type": "EdgeZone", + "extendedLocations": [ + "microsoftlosangeles1" + ] + }, + { + "location": "East US 2", + "type": "EdgeZone", + "extendedLocations": [ + "microsoftmiami1" + ] + } + ], + "capabilities": "CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation" + }, + { + "resourceType": "localNetworkGateways", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2018-01-01", + "2017-11-01", + "2017-10-01", + "2017-09-01", + "2017-08-01", + "2017-06-01", + "2017-04-01", + "2017-03-01", + "2016-12-01", + "2016-11-01", + "2016-10-01", + "2016-09-01", + "2016-08-01", + "2016-07-01", + "2016-06-01", + "2016-03-30", + "2015-06-15", + "2015-05-01-preview", + "2014-12-01-preview" + ], + "defaultApiVersion": "2020-03-01", + "apiProfiles": [ + { + "profileVersion": "2017-03-09-profile", + "apiVersion": "2015-06-15" + }, + { + "profileVersion": "2018-03-01-hybrid", + "apiVersion": "2017-03-01" + }, + { + "profileVersion": "2019-03-01-hybrid", + "apiVersion": "2017-10-01" + } + ], + "capabilities": "CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation" + }, + { + "resourceType": "connections", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2018-01-01", + "2017-11-01", + "2017-10-01", + "2017-09-01", + "2017-08-01", + "2017-06-01", + "2017-04-01", + "2017-03-01", + "2016-12-01", + "2016-11-01", + "2016-10-01", + "2016-09-01", + "2016-08-01", + "2016-07-01", + "2016-06-01", + "2016-03-30", + "2015-06-15", + "2015-05-01-preview", + "2014-12-01-preview" + ], + "defaultApiVersion": "2020-03-01", + "apiProfiles": [ + { + "profileVersion": "2017-03-09-profile", + "apiVersion": "2015-06-15" + }, + { + "profileVersion": "2018-03-01-hybrid", + "apiVersion": "2017-03-01" + }, + { + "profileVersion": "2019-03-01-hybrid", + "apiVersion": "2017-10-01" + } + ], + "capabilities": "CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation" + }, + { + "resourceType": "applicationGateways", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2018-01-01", + "2017-11-01", + "2017-10-01", + "2017-09-01", + "2017-08-01", + "2017-06-01", + "2017-04-01", + "2017-03-01", + "2016-12-01", + "2016-11-01", + "2016-10-01", + "2016-09-01", + "2016-08-01", + "2016-07-01", + "2016-06-01", + "2016-03-30", + "2015-06-15", + "2015-05-01-preview", + "2014-12-01-preview" + ], + "defaultApiVersion": "2020-03-01", + "zoneMappings": [ + { + "location": "Australia East", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Brazil South", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Canada Central", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Central India", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Central US", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Central US EUAP", + "zones": [ + "1", + "2" + ] + }, + { + "location": "East Asia", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "East US", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "East US 2", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "East US 2 EUAP", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "France Central", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Germany West Central", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Japan East", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Korea Central", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "North Central US", + "zones": [] + }, + { + "location": "North Europe", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Norway East", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Qatar Central", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "South Africa North", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "South Central US", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Southeast Asia", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Sweden Central", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Switzerland North", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "UAE North", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "UK South", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "West Europe", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "West US", + "zones": [] + }, + { + "location": "West US 2", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "West US 3", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Poland Central", + "zones": [ + "2", + "1", + "3" + ] + } + ], + "capabilities": "SupportsTags, SupportsLocation" + }, + { + "resourceType": "applicationGatewayWebApplicationFirewallPolicies", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01" + ], + "defaultApiVersion": "2020-03-01", + "capabilities": "SupportsTags, SupportsLocation" + }, + { + "resourceType": "locations", + "locations": [], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2018-01-01", + "2017-11-01", + "2017-10-01", + "2017-09-01", + "2017-08-01", + "2017-06-01", + "2017-04-01", + "2017-03-01", + "2016-12-01", + "2016-11-01", + "2016-10-01", + "2016-09-01", + "2016-08-01", + "2016-07-01", + "2016-06-01", + "2016-03-30", + "2015-06-15", + "2015-05-01-preview", + "2014-12-01-preview" + ], + "apiProfiles": [ + { + "profileVersion": "2017-03-09-profile", + "apiVersion": "2015-06-15" + }, + { + "profileVersion": "2018-03-01-hybrid", + "apiVersion": "2017-10-01" + }, + { + "profileVersion": "2019-03-01-hybrid", + "apiVersion": "2017-10-01" + } + ], + "capabilities": "None" + }, + { + "resourceType": "locations/operations", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2018-01-01", + "2017-11-01", + "2017-10-01", + "2017-09-01", + "2017-08-01", + "2017-06-01", + "2017-04-01", + "2017-03-01", + "2016-12-01", + "2016-11-01", + "2016-10-01", + "2016-09-01", + "2016-08-01", + "2016-07-01", + "2016-06-01", + "2016-03-30", + "2015-06-15", + "2015-05-01-preview", + "2014-12-01-preview" + ], + "apiProfiles": [ + { + "profileVersion": "2017-03-09-profile", + "apiVersion": "2015-06-15" + }, + { + "profileVersion": "2018-03-01-hybrid", + "apiVersion": "2017-10-01" + }, + { + "profileVersion": "2019-03-01-hybrid", + "apiVersion": "2017-10-01" + } + ], + "capabilities": "None" + }, + { + "resourceType": "locations/operationResults", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2018-01-01", + "2017-11-01", + "2017-10-01", + "2017-09-01", + "2017-08-01", + "2017-06-01", + "2017-04-01", + "2017-03-01", + "2016-12-01", + "2016-11-01", + "2016-10-01", + "2016-09-01", + "2016-08-01", + "2016-07-01", + "2016-06-01", + "2016-03-30", + "2015-06-15", + "2015-05-01-preview", + "2014-12-01-preview" + ], + "apiProfiles": [ + { + "profileVersion": "2017-03-09-profile", + "apiVersion": "2015-06-15" + }, + { + "profileVersion": "2018-03-01-hybrid", + "apiVersion": "2017-10-01" + }, + { + "profileVersion": "2019-03-01-hybrid", + "apiVersion": "2017-10-01" + } + ], + "capabilities": "None" + }, + { + "resourceType": "locations/CheckDnsNameAvailability", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2018-01-01", + "2017-11-01", + "2017-10-01", + "2017-09-01", + "2017-08-01", + "2017-06-01", + "2017-04-01", + "2017-03-01", + "2016-12-01", + "2016-11-01", + "2016-10-01", + "2016-09-01", + "2016-08-01", + "2016-07-01", + "2016-06-01", + "2016-03-30", + "2015-06-15", + "2015-05-01-preview", + "2014-12-01-preview" + ], + "capabilities": "None" + }, + { + "resourceType": "locations/setLoadBalancerFrontendPublicIpAddresses", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01" + ], + "capabilities": "None" + }, + { + "resourceType": "cloudServiceSlots", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01" + ], + "capabilities": "SupportsExtension" + }, + { + "resourceType": "locations/usages", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2018-01-01", + "2017-11-01", + "2017-10-01", + "2017-09-01", + "2017-08-01", + "2017-06-01", + "2017-04-01", + "2017-03-01", + "2016-12-01", + "2016-11-01", + "2016-10-01", + "2016-09-01", + "2016-08-01", + "2016-07-01", + "2016-06-01", + "2016-03-30", + "2015-06-15", + "2015-05-01-preview", + "2014-12-01-preview" + ], + "apiProfiles": [ + { + "profileVersion": "2017-03-09-profile", + "apiVersion": "2015-06-15" + }, + { + "profileVersion": "2018-03-01-hybrid", + "apiVersion": "2017-10-01" + }, + { + "profileVersion": "2019-03-01-hybrid", + "apiVersion": "2017-10-01" + } + ], + "capabilities": "None" + }, + { + "resourceType": "locations/virtualNetworkAvailableEndpointServices", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2018-01-01", + "2017-11-01", + "2017-10-01", + "2017-09-01", + "2017-08-01", + "2017-06-01", + "2017-04-01" + ], + "capabilities": "None" + }, + { + "resourceType": "locations/availableDelegations", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01" + ], + "capabilities": "None" + }, + { + "resourceType": "locations/ApplicationGatewayWafDynamicManifests", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01" + ], + "capabilities": "None" + }, + { + "resourceType": "locations/serviceTags", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01" + ], + "capabilities": "None" + }, + { + "resourceType": "locations/availablePrivateEndpointTypes", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01" + ], + "capabilities": "None" + }, + { + "resourceType": "locations/availableServiceAliases", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01" + ], + "capabilities": "None" + }, + { + "resourceType": "locations/checkPrivateLinkServiceVisibility", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01" + ], + "capabilities": "None" + }, + { + "resourceType": "locations/autoApprovedPrivateLinkServices", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01" + ], + "capabilities": "None" + }, + { + "resourceType": "locations/batchValidatePrivateEndpointsForResourceMove", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01" + ], + "capabilities": "None" + }, + { + "resourceType": "locations/batchNotifyPrivateEndpointsForResourceMove", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01" + ], + "capabilities": "None" + }, + { + "resourceType": "locations/supportedVirtualMachineSizes", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01" + ], + "capabilities": "None" + }, + { + "resourceType": "locations/setAzureNetworkManagerConfiguration", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01" + ], + "capabilities": "None" + }, + { + "resourceType": "locations/publishResources", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01" + ], + "capabilities": "None" + }, + { + "resourceType": "locations/getAzureNetworkManagerConfiguration", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01" + ], + "capabilities": "None" + }, + { + "resourceType": "locations/checkAcceleratedNetworkingSupport", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01" + ], + "capabilities": "None" + }, + { + "resourceType": "locations/validateResourceOwnership", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01" + ], + "capabilities": "None" + }, + { + "resourceType": "locations/setResourceOwnership", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01" + ], + "capabilities": "None" + }, + { + "resourceType": "locations/effectiveResourceOwnership", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01" + ], + "capabilities": "None" + }, + { + "resourceType": "operations", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2018-01-01", + "2017-11-01", + "2017-10-01", + "2017-09-01", + "2017-08-01", + "2017-06-01", + "2017-04-01", + "2017-03-01", + "2016-12-01", + "2016-11-01", + "2016-10-01", + "2016-09-01", + "2016-08-01", + "2016-07-01", + "2016-06-01", + "2016-03-30", + "2015-06-15", + "2015-05-01-preview", + "2014-12-01-preview" + ], + "capabilities": "None" + }, + { + "resourceType": "dnszones", + "locations": [ + "global" + ], + "apiVersions": [ + "2018-05-01", + "2018-03-01-preview", + "2017-10-01", + "2017-09-15-preview", + "2017-09-01", + "2016-04-01", + "2015-05-04-preview" + ], + "defaultApiVersion": "2018-05-01", + "apiProfiles": [ + { + "profileVersion": "2017-03-09-profile", + "apiVersion": "2016-04-01" + }, + { + "profileVersion": "2018-03-01-hybrid", + "apiVersion": "2016-04-01" + }, + { + "profileVersion": "2019-03-01-hybrid", + "apiVersion": "2016-04-01" + } + ], + "capabilities": "CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation" + }, + { + "resourceType": "dnsOperationResults", + "locations": [ + "global" + ], + "apiVersions": [ + "2018-05-01", + "2018-03-01-preview", + "2017-10-01", + "2017-09-15-preview", + "2017-09-01", + "2016-04-01" + ], + "defaultApiVersion": "2018-05-01", + "capabilities": "None" + }, + { + "resourceType": "dnsOperationStatuses", + "locations": [ + "global" + ], + "apiVersions": [ + "2018-05-01", + "2018-03-01-preview", + "2017-10-01", + "2017-09-15-preview", + "2017-09-01", + "2016-04-01" + ], + "defaultApiVersion": "2018-05-01", + "capabilities": "None" + }, + { + "resourceType": "getDnsResourceReference", + "locations": [ + "global" + ], + "apiVersions": [ + "2018-05-01" + ], + "defaultApiVersion": "2018-05-01", + "capabilities": "None" + }, + { + "resourceType": "internalNotify", + "locations": [ + "global" + ], + "apiVersions": [ + "2018-05-01" + ], + "defaultApiVersion": "2018-05-01", + "capabilities": "None" + }, + { + "resourceType": "dnszones/A", + "locations": [ + "global" + ], + "apiVersions": [ + "2018-05-01", + "2018-03-01-preview", + "2017-10-01", + "2017-09-15-preview", + "2017-09-01", + "2016-04-01", + "2015-05-04-preview" + ], + "defaultApiVersion": "2018-05-01", + "capabilities": "None" + }, + { + "resourceType": "dnszones/AAAA", + "locations": [ + "global" + ], + "apiVersions": [ + "2018-05-01", + "2018-03-01-preview", + "2017-10-01", + "2017-09-15-preview", + "2017-09-01", + "2016-04-01", + "2015-05-04-preview" + ], + "defaultApiVersion": "2018-05-01", + "capabilities": "None" + }, + { + "resourceType": "dnszones/CNAME", + "locations": [ + "global" + ], + "apiVersions": [ + "2018-05-01", + "2018-03-01-preview", + "2017-10-01", + "2017-09-15-preview", + "2017-09-01", + "2016-04-01", + "2015-05-04-preview" + ], + "defaultApiVersion": "2018-05-01", + "capabilities": "None" + }, + { + "resourceType": "dnszones/PTR", + "locations": [ + "global" + ], + "apiVersions": [ + "2018-05-01", + "2018-03-01-preview", + "2017-10-01", + "2017-09-15-preview", + "2017-09-01", + "2016-04-01", + "2015-05-04-preview" + ], + "defaultApiVersion": "2018-05-01", + "capabilities": "None" + }, + { + "resourceType": "dnszones/MX", + "locations": [ + "global" + ], + "apiVersions": [ + "2018-05-01", + "2018-03-01-preview", + "2017-10-01", + "2017-09-15-preview", + "2017-09-01", + "2016-04-01", + "2015-05-04-preview" + ], + "defaultApiVersion": "2018-05-01", + "capabilities": "None" + }, + { + "resourceType": "dnszones/TXT", + "locations": [ + "global" + ], + "apiVersions": [ + "2018-05-01", + "2018-03-01-preview", + "2017-10-01", + "2017-09-15-preview", + "2017-09-01", + "2016-04-01", + "2015-05-04-preview" + ], + "defaultApiVersion": "2018-05-01", + "capabilities": "None" + }, + { + "resourceType": "dnszones/SRV", + "locations": [ + "global" + ], + "apiVersions": [ + "2018-05-01", + "2018-03-01-preview", + "2017-10-01", + "2017-09-15-preview", + "2017-09-01", + "2016-04-01", + "2015-05-04-preview" + ], + "defaultApiVersion": "2018-05-01", + "capabilities": "None" + }, + { + "resourceType": "dnszones/SOA", + "locations": [ + "global" + ], + "apiVersions": [ + "2018-05-01", + "2018-03-01-preview", + "2017-10-01", + "2017-09-15-preview", + "2017-09-01", + "2016-04-01", + "2015-05-04-preview" + ], + "defaultApiVersion": "2018-05-01", + "capabilities": "None" + }, + { + "resourceType": "dnszones/NS", + "locations": [ + "global" + ], + "apiVersions": [ + "2018-05-01", + "2018-03-01-preview", + "2017-10-01", + "2017-09-15-preview", + "2017-09-01", + "2016-04-01", + "2015-05-04-preview" + ], + "defaultApiVersion": "2018-05-01", + "capabilities": "None" + }, + { + "resourceType": "dnszones/CAA", + "locations": [ + "global" + ], + "apiVersions": [ + "2018-05-01", + "2018-03-01-preview", + "2017-10-01", + "2017-09-15-preview", + "2017-09-01" + ], + "defaultApiVersion": "2018-05-01", + "capabilities": "None" + }, + { + "resourceType": "dnszones/recordsets", + "locations": [ + "global" + ], + "apiVersions": [ + "2018-05-01", + "2018-03-01-preview", + "2017-10-01", + "2017-09-15-preview", + "2017-09-01", + "2016-04-01", + "2015-05-04-preview" + ], + "defaultApiVersion": "2018-05-01", + "capabilities": "None" + }, + { + "resourceType": "dnszones/all", + "locations": [ + "global" + ], + "apiVersions": [ + "2018-05-01", + "2018-03-01-preview", + "2017-10-01", + "2017-09-15-preview", + "2017-09-01", + "2016-04-01", + "2015-05-04-preview" + ], + "defaultApiVersion": "2018-05-01", + "capabilities": "None" + }, + { + "resourceType": "privateDnsZones", + "locations": [ + "global" + ], + "apiVersions": [ + "2020-06-01", + "2020-01-01", + "2018-09-01" + ], + "defaultApiVersion": "2018-09-01", + "capabilities": "CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation" + }, + { + "resourceType": "privateDnsZones/virtualNetworkLinks", + "locations": [ + "global" + ], + "apiVersions": [ + "2020-06-01", + "2020-01-01", + "2018-09-01" + ], + "defaultApiVersion": "2018-09-01", + "capabilities": "CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation" + }, + { + "resourceType": "privateDnsOperationResults", + "locations": [ + "global" + ], + "apiVersions": [ + "2020-06-01", + "2020-01-01", + "2018-09-01" + ], + "defaultApiVersion": "2018-09-01", + "capabilities": "None" + }, + { + "resourceType": "privateDnsOperationStatuses", + "locations": [ + "global" + ], + "apiVersions": [ + "2020-06-01", + "2020-01-01", + "2018-09-01" + ], + "defaultApiVersion": "2018-09-01", + "capabilities": "None" + }, + { + "resourceType": "privateDnsZonesInternal", + "locations": [ + "global" + ], + "apiVersions": [ + "2020-06-01", + "2020-01-01" + ], + "defaultApiVersion": "2020-01-01", + "capabilities": "None" + }, + { + "resourceType": "privateDnsZones/A", + "locations": [ + "global" + ], + "apiVersions": [ + "2020-06-01", + "2020-01-01", + "2018-09-01" + ], + "defaultApiVersion": "2018-09-01", + "capabilities": "None" + }, + { + "resourceType": "privateDnsZones/AAAA", + "locations": [ + "global" + ], + "apiVersions": [ + "2020-06-01", + "2020-01-01", + "2018-09-01" + ], + "defaultApiVersion": "2018-09-01", + "capabilities": "None" + }, + { + "resourceType": "privateDnsZones/CNAME", + "locations": [ + "global" + ], + "apiVersions": [ + "2020-06-01", + "2020-01-01", + "2018-09-01" + ], + "defaultApiVersion": "2018-09-01", + "capabilities": "None" + }, + { + "resourceType": "privateDnsZones/PTR", + "locations": [ + "global" + ], + "apiVersions": [ + "2020-06-01", + "2020-01-01", + "2018-09-01" + ], + "defaultApiVersion": "2018-09-01", + "capabilities": "None" + }, + { + "resourceType": "privateDnsZones/MX", + "locations": [ + "global" + ], + "apiVersions": [ + "2020-06-01", + "2020-01-01", + "2018-09-01" + ], + "defaultApiVersion": "2018-09-01", + "capabilities": "None" + }, + { + "resourceType": "privateDnsZones/TXT", + "locations": [ + "global" + ], + "apiVersions": [ + "2020-06-01", + "2020-01-01", + "2018-09-01" + ], + "defaultApiVersion": "2018-09-01", + "capabilities": "None" + }, + { + "resourceType": "privateDnsZones/SRV", + "locations": [ + "global" + ], + "apiVersions": [ + "2020-06-01", + "2020-01-01", + "2018-09-01" + ], + "defaultApiVersion": "2018-09-01", + "capabilities": "None" + }, + { + "resourceType": "privateDnsZones/SOA", + "locations": [ + "global" + ], + "apiVersions": [ + "2020-06-01", + "2020-01-01", + "2018-09-01" + ], + "defaultApiVersion": "2018-09-01", + "capabilities": "None" + }, + { + "resourceType": "privateDnsZones/all", + "locations": [ + "global" + ], + "apiVersions": [ + "2020-06-01", + "2020-01-01", + "2018-09-01" + ], + "defaultApiVersion": "2018-09-01", + "capabilities": "None" + }, + { + "resourceType": "virtualNetworks/privateDnsZoneLinks", + "locations": [ + "global" + ], + "apiVersions": [ + "2020-06-01" + ], + "defaultApiVersion": "2020-06-01", + "capabilities": "None" + }, + { + "resourceType": "dnsResolvers", + "locations": [ + "West Central US", + "East US 2", + "West Europe", + "North Europe", + "Australia East", + "UK South", + "South Central US", + "East US", + "North Central US", + "West US 2", + "West US 3", + "Southeast Asia", + "Central India", + "Canada Central", + "Central US", + "France Central", + "Japan East", + "Germany West Central", + "South Africa North", + "Korea Central", + "Sweden Central", + "East Asia", + "Switzerland North", + "Brazil South", + "West US", + "Norway East", + "UAE North", + "Australia Southeast", + "Canada East", + "Japan West", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2022-07-01", + "2020-04-01-preview" + ], + "defaultApiVersion": "2020-04-01-preview", + "capabilities": "CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation" + }, + { + "resourceType": "dnsResolvers/inboundEndpoints", + "locations": [ + "West Central US", + "East US 2", + "West Europe", + "North Europe", + "Australia East", + "UK South", + "South Central US", + "East US", + "North Central US", + "West US 2", + "West US 3", + "Southeast Asia", + "Central India", + "Canada Central", + "Central US", + "France Central", + "Japan East", + "Germany West Central", + "South Africa North", + "Korea Central", + "Sweden Central", + "East Asia", + "Switzerland North", + "Brazil South", + "West US", + "Norway East", + "UAE North", + "Australia Southeast", + "Canada East", + "Japan West", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2022-07-01", + "2020-04-01-preview" + ], + "defaultApiVersion": "2020-04-01-preview", + "capabilities": "CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation" + }, + { + "resourceType": "dnsResolvers/outboundEndpoints", + "locations": [ + "West Central US", + "East US 2", + "West Europe", + "North Europe", + "Australia East", + "UK South", + "South Central US", + "East US", + "North Central US", + "West US 2", + "West US 3", + "Southeast Asia", + "Central India", + "Canada Central", + "Central US", + "France Central", + "Japan East", + "Germany West Central", + "South Africa North", + "Korea Central", + "Sweden Central", + "East Asia", + "Switzerland North", + "Brazil South", + "West US", + "Norway East", + "UAE North", + "Australia Southeast", + "Canada East", + "Japan West", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2022-07-01", + "2020-04-01-preview" + ], + "defaultApiVersion": "2020-04-01-preview", + "capabilities": "CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation" + }, + { + "resourceType": "dnsForwardingRulesets", + "locations": [ + "West Central US", + "East US 2", + "West Europe", + "North Europe", + "Australia East", + "UK South", + "South Central US", + "East US", + "North Central US", + "West US 2", + "West US 3", + "Southeast Asia", + "Central India", + "Canada Central", + "Central US", + "France Central", + "Japan East", + "Germany West Central", + "South Africa North", + "Korea Central", + "Sweden Central", + "East Asia", + "Switzerland North", + "Brazil South", + "West US", + "Norway East", + "UAE North", + "Australia Southeast", + "Canada East", + "Japan West", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2022-07-01", + "2020-04-01-preview" + ], + "defaultApiVersion": "2020-04-01-preview", + "capabilities": "CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation" + }, + { + "resourceType": "dnsForwardingRulesets/forwardingRules", + "locations": [ + "West Central US", + "East US 2", + "West Europe", + "North Europe", + "Australia East", + "UK South", + "South Central US", + "East US", + "North Central US", + "West US 2", + "West US 3", + "Southeast Asia", + "Central India", + "Canada Central", + "Central US", + "France Central", + "Japan East", + "Germany West Central", + "South Africa North", + "Korea Central", + "Sweden Central", + "East Asia", + "Switzerland North", + "Brazil South", + "West US", + "Norway East", + "UAE North", + "Australia Southeast", + "Canada East", + "Japan West", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2022-07-01", + "2020-04-01-preview" + ], + "defaultApiVersion": "2020-04-01-preview", + "capabilities": "None" + }, + { + "resourceType": "dnsForwardingRulesets/virtualNetworkLinks", + "locations": [ + "West Central US", + "East US 2", + "West Europe", + "North Europe", + "Australia East", + "UK South", + "South Central US", + "East US", + "North Central US", + "West US 2", + "West US 3", + "Southeast Asia", + "Central India", + "Canada Central", + "Central US", + "France Central", + "Japan East", + "Germany West Central", + "South Africa North", + "Korea Central", + "Sweden Central", + "East Asia", + "Switzerland North", + "Brazil South", + "West US", + "Norway East", + "UAE North", + "Australia Southeast", + "Canada East", + "Japan West", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2022-07-01", + "2020-04-01-preview" + ], + "defaultApiVersion": "2020-04-01-preview", + "capabilities": "None" + }, + { + "resourceType": "virtualNetworks/listDnsResolvers", + "locations": [ + "West Central US", + "East US 2", + "West Europe", + "North Europe", + "Australia East", + "UK South", + "South Central US", + "East US", + "North Central US", + "West US 2", + "West US 3", + "Southeast Asia", + "Central India", + "Canada Central", + "Central US", + "France Central", + "Japan East", + "Germany West Central", + "South Africa North", + "Korea Central", + "Sweden Central", + "East Asia", + "Switzerland North", + "Brazil South", + "West US", + "Norway East", + "UAE North", + "Australia Southeast", + "Canada East", + "Japan West", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2022-07-01", + "2020-04-01-preview" + ], + "defaultApiVersion": "2020-04-01-preview", + "capabilities": "None" + }, + { + "resourceType": "virtualNetworks/listDnsForwardingRulesets", + "locations": [ + "West Central US", + "East US 2", + "West Europe", + "North Europe", + "Australia East", + "UK South", + "South Central US", + "East US", + "North Central US", + "West US 2", + "West US 3", + "Southeast Asia", + "Central India", + "Canada Central", + "Central US", + "France Central", + "Japan East", + "Germany West Central", + "South Africa North", + "Korea Central", + "Sweden Central", + "East Asia", + "Switzerland North", + "Brazil South", + "West US", + "Norway East", + "UAE North", + "Australia Southeast", + "Canada East", + "Japan West", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2022-07-01", + "2020-04-01-preview" + ], + "defaultApiVersion": "2020-04-01-preview", + "capabilities": "None" + }, + { + "resourceType": "locations/dnsResolverOperationResults", + "locations": [], + "apiVersions": [ + "2022-07-01", + "2020-04-01-preview" + ], + "defaultApiVersion": "2020-04-01-preview", + "capabilities": "None" + }, + { + "resourceType": "locations/dnsResolverOperationStatuses", + "locations": [], + "apiVersions": [ + "2022-07-01", + "2020-04-01-preview" + ], + "defaultApiVersion": "2020-04-01-preview", + "capabilities": "None" + }, + { + "resourceType": "trafficmanagerprofiles", + "locations": [ + "global" + ], + "apiVersions": [ + "2022-12-01-preview", + "2022-04-01-preview", + "2018-08-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2017-05-01", + "2017-03-01", + "2015-11-01", + "2015-04-28-preview" + ], + "defaultApiVersion": "2018-08-01", + "capabilities": "CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation" + }, + { + "resourceType": "trafficmanagerprofiles/heatMaps", + "locations": [ + "global" + ], + "apiVersions": [ + "2022-04-01-preview", + "2018-08-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2017-09-01-preview" + ], + "defaultApiVersion": "2018-08-01", + "capabilities": "None" + }, + { + "resourceType": "trafficmanagerprofiles/azureendpoints", + "locations": [ + "global" + ], + "apiVersions": [ + "2022-12-01-preview", + "2022-04-01-preview", + "2018-08-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2017-05-01", + "2017-03-01", + "2015-11-01", + "2015-04-28-preview" + ], + "defaultApiVersion": "2018-08-01", + "capabilities": "None" + }, + { + "resourceType": "trafficmanagerprofiles/externalendpoints", + "locations": [ + "global" + ], + "apiVersions": [ + "2022-12-01-preview", + "2022-04-01-preview", + "2018-08-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2017-05-01", + "2017-03-01", + "2015-11-01", + "2015-04-28-preview" + ], + "defaultApiVersion": "2018-08-01", + "capabilities": "None" + }, + { + "resourceType": "trafficmanagerprofiles/nestedendpoints", + "locations": [ + "global" + ], + "apiVersions": [ + "2022-12-01-preview", + "2022-04-01-preview", + "2018-08-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2017-05-01", + "2017-03-01", + "2015-11-01", + "2015-04-28-preview" + ], + "defaultApiVersion": "2018-08-01", + "capabilities": "None" + }, + { + "resourceType": "checkTrafficManagerNameAvailability", + "locations": [ + "global" + ], + "apiVersions": [ + "2022-12-01-preview", + "2022-04-01-preview", + "2018-08-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2017-05-01", + "2017-03-01", + "2015-11-01", + "2015-04-28-preview" + ], + "defaultApiVersion": "2018-08-01", + "capabilities": "None" + }, + { + "resourceType": "checkTrafficManagerNameAvailabilityV2", + "locations": [ + "global" + ], + "apiVersions": [ + "2022-12-01-preview" + ], + "defaultApiVersion": "2022-12-01-preview", + "capabilities": "None" + }, + { + "resourceType": "trafficManagerUserMetricsKeys", + "locations": [ + "global" + ], + "apiVersions": [ + "2022-04-01-preview", + "2018-08-01", + "2018-04-01", + "2017-09-01-preview" + ], + "defaultApiVersion": "2018-08-01", + "capabilities": "None" + }, + { + "resourceType": "trafficManagerGeographicHierarchies", + "locations": [ + "global" + ], + "apiVersions": [ + "2022-12-01-preview", + "2022-04-01-preview", + "2018-08-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2017-05-01", + "2017-03-01" + ], + "defaultApiVersion": "2018-08-01", + "capabilities": "None" + }, + { + "resourceType": "expressRouteCircuits", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2018-01-01", + "2017-11-01", + "2017-10-01", + "2017-09-01", + "2017-08-01", + "2017-06-01", + "2017-04-01", + "2017-03-01", + "2016-12-01", + "2016-11-01", + "2016-10-01", + "2016-09-01", + "2016-08-01", + "2016-07-01", + "2016-06-01", + "2016-03-30", + "2015-06-15", + "2015-05-01-preview", + "2014-12-01-preview" + ], + "defaultApiVersion": "2020-03-01", + "capabilities": "SupportsTags, SupportsLocation" + }, + { + "resourceType": "expressRouteServiceProviders", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2018-01-01", + "2017-11-01", + "2017-10-01", + "2017-09-01", + "2017-08-01", + "2017-06-01", + "2017-04-01", + "2017-03-01", + "2016-12-01", + "2016-11-01", + "2016-10-01", + "2016-09-01", + "2016-08-01", + "2016-07-01", + "2016-06-01", + "2016-03-30", + "2015-06-15", + "2015-05-01-preview", + "2014-12-01-preview" + ], + "capabilities": "None" + }, + { + "resourceType": "applicationGatewayAvailableWafRuleSets", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2018-01-01", + "2017-11-01", + "2017-10-01", + "2017-09-01", + "2017-08-01", + "2017-06-01", + "2017-04-01", + "2017-03-01" + ], + "capabilities": "None" + }, + { + "resourceType": "applicationGatewayAvailableSslOptions", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2018-01-01", + "2017-11-01", + "2017-10-01", + "2017-09-01", + "2017-08-01", + "2017-06-01" + ], + "capabilities": "None" + }, + { + "resourceType": "applicationGatewayAvailableServerVariables", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01" + ], + "capabilities": "None" + }, + { + "resourceType": "applicationGatewayAvailableRequestHeaders", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01" + ], + "capabilities": "None" + }, + { + "resourceType": "applicationGatewayAvailableResponseHeaders", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01" + ], + "capabilities": "None" + }, + { + "resourceType": "routeFilters", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2018-01-01", + "2017-11-01", + "2017-10-01", + "2017-09-01", + "2017-08-01", + "2017-06-01", + "2017-04-01", + "2017-03-01", + "2016-12-01", + "2016-11-01", + "2016-10-01", + "2016-09-01", + "2016-08-01", + "2016-07-01", + "2016-06-01", + "2016-03-30", + "2015-06-15", + "2015-05-01-preview", + "2014-12-01-preview" + ], + "defaultApiVersion": "2020-03-01", + "capabilities": "SupportsTags, SupportsLocation" + }, + { + "resourceType": "bgpServiceCommunities", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2018-01-01", + "2017-11-01", + "2017-10-01", + "2017-09-01", + "2017-08-01", + "2017-06-01", + "2017-04-01", + "2017-03-01", + "2016-12-01" + ], + "capabilities": "None" + }, + { + "resourceType": "virtualWans", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2018-01-01", + "2017-11-01", + "2017-10-01", + "2017-09-01" + ], + "defaultApiVersion": "2020-03-01", + "capabilities": "SupportsTags, SupportsLocation" + }, + { + "resourceType": "vpnSites", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2018-01-01", + "2017-11-01", + "2017-10-01", + "2017-09-01" + ], + "defaultApiVersion": "2020-03-01", + "capabilities": "SupportsTags, SupportsLocation" + }, + { + "resourceType": "vpnServerConfigurations", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "South Africa North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01" + ], + "defaultApiVersion": "2020-03-01", + "capabilities": "SupportsTags, SupportsLocation" + }, + { + "resourceType": "virtualHubs", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2018-01-01", + "2017-11-01", + "2017-10-01", + "2017-09-01" + ], + "defaultApiVersion": "2020-03-01", + "capabilities": "SupportsTags, SupportsLocation" + }, + { + "resourceType": "vpnGateways", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2018-01-01", + "2017-11-01", + "2017-10-01", + "2017-09-01" + ], + "defaultApiVersion": "2020-03-01", + "capabilities": "SupportsTags, SupportsLocation" + }, + { + "resourceType": "p2sVpnGateways", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "UAE North", + "South Africa North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01" + ], + "defaultApiVersion": "2020-03-01", + "capabilities": "SupportsTags, SupportsLocation" + }, + { + "resourceType": "expressRouteGateways", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01" + ], + "defaultApiVersion": "2020-03-01", + "capabilities": "SupportsTags, SupportsLocation" + }, + { + "resourceType": "locations/hybridEdgeZone", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01" + ], + "capabilities": "None" + }, + { + "resourceType": "expressRoutePortsLocations", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01" + ], + "capabilities": "None" + }, + { + "resourceType": "expressRoutePorts", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "UAE North", + "South Africa North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01" + ], + "defaultApiVersion": "2020-03-01", + "capabilities": "SupportsTags, SupportsLocation" + }, + { + "resourceType": "firewallPolicies", + "locations": [ + "Qatar Central", + "UAE North", + "Australia Central 2", + "UAE Central", + "Germany North", + "Central India", + "Korea South", + "Switzerland North", + "Switzerland West", + "Japan West", + "France South", + "South Africa West", + "West India", + "Canada East", + "South India", + "Germany West Central", + "Norway East", + "Norway West", + "South Africa North", + "East Asia", + "Southeast Asia", + "Korea Central", + "Brazil South", + "Brazil Southeast", + "West US 3", + "Jio India West", + "Sweden Central", + "Japan East", + "UK West", + "West US", + "East US", + "North Europe", + "West Europe", + "West Central US", + "South Central US", + "Australia East", + "Australia Central", + "Australia Southeast", + "UK South", + "East US 2", + "West US 2", + "North Central US", + "Canada Central", + "France Central", + "Central US", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01" + ], + "defaultApiVersion": "2020-04-01", + "capabilities": "SupportsTags, SupportsLocation" + }, + { + "resourceType": "ipGroups", + "locations": [ + "Qatar Central", + "UAE North", + "Australia Central 2", + "UAE Central", + "Germany North", + "Central India", + "Korea South", + "Switzerland North", + "Switzerland West", + "Japan West", + "France South", + "South Africa West", + "West India", + "Canada East", + "South India", + "Germany West Central", + "Norway East", + "Norway West", + "South Africa North", + "East Asia", + "Southeast Asia", + "Korea Central", + "Brazil South", + "Brazil Southeast", + "West US 3", + "Jio India West", + "Sweden Central", + "Japan East", + "UK West", + "West US", + "East US", + "North Europe", + "West Europe", + "South Central US", + "Australia East", + "Australia Central", + "Australia Southeast", + "UK South", + "East US 2", + "West US 2", + "North Central US", + "Canada Central", + "France Central", + "West Central US", + "Central US", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01" + ], + "defaultApiVersion": "2020-04-01", + "capabilities": "SupportsTags, SupportsLocation" + }, + { + "resourceType": "azureWebCategories", + "locations": [], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01" + ], + "defaultApiVersion": "2020-08-01", + "capabilities": "None" + }, + { + "resourceType": "locations/nfvOperations", + "locations": [], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2018-01-01", + "2017-11-01", + "2017-10-01", + "2017-09-01" + ], + "capabilities": "None" + }, + { + "resourceType": "locations/nfvOperationResults", + "locations": [], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2018-01-01", + "2017-11-01", + "2017-10-01", + "2017-09-01" + ], + "capabilities": "None" + }, + { + "resourceType": "securityPartnerProviders", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01" + ], + "defaultApiVersion": "2020-03-01", + "capabilities": "SupportsTags, SupportsLocation" + }, + { + "resourceType": "azureFirewalls", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "France Central", + "Australia Central", + "Japan West", + "Japan East", + "Korea Central", + "Korea South", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01" + ], + "defaultApiVersion": "2020-03-01", + "zoneMappings": [ + { + "location": "Australia East", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Brazil South", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Canada Central", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Central India", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Central US", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Central US EUAP", + "zones": [ + "1", + "2" + ] + }, + { + "location": "East Asia", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "East US", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "East US 2", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "East US 2 EUAP", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "France Central", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Germany West Central", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Japan East", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Korea Central", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "North Central US", + "zones": [] + }, + { + "location": "North Europe", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Norway East", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Qatar Central", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "South Africa North", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "South Central US", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Southeast Asia", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Sweden Central", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Switzerland North", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "UAE North", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "UK South", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "West Europe", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "West US", + "zones": [] + }, + { + "location": "West US 2", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "West US 3", + "zones": [ + "2", + "1", + "3" + ] + }, + { + "location": "Poland Central", + "zones": [ + "2", + "1", + "3" + ] + } + ], + "capabilities": "SupportsTags, SupportsLocation" + }, + { + "resourceType": "azureFirewallFqdnTags", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01" + ], + "capabilities": "None" + }, + { + "resourceType": "virtualNetworkTaps", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01" + ], + "defaultApiVersion": "2020-03-01", + "capabilities": "SupportsTags, SupportsLocation" + }, + { + "resourceType": "privateLinkServices", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01" + ], + "defaultApiVersion": "2020-03-01", + "locationMappings": [ + { + "location": "East US 2 EUAP", + "type": "EdgeZone", + "extendedLocations": [ + "microsoftrrdclab1", + "microsoftrrdclab3" + ] + }, + { + "location": "West US", + "type": "EdgeZone", + "extendedLocations": [ + "microsoftlosangeles1" + ] + }, + { + "location": "East US 2", + "type": "EdgeZone", + "extendedLocations": [ + "microsoftmiami1" + ] + } + ], + "capabilities": "SupportsTags, SupportsLocation" + }, + { + "resourceType": "locations/privateLinkServices", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01" + ], + "capabilities": "None" + }, + { + "resourceType": "ddosProtectionPlans", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01", + "2018-03-01", + "2018-02-01" + ], + "defaultApiVersion": "2020-03-01", + "apiProfiles": [ + { + "profileVersion": "2017-03-09-profile", + "apiVersion": "2018-02-01" + } + ], + "capabilities": "SupportsTags, SupportsLocation" + }, + { + "resourceType": "networkProfiles", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01" + ], + "defaultApiVersion": "2020-03-01", + "capabilities": "SupportsTags, SupportsLocation" + }, + { + "resourceType": "checkFrontdoorNameAvailability", + "locations": [ + "global", + "Central US", + "East US", + "East US 2", + "North Central US", + "South Central US", + "West US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast" + ], + "apiVersions": [ + "2021-06-01", + "2020-07-01", + "2020-05-01", + "2020-01-01", + "2019-08-01", + "2019-05-01", + "2019-04-01", + "2018-08-01" + ], + "defaultApiVersion": "2020-07-01", + "capabilities": "None" + }, + { + "resourceType": "frontdoorWebApplicationFirewallManagedRuleSets", + "locations": [ + "global", + "Central US", + "East US", + "East US 2", + "North Central US", + "South Central US", + "West US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast" + ], + "apiVersions": [ + "2022-05-01", + "2020-11-01", + "2020-04-01", + "2019-10-01", + "2019-03-01" + ], + "defaultApiVersion": "2020-11-01", + "capabilities": "None" + }, + { + "resourceType": "locations/bareMetalTenants", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01" + ], + "capabilities": "None" + }, + { + "resourceType": "bastionHosts", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01" + ], + "defaultApiVersion": "2020-03-01", + "capabilities": "CrossResourceGroupResourceMove, SupportsTags, SupportsLocation" + }, + { + "resourceType": "virtualRouters", + "locations": [ + "Qatar Central", + "UAE North", + "Australia Central 2", + "UAE Central", + "Germany North", + "Central India", + "Korea South", + "Switzerland North", + "Switzerland West", + "Japan West", + "France South", + "South Africa West", + "West India", + "Canada East", + "South India", + "Germany West Central", + "Norway East", + "Norway West", + "South Africa North", + "East Asia", + "Southeast Asia", + "Korea Central", + "Brazil South", + "Brazil Southeast", + "West US 3", + "Jio India West", + "Sweden Central", + "Japan East", + "UK West", + "West US", + "East US", + "North Europe", + "West Europe", + "West Central US", + "South Central US", + "Australia East", + "Australia Central", + "Australia Southeast", + "UK South", + "East US 2", + "West US 2", + "North Central US", + "Canada Central", + "France Central", + "Central US", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01" + ], + "defaultApiVersion": "2020-04-01", + "capabilities": "SupportsTags, SupportsLocation" + }, + { + "resourceType": "networkVirtualAppliances", + "locations": [ + "Qatar Central", + "Brazil Southeast", + "West US 3", + "Jio India West", + "Sweden Central", + "UAE North", + "Australia Central 2", + "UAE Central", + "Germany North", + "Central India", + "Korea South", + "Switzerland North", + "Switzerland West", + "Japan West", + "France South", + "South Africa West", + "West India", + "Canada East", + "South India", + "Germany West Central", + "Norway East", + "Norway West", + "South Africa North", + "East Asia", + "Southeast Asia", + "Korea Central", + "Brazil South", + "Japan East", + "UK West", + "West US", + "East US", + "North Europe", + "West Europe", + "West Central US", + "South Central US", + "Australia East", + "Australia Central", + "Australia Southeast", + "UK South", + "East US 2", + "West US 2", + "North Central US", + "Canada Central", + "France Central", + "Central US", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01" + ], + "defaultApiVersion": "2020-04-01", + "capabilities": "SystemAssignedResourceIdentity, SupportsTags, SupportsLocation" + }, + { + "resourceType": "ipAllocations", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01" + ], + "defaultApiVersion": "2020-03-01", + "capabilities": "CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation" + }, + { + "resourceType": "networkManagers", + "locations": [ + "West Central US", + "North Central US", + "West US", + "West Europe", + "UAE Central", + "Germany North", + "East US", + "West India", + "East US 2", + "Australia Central", + "Australia Central 2", + "South Africa West", + "Brazil South", + "UK West", + "North Europe", + "Central US", + "UAE North", + "Germany West Central", + "Switzerland West", + "East Asia", + "Jio India West", + "South Africa North", + "UK South", + "South India", + "Australia Southeast", + "France South", + "West US 2", + "Sweden Central", + "Japan West", + "Norway East", + "France Central", + "West US 3", + "Central India", + "Korea South", + "Brazil Southeast", + "Korea Central", + "Southeast Asia", + "South Central US", + "Norway West", + "Australia East", + "Japan East", + "Canada East", + "Canada Central", + "Switzerland North", + "Qatar Central", + "Poland Central", + "East US 2 EUAP", + "Central US EUAP" + ], + "apiVersions": [ + "2023-03-01-preview", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-06-01-preview", + "2022-05-01", + "2022-04-01-preview", + "2022-01-01" + ], + "defaultApiVersion": "2022-05-01", + "capabilities": "CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation" + }, + { + "resourceType": "networkManagerConnections", + "locations": [], + "apiVersions": [ + "2023-03-01-preview", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-06-01-preview", + "2022-05-01", + "2022-04-01-preview", + "2022-01-01" + ], + "defaultApiVersion": "2022-05-01", + "capabilities": "SupportsExtension" + }, + { + "resourceType": "locations/queryNetworkSecurityPerimeter", + "locations": [ + "West Central US", + "Jio India West", + "North Central US", + "West US", + "West Europe", + "UAE Central", + "Germany North", + "East US", + "West India", + "East US 2", + "Australia Central", + "Australia Central 2", + "South Africa West", + "Brazil South", + "UK West", + "North Europe", + "Central US", + "UAE North", + "Germany West Central", + "Switzerland West", + "East Asia", + "South Africa North", + "UK South", + "South India", + "Australia Southeast", + "France South", + "West US 2", + "Sweden Central", + "Japan West", + "Norway East", + "France Central", + "West US 3", + "Central India", + "Korea South", + "Brazil Southeast", + "Korea Central", + "Southeast Asia", + "South Central US", + "Norway West", + "Australia East", + "Japan East", + "Canada East", + "Canada Central", + "Switzerland North", + "Qatar Central", + "Poland Central", + "East US 2 EUAP", + "Central US EUAP" + ], + "apiVersions": [ + "2022-02-01-preview", + "2021-05-01-preview", + "2021-02-01-preview" + ], + "defaultApiVersion": "2021-02-01-preview", + "capabilities": "None" + }, + { + "resourceType": "virtualNetworks/listNetworkManagerEffectiveConnectivityConfigurations", + "locations": [ + "West Central US", + "North Central US", + "West US", + "West Europe", + "UAE Central", + "Germany North", + "East US", + "West India", + "East US 2", + "Australia Central", + "Australia Central 2", + "South Africa West", + "Brazil South", + "UK West", + "North Europe", + "Central US", + "UAE North", + "Germany West Central", + "Switzerland West", + "East Asia", + "Jio India West", + "South Africa North", + "UK South", + "South India", + "Australia Southeast", + "France South", + "West US 2", + "Sweden Central", + "Japan West", + "Norway East", + "France Central", + "West US 3", + "Central India", + "Korea South", + "Brazil Southeast", + "Korea Central", + "Southeast Asia", + "South Central US", + "Norway West", + "Australia East", + "Japan East", + "Canada East", + "Canada Central", + "Switzerland North", + "Qatar Central", + "Poland Central", + "East US 2 EUAP", + "Central US EUAP" + ], + "apiVersions": [ + "2023-03-01-preview", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-06-01-preview", + "2022-05-01", + "2022-04-01-preview", + "2022-01-01" + ], + "defaultApiVersion": "2022-05-01", + "capabilities": "None" + }, + { + "resourceType": "virtualNetworks/listNetworkManagerEffectiveSecurityAdminRules", + "locations": [ + "West Central US", + "North Central US", + "West US", + "West Europe", + "UAE Central", + "Germany North", + "East US", + "West India", + "East US 2", + "Australia Central", + "Australia Central 2", + "South Africa West", + "Brazil South", + "UK West", + "North Europe", + "Central US", + "UAE North", + "Germany West Central", + "Switzerland West", + "East Asia", + "Jio India West", + "South Africa North", + "UK South", + "South India", + "Australia Southeast", + "France South", + "West US 2", + "Sweden Central", + "Japan West", + "Norway East", + "France Central", + "West US 3", + "Central India", + "Korea South", + "Brazil Southeast", + "Korea Central", + "Southeast Asia", + "South Central US", + "Norway West", + "Australia East", + "Japan East", + "Canada East", + "Canada Central", + "Switzerland North", + "Qatar Central", + "Poland Central", + "East US 2 EUAP", + "Central US EUAP" + ], + "apiVersions": [ + "2023-03-01-preview", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-06-01-preview", + "2022-05-01", + "2022-04-01-preview", + "2022-01-01" + ], + "defaultApiVersion": "2022-05-01", + "capabilities": "None" + }, + { + "resourceType": "networkGroupMemberships", + "locations": [ + "West Central US", + "North Central US", + "West US", + "West Europe", + "UAE Central", + "Germany North", + "East US", + "West India", + "East US 2", + "Australia Central", + "Australia Central 2", + "South Africa West", + "Brazil South", + "UK West", + "North Europe", + "Central US", + "UAE North", + "Germany West Central", + "Switzerland West", + "East Asia", + "Jio India West", + "South Africa North", + "UK South", + "South India", + "Australia Southeast", + "France South", + "West US 2", + "Sweden Central", + "Japan West", + "Norway East", + "France Central", + "West US 3", + "Central India", + "Korea South", + "Brazil Southeast", + "Korea Central", + "Southeast Asia", + "South Central US", + "Norway West", + "Australia East", + "Japan East", + "Canada East", + "Canada Central", + "Switzerland North", + "Qatar Central", + "Poland Central", + "East US 2 EUAP", + "Central US EUAP" + ], + "apiVersions": [ + "2022-06-01-preview" + ], + "defaultApiVersion": "2022-06-01-preview", + "capabilities": "SupportsExtension" + }, + { + "resourceType": "locations/commitInternalAzureNetworkManagerConfiguration", + "locations": [ + "West Central US", + "North Central US", + "West US", + "West Europe", + "UAE Central", + "Germany North", + "East US", + "West India", + "East US 2", + "Australia Central", + "Australia Central 2", + "South Africa West", + "Brazil South", + "UK West", + "North Europe", + "Central US", + "UAE North", + "Germany West Central", + "Switzerland West", + "East Asia", + "Jio India West", + "South Africa North", + "UK South", + "South India", + "Australia Southeast", + "France South", + "West US 2", + "Sweden Central", + "Japan West", + "Norway East", + "France Central", + "West US 3", + "Central India", + "Korea South", + "Brazil Southeast", + "Korea Central", + "Southeast Asia", + "South Central US", + "Norway West", + "Australia East", + "Japan East", + "Canada East", + "Canada Central", + "Switzerland North", + "Qatar Central", + "Poland Central", + "East US 2 EUAP", + "Central US EUAP" + ], + "apiVersions": [ + "2023-03-01-preview", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-06-01-preview", + "2022-05-01", + "2022-04-01-preview", + "2022-01-01" + ], + "capabilities": "None" + }, + { + "resourceType": "locations/internalAzureVirtualNetworkManagerOperation", + "locations": [ + "West Central US", + "North Central US", + "West US", + "West Europe", + "UAE Central", + "Germany North", + "East US", + "West India", + "East US 2", + "Australia Central", + "Australia Central 2", + "South Africa West", + "Brazil South", + "UK West", + "North Europe", + "Central US", + "UAE North", + "Germany West Central", + "Switzerland West", + "East Asia", + "Jio India West", + "South Africa North", + "UK South", + "South India", + "Australia Southeast", + "France South", + "West US 2", + "Sweden Central", + "Japan West", + "Norway East", + "France Central", + "West US 3", + "Central India", + "Korea South", + "Brazil Southeast", + "Korea Central", + "Southeast Asia", + "South Central US", + "Norway West", + "Australia East", + "Japan East", + "Canada East", + "Canada Central", + "Switzerland North", + "Qatar Central", + "Poland Central", + "East US 2 EUAP", + "Central US EUAP" + ], + "apiVersions": [ + "2023-03-01-preview", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-06-01-preview", + "2022-05-01", + "2022-04-01-preview", + "2022-01-01" + ], + "capabilities": "None" + }, + { + "resourceType": "networkVirtualApplianceSkus", + "locations": [], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01" + ], + "defaultApiVersion": "2020-04-01", + "capabilities": "None" + }, + { + "resourceType": "locations/serviceTagDetails", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01" + ], + "capabilities": "None" + }, + { + "resourceType": "locations/dataTasks", + "locations": [ + "West US", + "East US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "North Central US", + "South Central US", + "Central US", + "East US 2", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast", + "Central India", + "South India", + "West India", + "Canada Central", + "Canada East", + "West Central US", + "West US 2", + "UK West", + "UK South", + "Korea Central", + "Korea South", + "France Central", + "Australia Central", + "South Africa North", + "UAE North", + "Switzerland North", + "Germany West Central", + "Norway East", + "West US 3", + "Jio India West", + "Sweden Central", + "Qatar Central", + "Poland Central", + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01" + ], + "capabilities": "None" + }, + { + "resourceType": "networkWatchers/lenses", + "locations": [ + "Central US EUAP", + "East US 2 EUAP" + ], + "apiVersions": [ + "2023-02-01", + "2022-11-01", + "2022-09-01", + "2022-07-01", + "2022-05-01", + "2022-01-01", + "2021-12-01", + "2021-08-01", + "2021-06-01", + "2021-05-01", + "2021-04-01", + "2021-03-01", + "2021-02-01", + "2021-01-01", + "2020-11-01", + "2020-08-01", + "2020-07-01", + "2020-06-01", + "2020-05-01", + "2020-04-01", + "2020-03-01", + "2020-01-01", + "2019-12-01", + "2019-11-01", + "2019-09-01", + "2019-08-01", + "2019-07-01", + "2019-06-01", + "2019-04-01", + "2019-02-01", + "2018-12-01", + "2018-11-01", + "2018-10-01", + "2018-08-01", + "2018-07-01", + "2018-06-01", + "2018-05-01", + "2018-04-01", + "2018-03-01", + "2018-02-01", + "2018-01-01", + "2017-11-01", + "2017-10-01", + "2017-09-01" + ], + "defaultApiVersion": "2020-03-01", + "capabilities": "CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation" + }, + { + "resourceType": "frontdoorOperationResults", + "locations": [ + "global" + ], + "apiVersions": [ + "2022-05-01", + "2021-06-01", + "2020-11-01", + "2020-07-01", + "2020-05-01", + "2020-04-01", + "2020-01-01", + "2019-11-01", + "2019-10-01", + "2019-08-01", + "2019-05-01", + "2019-04-01", + "2019-03-01", + "2018-08-01" + ], + "defaultApiVersion": "2020-07-01", + "capabilities": "None" + }, + { + "resourceType": "frontdoors", + "locations": [ + "Central US EUAP", + "East US 2 EUAP", + "global", + "Central US", + "East US", + "East US 2", + "North Central US", + "South Central US", + "West US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast" + ], + "apiVersions": [ + "2021-06-01", + "2020-07-01", + "2020-05-01", + "2020-04-01", + "2020-01-01", + "2019-08-01", + "2019-05-01", + "2019-04-01", + "2018-08-01" + ], + "defaultApiVersion": "2020-07-01", + "capabilities": "SupportsTags, SupportsLocation" + }, + { + "resourceType": "frontdoors/frontendEndpoints", + "locations": [ + "Central US EUAP", + "East US 2 EUAP", + "global", + "Central US", + "East US", + "East US 2", + "North Central US", + "South Central US", + "West US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast" + ], + "apiVersions": [ + "2021-06-01", + "2020-07-01", + "2020-05-01", + "2020-04-01", + "2020-01-01", + "2019-08-01", + "2019-05-01", + "2019-04-01", + "2018-08-01" + ], + "defaultApiVersion": "2020-07-01", + "capabilities": "None" + }, + { + "resourceType": "frontdoors/frontendEndpoints/customHttpsConfiguration", + "locations": [ + "Central US EUAP", + "East US 2 EUAP", + "global", + "Central US", + "East US", + "East US 2", + "North Central US", + "South Central US", + "West US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast" + ], + "apiVersions": [ + "2021-06-01", + "2020-07-01", + "2020-05-01", + "2020-04-01", + "2020-01-01", + "2019-08-01", + "2019-05-01", + "2019-04-01", + "2018-08-01" + ], + "defaultApiVersion": "2020-07-01", + "capabilities": "None" + }, + { + "resourceType": "frontdoorWebApplicationFirewallPolicies", + "locations": [ + "East US 2 EUAP", + "global", + "Central US", + "East US", + "East US 2", + "North Central US", + "South Central US", + "West US", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast" + ], + "apiVersions": [ + "2022-05-01", + "2020-11-01", + "2020-04-01", + "2019-10-01", + "2019-03-01", + "2018-08-01" + ], + "defaultApiVersion": "2020-11-01", + "capabilities": "SupportsTags, SupportsLocation" + }, + { + "resourceType": "networkExperimentProfiles", + "locations": [ + "Central US EUAP", + "East US 2 EUAP", + "global", + "Central US", + "East US", + "East US 2", + "North Central US", + "South Central US", + "West US", + "West US 2", + "North Europe", + "West Europe", + "East Asia", + "Southeast Asia", + "Japan East", + "Japan West", + "Brazil South", + "Australia East", + "Australia Southeast" + ], + "apiVersions": [ + "2019-11-01" + ], + "defaultApiVersion": "2019-11-01", + "capabilities": "SupportsTags, SupportsLocation" + } + ], + "registrationState": "Registered", + "registrationPolicy": "RegistrationRequired" +} diff --git a/sdk/core/Azure.Core/samples/Serialization.md b/sdk/core/Azure.Core/samples/Serialization.md new file mode 100644 index 000000000000..1a4bfd4909cd --- /dev/null +++ b/sdk/core/Azure.Core/samples/Serialization.md @@ -0,0 +1,164 @@ +# Azure.Core public serialization samples + +## Using explicit cast + +When using protocol methods for advanced handling of RequestContext it is still possible to use the strongly typed models. +There is an explicit cast operator that can be used to convert the protocol Response to the strongly typed model. +There is also an explicit cast operator that can be used to convert the strongly typed model to the protocol RequestContent. + +### Serialization + +```C# Snippet:ExplicitCast_Serialize +PetStoreClient client = new PetStoreClient(new Uri("http://somewhere.com"), new MockCredential()); +DogListProperty dog = new DogListProperty("myPet"); +Response response = client.CreatePet("myPet", (RequestContent)dog); +var response2 = client.CreatePet("myPet", RequestContent.Create(dog)); +``` + +### Deserialization + +```C# Snippet:ExplicitCast_Deserialize +PetStoreClient client = new PetStoreClient(new Uri("http://somewhere.com"), new MockCredential()); +Response response = client.GetPet("myPet"); +DogListProperty dog = (DogListProperty)response; +Console.WriteLine(dog.IsHungry); +``` + +Given that explicit cast does not allow for serialization options we might also consider a static `FromResponse` and instance `ToRequestContent` methods. + +## Using ModelSerializer for NewtonSoftJson + +By using the ModelSerializer class, a new instance of Dog does not need to be created before calling Deserialize. Also added ObjectSerializer to Options class so different kinds of Serializers can be used. + +### Serialization + +```C# Snippet:NewtonSoft_Serialize +DogListProperty dog = new DogListProperty +{ + Name = "Doggo", + IsHungry = true, + Weight = 1.1, + FoodConsumed = { "kibble", "egg", "peanut butter" }, +}; +ModelSerializerOptions options = new ModelSerializerOptions(); +options.Serializers.Add(typeof(DogListProperty), new NewtonsoftJsonObjectSerializer()); + +BinaryData data = ModelSerializer.Serialize(dog, options); +``` + +### Deserialization + +```C# Snippet:NewtonSoft_Deserialize +ModelSerializerOptions options = new ModelSerializerOptions(); +options.Serializers.Add(typeof(DogListProperty), new NewtonsoftJsonObjectSerializer()); +string json = @"[{""LatinName"":""Animalia"",""Weight"":1.1,""Name"":""Doggo"",""IsHungry"":false,""FoodConsumed"":[""kibble"",""egg"",""peanut butter""],""NumberOfLegs"":4}]"; + +DogListProperty dog = ModelSerializer.Deserialize(BinaryData.FromString(json), options); +``` + +## Using ModelJsonConverter for JsonSerializer + +In order to better integrate with the rest of the .NET ecosystem, Azure.Core supports System.Text.Json serialization. The following example demonstrates using System.Text.Json for serialization and deserialization +If we go this route the IJsonSerializable interface will only be needed for compile time constraints and can most likely be methodless and renamed to IRehydratable. + +One limitation if we go this route is there isn't a clear place to pass in a flag to include additional properties during serialization and deserialization. + +By using the ModelJsonConverter class we can have a place to add additional properties to the JsonSerializerOptions. +This will allow us to add things like `IgnoreAdditionalProperties` and `Version` to the options without needing to have our own ModelSerializer. +The `SerializableOptions` would become internal and we would have a converter to convert from `JsonSerializerOptions` + `ModelJsonConverter` to `SerializableOptions`. + +### Serialization + +```C# Snippet:ModelConverter_Serialize +DogListProperty dog = new DogListProperty +{ + Name = "Doggo", + IsHungry = true, + Weight = 1.1, + FoodConsumed = { "kibble", "egg", "peanut butter" }, +}; + +JsonSerializerOptions options = new JsonSerializerOptions(); +options.Converters.Add(new ModelJsonConverter()); + +string json = System.Text.Json.JsonSerializer.Serialize(dog, options); +``` + +### Deserialization + +```C# Snippet:ModelConverter_Deserialize +string json = @"[{""LatinName"":""Animalia"",""Weight"":1.1,""Name"":""Doggo"",""IsHungry"":false,""FoodConsumed"":[""kibble"",""egg"",""peanut butter""],""NumberOfLegs"":4}]"; + +JsonSerializerOptions options = new JsonSerializerOptions(); +options.Converters.Add(new ModelJsonConverter()); + +DogListProperty dog = System.Text.Json.JsonSerializer.Deserialize(json, options); +``` + +## Envelope BYOM Case + +The following examples show a use case where a User brings a model unknown to the Serializer. The serialization used for each model can also be set in the SerializableOptions options property Serializers. + +Model Being Used by User +```C# Snippet:Example_Model +private class ModelT +{ + public string Name { get; set; } + public int Age { get; set; } +} +``` + +### Serialization + +```C# Snippet:BYOMWithNewtonsoftSerialize +Envelope envelope = new Envelope(); +envelope.ModelA = new CatReadOnlyProperty(); +envelope.ModelT = new ModelT { Name = "Fluffy", Age = 10 }; +ModelSerializerOptions options = new ModelSerializerOptions(); +options.Serializers.Add(typeof(ModelT), new NewtonsoftJsonObjectSerializer()); +BinaryData data = ModelSerializer.Serialize(envelope, options); +``` + +### Deserialization + +```C# Snippet:BYOMWithNewtonsoftDeserialize +string serviceResponse = + "{\"readOnlyProperty\":\"read\"," + + "\"modelA\":{\"name\":\"Cat\",\"isHungry\":false,\"weight\":2.5}," + + "\"modelT\":{\"Name\":\"hello\",\"Age\":1}" + + "}"; + +ModelSerializerOptions options = new ModelSerializerOptions(); +options.Serializers.Add(typeof(ModelT), new NewtonsoftJsonObjectSerializer()); + +Envelope model = ModelSerializer.Deserialize>(new BinaryData(Encoding.UTF8.GetBytes(serviceResponse)), options: options); +``` + +## Using ModelSerializer with generic IModelSerializable + +In this example we demonstrate how we could use the exact same method / interface to serialize either json or xml. +Above we demonstrate using `SerializeJson` and `SerializeXml` methods but this requires the user to know which serializer to use. +We could delegate needing to know this to each model itself since they for sure need to know how to serialize themselves. It does +introduce a question around what happens if a model could be serialized as both json and xml. + +### Serialization + +```C# Snippet:ModelSerializer_IModelSerializable_Serialize +XmlModelForCombinedInterface xmlModel = new XmlModelForCombinedInterface("Color", "Red", "ReadOnly"); +var data = ModelSerializer.Serialize(xmlModel); +string xmlString = data.ToString(); + +JsonModelForCombinedInterface jsonModel = new JsonModelForCombinedInterface("Color", "Red", "ReadOnly"); +data = ModelSerializer.Serialize(jsonModel); +string jsonString = data.ToString(); +``` + +### Deserialization + +```C# Snippet:ModelSerializer_IModelSerializable_Deserialize +string xmlResponse = "ColorRed"; +XmlModelForCombinedInterface xmlModel = ModelSerializer.Deserialize(new BinaryData(Encoding.UTF8.GetBytes(xmlResponse))); + +string jsonResponse = "{\"key\":\"Color\",\"value\":\"Red\",\"readOnlyProperty\":\"ReadOnly\",\"x\":\"extra\"}"; +JsonModelForCombinedInterface jsonModel = ModelSerializer.Deserialize(new BinaryData(Encoding.UTF8.GetBytes(jsonResponse))); +``` diff --git a/sdk/core/Azure.Core/src/Azure.Core.csproj b/sdk/core/Azure.Core/src/Azure.Core.csproj index 5442dd31e5c2..bf518aa3c947 100644 --- a/sdk/core/Azure.Core/src/Azure.Core.csproj +++ b/sdk/core/Azure.Core/src/Azure.Core.csproj @@ -1,4 +1,4 @@ - + This is the implementation of the Azure Client Pipeline Microsoft Azure Client Pipeline @@ -63,9 +63,21 @@ + + + + + + + + + + + + diff --git a/sdk/core/Azure.Core/src/DynamicData/DynamicData.cs b/sdk/core/Azure.Core/src/DynamicData/DynamicData.cs index 74cd79692c86..62285aca6f7a 100644 --- a/sdk/core/Azure.Core/src/DynamicData/DynamicData.cs +++ b/sdk/core/Azure.Core/src/DynamicData/DynamicData.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System; +using System.Buffers; using System.Collections; using System.Collections.Generic; using System.ComponentModel; @@ -46,6 +47,12 @@ internal void WriteTo(Stream stream) _element.WriteTo(writer); } + internal void WriteTo(Stream stream, StandardFormat format) + { + using Utf8JsonWriter writer = new(stream); + _element.WriteTo(writer, format); + } + private object? GetProperty(string name) { Argument.AssertNotNullOrEmpty(name, nameof(name)); diff --git a/sdk/core/Azure.Core/src/DynamicData/MutableJsonElement.WriteTo.cs b/sdk/core/Azure.Core/src/DynamicData/MutableJsonElement.WriteTo.cs deleted file mode 100644 index 8198122fa043..000000000000 --- a/sdk/core/Azure.Core/src/DynamicData/MutableJsonElement.WriteTo.cs +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text.Json; - -namespace Azure.Core.Json -{ - internal partial struct MutableJsonElement - { - 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) - { - 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 = change.GetSerializedValue(); - highWaterMark = change.Index; - } - - if (Changes.DescendantChanged(path, highWaterMark)) - { - switch (element.ValueKind) - { - case JsonValueKind.Object: - WriteObject(path, highWaterMark, element, writer); - break; - case JsonValueKind.Array: - WriteArray(path, highWaterMark, element, writer); - break; - default: - throw new InvalidOperationException("Element doesn't have descendants."); - } - - return; - } - - element.WriteTo(writer); - } - - private void WriteObject(string path, int highWaterMark, JsonElement element, Utf8JsonWriter writer) - { - writer.WriteStartObject(); - - foreach (JsonProperty property in element.EnumerateObject()) - { - string propertyPath = MutableJsonDocument.ChangeTracker.PushProperty(path, property.Name); - if (!Changes.WasRemoved(propertyPath, highWaterMark)) - { - writer.WritePropertyName(property.Name); - WriteElement(propertyPath, highWaterMark, property.Value, writer); - } - } - - IEnumerable added = Changes.GetAddedProperties(path, highWaterMark); - foreach (MutableJsonChange property in added) - { - string propertyName = property.AddedPropertyName!; - string propertyPath = MutableJsonDocument.ChangeTracker.PushProperty(path, propertyName); - - writer.WritePropertyName(propertyName); - WriteElement(propertyPath, highWaterMark, property.GetSerializedValue(), writer); - } - - writer.WriteEndObject(); - } - - private void WriteArray(string path, int highWaterMark, JsonElement element, Utf8JsonWriter writer) - { - writer.WriteStartArray(); - - int arrayIndex = 0; - foreach (JsonElement arrayElement in element.EnumerateArray()) - { - string arrayElementPath = MutableJsonDocument.ChangeTracker.PushIndex(path, arrayIndex++); - WriteElement(arrayElementPath, highWaterMark, arrayElement, writer); - } - - writer.WriteEndArray(); - } - } -} diff --git a/sdk/core/Azure.Core/src/RequestContent.cs b/sdk/core/Azure.Core/src/RequestContent.cs index fe19517dd70c..48cec64c9116 100644 --- a/sdk/core/Azure.Core/src/RequestContent.cs +++ b/sdk/core/Azure.Core/src/RequestContent.cs @@ -79,6 +79,14 @@ public abstract class RequestContent : IDisposable /// An instance of that wraps a . public static RequestContent Create(DynamicData content) => new DynamicDataContent(content); + /// + /// Generates JSON for JSON Merge Patch updates corresponding to the changes made to the DynamicData instance. + /// + /// + /// + /// + public static RequestContent CreatePatch(DynamicData content) => new DynamicDataContent(content, 'P'); + /// /// Creates an instance of that wraps a serialized version of an object. /// @@ -86,6 +94,13 @@ public abstract class RequestContent : IDisposable /// An instance of that wraps a serialized version of the object. public static RequestContent Create(object serializable) => Create(serializable, JsonObjectSerializer.Default); + /// + /// Creates an instance of that wraps a serialized version of an object. + /// + /// The that contains the serialized data. + /// An instance of that wraps a serialized version of the object. + public static RequestContent Create(SequenceWriter writer) => new SequenceWriterContent(writer); + /// /// Creates an instance of that wraps a serialized version of an object. /// @@ -220,6 +235,24 @@ public override void Dispose() } } + private sealed class SequenceWriterContent : RequestContent + { + private readonly SequenceWriter _writer; + + public SequenceWriterContent(SequenceWriter writer) + { + _writer = writer; + } + + public override void Dispose() => _writer.Dispose(); + + public override void WriteTo(Stream stream, CancellationToken cancellation) => _writer.WriteTo(stream, cancellation); + + public override bool TryComputeLength(out long length) => _writer.TryComputeLength(out length); + + public override async Task WriteToAsync(Stream stream, CancellationToken cancellation) => await _writer.WriteToAsync(stream, cancellation).ConfigureAwait(false); + } + private sealed class ArrayContent : RequestContent { private readonly byte[] _bytes; @@ -313,8 +346,13 @@ public override async Task WriteToAsync(Stream stream, CancellationToken cancell private sealed class DynamicDataContent : RequestContent { private readonly DynamicData _data; + private readonly StandardFormat _format; - public DynamicDataContent(DynamicData data) => _data = data; + public DynamicDataContent(DynamicData data, StandardFormat format = default) + { + _data = data; + _format = format; + } public override void Dispose() { @@ -323,7 +361,7 @@ public override void Dispose() public override void WriteTo(Stream stream, CancellationToken cancellation) { - _data.WriteTo(stream); + _data.WriteTo(stream, _format); } public override bool TryComputeLength(out long length) diff --git a/sdk/core/Azure.Core/src/SequenceWriter.cs b/sdk/core/Azure.Core/src/SequenceWriter.cs new file mode 100644 index 000000000000..5d0fbc980249 --- /dev/null +++ b/sdk/core/Azure.Core/src/SequenceWriter.cs @@ -0,0 +1,200 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Buffers; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace Azure.Core +{ + /// + /// . + /// + public sealed class SequenceWriter : IBufferWriter, IDisposable + { + private struct Buffer + { + public byte[] Array; + public int Written; + } + + private Buffer[] _buffers; // this is an array so items can be accessed by ref + private int _count; + private int _bufferSize; + + /// + /// . + /// + /// + public SequenceWriter(int bufferSize = 4096) + { + _bufferSize = bufferSize; + _buffers = Array.Empty(); + } + + /// + /// . + /// + /// + /// + public void Advance(int bytesWritten) + { + ref Buffer last = ref _buffers[_count - 1]; + last.Written += bytesWritten; + if (last.Written > last.Array.Length) + { + throw new ArgumentOutOfRangeException(nameof(bytesWritten)); + } + } + + /// + /// . + /// + /// + /// + public Memory GetMemory(int sizeHint = 0) + { + if (sizeHint < 256) + sizeHint = 256; + + if (_buffers.Length == 0) + { + _buffers = new Buffer[1]; + _buffers[0].Array = ArrayPool.Shared.Rent(_bufferSize); + _count = 1; + } + + ref Buffer last = ref _buffers[_count - 1]; + var free = last.Array.AsMemory(last.Written); + if (free.Length >= sizeHint) + return free; + + // else allocate a new buffer: + var newArray = ArrayPool.Shared.Rent(_bufferSize); + + // add buffer to _buffers + if (_buffers.Length == _count) + { + // resize _buffers + var resized = new Buffer[_buffers.Length * 2]; + _buffers.CopyTo(resized, 0); + _buffers = resized; + } + + _buffers[_count].Array = newArray; + _count++; + return newArray; + } + + /// + /// . + /// + /// + /// + public Span GetSpan(int sizeHint = 0) + { + Memory memory = GetMemory(sizeHint); + return memory.Span; + } + + /// + /// Disposes the SequenceWriter and returns the underlying buffers to the pool. + /// + public void Dispose() + { + // should we harden it? we really cannot afford use-after-free bugs. they might cause data corruption. + // should we lock other members on this instance when we are disposing? + for (int i = 0; i < _count; i++) + { + var buffer = _buffers[i]; + ArrayPool.Shared.Return(buffer.Array); + } + _buffers = Array.Empty(); + _count = 0; + } + + /// + /// Compute the length of the data written to the SequenceWriter. + /// + /// The length of the buffer returned. + /// A bool indicating whether or not the length was able to be calculated. + public bool TryComputeLength(out long length) + { + length = 0; + for (int i = 0; i < _count; i++) + { + var buffer = _buffers[i]; + length += buffer.Written; + } + return true; + } + + /// + /// . + /// + /// + /// + public void WriteTo(Stream stream, CancellationToken cancellation) + { + for (int i = 0; i < _count; i++) + { + var buffer = _buffers[i]; + stream.Write(buffer.Array, 0, buffer.Written); + } + } + + /// + /// . + /// + /// + /// + /// + public async Task WriteToAsync(Stream stream, CancellationToken cancellation) + { + for (int i = 0; i < _count; i++) + { + var buffer = _buffers[i]; + await stream.WriteAsync(buffer.Array, 0, buffer.Written).ConfigureAwait(false); + } + } + + private class MultiBufferSegment : ReadOnlySequenceSegment + { + public MultiBufferSegment(byte[] array, int length, long runningIndex) + { + Memory = new Memory(array, 0, length); + RunningIndex = runningIndex; + } + + public void Add(byte[] array, int length) + { + Next = new MultiBufferSegment(array, length, RunningIndex + Memory.Length); + } + } + + /// + /// . + /// + /// + public ReadOnlySequence GetReadOnlySequence() + { + if (_count == 0) + return ReadOnlySequence.Empty; + + if (_count == 1) + return new ReadOnlySequence(_buffers[0].Array, 0, _buffers[0].Written); + + MultiBufferSegment first = new MultiBufferSegment(_buffers[0].Array, _buffers[0].Written, 0); + MultiBufferSegment previous = first; + for (int i = 1; i < _count; i++) + { + previous!.Add(_buffers[i].Array, _buffers[i].Written); + previous = (MultiBufferSegment)previous.Next!; + } + + return new ReadOnlySequence(first, 0, previous, previous.Memory.Length); + } + } +} diff --git a/sdk/core/Azure.Core/src/Serialization/IJsonModelSerializable.cs b/sdk/core/Azure.Core/src/Serialization/IJsonModelSerializable.cs new file mode 100644 index 000000000000..29fc95292a3d --- /dev/null +++ b/sdk/core/Azure.Core/src/Serialization/IJsonModelSerializable.cs @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Text.Json; + +namespace Azure.Core.Serialization +{ + /// + /// . + /// + public interface IJsonModelSerializable : IModelSerializable + { + /// + /// . + /// + /// + /// +#pragma warning disable AZC0014 // Avoid using banned types in public API + void Serialize(Utf8JsonWriter writer, ModelSerializerOptions options); +#pragma warning restore AZC0014 // Avoid using banned types in public API + + /// + /// . + /// + /// + /// + /// +#pragma warning disable AZC0014 // Avoid using banned types in public API + object Deserialize(ref Utf8JsonReader reader, ModelSerializerOptions options); +#pragma warning restore AZC0014 // Avoid using banned types in public API + } +} diff --git a/sdk/core/Azure.Core/src/Serialization/IModelSerializable.cs b/sdk/core/Azure.Core/src/Serialization/IModelSerializable.cs new file mode 100644 index 000000000000..50635af8f1b6 --- /dev/null +++ b/sdk/core/Azure.Core/src/Serialization/IModelSerializable.cs @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.IO; + +namespace Azure.Core.Serialization +{ + /// + /// TODO + /// + public interface IModelSerializable + { + /// + /// . + /// + /// + /// + /// + object Deserialize(BinaryData data, ModelSerializerOptions options); + + /// + /// . + /// + BinaryData Serialize(ModelSerializerOptions options); + } +} diff --git a/sdk/core/Azure.Core/src/Serialization/IXmlModelSerializable.cs b/sdk/core/Azure.Core/src/Serialization/IXmlModelSerializable.cs new file mode 100644 index 000000000000..8b62bf80ee95 --- /dev/null +++ b/sdk/core/Azure.Core/src/Serialization/IXmlModelSerializable.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Xml; + +namespace Azure.Core.Serialization +{ + /// + /// . + /// + public interface IXmlModelSerializable : IModelSerializable + { + /// + /// . + /// + /// + /// + void Serialize(XmlWriter writer, ModelSerializerOptions options); + } +} diff --git a/sdk/core/Azure.Core/src/Serialization/ModelJsonConverter.cs b/sdk/core/Azure.Core/src/Serialization/ModelJsonConverter.cs new file mode 100644 index 000000000000..21404b807610 --- /dev/null +++ b/sdk/core/Azure.Core/src/Serialization/ModelJsonConverter.cs @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Azure.Core.Serialization +{ + /// + /// Add + /// +#pragma warning disable AZC0014 // Avoid using banned types in public API + public class ModelJsonConverter : JsonConverter +#pragma warning restore AZC0014 // Avoid using banned types in public API + { + /// + /// . + /// + public ModelSerializerOptions Options { get; } + + /// + /// Initializes a new instance of with a default format of . + /// + public ModelJsonConverter() + : this(ModelSerializerFormat.Json) { } + + /// + /// Initializes a new instance of . + /// + /// The format to serialize to and deserialize from. + public ModelJsonConverter(ModelSerializerFormat format) + { + Options = new ModelSerializerOptions(format); + } + + /// + /// Check if a certain type can be converted to IModelSerializable + /// + /// + /// + public override bool CanConvert(Type typeToConvert) + { + return !Attribute.IsDefined(typeToConvert, typeof(JsonConverterAttribute)); + } + + /// + /// todo + /// + /// + /// + /// + /// + /// +#pragma warning disable AZC0014 // Avoid using banned types in public API + public override IJsonModelSerializable Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) +#pragma warning restore AZC0014 // Avoid using banned types in public API + { + return (IJsonModelSerializable)ModelSerializer.Deserialize(BinaryData.FromString(JsonDocument.ParseValue(ref reader).RootElement.GetRawText()), typeToConvert, Options); + } + + /// + /// todo + /// + /// + /// + /// +#pragma warning disable AZC0014 // Avoid using banned types in public API + public override void Write(Utf8JsonWriter writer, IJsonModelSerializable value, JsonSerializerOptions options) +#pragma warning restore AZC0014 // Avoid using banned types in public API + { + value.Serialize(writer, Options); + } + } +} diff --git a/sdk/core/Azure.Core/src/Serialization/ModelSerializer.cs b/sdk/core/Azure.Core/src/Serialization/ModelSerializer.cs new file mode 100644 index 000000000000..d2ea96d95089 --- /dev/null +++ b/sdk/core/Azure.Core/src/Serialization/ModelSerializer.cs @@ -0,0 +1,97 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Reflection; + +namespace Azure.Core.Serialization +{ + /// + /// Serializer class for Azure models. + /// + public static class ModelSerializer + { + /// + /// Serialize a model. + /// + /// + /// + /// + /// + public static BinaryData Serialize(T model, ModelSerializerOptions options = default) where T : IModelSerializable + { + if (options.Serializers != null && options.Serializers.TryGetValue(typeof(T), out var serializer)) + return serializer.Serialize(model); + + return model.Serialize(options); + } + + /// + /// Serialize a model. + /// + /// + /// + /// + public static BinaryData Serialize(object model, ModelSerializerOptions options = default) + { + var iModel = model as IModelSerializable; + if (iModel is null) + throw new InvalidOperationException($"{model.GetType().Name} does not implement {nameof(IModelSerializable)}"); + + if (options.Serializers != null && options.Serializers.TryGetValue(model.GetType(), out var serializer)) + return serializer.Serialize(model); + + return iModel.Serialize(options); + } + + /// + /// Serialize a XML model. Todo: collapse this method when working - need compile check over runtime + /// + /// + public static T Deserialize(BinaryData data, ModelSerializerOptions options = default) where T : class, IModelSerializable + { + return (T)Deserialize(data, typeof(T), options); + } + + /// + /// . + /// + /// + /// + /// + /// + /// + public static object Deserialize(BinaryData data, Type typeToConvert, ModelSerializerOptions options = default) + { + if (options.Serializers != null && options.Serializers.TryGetValue(typeToConvert, out var serializer)) + { + var obj = serializer.Deserialize(data.ToStream(), typeToConvert, default); + return obj ?? throw new InvalidOperationException(); + } + + if (typeToConvert.IsAbstract) + return DeserializeObject(data, typeToConvert, options); + + var model = Activator.CreateInstance(typeToConvert, true) as IModelSerializable; + + return model!.Deserialize(data, options); + } + + private static readonly Type[] _combinedDeserializeMethodParameters = new Type[] { typeof(BinaryData), typeof(ModelSerializerOptions) }; + + internal static object DeserializeObject(BinaryData data, Type typeToConvert, ModelSerializerOptions options) + { + var classNameInMethod = typeToConvert.Name.AsSpan(); + int backtickIndex = classNameInMethod.IndexOf('`'); + if (backtickIndex != -1) + classNameInMethod = classNameInMethod.Slice(0, backtickIndex); + var methodName = $"Deserialize{classNameInMethod.ToString()}"; + + var method = typeToConvert.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Static, null, _combinedDeserializeMethodParameters, null); + if (method is null) + throw new NotSupportedException($"{typeToConvert.Name} does not have a deserialize method defined."); + + return method.Invoke(null, new object[] { data, options })!; + } + } +} diff --git a/sdk/core/Azure.Core/src/Serialization/ModelSerializerFormat.cs b/sdk/core/Azure.Core/src/Serialization/ModelSerializerFormat.cs new file mode 100644 index 000000000000..0f4857a89a96 --- /dev/null +++ b/sdk/core/Azure.Core/src/Serialization/ModelSerializerFormat.cs @@ -0,0 +1,79 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.ComponentModel; + +namespace Azure.Core.Serialization +{ + /// + /// Enumerator representing format of the serialized model. + /// + public readonly partial struct ModelSerializerFormat : IEquatable + { + internal const string JsonValue = "J"; + internal const string WireValue = "W"; + + /// + /// Specifies the data format where IgnoreReadOnly and IgnoreAdditionalProperties are false. + /// + public static readonly ModelSerializerFormat Json = new ModelSerializerFormat(JsonValue); + + /// + /// Specifies the wire format IgnoreReadOnly and IgnoreAdditionalProperties are true. + /// + public static readonly ModelSerializerFormat Wire = new ModelSerializerFormat(WireValue); + + private readonly string _value; + + /// + /// Instantiate a new . + /// + public ModelSerializerFormat(string value) + { + _value = value ?? throw new ArgumentNullException(nameof(value)); + } + + /// + /// Determines if two values are the same. + /// + /// The first to compare. + /// The second to compare. + /// True if and are the same; otherwise, false. + public static bool operator ==(ModelSerializerFormat left, ModelSerializerFormat right) => left.Equals(right); + + /// + /// Determines if two values are different. + /// + /// The first to compare. + /// The second to compare. + /// True if and are different; otherwise, false. + public static bool operator !=(ModelSerializerFormat left, ModelSerializerFormat right) => !left.Equals(right); + + /// + /// Converts a string to a . + /// + /// The string value to convert. + public static implicit operator ModelSerializerFormat(string value) => new ModelSerializerFormat(value); + + /// + /// Converts a to a string. + /// + /// The ModelSerializerFormat value to convert. + public static implicit operator string(ModelSerializerFormat value) => value.ToString(); + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals([System.Diagnostics.CodeAnalysis.AllowNull] object obj) => obj is ModelSerializerFormat other && Equals(other); + + /// + public bool Equals(ModelSerializerFormat other) => string.Equals(_value, other._value, StringComparison.Ordinal); + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() => _value?.GetHashCode() ?? 0; + + /// + public override string ToString() => _value; + } +} diff --git a/sdk/core/Azure.Core/src/Serialization/ModelSerializerOptions.cs b/sdk/core/Azure.Core/src/Serialization/ModelSerializerOptions.cs new file mode 100644 index 000000000000..ad4cc5f94aa3 --- /dev/null +++ b/sdk/core/Azure.Core/src/Serialization/ModelSerializerOptions.cs @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; + +namespace Azure.Core.Serialization +{ + /// + /// Provides the client options for serializing models. + /// + public struct ModelSerializerOptions + { + /// + /// . + /// + public static readonly ModelSerializerOptions AzureServiceDefault = new ModelSerializerOptions(ModelSerializerFormat.Wire); + + /// + /// Initializes a new instance of the class. Defaults to Data format "D". + /// + public ModelSerializerOptions() : this(ModelSerializerFormat.Json) { } + + /// + /// Initializes a new instance of the class. + /// + /// String that determines Format of serialized model. "D" = data format which means IgnoreReadOnly and IgnoreAdditionalProperties are false, "W" = wire format which means both properties are true. Default is "D". + public ModelSerializerOptions(ModelSerializerFormat format) + { + Format = format; + Serializers = new Dictionary(); + } + + /// + /// Gets the that determines Format of serialized model. + /// + public ModelSerializerFormat Format { get; } + + /// + /// Dictionary that holds all the serializers for the different model types. + /// + public Dictionary Serializers { get; internal set; } + } +} diff --git a/sdk/core/Azure.Core/src/Shared/ModelSerializerHelper.cs b/sdk/core/Azure.Core/src/Shared/ModelSerializerHelper.cs new file mode 100644 index 000000000000..a02ecb11b96a --- /dev/null +++ b/sdk/core/Azure.Core/src/Shared/ModelSerializerHelper.cs @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.IO; +using System.Text.Json; +using System.Xml; + +namespace Azure.Core +{ + internal static class ModelSerializerHelper + { + public static BinaryData SerializeToBinaryData(Action serialize) + { + using var writer = new SequenceWriter(); + using var jsonWriter = new Utf8JsonWriter(writer); + serialize(jsonWriter); + jsonWriter.Flush(); + writer.TryComputeLength(out var length); + using var stream = new MemoryStream((int)length); + writer.WriteTo(stream, default); + return new BinaryData(stream.GetBuffer().AsMemory(0, (int)stream.Position)); + } + + public static BinaryData SerializeToBinaryData(Action serialize) + { + using MemoryStream stream = new MemoryStream(); + using XmlWriter writer = XmlWriter.Create(stream); + serialize(writer); + writer.Flush(); + return new BinaryData(stream.GetBuffer().AsMemory(0, (int)stream.Position)); + } + } +} diff --git a/sdk/core/Azure.Core/src/DynamicData/MutableJsonChange.cs b/sdk/core/Azure.Core/src/Shared/MutableJsonChange.cs similarity index 79% rename from sdk/core/Azure.Core/src/DynamicData/MutableJsonChange.cs rename to sdk/core/Azure.Core/src/Shared/MutableJsonChange.cs index 6077a521b09f..c81f00aff809 100644 --- a/sdk/core/Azure.Core/src/DynamicData/MutableJsonChange.cs +++ b/sdk/core/Azure.Core/src/Shared/MutableJsonChange.cs @@ -4,6 +4,8 @@ using System; using System.Text.Json; +#nullable enable + namespace Azure.Core.Json { internal struct MutableJsonChange @@ -71,6 +73,16 @@ internal bool IsDescendant(string path) return Path.StartsWith(path, StringComparison.Ordinal); } + internal bool IsDescendant(MutableJsonChange? other) + { + if (other == null) + { + return false; + } + + return IsDescendant(other.Value.Path); + } + internal bool IsDirectDescendant(string path) { if (!IsDescendant(path)) @@ -85,6 +97,26 @@ internal bool IsDirectDescendant(string path) return ancestorPathLength == (descendantPathLength - 1); } + internal bool IsLessThan(MutableJsonChange? other) + { + if (other == null) + { + return true; + } + + return Path.AsSpan().SequenceCompareTo(other.Value.Path.AsSpan()) < 0; + } + + internal bool IsGreaterThan(MutableJsonChange? other) + { + if (other == null) + { + return true; + } + + return Path.AsSpan().SequenceCompareTo(other.Value.Path.AsSpan()) > 0; + } + internal string AsString() { return GetSerializedValue().ToString() ?? "null"; diff --git a/sdk/core/Azure.Core/src/DynamicData/MutableJsonChangeKind.cs b/sdk/core/Azure.Core/src/Shared/MutableJsonChangeKind.cs similarity index 100% rename from sdk/core/Azure.Core/src/DynamicData/MutableJsonChangeKind.cs rename to sdk/core/Azure.Core/src/Shared/MutableJsonChangeKind.cs diff --git a/sdk/core/Azure.Core/src/Shared/MutableJsonDictionary.cs b/sdk/core/Azure.Core/src/Shared/MutableJsonDictionary.cs new file mode 100644 index 000000000000..6c0db54bd095 --- /dev/null +++ b/sdk/core/Azure.Core/src/Shared/MutableJsonDictionary.cs @@ -0,0 +1,126 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Text.Json; + +#nullable enable + +namespace Azure.Core.Json +{ + internal readonly struct MutableJsonDictionary : IDictionary + { + private static JsonElement Empty = JsonDocument.Parse("{}"u8.ToArray()).RootElement; + + private readonly MutableJsonElement _element; + + public MutableJsonDictionary(MutableJsonElement element) + { + Debug.Assert(element.ValueKind == JsonValueKind.Object); + + _element = element; + } + + public T this[string key] + { + get + { + return _element.GetProperty(key).ConvertTo(); + } + + set => _element.SetProperty(key, value); + } + + // TODO: implement + public ICollection Keys => throw new NotImplementedException(); + + // TODO: implement + public ICollection Values => throw new NotImplementedException(); + + public int Count => _element.EnumerateObject().Count(); + + public bool IsReadOnly => false; + + public void Add(string key, T value) + { + Argument.AssertNotNull(key, nameof(key)); + + if (_element.TryGetProperty(key, out _)) + { + throw new ArgumentException($"An element with the same key already exists in the MutableJsonDictionary."); + } + + _element.SetProperty(key, value); + } + + public void Add(KeyValuePair item) => Add(item.Key, item.Value); + + public void Clear() => _element.Set(Empty); + + public bool Contains(KeyValuePair item) => _element.TryGetProperty(item.Key, out _); + + public bool ContainsKey(string key) => _element.TryGetProperty(key, out _); + + // TODO: Add test case + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + Argument.AssertNotNull(array, nameof(array)); + Argument.AssertInRange(arrayIndex, 0, int.MaxValue, nameof(arrayIndex)); + + int i = arrayIndex; + foreach ((string Name, MutableJsonElement Value) in _element.EnumerateObject()) + { + if (i >= array.Length) + { + throw new ArgumentException("The number of elements in the dictionary is greater than the available space from 'arrayIndex' to the end of the destination array."); + } + + array[i++] = new KeyValuePair(Name, Value.ConvertTo()); + } + } + + public IEnumerator> GetEnumerator() + { + foreach ((string Name, MutableJsonElement Value) in _element.EnumerateObject()) + { + yield return new KeyValuePair(Name, Value.ConvertTo()); + } + } + + // TODO: Add test case + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public bool Remove(string key) + { + _element.RemoveProperty(key); + return true; + } + + public bool Remove(KeyValuePair item) + { + _element.RemoveProperty(item.Key); + return true; + } + +#if NET6_0_OR_GREATER + public bool TryGetValue(string key, [MaybeNullWhen(false)] out T value) +#else + public bool TryGetValue(string key, out T value) +#endif + { + if (_element.TryGetProperty(key, out MutableJsonElement element)) + { + value = element.ConvertTo(); + return true; + } + + value = default!; + return false; + } + } +} diff --git a/sdk/core/Azure.Core/src/DynamicData/MutableJsonDocument.ChangeTracker.cs b/sdk/core/Azure.Core/src/Shared/MutableJsonDocument.ChangeTracker.cs similarity index 68% rename from sdk/core/Azure.Core/src/DynamicData/MutableJsonDocument.ChangeTracker.cs rename to sdk/core/Azure.Core/src/Shared/MutableJsonDocument.ChangeTracker.cs index 3b6982e349ec..40d5e015b1b8 100644 --- a/sdk/core/Azure.Core/src/DynamicData/MutableJsonDocument.ChangeTracker.cs +++ b/sdk/core/Azure.Core/src/Shared/MutableJsonDocument.ChangeTracker.cs @@ -1,8 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +#nullable enable + using System; using System.Collections.Generic; +using System.Diagnostics; using System.Text.Json; namespace Azure.Core.Json @@ -64,6 +67,11 @@ internal bool DescendantChanged(string path, int highWaterMark) } internal bool TryGetChange(string path, in int lastAppliedChange, out MutableJsonChange change) + { + return TryGetChange(path.AsSpan(), lastAppliedChange, out change); + } + + internal bool TryGetChange(ReadOnlySpan path, in int lastAppliedChange, out MutableJsonChange change) { if (_changes == null) { @@ -74,7 +82,7 @@ internal bool TryGetChange(string path, in int lastAppliedChange, out MutableJso for (int i = _changes!.Count - 1; i > lastAppliedChange; i--) { MutableJsonChange c = _changes[i]; - if (c.Path == path) + if (c.Path.AsSpan().SequenceEqual(path)) { change = c; return true; @@ -135,6 +143,45 @@ internal IEnumerable GetRemovedProperties(string path, int hi } } + internal MutableJsonChange? GetNextChange(MutableJsonChange? lastChange, out int maxPathLength) + { + // This method gets changes from the list in sorted order by path. + // It is intended for use by JSON Merge Patch WriteTo routine. + + maxPathLength = -1; + + if (_changes == null) + { + // null means there's no next change, we can exit a loop + return null; + } + + MutableJsonChange? min = null; + + // This implementation is based on the assumption that iterating through + // list elements is fast. + // Iterating backwards means we get the latest change for a given path. + for (int i = _changes!.Count - 1; i >= 0; i--) + { + MutableJsonChange c = _changes[i]; + + if (c.IsGreaterThan(lastChange) && + c.IsLessThan(min) && + // Ignore descendant if its ancestor changed + !c.IsDescendant(lastChange)) + { + min = c; + } + + if (c.Path.Length > maxPathLength) + { + maxPathLength = c.Path.Length; + } + } + + return min; + } + internal bool WasRemoved(string path, int highWaterMark) { if (_changes == null) @@ -175,16 +222,21 @@ internal static string PushProperty(string path, string value) return string.Concat(path, Delimiter, value); } - internal static string PushProperty(string path, ReadOnlySpan value) + internal static void PushProperty(Span path, ref int pathLength, ReadOnlySpan value, int valueLength) { - string propertyName = BinaryData.FromBytes(value.ToArray()).ToString(); + // Validate that path is large enough to write value into + Debug.Assert(path.Length - pathLength >= valueLength); - if (path.Length == 0) + if (pathLength == 0) { - return propertyName; + value.Slice(0, valueLength).CopyTo(path); + pathLength = valueLength; + return; } - return string.Concat(path, Delimiter, propertyName); + path[pathLength] = Delimiter; + value.Slice(0, valueLength).CopyTo(path.Slice(pathLength + 1)); + pathLength += valueLength + 1; } internal static string PopProperty(string path) @@ -198,6 +250,12 @@ internal static string PopProperty(string path) return path.Substring(0, lastDelimiter); } + + internal static void PopProperty(Span path, ref int pathLength) + { + int lastDelimiter = path.Slice(0, pathLength).LastIndexOf(Delimiter); + pathLength = lastDelimiter == -1 ? 0 : lastDelimiter; + } } } } diff --git a/sdk/core/Azure.Core/src/DynamicData/MutableJsonDocument.cs b/sdk/core/Azure.Core/src/Shared/MutableJsonDocument.cs similarity index 87% rename from sdk/core/Azure.Core/src/DynamicData/MutableJsonDocument.cs rename to sdk/core/Azure.Core/src/Shared/MutableJsonDocument.cs index 2f22c4714886..5d08f3bf42a4 100644 --- a/sdk/core/Azure.Core/src/DynamicData/MutableJsonDocument.cs +++ b/sdk/core/Azure.Core/src/Shared/MutableJsonDocument.cs @@ -8,6 +8,8 @@ using System.Text.Json; using System.Text.Json.Serialization; +#nullable enable + namespace Azure.Core.Json { /// @@ -16,6 +18,9 @@ namespace Azure.Core.Json [JsonConverter(typeof(MutableJsonDocumentConverter))] internal sealed partial class MutableJsonDocument : IDisposable { + private static ReadOnlyMemory _emptyJson = "{}"u8.ToArray(); + public static ReadOnlyMemory EmptyJson => _emptyJson; + private readonly ReadOnlyMemory _original; private readonly JsonDocument _originalDocument; @@ -47,16 +52,30 @@ public MutableJsonElement RootElement /// A format string indicating the format to use when writing the document. /// The parameter is . /// Thrown if an unsupported value is passed for format. - /// The value of can be default or 'J' to write the document as JSON. + /// The value of can be default or 'J' to write the document as JSON, or 'P' to write the changes as JSON Merge Patch. public void WriteTo(Stream stream, StandardFormat format = default) { Argument.AssertNotNull(stream, nameof(stream)); - if (format != default && format.Symbol != 'J') + if (format != default && format.Symbol != 'J' && format.Symbol != 'P') + { + throw new FormatException($"Unsupported format {format.Symbol}. Supported formats are: 'J' - JSON, 'P' - JSON Merge Patch."); + } + + switch (format.Symbol) { - throw new FormatException($"Unsupported format {format.Symbol}. Supported formats are: 'J' - JSON."); + case 'P': + WritePatch(stream); + break; + case 'J': + default: + WriteJson(stream); + break; } + } + private void WriteJson(Stream stream) + { if (!Changes.HasChanges) { Write(stream, _original.Span); @@ -67,6 +86,17 @@ public void WriteTo(Stream stream, StandardFormat format = default) RootElement.WriteTo(writer); } + private void WritePatch(Stream stream) + { + if (!Changes.HasChanges) + { + return; + } + + using Utf8JsonWriter writer = new(stream); + RootElement.WritePatch(writer); + } + /// /// Writes the document to the provided stream as a JSON value. /// @@ -110,7 +140,7 @@ private static void Write(Stream stream, ReadOnlySpan buffer) /// does not represent a valid single JSON value. public static MutableJsonDocument Parse(ReadOnlyMemory utf8Json, JsonSerializerOptions? serializerOptions = default) { - var doc = JsonDocument.Parse(utf8Json); + JsonDocument doc = JsonDocument.Parse(utf8Json); return new MutableJsonDocument(doc, utf8Json, serializerOptions); } @@ -123,7 +153,7 @@ public static MutableJsonDocument Parse(ReadOnlyMemory utf8Json, JsonSeria /// does not represent a valid single JSON value. public static MutableJsonDocument Parse(BinaryData utf8Json, JsonSerializerOptions? serializerOptions = default) { - var doc = JsonDocument.Parse(utf8Json); + JsonDocument doc = JsonDocument.Parse(utf8Json); return new MutableJsonDocument(doc, utf8Json.ToMemory(), serializerOptions); } diff --git a/sdk/core/Azure.Core/src/DynamicData/MutableJsonElement.ArrayEnumerator.cs b/sdk/core/Azure.Core/src/Shared/MutableJsonElement.ArrayEnumerator.cs similarity index 100% rename from sdk/core/Azure.Core/src/DynamicData/MutableJsonElement.ArrayEnumerator.cs rename to sdk/core/Azure.Core/src/Shared/MutableJsonElement.ArrayEnumerator.cs diff --git a/sdk/core/Azure.Core/src/DynamicData/MutableJsonElement.ObjectEnumerator.cs b/sdk/core/Azure.Core/src/Shared/MutableJsonElement.ObjectEnumerator.cs similarity index 100% rename from sdk/core/Azure.Core/src/DynamicData/MutableJsonElement.ObjectEnumerator.cs rename to sdk/core/Azure.Core/src/Shared/MutableJsonElement.ObjectEnumerator.cs diff --git a/sdk/core/Azure.Core/src/Shared/MutableJsonElement.WriteTo.cs b/sdk/core/Azure.Core/src/Shared/MutableJsonElement.WriteTo.cs new file mode 100644 index 000000000000..9dc9ee1b502c --- /dev/null +++ b/sdk/core/Azure.Core/src/Shared/MutableJsonElement.WriteTo.cs @@ -0,0 +1,336 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Buffers; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Text.Json; + +namespace Azure.Core.Json +{ + internal partial struct MutableJsonElement + { + internal void WriteTo(Utf8JsonWriter writer) + { + WriteElement(_path, _highWaterMark, _element, writer); + } + + internal void WriteTo(Utf8JsonWriter writer, StandardFormat format) + { + // TODO: consolidate format switching with root + if (format != default && format.Symbol != 'J' && format.Symbol != 'P') + { + throw new FormatException($"Unsupported format {format.Symbol}. Supported formats are: 'J' - JSON, 'P' - JSON Merge Patch."); + } + + switch (format.Symbol) + { + case 'P': + WritePatch(writer); + break; + case 'J': + default: + WriteTo(writer); + break; + } + + // TODO: Test case: Make sure we write the current element, not the root + } + + private void WriteElement(string path, int highWaterMark, JsonElement element, Utf8JsonWriter writer) + { + if (Changes.TryGetChange(path, highWaterMark, out MutableJsonChange change)) + { + switch (change.Value) + { + 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 = change.GetSerializedValue(); + highWaterMark = change.Index; + } + + if (Changes.DescendantChanged(path, highWaterMark)) + { + switch (element.ValueKind) + { + case JsonValueKind.Object: + WriteObject(path, highWaterMark, element, writer); + break; + case JsonValueKind.Array: + WriteArray(path, highWaterMark, element, writer); + break; + default: + throw new InvalidOperationException("Element doesn't have descendants."); + } + + return; + } + + element.WriteTo(writer); + } + + private void WriteObject(string path, int highWaterMark, JsonElement element, Utf8JsonWriter writer) + { + writer.WriteStartObject(); + + foreach (JsonProperty property in element.EnumerateObject()) + { + string propertyPath = MutableJsonDocument.ChangeTracker.PushProperty(path, property.Name); + if (!Changes.WasRemoved(propertyPath, highWaterMark)) + { + writer.WritePropertyName(property.Name); + WriteElement(propertyPath, highWaterMark, property.Value, writer); + } + } + + IEnumerable added = Changes.GetAddedProperties(path, highWaterMark); + foreach (MutableJsonChange property in added) + { + string propertyName = property.AddedPropertyName!; + string propertyPath = MutableJsonDocument.ChangeTracker.PushProperty(path, propertyName); + + writer.WritePropertyName(propertyName); + WriteElement(propertyPath, highWaterMark, property.GetSerializedValue(), writer); + } + + writer.WriteEndObject(); + } + + private void WriteArray(string path, int highWaterMark, JsonElement element, Utf8JsonWriter writer) + { + writer.WriteStartArray(); + + int arrayIndex = 0; + foreach (JsonElement arrayElement in element.EnumerateArray()) + { + string arrayElementPath = MutableJsonDocument.ChangeTracker.PushIndex(path, arrayIndex++); + WriteElement(arrayElementPath, highWaterMark, arrayElement, writer); + } + + writer.WriteEndArray(); + } + + internal void WritePatch(Utf8JsonWriter writer) + { + if (!_root.Changes.HasChanges) + { + return; + } + + MutableJsonChange? change = _root.Changes.GetNextChange(null, out int maxPathLength); + + // patchPath tracks the global path we're on in writing out the PATCH JSON. + // We only iterate forward through the PATCH JSON. + Span patchPath = stackalloc char[maxPathLength]; + int patchPathLength = 0; + + // patchElement tracks the element we're currently writing into the PATCH JSON. + // It should match the element indicated by patchPath. + MutableJsonElement patchElement = _root.RootElement; + + // currentPath tracks the path of the current change we're writing in + // a given iteration of the loop over changes. + Span currentPath = stackalloc char[maxPathLength]; + int currentPathLength = 0; + + // TODO: this breaks for arrays + + // Write the start of the PATCH JSON + writer.WriteStartObject(); + + while (change != null) + { + ReadOnlySpan changePath = change.Value.Path.AsSpan(); + + CopyTo(currentPath, ref currentPathLength, GetFirstSegment(changePath)); + + // If the change we're on starts in a different object than we were writing to in + // the last iteration, we need to write the end of any open objects. Do that now. + CloseOpenObjects(writer, currentPath, currentPathLength, patchPath, ref patchPathLength, ref patchElement); + + // Open any objects that aren't yet open between the first and last path segments + // for the change we're writing in this loop iteration. + OpenAncestorObjects(writer, changePath, currentPath, ref currentPathLength, patchPath, ref patchPathLength, ref patchElement); + + // Now we're at the last segment of the change path and patchElement points to + // the node that contains the change we want to write into the PATCH JSON. + ReadOnlySpan segment = GetLastSegment(changePath); + + writer.WritePropertyName(segment); + if (change.Value.ChangeKind == MutableJsonChangeKind.PropertyRemoval) + { + writer.WriteNullValue(); + } + else + { + patchElement.GetProperty(segment).WriteTo(writer); + } + + change = _root.Changes.GetNextChange(change, out _); + } + + // The above loop will have written out the values of all the elements on the + // list of changes, but if the last element was multiple levels down the object + // tree, it may not have written the ends of its ancestor objects. Do that now. + CloseFinalObjects(writer, patchPath, patchPathLength); + + // Write the end of the PATCH JSON. + writer.WriteEndObject(); + } + private ReadOnlySpan GetFirstSegment(ReadOnlySpan path) + { + int idx = path.IndexOf(MutableJsonDocument.ChangeTracker.Delimiter); + return idx == -1 ? path : path.Slice(0, idx); + } + + private ReadOnlySpan GetLastSegment(ReadOnlySpan path) + { + int idx = path.LastIndexOf(MutableJsonDocument.ChangeTracker.Delimiter); + return idx == -1 ? path : path.Slice(idx + 1); + } + + private void CopyTo(Span target, ref int targetLength, ReadOnlySpan value) + { + Debug.Assert(target.Length >= value.Length); + + value.CopyTo(target); + targetLength = value.Length; + } + + private void CloseOpenObjects(Utf8JsonWriter writer, ReadOnlySpan currentPath, int currentPathLength, Span patchPath, ref int patchPathLength, ref MutableJsonElement patchElement) + { + bool closedObject = false; + while (!currentPath.Slice(0, currentPathLength).StartsWith(patchPath.Slice(0, patchPathLength))) + { + writer.WriteEndObject(); + + MutableJsonDocument.ChangeTracker.PopProperty(patchPath, ref patchPathLength); + + closedObject = true; + } + + if (closedObject) + { + patchElement = GetPropertyFromRoot(patchPath, patchPathLength); + } + } + + private MutableJsonElement GetPropertyFromRoot(ReadOnlySpan path, int pathLength) + { + MutableJsonElement current = _root.RootElement; + + if (pathLength == 0) + { + return current; + } + + int length = pathLength; + int start = 0; + int end; + do + { + end = path.Slice(start).IndexOf(MutableJsonDocument.ChangeTracker.Delimiter); + if (end == -1) + { + end = length - start; + } + + if (end != 0) + { + current = current.GetProperty(path.Slice(start, end)); + } + + start += end + 1; + } + while (start < length); + + return current; + } + + private void CloseFinalObjects(Utf8JsonWriter writer, ReadOnlySpan patchPath, int patchPathLength) + { + int length = patchPathLength; + int start = 0; + int end; + do + { + end = patchPath.Slice(start).IndexOf(MutableJsonDocument.ChangeTracker.Delimiter); + if (end == -1) + { + end = length - start; + } + + if (end != 0) + { + writer.WriteEndObject(); + } + + start += end + 1; + } + while (start < length); + } + + private void OpenAncestorObjects(Utf8JsonWriter writer, ReadOnlySpan path, Span currentPath, ref int currentPathLength, Span patchPath, ref int patchPathLength, ref MutableJsonElement patchElement) + { + int length = path.Length; + int start = 0; + int end; + + do + { + end = path.Slice(start).IndexOf(MutableJsonDocument.ChangeTracker.Delimiter); + if (end == -1) + { + // skip the last segment + break; + } + + // currentPath already holds the first property, so don't push it if we're + // still on the first one. + if (end != 0 && start != 0) + { + MutableJsonDocument.ChangeTracker.PushProperty(currentPath, ref currentPathLength, path.Slice(start), end); + } + + // if we haven't opened this object yet in the PATCH JSON, open it and set + // currentElement to the corresponding element for the object. + if (!patchPath.Slice(0, patchPathLength).StartsWith(currentPath.Slice(0, currentPathLength)) && + currentPath.Slice(0, currentPathLength).StartsWith(patchPath.Slice(0, patchPathLength))) + { + writer.WritePropertyName(path.Slice(start, end)); + writer.WriteStartObject(); + + MutableJsonDocument.ChangeTracker.PushProperty(patchPath, ref patchPathLength, path.Slice(start), end); + patchElement = patchElement.GetProperty(path.Slice(start, end)); + } + + start += end + 1; + } + while (start < length); + } + } +} diff --git a/sdk/core/Azure.Core/src/DynamicData/MutableJsonElement.cs b/sdk/core/Azure.Core/src/Shared/MutableJsonElement.cs similarity index 90% rename from sdk/core/Azure.Core/src/DynamicData/MutableJsonElement.cs rename to sdk/core/Azure.Core/src/Shared/MutableJsonElement.cs index ee0f0975e7b3..695baae00d4f 100644 --- a/sdk/core/Azure.Core/src/DynamicData/MutableJsonElement.cs +++ b/sdk/core/Azure.Core/src/Shared/MutableJsonElement.cs @@ -4,9 +4,13 @@ using System; using System.Diagnostics; using System.IO; +using System.Linq; using System.Text; using System.Text.Json; using System.Text.Json.Serialization; +using Azure.Core.Serialization; + +#nullable enable namespace Azure.Core.Json { @@ -48,14 +52,61 @@ public JsonValueKind? ValueKind } } + internal T ConvertTo() + { + JsonElement element = GetJsonElement(); + Utf8JsonReader reader = GetReaderForElement(element); + + try + { + // TODO: change this implementation so we don't deserialize known types. + // i.e. check typeof(T) and call relevant GetXx() method for supported types. + // Can we do better here per perf? + + T? value; + + // Use custom deserialization for an Azure model type. + if (typeof(T).GetInterfaces().Contains(typeof(IJsonModelSerializable)) && + Activator.CreateInstance(typeof(T), true) is IJsonModelSerializable model) + { + // TODO: Do we want default options or something else here? + return (T)model.Deserialize(ref reader, ModelSerializerOptions.AzureServiceDefault); + } + +#if NET6_0_OR_GREATER + value = JsonSerializer.Deserialize(element, _root.SerializerOptions); +#else + value = JsonSerializer.Deserialize(ref reader, _root.SerializerOptions); +#endif + + if (value is null) + { + throw new InvalidCastException($"Unable to convert value of kind '{element.ValueKind}' to type '{typeof(T)}'."); + } + return value!; + } + catch (JsonException e) + { + throw new InvalidCastException($"Unable to convert value of kind '{element.ValueKind}' to type '{typeof(T)}'.", e); + } + } + /// /// Gets the MutableJsonElement for the value of the property with the specified name. /// public MutableJsonElement GetProperty(string name) + { + return GetProperty(name.AsSpan()); + } + + /// + /// Gets the MutableJsonElement for the value of the property with the specified name. + /// + public MutableJsonElement GetProperty(ReadOnlySpan name) { if (!TryGetProperty(name, out MutableJsonElement value)) { - throw new InvalidOperationException($"'{_path}' does not contain property called '{name}'"); + throw new InvalidOperationException($"'{_path}' does not contain property called '{GetString(name, 0, name.Length)}'"); } return value; @@ -68,12 +119,30 @@ public MutableJsonElement GetProperty(string name) /// The value to assign to the element. /// public bool TryGetProperty(string name, out MutableJsonElement value) + { + return TryGetProperty(name.AsSpan(), out value); + } + + /// + /// Looks for a property named propertyName in the current object, returning a value that indicates whether or not such a property exists. When the property exists, its value is assigned to the value argument. + /// + /// + /// The value to assign to the element. + /// + public bool TryGetProperty(ReadOnlySpan name, out MutableJsonElement value) { EnsureValid(); EnsureObject(); - string path = MutableJsonDocument.ChangeTracker.PushProperty(_path, name); + int space = name.Length; + space += _path.Length > 0 ? _path.Length + 1 : 0; + Span path = stackalloc char[space]; + _path.AsSpan().CopyTo(path); + int pathLength = _path.Length; + + MutableJsonDocument.ChangeTracker.PushProperty(path, ref pathLength, name, name.Length); + if (Changes.TryGetChange(path, _highWaterMark, out MutableJsonChange change)) { if (change.ChangeKind == MutableJsonChangeKind.PropertyRemoval) @@ -82,7 +151,7 @@ public bool TryGetProperty(string name, out MutableJsonElement value) return false; } - value = new MutableJsonElement(_root, change.GetSerializedValue(), path, change.Index); + value = new MutableJsonElement(_root, change.GetSerializedValue(), GetString(path, 0, pathLength), change.Index); return true; } @@ -93,10 +162,19 @@ public bool TryGetProperty(string name, out MutableJsonElement value) return false; } - value = new MutableJsonElement(_root, element, path, _highWaterMark); + value = new MutableJsonElement(_root, element, GetString(path, 0, pathLength), _highWaterMark); return true; } + private string GetString(ReadOnlySpan value, int start, int end) + { +#if NET5_0_OR_GREATER + return new string(value.Slice(start, end)); +#else + return new string(value.Slice(start, end).ToArray()); +#endif + } + public int GetArrayLength() { EnsureValid(); @@ -747,7 +825,7 @@ public ObjectEnumerator EnumerateObject() /// /// /// The value to assign to the element. - public MutableJsonElement SetProperty(string name, object value) + public MutableJsonElement SetProperty(string name, object? value) { if (TryGetProperty(name, out MutableJsonElement element)) { @@ -970,7 +1048,7 @@ public void Set(DateTimeOffset value) /// 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(object? value) { EnsureValid(); @@ -1033,8 +1111,10 @@ public void Set(object value) } } - private object GetSerializedValue(object value) + private object GetSerializedValue(object? value) { + // TODO: handle null + if (value is JsonDocument doc) { return doc.RootElement; @@ -1055,10 +1135,21 @@ private object GetSerializedValue(object value) mje.EnsureValid(); } + // Use custom serialization for an Azure model type. + if (value is IJsonModelSerializable model) + { + using MemoryStream stream = new(); + using Utf8JsonWriter writer = new(stream); + + // TODO: Are default serializer options sufficient here? + model.Serialize(writer, ModelSerializerOptions.AzureServiceDefault); + writer.Flush(); + stream.Position = 0; + BinaryData data = BinaryData.FromStream(stream); + return JsonDocument.Parse(data).RootElement; + } + // If it's not a special type, we'll serialize it on assignment. - // for debugging - string sval = JsonSerializer.Serialize(value, _root.SerializerOptions); - // end byte[] bytes = JsonSerializer.SerializeToUtf8Bytes(value, _root.SerializerOptions); return JsonDocument.Parse(bytes).RootElement; } diff --git a/sdk/core/Azure.Core/src/Shared/MutableJsonReadOnlyList.cs b/sdk/core/Azure.Core/src/Shared/MutableJsonReadOnlyList.cs new file mode 100644 index 000000000000..191ffb4f0ce1 --- /dev/null +++ b/sdk/core/Azure.Core/src/Shared/MutableJsonReadOnlyList.cs @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text.Json; + +#nullable enable + +namespace Azure.Core.Json +{ + // TODO: Add test coverage + internal readonly struct MutableJsonReadOnlyList : IReadOnlyList + { + private readonly MutableJsonElement _element; + + public MutableJsonReadOnlyList(MutableJsonElement element) + { + Debug.Assert(element.ValueKind == JsonValueKind.Array); + + _element = element; + } + + public T this[int index] => _element.GetIndexElement(index).ConvertTo(); + + public int Count => _element.GetArrayLength(); + + public IEnumerator GetEnumerator() + { + foreach (MutableJsonElement item in _element.EnumerateArray()) + { + yield return item.ConvertTo(); + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + foreach (MutableJsonElement item in _element.EnumerateArray()) + { + yield return item.ConvertTo(); + } + } + } +} diff --git a/sdk/core/Azure.Core/src/Shared/Utf8JsonDelayedRequestContent.cs b/sdk/core/Azure.Core/src/Shared/Utf8JsonDelayedRequestContent.cs new file mode 100644 index 000000000000..2ef39577d0b6 --- /dev/null +++ b/sdk/core/Azure.Core/src/Shared/Utf8JsonDelayedRequestContent.cs @@ -0,0 +1,83 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#nullable enable + +using System.IO; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Azure.Core.Serialization; + +namespace Azure.Core +{ + internal class Utf8JsonDelayedRequestContent : RequestContent + { + private readonly object _serializedLock = new object(); + + private SequenceWriter? _sequenceWriter; + private IJsonModelSerializable _model; + private ModelSerializerOptions _serializerOptions; + private Utf8JsonWriter? _writer; + private RequestContent? _content; + + public Utf8JsonDelayedRequestContent(IJsonModelSerializable model, ModelSerializerOptions? options = default) + { + _model = model; + _serializerOptions = options ?? ModelSerializerOptions.AzureServiceDefault; + } + + private SequenceWriter SequenceWriter => _sequenceWriter ??= new SequenceWriter(); + +#pragma warning disable AZC0014 // Avoid using banned types in public API + private Utf8JsonWriter JsonWriter => _writer ??= new Utf8JsonWriter(SequenceWriter); +#pragma warning restore AZC0014 // Avoid using banned types in public API + + private RequestContent Content => _content ??= Create(SequenceWriter); + + /// + public override void Dispose() + { + _writer?.Dispose(); + _sequenceWriter?.Dispose(); + } + + /// + public override bool TryComputeLength(out long length) + { + Serialize(); + return SequenceWriter.TryComputeLength(out length); + } + + private void Serialize() + { + SequenceWriter.TryComputeLength(out var len); + if (len == 0) + { + lock (_serializedLock) + { + SequenceWriter.TryComputeLength(out len); + if (len == 0) + { + _model.Serialize(JsonWriter, _serializerOptions); + JsonWriter.Flush(); + } + } + } + } + + /// + public override void WriteTo(Stream stream, CancellationToken cancellation) + { + Serialize(); + Content.WriteTo(stream, cancellation); + } + + /// + public override async Task WriteToAsync(Stream stream, CancellationToken cancellation) + { + Serialize(); + await Content.WriteToAsync(stream, cancellation).ConfigureAwait(false); + } + } +} diff --git a/sdk/core/Azure.Core/src/Shared/Utf8JsonReaderExtensions.cs b/sdk/core/Azure.Core/src/Shared/Utf8JsonReaderExtensions.cs new file mode 100644 index 000000000000..d261dc22e98b --- /dev/null +++ b/sdk/core/Azure.Core/src/Shared/Utf8JsonReaderExtensions.cs @@ -0,0 +1,166 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#nullable disable + +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json; +using Azure.Core.Serialization; + +namespace Azure.Core +{ + internal static class Utf8JsonReaderExtensions + { + public delegate void PropertySetter(ReadOnlySpan propertyName, ref T properties, ref Utf8JsonReader reader, ModelSerializerOptions options); + private delegate object ReadValue(ref Utf8JsonReader reader); + + private static readonly Dictionary _readerActions = new Dictionary() + { + { typeof(string), (ref Utf8JsonReader reader) => reader.GetString() }, + { typeof(bool), (ref Utf8JsonReader reader) => reader.GetBoolean() }, + { typeof(int), (ref Utf8JsonReader reader) => reader.GetInt32() }, + { typeof(long), (ref Utf8JsonReader reader) => reader.GetInt64() }, + { typeof(double), (ref Utf8JsonReader reader) => reader.GetDouble() }, + { typeof(float), (ref Utf8JsonReader reader) => reader.GetSingle() }, + { typeof(decimal), (ref Utf8JsonReader reader) => reader.GetDecimal() }, + { typeof(DateTimeOffset), (ref Utf8JsonReader reader) => reader.GetDateTimeOffset() }, + { typeof(Guid), (ref Utf8JsonReader reader) => reader.GetGuid() }, + { typeof(byte[]), (ref Utf8JsonReader reader) => reader.GetBytesFromBase64() }, + }; + + private static readonly Dictionary> _keyConverter = new Dictionary>() + { + { typeof(int), str => Convert.ToInt32(str) }, + { typeof(string), str => str }, + }; + + private static string ConvertKeyToString(Type typeToConvert, string key) => key; + + public static List GetList(this ref Utf8JsonReader reader, ModelSerializerOptions options) + { + List list = new List(); + + if (reader.TokenType == JsonTokenType.Null) + return null; + + if (reader.TokenType != JsonTokenType.StartArray) + throw new InvalidOperationException("Expected StartArray token"); + + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndArray) + break; + + list.Add(reader.GetObject(options)); + } + + return list; + } + + public static Dictionary GetDictionary(this ref Utf8JsonReader reader, ModelSerializerOptions options) + where TKey : notnull + { + if (!_keyConverter.TryGetValue(typeof(TKey), out var converter)) + throw new InvalidOperationException($"Cannot use type {typeof(TKey).Name} as a key for a dictionary"); + + Dictionary dictionary = new Dictionary(); + reader.Read(); + + if (reader.TokenType == JsonTokenType.Null) + return null; + + if (reader.TokenType != JsonTokenType.StartObject) + throw new InvalidOperationException("Expected StartObject token"); + + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + break; + + if (reader.TokenType != JsonTokenType.PropertyName) + throw new InvalidOperationException("Expected PropertyName token"); + + var propertyName = reader.GetString(); + reader.Read(); + dictionary.Add((TKey)converter(propertyName), reader.GetObject(options)); + } + + return dictionary; + } + + public static T GetObject(this ref Utf8JsonReader reader, ModelSerializerOptions options) + { + var typeToConvert = typeof(T); + + if (_readerActions.TryGetValue(typeToConvert, out var readValue)) + { + return (T)readValue(ref reader); + } + + var model = Activator.CreateInstance(typeToConvert, true) as IJsonModelSerializable; + if (model is null) + throw new InvalidOperationException($"{typeToConvert.Name} does not implement {nameof(IJsonModelSerializable)}"); + + return (T)model.Deserialize(ref reader, options); + } + + public static bool TryDeserialize(this ref Utf8JsonReader reader, ModelSerializerOptions options, PropertySetter setProperty, out T properties) + where T : new() + { + properties = new T(); + + if (reader.TokenType != JsonTokenType.StartObject) + { + reader.Read(); + if (reader.TokenType == JsonTokenType.Null) + return false; + } + + if (reader.TokenType != JsonTokenType.StartObject) + throw new FormatException("Expected StartObject token"); + + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + break; + + if (reader.TokenType != JsonTokenType.PropertyName) + throw new FormatException("Expected PropertyName token"); + + setProperty(reader.ValueSpan, ref properties, ref reader, options); + } + + return true; + } + + public delegate void PropertySetter2(ReadOnlySpan propertyName, ref Utf8JsonReader reader, ModelSerializerOptions options); + + public static bool TryDeserialize(this ref Utf8JsonReader reader, ModelSerializerOptions options, PropertySetter2 setProperty) + { + if (reader.TokenType != JsonTokenType.StartObject) + { + reader.Read(); + if (reader.TokenType == JsonTokenType.Null) + return false; + } + + if (reader.TokenType != JsonTokenType.StartObject) + throw new FormatException("Expected StartObject token"); + + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + break; + + if (reader.TokenType != JsonTokenType.PropertyName) + throw new FormatException("Expected PropertyName token"); + + setProperty(reader.ValueSpan, ref reader, options); + } + + return true; + } + } +} diff --git a/sdk/core/Azure.Core/tests/DynamicData/MutableJsonDocumentChangeTrackerTests.cs b/sdk/core/Azure.Core/tests/DynamicData/MutableJsonDocumentChangeTrackerTests.cs new file mode 100644 index 000000000000..68efd1585285 --- /dev/null +++ b/sdk/core/Azure.Core/tests/DynamicData/MutableJsonDocumentChangeTrackerTests.cs @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.Json; +using Azure.Core.Json; +using NUnit.Framework; + +namespace Azure.Core.Tests +{ + internal class MutableJsonDocumentChangeTrackerTests + { + [Test] + public void CanCompareChangesByPath() + { + char delimiter = MutableJsonDocument.ChangeTracker.Delimiter; + + // Test IsLessThan + Assert.IsTrue(CreateChange("a").IsLessThan(CreateChange("b"))); + Assert.IsTrue(CreateChange("a").IsLessThan(CreateChange($"a{delimiter}a"))); + + Assert.IsFalse(CreateChange("a").IsLessThan(CreateChange("a"))); + + Assert.IsFalse(CreateChange("b").IsLessThan(CreateChange("a"))); + Assert.IsFalse(CreateChange($"a{delimiter}a").IsLessThan(CreateChange("a"))); + + // Test IsGreaterThan + Assert.IsTrue(CreateChange("b").IsGreaterThan(CreateChange("a"))); + Assert.IsTrue(CreateChange($"a{delimiter}a").IsGreaterThan(CreateChange("a"))); + + Assert.IsFalse(CreateChange("a").IsGreaterThan(CreateChange("a"))); + + Assert.IsFalse(CreateChange("a").IsGreaterThan(CreateChange("b"))); + Assert.IsFalse(CreateChange("a").IsGreaterThan(CreateChange($"a{delimiter}a"))); + } + + [Test] + public void CanGetSortedChanges() + { + MutableJsonDocument.ChangeTracker changeTracker = new(null); + char delimiter = MutableJsonDocument.ChangeTracker.Delimiter; + + changeTracker.AddChange("a", 1); + changeTracker.AddChange("b", 1); + changeTracker.AddChange($"a{delimiter}a", 1); + changeTracker.AddChange("a", 2); + + List changes = new(); + + MutableJsonChange? change = changeTracker.GetNextChange(null, out int length); + + Assert.AreEqual(3, length); + + while (change != null) + { + changes.Add(change.Value); + change = changeTracker.GetNextChange(change, out length); + } + + // Note, descendants are ignored + Assert.AreEqual(2, changes.Count); + Assert.AreEqual("a", changes[0].Path); + Assert.AreEqual("b", changes[1].Path); + } + + #region Helpers + private MutableJsonChange CreateChange(string name) + { + return new MutableJsonChange(name, -1, null, null, MutableJsonChangeKind.PropertyValue, null); + } + #endregion + } +} diff --git a/sdk/core/Azure.Core/tests/DynamicData/MutableJsonDocumentPatchTests.cs b/sdk/core/Azure.Core/tests/DynamicData/MutableJsonDocumentPatchTests.cs new file mode 100644 index 000000000000..e076ea153170 --- /dev/null +++ b/sdk/core/Azure.Core/tests/DynamicData/MutableJsonDocumentPatchTests.cs @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.IO; +using Azure.Core.Json; +using NUnit.Framework; + +namespace Azure.Core.Tests +{ + internal class MutableJsonDocumentPatchTests + { + [Test] + public void CanGetChanges() + { + string json = """ + { + "parent": { + "child": { + "a": "a", + "b": "b" + } + } + } + """; + + MutableJsonDocument mdoc = MutableJsonDocument.Parse(json); + + // Updates - 1 + mdoc.RootElement.GetProperty("parent").GetProperty("child").GetProperty("a").Set("a1"); + mdoc.RootElement.GetProperty("parent").GetProperty("child").GetProperty("b").Set("b1"); + + // Updates - 2 + mdoc.RootElement.GetProperty("parent").SetProperty("child", new { b = "b2", c = "c2" }); + + // Updates - 3 + mdoc.RootElement.GetProperty("parent").GetProperty("child").GetProperty("b").Set("b3"); + + Assert.AreEqual(false, mdoc.RootElement.GetProperty("parent").GetProperty("child").TryGetProperty("a", out _)); + + // Note: these throw because property "a" is not there. + //Assert.AreEqual(null, mdoc.RootElement.GetProperty("parent").GetProperty("child").GetProperty("a").GetString()); + //Assert.AreEqual(JsonValueKind.Null, mdoc.RootElement.GetProperty("parent").GetProperty("child").GetProperty("a").GetJsonElement().ValueKind); + + // Note: The changelist currently won't see "a" as a removed property + // TODO: We'll need to add a diff routine to identify added and removed properties + // when whole objects are modified. + + using MemoryStream stream = new(); + mdoc.WriteTo(stream, 'J'); + + //mdoc.WriteTo(stream, 'P'); + } + } +} diff --git a/sdk/core/Azure.Core/tests/DynamicData/MutableJsonDocumentWriteToTests.cs b/sdk/core/Azure.Core/tests/DynamicData/MutableJsonDocumentWriteToTests.cs index 1de36a8910d1..297f3adc1ad9 100644 --- a/sdk/core/Azure.Core/tests/DynamicData/MutableJsonDocumentWriteToTests.cs +++ b/sdk/core/Azure.Core/tests/DynamicData/MutableJsonDocumentWriteToTests.cs @@ -498,13 +498,31 @@ public void CanWriteToStreamWithJsonFormat() } [Test] - public void CannotWriteToStreamWithPatchFormat() + public void CanWriteToStreamWithPatchFormat() + { + string json = """{"foo":1}"""; + MutableJsonDocument mdoc = MutableJsonDocument.Parse(json); + + mdoc.RootElement.GetProperty("foo").Set(2); + + using Stream stream = new MemoryStream(); + mdoc.WriteTo(stream, 'P'); + stream.Flush(); + stream.Position = 0; + + string actual = BinaryData.FromStream(stream).ToString(); + + Assert.AreEqual("""{"foo":2}""", actual); + } + + [Test] + public void CannotWriteToStreamWithUnknownFormat() { string json = """{ "foo" : 1 }"""; MutableJsonDocument mdoc = MutableJsonDocument.Parse(json); Stream stream = new MemoryStream(); - Assert.Throws(() => mdoc.WriteTo(stream, 'P')); + Assert.Throws(() => mdoc.WriteTo(stream, 'U')); } [TestCaseSource(nameof(TestCases))] @@ -652,6 +670,655 @@ public void CanWriteDateTimeOffset() MutableJsonDocumentTests.ValidateWriteTo($"{{\"foo\":\"{fooString}\",\"bar\":\"{barString}\"}}", mdoc); } + #region JSON Merge Patch + + // TODO: Validate that all of these are appropriately represented + // Test case: properties on the same descendant object are updated + // between updates to different descendant objects, but they group + // together correctly in the PATCH JSON. + + // Test case: ancestor changes then descendant changes + // Test case: descendant changes then ancestor changes + + // Test case: array elements change + // Test case: enum values change + // Test case: new property is added + // Test case: property is deleted + + [Test] + public void CanWritePatchChangeRoot() + { + string json = """{"foo": 1}"""; + MutableJsonDocument mdoc = MutableJsonDocument.Parse(json); + + mdoc.RootElement.GetProperty("foo").Set(2); + + using Stream stream = new MemoryStream(); + mdoc.WriteTo(stream, 'P'); + stream.Flush(); + stream.Position = 0; + + string actual = BinaryData.FromStream(stream).ToString(); + + AreEqualJson("""{"foo": 2}""", actual); + } + + [Test] + public void CanWritePatchChangeRootOnePropertyChangesAnotherDoesnt() + { + string json = """{"foo": 1, "bar": "a"}"""; + MutableJsonDocument mdoc = MutableJsonDocument.Parse(json); + + mdoc.RootElement.GetProperty("foo").Set(2); + + using Stream stream = new MemoryStream(); + mdoc.WriteTo(stream, 'P'); + stream.Flush(); + stream.Position = 0; + + string actual = BinaryData.FromStream(stream).ToString(); + + AreEqualJson("""{"foo": 2}""", actual); + } + + [Test] + public void CanWritePatchChangeRootChangeMultipleProperties() + { + string json = """{"foo": 1, "bar": "a"}"""; + MutableJsonDocument mdoc = MutableJsonDocument.Parse(json); + + mdoc.RootElement.GetProperty("foo").Set(2); + mdoc.RootElement.GetProperty("bar").Set("b"); + + using Stream stream = new MemoryStream(); + mdoc.WriteTo(stream, 'P'); + stream.Flush(); + stream.Position = 0; + + string actual = BinaryData.FromStream(stream).ToString(); + + // TODO: does order matter? This fails: + //AreEqualJson("""{"foo": 2, "bar": "b"}""", actual); + + AreEqualJson("""{"bar": "b", "foo": 2}""", actual); + } + + [Test] + public void CanWritePatchChangeRootMultipleChangesSameProperty() + { + string json = """{"foo": 1, "bar": "a"}"""; + MutableJsonDocument mdoc = MutableJsonDocument.Parse(json); + + mdoc.RootElement.GetProperty("foo").Set(2); + mdoc.RootElement.GetProperty("foo").Set(3); + + using Stream stream = new MemoryStream(); + mdoc.WriteTo(stream, 'P'); + stream.Flush(); + stream.Position = 0; + + string actual = BinaryData.FromStream(stream).ToString(); + + AreEqualJson("""{"foo": 3}""", actual); + } + + [Test] + public void CanWritePatchForNonRootElement() + { + string json = """ + { + "parent": { + "child": true + } + } + """; + MutableJsonDocument mdoc = MutableJsonDocument.Parse(json); + + mdoc.RootElement.GetProperty("parent").GetProperty("child").Set(false); + + using Stream stream = new MemoryStream(); + mdoc.WriteTo(stream, 'P'); + stream.Flush(); + stream.Position = 0; + + string actual = BinaryData.FromStream(stream).ToString(); + + AreEqualJson("""{"parent": {"child": false}}""", actual); + } + + [Test] + public void CanWritePatch_ThreeLevels() + { + string json = """ + { + "a": { + "b": { + "c": 1 + } + } + } + """; + MutableJsonDocument mdoc = MutableJsonDocument.Parse(json); + + mdoc.RootElement.GetProperty("a").GetProperty("b").GetProperty("c").Set(2); + + using Stream stream = new MemoryStream(); + mdoc.WriteTo(stream, 'P'); + stream.Flush(); + stream.Position = 0; + + string actual = BinaryData.FromStream(stream).ToString(); + + AreEqualJson("""{"a": {"b": {"c": 2}}}""", actual); + } + + [Test] + public void CanWritePatch_ThreeLevelPeers() + { + string json = """ + { + "a": { + "b": { + "c": 1 + } + }, + "d": { + "e": { + "f": 1 + } + } + } + """; + MutableJsonDocument mdoc = MutableJsonDocument.Parse(json); + + mdoc.RootElement.GetProperty("a").GetProperty("b").GetProperty("c").Set(2); + mdoc.RootElement.GetProperty("d").GetProperty("e").GetProperty("f").Set(3); + + using Stream stream = new MemoryStream(); + mdoc.WriteTo(stream, 'P'); + stream.Flush(); + stream.Position = 0; + + string actual = BinaryData.FromStream(stream).ToString(); + + AreEqualJson("""{"a": {"b": {"c": 2}}, "d": {"e": {"f": 3}}}""", actual); + } + + [Test] + public void CanWritePatch_ThreeLevelsNested() + { + string json = """ + { + "a": { + "b": { + "c": 1 + }, + "bb": { + "cc": 1 + } + }, + "d": { + "e": { + "f": 1 + } + } + } + """; + MutableJsonDocument mdoc = MutableJsonDocument.Parse(json); + + mdoc.RootElement.GetProperty("a").GetProperty("b").GetProperty("c").Set(2); + mdoc.RootElement.GetProperty("a").GetProperty("bb").GetProperty("cc").Set(3); + mdoc.RootElement.GetProperty("d").GetProperty("e").GetProperty("f").Set(4); + + using Stream stream = new MemoryStream(); + mdoc.WriteTo(stream, 'P'); + stream.Flush(); + stream.Position = 0; + + string actual = BinaryData.FromStream(stream).ToString(); + + AreEqualJson("""{"a": {"b": {"c": 2}, "bb": {"cc": 3}}, "d": {"e": {"f": 4}}}""", actual); + } + + [Test] + public void CanWritePatchInterleaveChildObjectChanges() + { + string json = """ + { + "a": { + "aa": 1, + "ab": 2 + }, + "b": { + "ba": "1", + "bb": "2" + } + } + """; + MutableJsonDocument mdoc = MutableJsonDocument.Parse(json); + + mdoc.RootElement.GetProperty("a").GetProperty("aa").Set(3); + mdoc.RootElement.GetProperty("b").GetProperty("ba").Set("3"); + mdoc.RootElement.GetProperty("a").GetProperty("ab").Set(4); + mdoc.RootElement.GetProperty("b").GetProperty("ba").Set("4"); + mdoc.RootElement.GetProperty("a").GetProperty("aa").Set(5); + + string expected = """ + { + "a": { + "aa": 5, + "ab": 4 + }, + "b": { + "ba": "4" + } + } + """; + + using Stream stream = new MemoryStream(); + mdoc.WriteTo(stream, 'P'); + stream.Flush(); + stream.Position = 0; + + string actual = BinaryData.FromStream(stream).ToString(); + + AreEqualJson(expected, actual); + } + + [Test] + public void CanWritePatchInterleaveParentAndChildChanges() + { + string json = """ + { + "a": { + "aa": 1, + "ab": 2 + }, + "b": { + "ba": "1", + "bb": "2" + } + } + """; + 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" }); + mdoc.RootElement.GetProperty("a").GetProperty("ab").Set(4); + mdoc.RootElement.GetProperty("b").GetProperty("ba").Set("5"); + mdoc.RootElement.GetProperty("a").GetProperty("aa").Set(5); + + string expected = """ + { + "a": { + "aa": 5, + "ab": 4 + }, + "b": { + "ba": "5", + "bb": "4" + } + } + """; + + using Stream stream = new MemoryStream(); + mdoc.WriteTo(stream, 'P'); + stream.Flush(); + stream.Position = 0; + + string actual = BinaryData.FromStream(stream).ToString(); + + AreEqualJson(expected, actual); + } + + [Test] + public void CanWritePatchToArrayElement() + { + throw new NotImplementedException(); + } + + [Test] + public void CanWritePatchReplaceObject() + { + string json = """ + { + "a": { + "aa": 1, + "ab": 2 + }, + "b": { + "ba": "1", + "bb": "2" + } + } + """; + MutableJsonDocument mdoc = MutableJsonDocument.Parse(json); + + mdoc.RootElement.GetProperty("a").Set(new { aa = 3, ab = 4 }); + + string expected = """ + { + "a": { + "aa": 3, + "ab": 4 + } + } + """; + + using Stream stream = new MemoryStream(); + mdoc.WriteTo(stream, 'P'); + stream.Flush(); + stream.Position = 0; + + string actual = BinaryData.FromStream(stream).ToString(); + + AreEqualJson(expected, actual); + } + + [Test] + public void CanWritePatchReplaceObject_Deletions() + { + string json = """ + { + "a": { + "aa": 1, + "ab": 2 + }, + "b": { + "ba": "1", + "bb": "2" + } + } + """; + MutableJsonDocument mdoc = MutableJsonDocument.Parse(json); + + mdoc.RootElement.GetProperty("a").Set(new { ac = 3 }); + + string expected = """ + { + "a": { + "aa": null, + "ab": null, + "ac": 3 + } + } + """; + + using Stream stream = new MemoryStream(); + mdoc.WriteTo(stream, 'P'); + stream.Flush(); + stream.Position = 0; + + string actual = BinaryData.FromStream(stream).ToString(); + + AreEqualJson(expected, actual); + } + + [Test] + public void CanWritePatchAddProperty() + { + string json = """ + { + "a": { + "aa": 1, + "ab": 2 + }, + "b": { + "ba": "1", + "bb": "2" + } + } + """; + MutableJsonDocument mdoc = MutableJsonDocument.Parse(json); + + mdoc.RootElement.SetProperty("c", 3); + + string expected = """ + { + "c": 3 + } + """; + + using Stream stream = new MemoryStream(); + mdoc.WriteTo(stream, 'P'); + stream.Flush(); + stream.Position = 0; + + string actual = BinaryData.FromStream(stream).ToString(); + + AreEqualJson(expected, actual); + } + + [Test] + public void CanWritePatchAddNestedProperty() + { + string json = """ + { + "a": { + "aa": 1, + "ab": 2 + }, + "b": { + "ba": "1", + "bb": "2" + } + } + """; + MutableJsonDocument mdoc = MutableJsonDocument.Parse(json); + + mdoc.RootElement.GetProperty("b").SetProperty("bc", "3"); + + string expected = """ + { + "b": { + "bc": "3" + } + } + """; + + using Stream stream = new MemoryStream(); + mdoc.WriteTo(stream, 'P'); + stream.Flush(); + stream.Position = 0; + + string actual = BinaryData.FromStream(stream).ToString(); + + AreEqualJson(expected, actual); + } + + [Test] + public void CanWritePatchAddObject() + { + string json = """ + { + "a": { + "aa": 1, + "ab": 2 + }, + "b": { + "ba": "1", + "bb": "2" + } + } + """; + MutableJsonDocument mdoc = MutableJsonDocument.Parse(json); + + mdoc.RootElement.SetProperty("c", new { ca = true, cb = false }); + + string expected = """ + { + "c": { + "ca": true, + "cb": false + } + } + """; + + using Stream stream = new MemoryStream(); + mdoc.WriteTo(stream, 'P'); + stream.Flush(); + stream.Position = 0; + + string actual = BinaryData.FromStream(stream).ToString(); + + AreEqualJson(expected, actual); + } + + [Test] + public void CanWritePatchDeleteProperty_SetNull() + { + string json = """ + { + "a": { + "aa": 1, + "ab": 2 + }, + "b": { + "ba": "1", + "bb": "2" + } + } + """; + MutableJsonDocument mdoc = MutableJsonDocument.Parse(json); + + mdoc.RootElement.GetProperty("a").GetProperty("aa").Set(null); + + string expected = """ + { + "a": { + "aa": null + } + } + """; + + using Stream stream = new MemoryStream(); + mdoc.WriteTo(stream, 'P'); + stream.Flush(); + stream.Position = 0; + + string actual = BinaryData.FromStream(stream).ToString(); + + AreEqualJson(expected, actual); + } + + [Test] + public void CanWritePatchDeleteProperty() + { + string json = """ + { + "a": { + "aa": 1, + "ab": 2 + }, + "b": { + "ba": "1", + "bb": "2" + } + } + """; + MutableJsonDocument mdoc = MutableJsonDocument.Parse(json); + + mdoc.RootElement.GetProperty("a").RemoveProperty("aa"); + + string expected = """ + { + "a": { + "aa": null + } + } + """; + + using Stream stream = new MemoryStream(); + mdoc.WriteTo(stream, 'P'); + stream.Flush(); + stream.Position = 0; + + string actual = BinaryData.FromStream(stream).ToString(); + + AreEqualJson(expected, actual); + } + + [Test] + public void CanWritePatchDeleteObject() + { + string json = """ + { + "a": { + "aa": 1, + "ab": 2 + }, + "b": { + "ba": "1", + "bb": "2" + } + } + """; + MutableJsonDocument mdoc = MutableJsonDocument.Parse(json); + + mdoc.RootElement.RemoveProperty("b"); + + string expected = """ + { + "b": null + } + """; + + using Stream stream = new MemoryStream(); + mdoc.WriteTo(stream, 'P'); + stream.Flush(); + stream.Position = 0; + + string actual = BinaryData.FromStream(stream).ToString(); + + AreEqualJson(expected, actual); + } + + [Test] + public void CanWritePatchDeleteObject_SetNull() + { + string json = """ + { + "a": { + "aa": 1, + "ab": 2 + }, + "b": { + "ba": "1", + "bb": "2" + } + } + """; + MutableJsonDocument mdoc = MutableJsonDocument.Parse(json); + + mdoc.RootElement.GetProperty("b").Set(null); + + string expected = """ + { + "b": null + } + """; + + using Stream stream = new MemoryStream(); + mdoc.WriteTo(stream, 'P'); + stream.Flush(); + stream.Position = 0; + + string actual = BinaryData.FromStream(stream).ToString(); + + AreEqualJson(expected, actual); + } + + #endregion + + #region Helpers + private static void AreEqualJson(string expected, string actual) + { + JsonDocument doc = JsonDocument.Parse(expected); + BinaryData buffer = MutableJsonDocumentTests.GetWriteToBuffer(doc); + Assert.AreEqual(buffer.ToString(), actual); + } + public static IEnumerable TestCases() { yield return """ @@ -770,7 +1437,6 @@ public static IEnumerable TestCases() """; } - #region Helpers private class TestClass { public string StringProperty { get; set; } diff --git a/sdk/core/Azure.Core/tests/HttpPipelineRequestContentTests.cs b/sdk/core/Azure.Core/tests/HttpPipelineRequestContentTests.cs index 6d58550f1a3f..925494b27e75 100644 --- a/sdk/core/Azure.Core/tests/HttpPipelineRequestContentTests.cs +++ b/sdk/core/Azure.Core/tests/HttpPipelineRequestContentTests.cs @@ -196,5 +196,48 @@ public void CamelCaseContent() CollectionAssert.AreEqual(expected.ToArray(), destination.ToArray()); } + + [Test] + public void JsonMergePatchContent() + { + ReadOnlySpan utf8Json = """ + { + "value": { + "a": true, + "b": 1, + "c": "unmodified value" + } + } + """u8; + ReadOnlyMemory json = new ReadOnlyMemory(utf8Json.ToArray()); + + using dynamic content = new BinaryData(json).ToDynamicFromJson(); + content.value.a = null; + content.value.b = 2; + + using RequestContent patch = RequestContent.CreatePatch(content); + + ReadOnlySpan patchJson = """ + { + "value": { + "a": null, + "b": 2 + } + } + """u8; + ReadOnlyMemory expected = new ReadOnlyMemory(patchJson.ToArray()); + + //using JsonDocument expectedDoc = JsonDocument.Parse(expected); + //using MemoryStream expectedStream = new(); + //using Utf8JsonWriter writer = new(expectedStream); + //expectedDoc.WriteTo(writer); + //writer.Flush(); + //expectedStream.Position = 0; + + using MemoryStream destination = new(); + patch.WriteTo(destination, default); + + CollectionAssert.AreEqual(expected.ToArray(), destination.ToArray()); + } } } diff --git a/sdk/core/Azure.Core/tests/TestClients/LowLevelClient/PetStoreClient.cs b/sdk/core/Azure.Core/tests/TestClients/LowLevelClient/PetStoreClient.cs index 6e98e17764de..5a41b6bbd9e4 100644 --- a/sdk/core/Azure.Core/tests/TestClients/LowLevelClient/PetStoreClient.cs +++ b/sdk/core/Azure.Core/tests/TestClients/LowLevelClient/PetStoreClient.cs @@ -95,6 +95,48 @@ public virtual Response GetPet(string id, RequestContext context = null) } } + /// Get a pet by its Id. + /// Id of pet to return. + /// The request context. +#pragma warning disable AZC0002 + public virtual async Task CreatePetAsync(string id, RequestContent content, RequestContext context = null) +#pragma warning restore AZC0002 + { + using var scope = _clientDiagnostics.CreateScope("PetStoreClient.GetPet"); + scope.Start(); + try + { + using HttpMessage message = CreateGetPetRequest(id, context); + return await Pipeline.ProcessMessageAsync(message, context).ConfigureAwait(false); + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + + /// Get a pet by its Id. + /// Id of pet to return. + /// The request context. +#pragma warning disable AZC0002 + public virtual Response CreatePet(string id, RequestContent content, RequestContext context = null) +#pragma warning restore AZC0002 + { + using var scope = _clientDiagnostics.CreateScope("PetStoreClient.GetPet"); + scope.Start(); + try + { + using HttpMessage message = CreateGetPetRequest(id, context); + return Pipeline.ProcessMessage(message, context); + } + catch (Exception e) + { + scope.Failed(e); + throw; + } + } + /// Create Request for and operations. /// Id of pet to return. /// The request context. diff --git a/sdk/core/Azure.Core/tests/public/Azure.Core.Tests.Public.csproj b/sdk/core/Azure.Core/tests/public/Azure.Core.Tests.Public.csproj index 729b5f2a54e6..dd376c6e9c68 100644 --- a/sdk/core/Azure.Core/tests/public/Azure.Core.Tests.Public.csproj +++ b/sdk/core/Azure.Core/tests/public/Azure.Core.Tests.Public.csproj @@ -1,6 +1,8 @@  $(RequiredTargetFrameworks) + true + true @@ -8,6 +10,7 @@ + @@ -16,8 +19,34 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + Always + diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/AvailabilitySetDataTests.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/AvailabilitySetDataTests.cs new file mode 100644 index 000000000000..8e6279389f15 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/AvailabilitySetDataTests.cs @@ -0,0 +1,201 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Text; +using System.Text.Json; +using Azure.Core.Serialization; +using Azure.Core.Tests.Public.ResourceManager.Compute; +using Microsoft.Extensions.Options; +using NUnit.Framework; + +namespace Azure.Core.Tests.Public.ModelSerializationTests +{ + internal class AvailabilitySetDataTests + { + private const string _serviceResponse = "{\"name\":\"testAS-3375\",\"id\":\"/subscriptions/e37510d7-33b6-4676-886f-ee75bcc01871/resourceGroups/testRG-6497/providers/Microsoft.Compute/availabilitySets/testAS-3375\",\"type\":\"Microsoft.Compute/availabilitySets\",\"location\":\"eastus\",\"tags\":{\"key\":\"value\"},\"properties\":{\"platformUpdateDomainCount\":5,\"platformFaultDomainCount\":3},\"sku\":{\"name\":\"Classic\",\"extraSku\":\"extraSku\"},\"extraRoot\":\"extraRoot\"}"; + + [TestCase("W")] + [TestCase("J")] + public void RoundTripTest(string format) => + RoundTripTest(format, SerializeWithModelSerializer, DeserializeWithModelSerializer); + + [TestCase("W")] + [TestCase("J")] + public void BufferTest(string format) => + RoundTripTest(format, SerializeWithBuffer, DeserializeWithModelSerializer); + + [Test] + public void ImplicitCastTest() => + RoundTripTest(ModelSerializerFormat.Wire, SerializeWithImplicitCast, DeserializeWithModelSerializer); + + [TestCase("W")] + [TestCase("J")] + public void JsonReaderTest(string format) => + RoundTripTest(format, SerializeWithModelSerializer, DeserializeWithJsonReader); + + [TestCase("W")] + [TestCase("J")] + public void UsingSequence(string format) => + RoundTripTest(format, SerializeWithModelSerializer, DeserializeWithSequence); + + [TestCase("W")] + [TestCase("J")] + public void UseNonGeneric(string format) => + RoundTripTest(format, SerializeWithModelSerializerNonGeneric, DeserializeWithModelSerializerNonGeneric); + + [Test] + public void UseInternal() => + RoundTripTest("W", SerializeWithInternal, DeserializeWithInternal); + + private void RoundTripTest(string format, Func serialize, Func deserialize) + { + ModelSerializerOptions options = new ModelSerializerOptions(format); + + var expectedSerializedString = "{"; + if (format == ModelSerializerFormat.Json) + expectedSerializedString += "\"name\":\"testAS-3375\",\"id\":\"/subscriptions/e37510d7-33b6-4676-886f-ee75bcc01871/resourceGroups/testRG-6497/providers/Microsoft.Compute/availabilitySets/testAS-3375\",\"type\":\"Microsoft.Compute/availabilitySets\","; + expectedSerializedString += "\"sku\":{\"name\":\"Classic\""; + //if (!ignoreAdditionalProperties) + // expectedSerializedString += ",\"extraSku\":\"extraSku\""; + expectedSerializedString += "},\"tags\":{\"key\":\"value\"},\"location\":\"eastus\",\"properties\":{\"platformUpdateDomainCount\":5,\"platformFaultDomainCount\":3}"; + //if (!ignoreAdditionalProperties) + // expectedSerializedString += ",\"extraRoot\":\"extraRoot\""; + expectedSerializedString += "}"; + + AvailabilitySetData model = deserialize(_serviceResponse, options); + + ValidateModel(model); + string roundTrip = serialize(model, options); + + Assert.That(roundTrip, Is.EqualTo(expectedSerializedString)); + + AvailabilitySetData model2 = deserialize(roundTrip, options); + CompareModels(model, model2, format); + } + + private AvailabilitySetData DeserializeWithJsonReader(string json, ModelSerializerOptions options) + { + Utf8JsonReader reader = new Utf8JsonReader(Encoding.UTF8.GetBytes(json)); + var model = Activator.CreateInstance(typeof(AvailabilitySetData), true) as IJsonModelSerializable; + return (AvailabilitySetData)model.Deserialize(ref reader, options); + } + + private AvailabilitySetData DeserializeWithSequence(string json, ModelSerializerOptions options) + { + using SequenceWriter writer = WriteStringToBuffer(json); + var sequence = writer.GetReadOnlySequence(); + using var doc = JsonDocument.Parse(writer.GetReadOnlySequence()); + return AvailabilitySetData.DeserializeAvailabilitySetData(doc.RootElement); + } + + private AvailabilitySetData DeserializeWithModelSerializer(string json, ModelSerializerOptions options) + { + return ModelSerializer.Deserialize(new BinaryData(Encoding.UTF8.GetBytes(json)), options); + } + private AvailabilitySetData DeserializeWithInternal(string json, ModelSerializerOptions options) + { + using JsonDocument doc = JsonDocument.Parse(json); + return AvailabilitySetData.DeserializeAvailabilitySetData(doc.RootElement); + } + + private AvailabilitySetData DeserializeWithModelSerializerNonGeneric(string json, ModelSerializerOptions options) + { + return (AvailabilitySetData)ModelSerializer.Deserialize(new BinaryData(Encoding.UTF8.GetBytes(json)), typeof(AvailabilitySetData), options); + } + + private SequenceWriter WriteStringToBuffer(string json) + { + SequenceWriter writer = new SequenceWriter(); + var bytes = Encoding.UTF8.GetBytes(json); + int fullBuffers = bytes.Length / 4096; + for (int i = 0; i < fullBuffers; i++) + { + int index = i * 4096; + bytes.AsSpan(index, 4096).CopyTo(writer.GetSpan(4096)); + writer.Advance(4096); + } + var remainder = bytes.Length % 4096; + bytes.AsSpan(fullBuffers * 4096, remainder).CopyTo(writer.GetSpan(remainder)); + writer.Advance(remainder); + return writer; + } + + private string SerializeWithImplicitCast(AvailabilitySetData model, ModelSerializerOptions options) + { + RequestContent content = model; + return GetStringFromContent(content); + } + + private string SerializeWithModelSerializer(AvailabilitySetData model, ModelSerializerOptions options) + { + var data = ModelSerializer.Serialize(model, options); + return data.ToString(); + } + + private string SerializeWithModelSerializerNonGeneric(object model, ModelSerializerOptions options) + { + var data = ModelSerializer.Serialize(model, options); + return data.ToString(); + } + + private string SerializeWithInternal(AvailabilitySetData model, ModelSerializerOptions options) + { + using MemoryStream stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream); + model.Serialize(writer); + writer.Flush(); + stream.Position = 0; + using var reader = new StreamReader(stream); + return reader.ReadToEnd(); + } + + private string SerializeWithBuffer(AvailabilitySetData model, ModelSerializerOptions options) + { + using var sequenceWriter = new SequenceWriter(); + using var writer = new Utf8JsonWriter(sequenceWriter); + ((IJsonModelSerializable)model).Serialize(writer, options); + writer.Flush(); + return GetStringFromContent(RequestContent.Create(sequenceWriter)); + } + + private static string GetStringFromContent(RequestContent content) + { + MemoryStream stream = new MemoryStream(); + content.WriteTo(stream, default); + stream.Position = 0; + StreamReader reader = new StreamReader(stream); + return reader.ReadToEnd(); + } + + private void CompareModels(AvailabilitySetData model, AvailabilitySetData model2, string format) + { + Assert.AreEqual(format == ModelSerializerFormat.Wire ? null : model.Id, model2.Id); + Assert.AreEqual(model.Location, model2.Location); + Assert.AreEqual(format == ModelSerializerFormat.Wire ? null : model.Name, model2.Name); + Assert.AreEqual(model.PlatformFaultDomainCount, model2.PlatformFaultDomainCount); + Assert.AreEqual(model.PlatformUpdateDomainCount, model2.PlatformUpdateDomainCount); + if (format == ModelSerializerFormat.Json) + Assert.AreEqual(model.ResourceType, model2.ResourceType); + CollectionAssert.AreEquivalent(model.Tags, model2.Tags); + Assert.AreEqual(model.Sku.Name, model2.Sku.Name); + } + + private void ValidateModel(AvailabilitySetData model) + { + Dictionary expectedTags = new Dictionary() { { "key", "value" } }; + + Assert.AreEqual("/subscriptions/e37510d7-33b6-4676-886f-ee75bcc01871/resourceGroups/testRG-6497/providers/Microsoft.Compute/availabilitySets/testAS-3375", model.Id.ToString()); + CollectionAssert.AreEquivalent(expectedTags, model.Tags); + Assert.AreEqual(AzureLocation.EastUS, model.Location); + Assert.AreEqual("testAS-3375", model.Name); + Assert.AreEqual("Microsoft.Compute/availabilitySets", model.ResourceType.ToString()); + Assert.AreEqual(5, model.PlatformUpdateDomainCount); + Assert.AreEqual(3, model.PlatformFaultDomainCount); + Assert.AreEqual("Classic", model.Sku.Name); + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/CombinedInterfaceTests.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/CombinedInterfaceTests.cs new file mode 100644 index 000000000000..4dbed87cd2fb --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/CombinedInterfaceTests.cs @@ -0,0 +1,127 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.IO; +using System.Text; +using NUnit.Framework; +using Azure.Core.Tests.Public.ModelSerializationTests.Models; +using Azure.Core.Serialization; +using System.Collections.Generic; +using System; + +namespace Azure.Core.Tests.Public.ModelSerializationTests +{ + internal class CombinedInterfaceTests + { + private const string _xmlServiceResponse = + "" + + "Color" + + "Red" + + "ReadOnly" + + ""; + + private const string _jsonServiceResponse = "{\"key\":\"Color\",\"value\":\"Red\",\"readOnlyProperty\":\"ReadOnly\"}"; + + [Test] + public void RoundTripXmlModelWithWire() => CanRoundTripFutureVersionWithoutLossXml(ModelSerializerFormat.Wire, _xmlServiceResponse); + + [Test] + public void RoundTripXmlModelWithJson() => CanRoundTripFutureVersionWithoutLossXml(ModelSerializerFormat.Json, _jsonServiceResponse); + + private void CanRoundTripFutureVersionWithoutLossXml(ModelSerializerFormat format, string serviceResponse) + { + ModelSerializerOptions options = new ModelSerializerOptions(format); + + var expectedSerializedString = GetExpectedResult(format); + + XmlModelForCombinedInterface model = ModelSerializer.Deserialize(new BinaryData(Encoding.UTF8.GetBytes(serviceResponse)), options); + + Assert.AreEqual("Color", model.Key); + Assert.AreEqual("Red", model.Value); + Assert.AreEqual("ReadOnly", model.ReadOnlyProperty); + var data = ModelSerializer.Serialize(model, options); + string roundTrip = data.ToString(); + + Assert.That(roundTrip, Is.EqualTo(expectedSerializedString)); + + XmlModelForCombinedInterface model2 = ModelSerializer.Deserialize(new BinaryData(Encoding.UTF8.GetBytes(roundTrip)), options); + VerifyModelXmlModelForCombinedInterface(model, model2, format); + } + + [TestCase("J")] + [TestCase("W")] + public void CanRoundTripFutureVersionWithoutLossJson(string format) + { + ModelSerializerOptions options = new ModelSerializerOptions(format); + + string serviceResponse = "{\"key\":\"Color\",\"value\":\"Red\",\"readOnlyProperty\":\"ReadOnly\",\"x\":\"extra\"}"; + + var expectedSerializedString = "{\"key\":\"Color\",\"value\":\"Red\""; + if (format.Equals(ModelSerializerFormat.Json)) + { + expectedSerializedString += ",\"readOnlyProperty\":\"ReadOnly\""; + expectedSerializedString += ",\"x\":\"extra\""; + } + expectedSerializedString += "}"; + + JsonModelForCombinedInterface model = ModelSerializer.Deserialize(new BinaryData(Encoding.UTF8.GetBytes(serviceResponse)), options); + + Assert.AreEqual("Color", model.Key); + Assert.AreEqual("Red", model.Value); + Assert.AreEqual("ReadOnly", model.ReadOnlyProperty); + var additionalProperties = typeof(JsonModelForCombinedInterface).GetProperty("RawData", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(model) as Dictionary; + Assert.IsNotNull(additionalProperties); + Assert.AreEqual(format.Equals(ModelSerializerFormat.Json), additionalProperties.ContainsKey("x")); + var data = ModelSerializer.Serialize(model, options); + string roundTrip = data.ToString(); + + Assert.That(roundTrip, Is.EqualTo(expectedSerializedString)); + + JsonModelForCombinedInterface model2 = ModelSerializer.Deserialize(new BinaryData(Encoding.UTF8.GetBytes(roundTrip)), options); + VerifyModelJsonModelForCombinedInterface(model, model2, format); + } + + internal static void VerifyModelXmlModelForCombinedInterface(XmlModelForCombinedInterface expected, XmlModelForCombinedInterface actual, string format) + { + Assert.AreEqual(expected.Key, actual.Key); + Assert.AreEqual(expected.Value, actual.Value); + if (format.Equals(ModelSerializerFormat.Json)) + Assert.AreEqual(expected.ReadOnlyProperty, actual.ReadOnlyProperty); + } + + internal static void VerifyModelJsonModelForCombinedInterface(JsonModelForCombinedInterface expected, JsonModelForCombinedInterface actual, string format) + { + Assert.AreEqual(expected.Key, actual.Key); + Assert.AreEqual(expected.Value, actual.Value); + if (format.Equals(ModelSerializerFormat.Json)) + Assert.AreEqual(expected.ReadOnlyProperty, actual.ReadOnlyProperty); + var rawDataProperty = typeof(JsonModelForCombinedInterface).GetProperty("RawData", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic); + var expectedRawData = rawDataProperty.GetValue(expected) as Dictionary; + var actualRawData = rawDataProperty.GetValue(actual) as Dictionary; + Assert.AreEqual(expectedRawData.Count, actualRawData.Count); + if (format.Equals(ModelSerializerFormat.Json)) + Assert.AreEqual(expectedRawData["x"].ToString(), actualRawData["x"].ToString()); + } + + private string GetExpectedResult(ModelSerializerFormat format) + { + if (format == ModelSerializerFormat.Wire) + { + var expectedSerializedString = "\uFEFFColorRed"; + if (format.Equals(ModelSerializerFormat.Json)) + expectedSerializedString += "ReadOnly"; + expectedSerializedString += ""; + return expectedSerializedString; + } + if (format == ModelSerializerFormat.Json) + { + var expectedSerializedString = "{\"key\":\"Color\",\"value\":\"Red\""; + if (format.Equals(ModelSerializerFormat.Json)) + expectedSerializedString += ",\"readOnlyProperty\":\"ReadOnly\""; + expectedSerializedString += "}"; + return expectedSerializedString; + } + throw new InvalidOperationException($"Unknown format used in test {format}"); + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Envelope.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Envelope.cs new file mode 100644 index 000000000000..f73c13f7c0fa --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Envelope.cs @@ -0,0 +1,161 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Text.Json; +using Azure.Core.Serialization; +using Microsoft.Extensions.Options; +using Newtonsoft.Json.Linq; + +namespace Azure.Core.Tests.Public.ModelSerializationTests +{ + public class Envelope : IJsonModelSerializable, IUtf8JsonSerializable + { + private Dictionary RawData { get; set; } = new Dictionary(); + + public string ReadOnlyProperty { get; private set; } = "readonly"; + + public Envelope() + { + } + + public Envelope(string location) + { + ReadOnlyProperty = location; + } + + internal Envelope(string property, CatReadOnlyProperty cat, T modelC, Dictionary rawData) + { + ReadOnlyProperty = property; + ModelA = cat; + ModelT = modelC; + RawData = rawData; + } + + public CatReadOnlyProperty ModelA { get; set; } + public T ModelT { get; set; } + + #region Serialization + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => ((IJsonModelSerializable)this).Serialize(writer, ModelSerializerOptions.AzureServiceDefault); + + void IJsonModelSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) => Serialize(writer, options); + + private void Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + writer.WriteStartObject(); + if (options.Format == ModelSerializerFormat.Json) + { + writer.WritePropertyName("readOnlyProperty"u8); + writer.WriteStringValue(ReadOnlyProperty); + } + + writer.WritePropertyName("modelA"u8); + ((IJsonModelSerializable)ModelA).Serialize(writer, options); + writer.WritePropertyName("modelC"u8); + SerializeT(writer, options); + + if (options.Format == ModelSerializerFormat.Json) + { + //write out the raw data + foreach (var property in RawData) + { + writer.WritePropertyName(property.Key); +#if NET6_0_OR_GREATER + writer.WriteRawValue(property.Value); +#else + JsonSerializer.Serialize(writer, JsonDocument.Parse(property.Value.ToString()).RootElement); +#endif + } + } + writer.WriteEndObject(); + } + + internal static Envelope DeserializeEnvelope(JsonElement element, ModelSerializerOptions options) + { + string readonlyProperty = ""; + CatReadOnlyProperty modelA = new CatReadOnlyProperty(); + T modelC = default(T); + Dictionary rawData = new Dictionary(); + + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("readOnlyProperty"u8)) + { + readonlyProperty = property.Value.GetString(); + continue; + } + if (property.NameEquals("modelA"u8)) + { + modelA = CatReadOnlyProperty.DeserializeCatReadOnlyProperty(property.Value, options); + continue; + } + if (property.NameEquals("modelC"u8)) + { + modelC = DeserializeT(property.Value, options); + continue; + } + + if (options.Format == ModelSerializerFormat.Json) + { + //this means it's an modelC property we got + rawData.Add(property.Name, BinaryData.FromString(property.Value.GetRawText())); + } + } + return new Envelope(readonlyProperty, modelA, modelC, rawData); + } + + private void SerializeT(Utf8JsonWriter writer, ModelSerializerOptions options) + { + ObjectSerializer serializer = GetObjectSerializer(options); + BinaryData data = serializer.Serialize(ModelT); +#if NET6_0_OR_GREATER + writer.WriteRawValue(data); +#else + JsonSerializer.Serialize(writer, JsonDocument.Parse(data.ToString()).RootElement); +#endif + } + + private static ObjectSerializer GetObjectSerializer(ModelSerializerOptions options) + { + ObjectSerializer serializer; + if (options.Serializers.TryGetValue(typeof(T), out serializer)) + { + // serializer is from the dictionary + return serializer; + } + // default + return JsonObjectSerializer.Default; + } + + private static T DeserializeT(JsonElement element, ModelSerializerOptions options) + { + ObjectSerializer serializer = GetObjectSerializer(options); + MemoryStream m = new MemoryStream(); + Utf8JsonWriter w = new Utf8JsonWriter(m); + element.WriteTo(w); + w.Flush(); + m.Position = 0; + return (T)serializer.Deserialize(m, typeof(T), default); + } + + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + { + return DeserializeEnvelope(JsonDocument.Parse(data.ToString()).RootElement, options); + } + + object IJsonModelSerializable.Deserialize(ref Utf8JsonReader reader, ModelSerializerOptions options) + { + using var doc = JsonDocument.ParseValue(ref reader); + return DeserializeEnvelope(doc.RootElement, options); + } + + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) + { + return ModelSerializerHelper.SerializeToBinaryData((writer) => { Serialize(writer, options); }); + } + #endregion + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/EnvelopeTests.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/EnvelopeTests.cs new file mode 100644 index 000000000000..7d32d53f85ea --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/EnvelopeTests.cs @@ -0,0 +1,109 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.IO; +using System.Text; +using Azure.Core.Serialization; +using NUnit.Framework; +using Newtonsoft.Json; +using System; + +namespace Azure.Core.Tests.Public.ModelSerializationTests +{ + internal class EnvelopeTests + { + [TestCase("J")] + [TestCase("W")] + public void CanRoundTripFutureVersionWithoutLoss(string format) + { + string serviceResponse = + "{\"readOnlyProperty\":\"read\"," + + "\"modelA\":{\"name\":\"Cat\",\"isHungry\":false,\"weight\":2.5}," + + "\"modelC\":{\"x\":\"hello\",\"y\":\"bye\"}" + + "}"; + + StringBuilder expectedSerialized = new StringBuilder("{"); + if (format.Equals(ModelSerializerFormat.Json)) + { + expectedSerialized.Append("\"readOnlyProperty\":\"read\","); + } + expectedSerialized.Append("\"modelA\":{"); + if (format.Equals(ModelSerializerFormat.Json)) + { + expectedSerialized.Append("\"latinName\":\"Felis catus\",\"hasWhiskers\":false,"); + } + expectedSerialized.Append("\"name\":\"Cat\",\"isHungry\":false,\"weight\":2.5},"); + expectedSerialized.Append("\"modelC\":{\"X\":\"hello\",\"Y\":\"bye\"}"); //using NewtonSoft Serializer + expectedSerialized.Append("}"); + var expectedSerializedString = expectedSerialized.ToString(); + + ModelSerializerOptions options = new ModelSerializerOptions(format); + + if (format == ModelSerializerFormat.Wire) + { + JsonSerializerSettings settings = new JsonSerializerSettings + { + ContractResolver = new IgnoreReadOnlyPropertiesResolver() + }; + options.Serializers.Add(typeof(ModelC), new NewtonsoftJsonObjectSerializer(settings)); + } + else + options.Serializers.Add(typeof(ModelC), new NewtonsoftJsonObjectSerializer()); + + Envelope model = ModelSerializer.Deserialize>(new BinaryData(Encoding.UTF8.GetBytes(serviceResponse)), options: options); + + if (format.Equals(ModelSerializerFormat.Json)) + { + Assert.That(model.ReadOnlyProperty, Is.EqualTo("read")); + } + + CatReadOnlyProperty correctCat = new CatReadOnlyProperty(2.5, default, "Cat", false, default); + VerifyModels.CheckAnimals(correctCat, model.ModelA, options); + Assert.AreEqual("hello", model.ModelT.X); + Assert.AreEqual("bye", model.ModelT.Y); + var data = ModelSerializer.Serialize(model, options); + string roundTrip = data.ToString(); + + Assert.That(roundTrip, Is.EqualTo(expectedSerializedString)); + + var model2 = ModelSerializer.Deserialize>(new BinaryData(Encoding.UTF8.GetBytes(roundTrip)), options: options); + ModelC correctModelC = new ModelC("hello", "bye"); + ModelC.VerifyModelC(correctModelC, model2.ModelT); + } + + // Generate a class that implements the NewtonSoft default contract resolver so that ReadOnly properties are not serialized + // This is used to verify that the ReadOnly properties are not serialized when IgnoreReadOnlyProperties is set to true + private class IgnoreReadOnlyPropertiesResolver : Newtonsoft.Json.Serialization.DefaultContractResolver + { + protected override Newtonsoft.Json.Serialization.JsonProperty CreateProperty(System.Reflection.MemberInfo member, MemberSerialization memberSerialization) + { + Newtonsoft.Json.Serialization.JsonProperty property = base.CreateProperty(member, memberSerialization); + + if (!property.Writable) + { + property.ShouldSerialize = obj => false; + } + + return property; + } + } + + private class ModelC + { + public ModelC(string x1, string y1) + { + X = x1; + Y = y1; + } + + public string X { get; set; } + public string Y { get; set; } + + public static void VerifyModelC(ModelC c1, ModelC c2) + { + Assert.That(c1.X == c2.X); + Assert.That(c1.Y == c2.Y); + } + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ExplicitCastTests.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ExplicitCastTests.cs new file mode 100644 index 000000000000..58e86a7fd4ef --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ExplicitCastTests.cs @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Azure.Core.TestFramework; +using NUnit.Framework; + +namespace Azure.Core.Tests.Public.ModelSerializationTests +{ + public class ExplicitCastTests + { + [Test] + public void CastFromResponse() + { + string serviceResponse = "{\"latinName\":\"Animalia\",\"weight\":1.5,\"name\":\"Doggo\",\"isHungry\":false,\"foodConsumed\":[\"kibble\",\"egg\",\"peanut butter\"],\"numberOfLegs\":4}"; + var stream = new MemoryStream(Encoding.UTF8.GetBytes(serviceResponse)); + stream.Position = 0; + Response response = new MockResponse(200); + response.ContentStream = stream; + DogListProperty dog = (DogListProperty)response; + + Assert.AreEqual("Doggo", dog.Name); + Assert.AreEqual(false, dog.IsHungry); + Assert.AreEqual(1.5, dog.Weight); + Assert.AreEqual(new List { "kibble", "egg", "peanut butter" }, dog.FoodConsumed); + Assert.AreEqual("Animalia", dog.LatinName); + + var additionalProperties = typeof(Animal).GetProperty("RawData", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(dog) as Dictionary; + Assert.IsNotNull(additionalProperties); + Assert.IsFalse(additionalProperties.ContainsKey("numberOfLegs")); + + string expected = "{\"name\":\"Doggo\",\"isHungry\":false,\"weight\":1.5,\"foodConsumed\":[\"kibble\",\"egg\",\"peanut butter\"]}"; + + RequestContent content = dog; + var requestStream = new MemoryStream(); + content.WriteTo(requestStream, default); + requestStream.Position = 0; + string contentString = new StreamReader(requestStream).ReadToEnd(); + Assert.AreEqual(expected, contentString); + } + + [Test] + public void CastToRequestContent() + { + string requestContent = "{\"name\":\"Doggo\",\"isHungry\":false,\"weight\":1.5,\"foodConsumed\":[\"kibble\",\"egg\",\"peanut butter\"]}"; + var dog = new DogListProperty + { + Name = "Doggo", + IsHungry = false, + Weight = 1.5, + FoodConsumed = { "kibble", "egg", "peanut butter" }, + }; + RequestContent content = (RequestContent)dog; + var stream = new MemoryStream(); + content.WriteTo(stream, default); + stream.Position = 0; + string contentString = new StreamReader(stream).ReadToEnd(); + Assert.AreEqual(requestContent, contentString); + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ModelJsonConverterTests.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ModelJsonConverterTests.cs new file mode 100644 index 000000000000..1aea8bbadc28 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ModelJsonConverterTests.cs @@ -0,0 +1,203 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using Azure.Core.Serialization; +using Azure.Core.Tests.Public.ModelSerializationTests.Models; +using NUnit.Framework; + +namespace Azure.Core.Tests.Public.ModelSerializationTests +{ + public class ModelJsonConverterTests + { + [TestCase("J")] + [TestCase("W")] + public void SerializeTest(string format) + { + JsonSerializerOptions options = new JsonSerializerOptions(); + options.Converters.Add(new ModelJsonConverter(format)); + + string expected = "{"; + if (format.Equals(ModelSerializerFormat.Json)) + expected += "\"latinName\":\"Animalia\","; + expected += "\"name\":\"Doggo\",\"isHungry\":false,"; + expected += "\"weight\":1.5,"; + expected += "\"foodConsumed\":[\"kibble\",\"egg\",\"peanut butter\"]}"; + var dog = new DogListProperty + { + Name = "Doggo", + IsHungry = false, + Weight = 1.5, + FoodConsumed = { "kibble", "egg", "peanut butter" }, + }; + var actual = JsonSerializer.Serialize(dog, options); + Assert.AreEqual(expected, actual); + } + + [TestCase("J")] + [TestCase("W")] + public void DeserializeTest(string format) + { + string serviceResponse = "{\"latinName\":\"Animalia\",\"weight\":1.5,\"name\":\"Doggo\",\"isHungry\":false,\"foodConsumed\":[\"kibble\",\"egg\",\"peanut butter\"],\"numberOfLegs\":4}"; + + JsonSerializerOptions options = new JsonSerializerOptions(); + options.Converters.Add(new ModelJsonConverter(format)); + + var dog = JsonSerializer.Deserialize(serviceResponse, options); + + Assert.AreEqual("Doggo", dog.Name); + Assert.AreEqual(false, dog.IsHungry); + Assert.AreEqual(1.5, dog.Weight); + CollectionAssert.AreEquivalent(new List { "kibble", "egg", "peanut butter" }, dog.FoodConsumed); + Assert.AreEqual("Animalia", dog.LatinName); + + var additionalProperties = typeof(Animal).GetProperty("RawData", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(dog) as Dictionary; + Assert.IsNotNull(additionalProperties); + Assert.AreEqual(format.Equals(ModelSerializerFormat.Json), additionalProperties.ContainsKey("numberOfLegs")); + if (format.Equals(ModelSerializerFormat.Json)) + Assert.AreEqual("4", additionalProperties["numberOfLegs"].ToString()); + + string expected = "{"; + if (format.Equals(ModelSerializerFormat.Json)) + expected += "\"latinName\":\"Animalia\","; + expected += "\"name\":\"Doggo\",\"isHungry\":false,"; + expected += "\"weight\":1.5,"; + expected += "\"foodConsumed\":[\"kibble\",\"egg\",\"peanut butter\"]"; + if (format.Equals(ModelSerializerFormat.Json)) + { + expected += ",\"numberOfLegs\":4,\"DogListPropertyConverterMarker\":true"; //validate marker exists to ensure we are using the class converter if it exists + } + expected += "}"; + + var actual = JsonSerializer.Serialize(dog, options); + Assert.AreEqual(expected, actual); + } + + [Test] + public void UsesMoreConcreteConverter() + { + string serviceResponse = "{\"latinName\":\"Animalia\",\"weight\":1.5,\"name\":\"Doggo\",\"isHungry\":false,\"foodConsumed\":[\"kibble\",\"egg\",\"peanut butter\"],\"numberOfLegs\":4}"; + + JsonSerializerOptions options = new JsonSerializerOptions(); + options.Converters.Add(new ModelJsonConverter()); + options.Converters.Add(new DogListPropertyBlankConverter()); + + //the more concrete converter should be used so we will validate the default values are used + var dog = JsonSerializer.Deserialize(serviceResponse, options); + + var additionalProperties = typeof(Animal).GetProperty("RawData", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(dog) as Dictionary; + Assert.IsNotNull(additionalProperties); + //since this falls back to the converter defined on DogListProperty it will use the default behavior and ignore additional properties + Assert.IsFalse(additionalProperties.ContainsKey("DogListPropertyConverterMarker")); + + var actual = JsonSerializer.Serialize(dog, options); + Assert.AreEqual("", actual); + } + + [TestCase("J")] + [TestCase("W")] + public void CanSerializeDerivedModel(string format) + { + string serviceResponse = "{\"kind\":\"X\",\"name\":\"xmodel\",\"xProperty\":100,\"extra\":\"stuff\"}"; + + JsonSerializerOptions options = new JsonSerializerOptions(); + options.Converters.Add(new ModelJsonConverter(format)); + + var modelX = JsonSerializer.Deserialize(serviceResponse, options); + Assert.AreEqual("xmodel", modelX.Name); + Assert.AreEqual(100, modelX.XProperty); + Assert.AreEqual("X", modelX.Kind); + var additionalProperties = typeof(ModelX).GetProperty("RawData", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(modelX) as Dictionary; + Assert.IsNotNull(additionalProperties); + Assert.AreEqual(format.Equals(ModelSerializerFormat.Json), additionalProperties.ContainsKey("extra")); + if (format.Equals(ModelSerializerFormat.Json)) + Assert.AreEqual("\"stuff\"", additionalProperties["extra"].ToString()); + + string expected = "{\"kind\":\"X\",\"name\":\"xmodel\""; + if (format.Equals(ModelSerializerFormat.Json)) + expected += ",\"xProperty\":100"; + if (format.Equals(ModelSerializerFormat.Json)) + expected += ",\"extra\":\"stuff\""; + expected += "}"; + var actual = JsonSerializer.Serialize(modelX, options); + Assert.AreEqual(expected, actual); + } + + [TestCase("J")] + [TestCase("W")] + public void CanSerializeBaseModel(string format) + { + string serviceResponse = "{\"kind\":\"X\",\"name\":\"xmodel\",\"xProperty\":100,\"extra\":\"stuff\"}"; + + JsonSerializerOptions options = new JsonSerializerOptions(); + options.Converters.Add(new ModelJsonConverter(format)); + + var baseModel = JsonSerializer.Deserialize(serviceResponse, options); + var modelX = baseModel as ModelX; + Assert.IsNotNull(modelX); + Assert.AreEqual("xmodel", modelX.Name); + Assert.AreEqual(100, modelX.XProperty); + Assert.AreEqual("X", modelX.Kind); + var additionalProperties = typeof(ModelX).GetProperty("RawData", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(modelX) as Dictionary; + Assert.IsNotNull(additionalProperties); + Assert.AreEqual(format.Equals(ModelSerializerFormat.Json), additionalProperties.ContainsKey("extra")); + if (format.Equals(ModelSerializerFormat.Json)) + Assert.AreEqual("\"stuff\"", additionalProperties["extra"].ToString()); + + string expected = "{\"kind\":\"X\",\"name\":\"xmodel\""; + if (format.Equals(ModelSerializerFormat.Json)) + { + expected += ",\"xProperty\":100"; + expected += ",\"extra\":\"stuff\""; + } + expected += "}"; + var actual = JsonSerializer.Serialize(baseModel, options); + Assert.AreEqual(expected, actual); + } + + [TestCase("J")] + [TestCase("W")] + public void CanSerializeUnknown(string format) + { + string serviceResponse = "{\"kind\":\"Z\",\"name\":\"zmodel\",\"zProperty\":1.5,\"extra\":\"stuff\"}"; + + JsonSerializerOptions options = new JsonSerializerOptions(); + options.Converters.Add(new ModelJsonConverter(format)); + + var baseModel = JsonSerializer.Deserialize(serviceResponse, options); + var unknownBaseModel = baseModel as UnknownBaseModel; + Assert.IsNotNull(unknownBaseModel); + Assert.AreEqual("zmodel", baseModel.Name); + Assert.AreEqual("Z", baseModel.Kind); + var additionalProperties = typeof(UnknownBaseModel).GetProperty("RawData", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(baseModel) as Dictionary; + Assert.IsNotNull(additionalProperties); + Assert.AreEqual(format.Equals(ModelSerializerFormat.Json), additionalProperties.ContainsKey("extra")); + Assert.AreEqual(format.Equals(ModelSerializerFormat.Json), additionalProperties.ContainsKey("zProperty")); + if (format.Equals(ModelSerializerFormat.Json)) + { + Assert.AreEqual("\"stuff\"", additionalProperties["extra"].ToString()); + Assert.AreEqual("1.5", additionalProperties["zProperty"].ToString()); + } + + string expected = "{\"kind\":\"Z\",\"name\":\"zmodel\""; + if (format.Equals(ModelSerializerFormat.Json)) + { + expected += ",\"zProperty\":1.5"; + expected += ",\"extra\":\"stuff\""; + } + expected += "}"; + var actual = JsonSerializer.Serialize(baseModel, options); + Assert.AreEqual(expected, actual); + } + + [Test] + public void ShouldThrowIfWrongDiscriminatorType() + { + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ModelXmlTests.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ModelXmlTests.cs new file mode 100644 index 000000000000..f93a7a1b96a5 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ModelXmlTests.cs @@ -0,0 +1,117 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.IO; +using System.Text; +using NUnit.Framework; +using Azure.Core.Tests.Public.ModelSerializationTests.Models; +using Azure.Core.Serialization; +using System; +using System.Text.Json; +using System.Xml; + +namespace Azure.Core.Tests.Public.ModelSerializationTests +{ + internal class ModelXmlTests + { + private const string _xmlServiceResponse = + "" + + "Color" + + "Red" + + "ReadOnly" + + "ChildRedChildReadOnly" + + ""; + + private const string _jsonServiceResponse = "{\"key\":\"Color\",\"value\":\"Red\",\"readOnlyProperty\":\"ReadOnly\",\"renamedChildModelXml\":{\"childValue\":\"ChildRed\",\"childReadOnlyProperty\":\"ChildReadOnly\"}}"; + + [Test] + public void RoundTripWithWire() => CanRoundTripFutureVersionWithoutLoss(ModelSerializerFormat.Wire, _xmlServiceResponse); + + [Test] + public void RoundTripWithJson() => CanRoundTripFutureVersionWithoutLoss(ModelSerializerFormat.Json, _jsonServiceResponse); + + [Test] + public void ThrowsIfUnknownFormat() + { + ModelSerializerOptions options = new ModelSerializerOptions("x"); + Assert.Throws(() => ModelSerializer.Serialize(new ModelXml(), options)); + Assert.Throws(() => ModelSerializer.Deserialize(new BinaryData("x"), options)); + } + + [Test] + public void ThrowsIfMismatch() + { + ModelSerializerOptions jsonOptions = new ModelSerializerOptions(ModelSerializerFormat.Json); + ModelXml model = ModelSerializer.Deserialize(new BinaryData(Encoding.UTF8.GetBytes(_jsonServiceResponse)), jsonOptions); + + Assert.Throws(Is.InstanceOf(), () => ModelSerializer.Deserialize(new BinaryData(Encoding.UTF8.GetBytes(_xmlServiceResponse)), jsonOptions)); + + ModelSerializerOptions wireOptions = new ModelSerializerOptions(ModelSerializerFormat.Wire); + Assert.Throws(() => ModelSerializer.Deserialize(new BinaryData(Encoding.UTF8.GetBytes(_jsonServiceResponse)), wireOptions)); + } + + private void CanRoundTripFutureVersionWithoutLoss(ModelSerializerFormat format, string serviceResponse) + { + ModelSerializerOptions options = new ModelSerializerOptions(format); + + var expectedSerializedString = GetExpectedResult(format); + + ModelXml model = ModelSerializer.Deserialize(new BinaryData(Encoding.UTF8.GetBytes(serviceResponse)), options); + + Assert.AreEqual("Color", model.Key); + Assert.AreEqual("Red", model.Value); + Assert.AreEqual("ReadOnly", model.ReadOnlyProperty); + Assert.IsNotNull(model.RenamedChildModelXml); + Assert.AreEqual("ChildRed", model.RenamedChildModelXml.ChildValue); + Assert.AreEqual("ChildReadOnly", model.RenamedChildModelXml.ChildReadOnlyProperty); + var data = ModelSerializer.Serialize(model, options); + string roundTrip = data.ToString(); + + Assert.That(roundTrip, Is.EqualTo(expectedSerializedString)); + + ModelXml model2 = ModelSerializer.Deserialize(new BinaryData(Encoding.UTF8.GetBytes(roundTrip)), options); + VerifyModelXml(model, model2, format); + } + + private string GetExpectedResult(ModelSerializerFormat format) + { + if (format == ModelSerializerFormat.Wire) + { + var expectedSerializedString = "\uFEFFColorRed"; + if (format.Equals(ModelSerializerFormat.Json)) + expectedSerializedString += "ReadOnly"; + expectedSerializedString += "ChildRed"; + //TODO this is broken until we update the IXmlSerializable interface to include ModelSerializerOptions + //if (format.Equals(ModelSerializerFormat.Json)) + // expectedSerializedString += "ChildReadOnly"; + expectedSerializedString += ""; + return expectedSerializedString; + } + if (format == ModelSerializerFormat.Json) + { + var expectedSerializedString = "{\"key\":\"Color\",\"value\":\"Red\""; + if (format.Equals(ModelSerializerFormat.Json)) + expectedSerializedString += ",\"readOnlyProperty\":\"ReadOnly\""; + expectedSerializedString += ",\"renamedChildModelXml\":{\"childValue\":\"ChildRed\""; + //TODO this is broken until we update the IXmlSerializable interface to include ModelSerializerOptions + //if (format.Equals(ModelSerializerFormat.Json)) + // expectedSerializedString += ",\"childReadOnlyProperty\":\"ChildReadOnly\""; + expectedSerializedString += "}}"; + return expectedSerializedString; + } + throw new InvalidOperationException($"Unknown format used in test {format}"); + } + + internal static void VerifyModelXml(ModelXml correctModelXml, ModelXml model2, string format) + { + Assert.AreEqual(correctModelXml.Key, model2.Key); + Assert.AreEqual(correctModelXml.Value, model2.Value); + if (format.Equals(ModelSerializerFormat.Json)) + Assert.AreEqual(correctModelXml.ReadOnlyProperty, model2.ReadOnlyProperty); + Assert.AreEqual(correctModelXml.RenamedChildModelXml.ChildValue, model2.RenamedChildModelXml.ChildValue); + //TODO this is broken until we update the IXmlSerializable interface to include ModelSerializerOptions + //if (format.Equals(ModelSerializerFormat.Data)) + // Assert.AreEqual(correctModelXml.RenamedChildModelXml.ChildReadOnlyProperty, model2.RenamedChildModelXml.ChildReadOnlyProperty); + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/Animal.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/Animal.cs new file mode 100644 index 000000000000..a7910480e596 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/Animal.cs @@ -0,0 +1,145 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.Json; +using Azure.Core; +using Azure.Core.Serialization; + +namespace Azure.Core.Tests.Public.ModelSerializationTests +{ + public class Animal : IUtf8JsonSerializable, IJsonModelSerializable + { + private Dictionary RawData { get; set; } = new Dictionary(); + + public bool IsHungry { get; set; } = false; + public double Weight { get; set; } = 1.1d; + public string LatinName { get; private set; } = "Animalia"; + public string Name { get; set; } = "Animal"; + + public Animal() + { + } + + public Animal(double weight, string latinName, string name, bool isHungry) + { + Weight = weight; + LatinName = latinName; + Name = name; + IsHungry = isHungry; + } + + internal Animal(double weight, string latinName, string name, bool isHungry, Dictionary rawData) + { + Weight = weight; + LatinName = latinName; + Name = name; + IsHungry = isHungry; + RawData = rawData; + } + + internal Animal(string name) + { + Name = name; + } + + #region Serialization + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => ((IJsonModelSerializable)this).Serialize(writer, ModelSerializerOptions.AzureServiceDefault); + + void IJsonModelSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) => Serialize(writer, options); + + private void Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + writer.WriteStartObject(); + if (options.Format == ModelSerializerFormat.Json) + { + writer.WritePropertyName("latinName"u8); + writer.WriteStringValue(LatinName); + } + writer.WritePropertyName("name"u8); + writer.WriteStringValue(Name); + writer.WritePropertyName("isHungry"u8); + writer.WriteBooleanValue(IsHungry); + writer.WritePropertyName("weight"u8); + writer.WriteNumberValue(Weight); + + if (options.Format == ModelSerializerFormat.Json) + { + //write out the raw data + foreach (var property in RawData) + { + writer.WritePropertyName(property.Key); +#if NET6_0_OR_GREATER + writer.WriteRawValue(property.Value); +#else + JsonSerializer.Serialize(writer, JsonDocument.Parse(property.Value.ToString()).RootElement); +#endif + } + } + writer.WriteEndObject(); + } + + internal static Animal DeserializeAnimal(JsonElement element, ModelSerializerOptions options) + { + double weight = default; + string name = ""; + string latinName = ""; + bool isHungry = default; + + Dictionary rawData = new Dictionary(); + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("weight"u8)) + { + weight = property.Value.GetDouble(); + continue; + } + if (property.NameEquals("name"u8)) + { + name = property.Value.GetString(); + continue; + } + if (property.NameEquals("latinName"u8)) + { + latinName = property.Value.GetString(); + continue; + } + if (property.NameEquals("isHungry"u8)) + { + isHungry = property.Value.GetBoolean(); + continue; + } + + if (options.Format == ModelSerializerFormat.Json) + { + //this means it's an unknown property we got + rawData.Add(property.Name, BinaryData.FromString(property.Value.GetRawText())); + } + } + return new Animal(weight, latinName, name, isHungry, rawData); + } + #endregion + + #region InterfaceImplementation + + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + { + return DeserializeAnimal(JsonDocument.Parse(data.ToString()).RootElement, options); + } + + object IJsonModelSerializable.Deserialize(ref Utf8JsonReader reader, ModelSerializerOptions options) + { + using var doc = JsonDocument.ParseValue(ref reader); + return DeserializeAnimal(doc.RootElement, options); + } + + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) + { + return ModelSerializerHelper.SerializeToBinaryData((writer) => { Serialize(writer, options); }); + } + + #endregion + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/CatReadOnlyProperty.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/CatReadOnlyProperty.cs new file mode 100644 index 000000000000..1ed689bb1f14 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/CatReadOnlyProperty.cs @@ -0,0 +1,122 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.Json; +using Azure.Core.Serialization; + +namespace Azure.Core.Tests.Public.ModelSerializationTests +{ + public class CatReadOnlyProperty : Animal, IJsonModelSerializable, IUtf8JsonSerializable + { + private Dictionary RawData { get; set; } = new Dictionary(); + + public CatReadOnlyProperty(double weight, string latinName, string name, bool isHungry, bool hasWhiskers) : base(weight, "Felis catus", name, isHungry) + { + HasWhiskers = hasWhiskers; + } + + internal CatReadOnlyProperty(double weight, string latinName, string name, bool isHungry, bool hasWhiskers, Dictionary rawData) : this(weight, latinName, name, isHungry, hasWhiskers) + { + RawData = rawData; + } + + internal CatReadOnlyProperty() + { + } + + public bool HasWhiskers { get; private set; } = true; + + #region Serialization + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => ((IJsonModelSerializable)this).Serialize(writer, ModelSerializerOptions.AzureServiceDefault); + + void IJsonModelSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + writer.WriteStartObject(); + if (options.Format == ModelSerializerFormat.Json) + { + writer.WritePropertyName("latinName"u8); + writer.WriteStringValue(LatinName); + + writer.WritePropertyName("hasWhiskers"u8); + writer.WriteBooleanValue(HasWhiskers); + } + writer.WritePropertyName("name"u8); + writer.WriteStringValue(Name); + writer.WritePropertyName("isHungry"u8); + writer.WriteBooleanValue(IsHungry); + writer.WritePropertyName("weight"u8); + writer.WriteNumberValue(Weight); + + if (options.Format == ModelSerializerFormat.Json) + { + //write out the raw data + foreach (var property in RawData) + { + writer.WritePropertyName(property.Key); +#if NET6_0_OR_GREATER + writer.WriteRawValue(property.Value); +#else + JsonSerializer.Serialize(writer, JsonDocument.Parse(property.Value.ToString()).RootElement); +#endif + } + } + writer.WriteEndObject(); + } + + internal static CatReadOnlyProperty DeserializeCatReadOnlyProperty(JsonElement element, ModelSerializerOptions options) + { + double weight = default; + string name = ""; + string latinName = ""; + bool isHungry = default; + bool hasWhiskers = default; + + Dictionary rawData = new Dictionary(); + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("weight"u8)) + { + weight = property.Value.GetDouble(); + continue; + } + if (property.NameEquals("name"u8)) + { + name = property.Value.GetString(); + continue; + } + if (property.NameEquals("latinName"u8)) + { + latinName = property.Value.GetString(); + continue; + } + + if (property.NameEquals("isHungry"u8)) + { + isHungry = property.Value.GetBoolean(); + continue; + } + if (property.NameEquals("hasWhiskers"u8)) + { + hasWhiskers = property.Value.GetBoolean(); + continue; + } + if (options.Format == ModelSerializerFormat.Json) + { + //this means its an unknown property we got + rawData.Add(property.Name, BinaryData.FromString(property.Value.GetRawText())); + } + } + return new CatReadOnlyProperty(weight, latinName, name, isHungry, hasWhiskers, rawData); + } + + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + { + return DeserializeCatReadOnlyProperty(JsonDocument.Parse(data.ToString()).RootElement, options); + } + + #endregion + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/ChildModelXml.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/ChildModelXml.cs new file mode 100644 index 000000000000..4b8dadf5f2ba --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/ChildModelXml.cs @@ -0,0 +1,148 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Xml.Linq; +using System.Xml; +using Azure.Core.Serialization; +using System.Xml.Serialization; +using System.Collections.Generic; +using System.IO; +using System.Text.Json; + +namespace Azure.Core.Tests.Public.ModelSerializationTests.Models +{ + [XmlRoot("ChildTag")] + internal class ChildModelXml : IXmlSerializable, IXmlModelSerializable, IJsonModelSerializable, IUtf8JsonSerializable + { + internal ChildModelXml() { } + + /// Initializes a new instance of ModelXml for testing. + /// + /// is null. + public ChildModelXml(string value, string readonlyProperty) + { + Argument.AssertNotNull(value, nameof(value)); + + ChildValue = value; + ChildReadOnlyProperty = readonlyProperty; + } + + /// Gets or sets the value. + [XmlElement("ChildValue")] + public string ChildValue { get; set; } + /// Gets or sets the value. + [XmlElement("ChildReadOnlyProperty")] + public string ChildReadOnlyProperty { get; } + + void IXmlSerializable.Write(XmlWriter writer, string nameHint) => + Serialize(writer, ModelSerializerOptions.AzureServiceDefault, nameHint); + + void IXmlModelSerializable.Serialize(XmlWriter writer, ModelSerializerOptions options) + => Serialize(writer, options, null); + + private void Serialize(XmlWriter writer, ModelSerializerOptions options, string nameHint) + { + writer.WriteStartElement(nameHint ?? "ChildTag"); + writer.WriteStartElement("ChildValue"); + writer.WriteValue(ChildValue); + writer.WriteEndElement(); + if (options.Format == ModelSerializerFormat.Json) + { + writer.WriteStartElement("ChildReadOnlyProperty"); + writer.WriteValue(ChildReadOnlyProperty); + writer.WriteEndElement(); + } + writer.WriteEndElement(); + } + + internal static ChildModelXml DeserializeChildModelXml(XElement element, ModelSerializerOptions options = default) + { + string value = default; + string readonlyProperty = default; + if (element.Element("ChildValue") is XElement valueElement) + { + value = (string)valueElement; + } + if (element.Element("ChildReadOnlyProperty") is XElement readonlyPropertyElement) + { + readonlyProperty = (string)readonlyPropertyElement; + } + return new ChildModelXml(value, readonlyProperty); + } + + internal static ChildModelXml DeserializeChildModelXml(JsonElement element, ModelSerializerOptions options) + { + string childValue = default; + string childReadOnlyProperty = default; + + Dictionary rawData = new Dictionary(); + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("childValue"u8)) + { + childValue = property.Value.GetString(); + continue; + } + if (property.NameEquals("childReadOnlyProperty"u8)) + { + childReadOnlyProperty = property.Value.GetString(); + continue; + } + } + return new ChildModelXml(childValue, childReadOnlyProperty); + } + + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + { + if (options.Format == ModelSerializerFormat.Json) + { + using var doc = JsonDocument.Parse(data); + return DeserializeChildModelXml(doc.RootElement, options); + } + if (options.Format == ModelSerializerFormat.Wire) + { + return DeserializeChildModelXml(XElement.Load(data.ToStream()), options); + } + throw new InvalidOperationException($"Unsupported format '{options.Format}' request for '{GetType().Name}'"); + } + + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) + { + if (options.Format == ModelSerializerFormat.Json) + { + return ModelSerializerHelper.SerializeToBinaryData((writer) => { Serialize(writer, options); }); + } + if (options.Format == ModelSerializerFormat.Wire) + { + return ModelSerializerHelper.SerializeToBinaryData((writer) => { Serialize(writer, options, null); }); + } + throw new InvalidOperationException($"Unsupported format '{options.Format}' request for '{GetType().Name}'"); + } + + private void Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + writer.WriteStartObject(); + writer.WritePropertyName("childValue"u8); + writer.WriteStringValue(ChildValue); + if (options.Format == ModelSerializerFormat.Json) + { + writer.WritePropertyName("childReadOnlyProperty"u8); + writer.WriteStringValue(ChildReadOnlyProperty); + } + writer.WriteEndObject(); + } + + void IJsonModelSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) => + Serialize(writer, options); + + object IJsonModelSerializable.Deserialize(ref Utf8JsonReader reader, ModelSerializerOptions options) + { + using var doc = JsonDocument.ParseValue(ref reader); + return DeserializeChildModelXml(doc.RootElement, options); + } + + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => + Serialize(writer, ModelSerializerOptions.AzureServiceDefault); + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/DiscriminatorSet/BaseModel.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/DiscriminatorSet/BaseModel.cs new file mode 100644 index 000000000000..2485778368ba --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/DiscriminatorSet/BaseModel.cs @@ -0,0 +1,89 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using Azure.Core.Serialization; +using Azure.Core; + +namespace Azure.Core.Tests.Public.ModelSerializationTests.Models +{ + public abstract class BaseModel : IUtf8JsonSerializable, IJsonModelSerializable + { + private Dictionary RawData { get; set; } = new Dictionary(); + + public string Kind { get; internal set; } + public string Name { get; set; } + + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => ((IJsonModelSerializable)this).Serialize(writer, ModelSerializerOptions.AzureServiceDefault); + + void IJsonModelSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) => Serialize(writer, options); + + private void Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + writer.WriteStartObject(); + writer.WritePropertyName("kind"u8); + writer.WriteStringValue(Kind); + if (Optional.IsDefined(Name)) + { + writer.WritePropertyName("name"u8); + writer.WriteStringValue(Name); + } + if (options.Format == ModelSerializerFormat.Json) + { + //write out the raw data + foreach (var property in RawData) + { + writer.WritePropertyName(property.Key); +#if NET6_0_OR_GREATER + writer.WriteRawValue(property.Value); +#else + JsonSerializer.Serialize(writer, JsonDocument.Parse(property.Value.ToString()).RootElement); +#endif + } + } + writer.WriteEndObject(); + } + + internal static BaseModel DeserializeBaseModel(BinaryData data, ModelSerializerOptions options) + => DeserializeBaseModel(JsonDocument.Parse(data.ToString()).RootElement, options); + + internal static BaseModel DeserializeBaseModel(JsonElement element, ModelSerializerOptions options = default) + { + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + if (element.TryGetProperty("kind", out JsonElement discriminator)) + { + switch (discriminator.GetString()) + { + case "X": + return ModelX.DeserializeModelX(element, options); + case "Y": + return ModelY.DeserializeModelY(element, options); + } + } + return UnknownBaseModel.DeserializeUnknownBaseModel(element, options); + } + + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + => DeserializeBaseModel(JsonDocument.Parse(data.ToString()).RootElement, options); + + object IJsonModelSerializable.Deserialize(ref Utf8JsonReader reader, ModelSerializerOptions options) + { + using var doc = JsonDocument.ParseValue(ref reader); + return DeserializeBaseModel(doc.RootElement, options); + } + + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) + { + return ModelSerializerHelper.SerializeToBinaryData((writer) => { Serialize(writer, options); }); + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/DiscriminatorSet/ModelX.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/DiscriminatorSet/ModelX.cs new file mode 100644 index 000000000000..66a5f2f61f5c --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/DiscriminatorSet/ModelX.cs @@ -0,0 +1,123 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using Azure.Core.Serialization; + +namespace Azure.Core.Tests.Public.ModelSerializationTests.Models +{ + public class ModelX : BaseModel, IUtf8JsonSerializable, IJsonModelSerializable + { + private Dictionary RawData { get; set; } = new Dictionary(); + + public ModelX() + { + Kind = "X"; + } + + internal ModelX(string kind, string name, int xProperty, Dictionary rawData) + { + Kind = kind; + Name = name; + XProperty = xProperty; + RawData = rawData; + } + + public int XProperty { get; private set; } + + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => ((IJsonModelSerializable)this).Serialize(writer, ModelSerializerOptions.AzureServiceDefault); + + public static implicit operator RequestContent(ModelX modelX) + { + var content = new Utf8JsonRequestContent(); + ((IUtf8JsonSerializable)modelX).Write(content.JsonWriter); + return content; + } + + void IJsonModelSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + writer.WriteStartObject(); + writer.WritePropertyName("kind"u8); + writer.WriteStringValue(Kind); + if (Optional.IsDefined(Name)) + { + writer.WritePropertyName("name"u8); + writer.WriteStringValue(Name); + } + if (options.Format == ModelSerializerFormat.Json) + { + writer.WritePropertyName("xProperty"u8); + writer.WriteNumberValue(XProperty); + } + if (options.Format == ModelSerializerFormat.Json) + { + //write out the raw data + foreach (var property in RawData) + { + writer.WritePropertyName(property.Key); +#if NET6_0_OR_GREATER + writer.WriteRawValue(property.Value); +#else + JsonSerializer.Serialize(writer, JsonDocument.Parse(property.Value.ToString()).RootElement); +#endif + } + } + writer.WriteEndObject(); + } + + internal static ModelX DeserializeModelX(JsonElement element, ModelSerializerOptions? options = default) + { + options ??= ModelSerializerOptions.AzureServiceDefault; + + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + string kind = default; + Optional name = default; + int xProperty = default; + Dictionary rawData = new Dictionary(); + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("kind"u8)) + { + kind = property.Value.GetString(); + continue; + } + if (property.NameEquals("name"u8)) + { + name = property.Value.GetString(); + continue; + } + if (property.NameEquals("xProperty"u8)) + { + xProperty = property.Value.GetInt32(); + continue; + } + if (options.Value.Format == ModelSerializerFormat.Json) + { + //this means it's an unknown property we got + rawData.Add(property.Name, BinaryData.FromString(property.Value.GetRawText())); + } + } + return new ModelX(kind, name, xProperty, rawData); + } + + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + { + return DeserializeModelX(JsonDocument.Parse(data.ToString()).RootElement, options); + } + + //public method to serialize with internal interface + public void Serialize(Utf8JsonWriter writer) + { + ((IUtf8JsonSerializable)this).Write(writer); + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/DiscriminatorSet/ModelY.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/DiscriminatorSet/ModelY.cs new file mode 100644 index 000000000000..72fe49be3066 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/DiscriminatorSet/ModelY.cs @@ -0,0 +1,109 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using Azure.Core.Serialization; +using Newtonsoft.Json.Linq; + +namespace Azure.Core.Tests.Public.ModelSerializationTests.Models +{ + public class ModelY : BaseModel, IUtf8JsonSerializable, IJsonModelSerializable + { + private Dictionary RawData { get; set; } = new Dictionary(); + + public ModelY() + { + Kind = "Y"; + } + + internal ModelY(string kind, string name, string yProperty, Dictionary rawData) + { + Kind = kind; + Name = name; + YProperty = yProperty; + RawData = rawData; + } + + public string YProperty { get; private set; } + + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => ((IJsonModelSerializable)this).Serialize(writer, ModelSerializerOptions.AzureServiceDefault); + + void IJsonModelSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + writer.WriteStartObject(); + writer.WritePropertyName("kind"u8); + writer.WriteStringValue(Kind); + if (options.Format == ModelSerializerFormat.Json) + { + if (Optional.IsDefined(Name)) + { + writer.WritePropertyName("name"u8); + writer.WriteStringValue(Name); + } + } + writer.WritePropertyName("yProperty"u8); + writer.WriteStringValue(YProperty); + if (options.Format == ModelSerializerFormat.Json) + { + //write out the raw data + foreach (var property in RawData) + { + writer.WritePropertyName(property.Key); +#if NET6_0_OR_GREATER + writer.WriteRawValue(property.Value); +#else + JsonSerializer.Serialize(writer, JsonDocument.Parse(property.Value.ToString()).RootElement); +#endif + } + } + writer.WriteEndObject(); + } + + internal static ModelY DeserializeModelY(JsonElement element, ModelSerializerOptions options = default) + { + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + string kind = default; + Optional name = default; + Optional yProperty = default; + Dictionary rawData = new Dictionary(); + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("kind"u8)) + { + kind = property.Value.GetString(); + continue; + } + if (property.NameEquals("name"u8)) + { + name = property.Value.GetString(); + continue; + } + if (property.NameEquals("yProperty"u8)) + { + yProperty = property.Value.GetString(); + continue; + } + if (options.Format == ModelSerializerFormat.Json) + { + //this means it's an unknown property we got + rawData.Add(property.Name, BinaryData.FromString(property.Value.GetRawText())); + } + } + return new ModelY(kind, name, yProperty, rawData); + } + + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + { + return DeserializeModelY(JsonDocument.Parse(data.ToString()).RootElement, options); + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/DiscriminatorSet/UnknownBaseModel.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/DiscriminatorSet/UnknownBaseModel.cs new file mode 100644 index 000000000000..7ab0715bbf37 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/DiscriminatorSet/UnknownBaseModel.cs @@ -0,0 +1,95 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; +using Azure.Core.Serialization; +using Newtonsoft.Json.Linq; + +namespace Azure.Core.Tests.Public.ModelSerializationTests.Models +{ + internal class UnknownBaseModel : BaseModel, IUtf8JsonSerializable, IJsonModelSerializable + { + private Dictionary RawData { get; set; } = new Dictionary(); + + public UnknownBaseModel() + { + Kind = "Unknown"; + } + + internal UnknownBaseModel(string kind, string name, Dictionary rawData) + { + Kind = kind; + Name = name; + RawData = rawData; + } + + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => ((IJsonModelSerializable)this).Serialize(writer, ModelSerializerOptions.AzureServiceDefault); + + void IJsonModelSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + writer.WriteStartObject(); + writer.WritePropertyName("kind"u8); + writer.WriteStringValue(Kind); + if (Optional.IsDefined(Name)) + { + writer.WritePropertyName("name"u8); + writer.WriteStringValue(Name); + } + if (options.Format == ModelSerializerFormat.Json) + { + //write out the raw data + foreach (var property in RawData) + { + writer.WritePropertyName(property.Key); +#if NET6_0_OR_GREATER + writer.WriteRawValue(property.Value); +#else + JsonSerializer.Serialize(writer, JsonDocument.Parse(property.Value.ToString()).RootElement); +#endif + } + } + writer.WriteEndObject(); + } + + internal static UnknownBaseModel DeserializeUnknownBaseModel(JsonElement element, ModelSerializerOptions options = default) + { + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + string kind = default; + Optional name = default; + Dictionary rawData = new Dictionary(); + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("kind"u8)) + { + kind = property.Value.GetString(); + continue; + } + if (property.NameEquals("name"u8)) + { + name = property.Value.GetString(); + continue; + } + if (options.Format == ModelSerializerFormat.Json) + { + //this means it's an unknown property we got + rawData.Add(property.Name, BinaryData.FromString(property.Value.GetRawText())); + } + } + return new UnknownBaseModel(kind, name, rawData); + } + + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + { + return DeserializeUnknownBaseModel(JsonDocument.Parse(data.ToString()).RootElement, options); + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/DogListProperty.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/DogListProperty.cs new file mode 100644 index 000000000000..2821bfc99ff7 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/DogListProperty.cs @@ -0,0 +1,172 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.Json; +using System.Text.Json.Serialization; +using Azure.Core.Serialization; + +namespace Azure.Core.Tests.Public.ModelSerializationTests +{ + [JsonConverter(typeof(DogListPropertyConverter))] + public class DogListProperty : Animal, IJsonModelSerializable, IUtf8JsonSerializable + { + private Dictionary RawData { get; set; } = new Dictionary(); + public IList FoodConsumed { get; private set; } + + public DogListProperty(string name) : base(name) + { + Name = name; + FoodConsumed = new ChangeTrackingList(); + } + + internal DogListProperty(double weight, string latinName, string name, bool isHungry, IList foodConsumed, Dictionary rawData) : base(weight, latinName, name, isHungry, rawData) + { + RawData = rawData; + FoodConsumed = foodConsumed; + } + + public DogListProperty() + { + FoodConsumed = new ChangeTrackingList(); + } + + public static explicit operator DogListProperty(Response response) + { + using JsonDocument jsonDocument = JsonDocument.Parse(response.ContentStream); + return DeserializeDogListProperty(jsonDocument.RootElement); + } + + public static implicit operator RequestContent(DogListProperty dog) + { + return new Utf8JsonDelayedRequestContent(dog); + } + + #region Serialization + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => ((IJsonModelSerializable)this).Serialize(writer, ModelSerializerOptions.AzureServiceDefault); + + void IJsonModelSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + writer.WriteStartObject(); + if (options.Format == ModelSerializerFormat.Json) + { + writer.WritePropertyName("latinName"u8); + writer.WriteStringValue(LatinName); + } + writer.WritePropertyName("name"u8); + writer.WriteStringValue(Name); + writer.WritePropertyName("isHungry"u8); + writer.WriteBooleanValue(IsHungry); + writer.WritePropertyName("weight"u8); + writer.WriteNumberValue(Weight); + + if (Optional.IsCollectionDefined(FoodConsumed)) + { + writer.WritePropertyName("foodConsumed"u8); + writer.WriteStartArray(); + foreach (var item in FoodConsumed) + { + writer.WriteStringValue($"{item}"); + } + writer.WriteEndArray(); + } + + if (options.Format == ModelSerializerFormat.Json) + { + //write out the raw data + foreach (var property in RawData) + { + writer.WritePropertyName(property.Key); +#if NET6_0_OR_GREATER + writer.WriteRawValue(property.Value); +#else + JsonSerializer.Serialize(writer, JsonDocument.Parse(property.Value.ToString()).RootElement); +#endif + } + } + writer.WriteEndObject(); + } + + internal static DogListProperty DeserializeDogListProperty(JsonElement element, ModelSerializerOptions? options = default) + { + options ??= ModelSerializerOptions.AzureServiceDefault; + + double weight = default; + string name = ""; + string latinName = ""; + bool isHungry = default; + Dictionary rawData = new Dictionary(); + List foodConsumed = new List(); + + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("weight"u8)) + { + weight = property.Value.GetDouble(); + continue; + } + if (property.NameEquals("name"u8)) + { + name = property.Value.GetString(); + continue; + } + if (property.NameEquals("latinName"u8)) + { + latinName = property.Value.GetString(); + continue; + } + if (property.NameEquals("isHungry"u8)) + { + isHungry = property.Value.GetBoolean(); + continue; + } + if (property.NameEquals("foodConsumed"u8)) + { + foreach (var item in property.Value.EnumerateArray()) + { + foodConsumed.Add(item.GetString()); + } + continue; + } + if (options.Value.Format == ModelSerializerFormat.Json) + { + //this means its an unknown property we got + rawData.Add(property.Name, BinaryData.FromString(property.Value.GetRawText())); + } + } + return new DogListProperty(weight, latinName, name, isHungry, foodConsumed, rawData); + } + #endregion + + internal class DogListPropertyConverter : JsonConverter + { + public override DogListProperty Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var model = DeserializeDogListProperty(JsonDocument.ParseValue(ref reader).RootElement, GetOptions(options)); + //marker used for testing to know if this converter fires + model.RawData.Add("DogListPropertyConverterMarker", new BinaryData("true")); + return model; + } + + public override void Write(Utf8JsonWriter writer, DogListProperty value, JsonSerializerOptions options) + { + ((IJsonModelSerializable)value).Serialize(writer, GetOptions(options)); + } + + private ModelSerializerOptions GetOptions(JsonSerializerOptions options) + { + //pulls the additional properties setting from the ModelJsonConverter if it exists + //if it does not exist it uses the default value of true for azure sdk use cases + var modelConverter = options.Converters.FirstOrDefault(c => c.GetType() == typeof(ModelJsonConverter)) as ModelJsonConverter; + return modelConverter is not null ? modelConverter.Options : ModelSerializerOptions.AzureServiceDefault; + } + } + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + { + return DeserializeDogListProperty(JsonDocument.Parse(data.ToString()).RootElement, options); + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/DogListPropertyBlankConverter.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/DogListPropertyBlankConverter.cs new file mode 100644 index 000000000000..188048b7250d --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/DogListPropertyBlankConverter.cs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Azure.Core.Tests.Public.ModelSerializationTests +{ + public class DogListPropertyBlankConverter : JsonConverter + { + public override DogListProperty Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var model = JsonDocument.ParseValue(ref reader); + return new DogListProperty(); + } + + public override void Write(Utf8JsonWriter writer, DogListProperty value, JsonSerializerOptions options) + { + return; + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/JsonModelForCombinedInterface.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/JsonModelForCombinedInterface.cs new file mode 100644 index 000000000000..6fae5137598f --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/JsonModelForCombinedInterface.cs @@ -0,0 +1,129 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using Azure.Core.Serialization; +using NUnit.Framework; +using System.Text.Json; +using System.IO; +using System.Xml; +using System.Collections.Generic; + +namespace Azure.Core.Tests.Public.ModelSerializationTests.Models +{ + internal class JsonModelForCombinedInterface : IUtf8JsonSerializable, IJsonModelSerializable + { + private Dictionary RawData { get; set; } = new Dictionary(); + + public JsonModelForCombinedInterface() { } + + /// Initializes a new instance of ModelXml for testing. + /// + /// + /// or is null. + public JsonModelForCombinedInterface(string key, string value, string readOnlyProperty) + { + Argument.AssertNotNull(key, nameof(key)); + Argument.AssertNotNull(value, nameof(value)); + + Key = key; + Value = value; + ReadOnlyProperty = readOnlyProperty; + } + + internal JsonModelForCombinedInterface(string key, string value, string readOnlyProperty, Dictionary rawData) + { + Key = key; + Value = value; + ReadOnlyProperty = readOnlyProperty; + RawData = rawData; + } + + /// Gets or sets the key. + public string Key { get; set; } + /// Gets or sets the value. + public string Value { get; set; } + public string ReadOnlyProperty { get; } + + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => ((IJsonModelSerializable)this).Serialize(writer, ModelSerializerOptions.AzureServiceDefault); + + internal static JsonModelForCombinedInterface DeserializeJsonModelForCombinedInterface(JsonElement element, ModelSerializerOptions options) + { + string key = default; + string value = default; + string readOnlyProperty = default; + Dictionary rawData = new Dictionary(); + + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("key"u8)) + { + key = property.Value.GetString(); + continue; + } + if (property.NameEquals("value"u8)) + { + value = property.Value.GetString(); + continue; + } + if (property.NameEquals("readOnlyProperty"u8)) + { + readOnlyProperty = property.Value.GetString(); + continue; + } + if (options.Format == ModelSerializerFormat.Json) + { + //this means its an unknown property we got + rawData.Add(property.Name, BinaryData.FromString(property.Value.GetRawText())); + } + } + return new JsonModelForCombinedInterface(key, value, readOnlyProperty, rawData); + } + + void IJsonModelSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) => Serialize(writer, options); + + private void Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + writer.WriteStartObject(); + writer.WritePropertyName("key"u8); + writer.WriteStringValue(Key); + writer.WritePropertyName("value"u8); + writer.WriteStringValue(Value); + if (options.Format == ModelSerializerFormat.Json) + { + writer.WritePropertyName("readOnlyProperty"u8); + writer.WriteStringValue(ReadOnlyProperty); + } + if (options.Format == ModelSerializerFormat.Json) + { + //write out the raw data + foreach (var property in RawData) + { + writer.WritePropertyName(property.Key); +#if NET6_0_OR_GREATER + writer.WriteRawValue(property.Value); +#else + JsonSerializer.Serialize(writer, JsonDocument.Parse(property.Value.ToString()).RootElement); +#endif + } + } + writer.WriteEndObject(); + } + + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + { + return DeserializeJsonModelForCombinedInterface(JsonDocument.Parse(data.ToString()).RootElement, options); + } + + object IJsonModelSerializable.Deserialize(ref Utf8JsonReader reader, ModelSerializerOptions options) + { + using var doc = JsonDocument.ParseValue(ref reader); + return DeserializeJsonModelForCombinedInterface(doc.RootElement, options); + } + + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) + { + return ModelSerializerHelper.SerializeToBinaryData((writer) => { Serialize(writer, options); }); + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/ModelXml.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/ModelXml.cs new file mode 100644 index 000000000000..d5791c083a0d --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/ModelXml.cs @@ -0,0 +1,176 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Xml.Linq; +using System.Xml; +using Azure.Core.Serialization; +using NUnit.Framework; +using System.Xml.Serialization; +using System.Collections.Generic; +using System.IO; +using System.Text.Json; + +namespace Azure.Core.Tests.Public.ModelSerializationTests.Models +{ + [XmlRoot("Tag")] + internal class ModelXml : IXmlSerializable, IXmlModelSerializable + { + internal ModelXml() { } + + /// Initializes a new instance of ModelXml for testing. + /// + /// + /// or is null. + public ModelXml(string key, string value, string readonlyProperty, ChildModelXml childModel) + { + Argument.AssertNotNull(key, nameof(key)); + Argument.AssertNotNull(value, nameof(value)); + + Key = key; + Value = value; + ReadOnlyProperty = readonlyProperty; + RenamedChildModelXml = childModel; + } + + /// Gets or sets the key. + [XmlElement("Key")] + public string Key { get; set; } + /// Gets or sets the value. + [XmlElement("Value")] + public string Value { get; set; } + /// Gets or sets the value. + [XmlElement("ReadOnlyProperty")] + public string ReadOnlyProperty { get; } + [XmlElement("RenamedChildModelXml")] + public ChildModelXml RenamedChildModelXml { get; set; } + + void IXmlSerializable.Write(XmlWriter writer, string nameHint) => + Serialize(writer, ModelSerializerOptions.AzureServiceDefault, nameHint); + + void IXmlModelSerializable.Serialize(XmlWriter writer, ModelSerializerOptions options) + => Serialize(writer, options, null); + + private void Serialize(XmlWriter writer, ModelSerializerOptions options, string nameHint) + { + writer.WriteStartElement(nameHint ?? "Tag"); + writer.WriteStartElement("Key"); + writer.WriteValue(Key); + writer.WriteEndElement(); + writer.WriteStartElement("Value"); + writer.WriteValue(Value); + writer.WriteEndElement(); + if (options.Format == ModelSerializerFormat.Json) + { + writer.WriteStartElement("ReadOnlyProperty"); + writer.WriteValue(ReadOnlyProperty); + writer.WriteEndElement(); + } + writer.WriteObjectValue(RenamedChildModelXml, "RenamedChildModelXml"); + writer.WriteEndElement(); + } + + private void Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + writer.WriteStartObject(); + writer.WritePropertyName("key"u8); + writer.WriteStringValue(Key); + writer.WritePropertyName("value"u8); + writer.WriteStringValue(Value); + if (options.Format == ModelSerializerFormat.Json) + { + writer.WritePropertyName("readOnlyProperty"u8); + writer.WriteStringValue(ReadOnlyProperty); + } + writer.WritePropertyName("renamedChildModelXml"u8); + writer.WriteObjectValue(RenamedChildModelXml); + writer.WriteEndObject(); + } + + internal static ModelXml DeserializeModelXml(XElement element, ModelSerializerOptions options = default) + { + string key = default; + string value = default; + string readonlyProperty = default; + ChildModelXml childModelXml = default; + if (element.Element("Key") is XElement keyElement) + { + key = (string)keyElement; + } + if (element.Element("Value") is XElement valueElement) + { + value = (string)valueElement; + } + if (element.Element("ReadOnlyProperty") is XElement readonlyPropertyElement) + { + readonlyProperty = (string)readonlyPropertyElement; + } + if (element.Element("RenamedChildModelXml") is XElement renamedChildModelXmlElement) + { + childModelXml = ChildModelXml.DeserializeChildModelXml(renamedChildModelXmlElement, options); + } + return new ModelXml(key, value, readonlyProperty, childModelXml); + } + + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) + { + if (options.Format == ModelSerializerFormat.Json) + { + return ModelSerializerHelper.SerializeToBinaryData((writer) => { Serialize(writer, options); }); + } + if (options.Format == ModelSerializerFormat.Wire) + { + return ModelSerializerHelper.SerializeToBinaryData((writer) => { Serialize(writer, options, null); }); + } + throw new InvalidOperationException($"Unsupported format '{options.Format}' request for '{GetType().Name}'"); + } + + internal static ModelXml DeserializeModelXml(JsonElement element, ModelSerializerOptions options) + { + string key = default; + string value = default; + string readOnlyProperty = default; + ChildModelXml childModelXml = default; + + Dictionary rawData = new Dictionary(); + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("key"u8)) + { + key = property.Value.GetString(); + continue; + } + if (property.NameEquals("value"u8)) + { + value = property.Value.GetString(); + continue; + } + if (property.NameEquals("readOnlyProperty"u8)) + { + readOnlyProperty = property.Value.GetString(); + continue; + } + if (property.NameEquals("renamedChildModelXml"u8)) + { + childModelXml = ChildModelXml.DeserializeChildModelXml(property.Value, options); + continue; + } + } + return new ModelXml(key, value, readOnlyProperty, childModelXml); + } + + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + { + if (options.Format == ModelSerializerFormat.Json) + { + using var doc = JsonDocument.Parse(data); + return DeserializeModelXml(doc.RootElement, options); + } + if (options.Format == ModelSerializerFormat.Wire) + { + return DeserializeModelXml(XElement.Load(data.ToStream()), options); + } + throw new InvalidOperationException($"Unsupported format '{options.Format}' request for '{GetType().Name}'"); + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/XmlModelForCombinedInterface.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/XmlModelForCombinedInterface.cs new file mode 100644 index 000000000000..5e521ee256b6 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/Models/XmlModelForCombinedInterface.cs @@ -0,0 +1,159 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Xml.Linq; +using System.Xml; +using Azure.Core.Serialization; +using NUnit.Framework; +using System.Xml.Serialization; +using System.IO; +using System.Text.Json; +using System.Collections.Generic; + +namespace Azure.Core.Tests.Public.ModelSerializationTests.Models +{ + [XmlRoot("Tag")] + internal class XmlModelForCombinedInterface : IXmlSerializable, IXmlModelSerializable + { + public XmlModelForCombinedInterface() { } + + /// Initializes a new instance of ModelXml for testing. + /// + /// + /// or is null. + public XmlModelForCombinedInterface(string key, string value, string readOnlyProperty) + { + Argument.AssertNotNull(key, nameof(key)); + Argument.AssertNotNull(value, nameof(value)); + + Key = key; + Value = value; + ReadOnlyProperty = readOnlyProperty; + } + + /// Gets or sets the key. + [XmlElement("Key")] + public string Key { get; set; } + /// Gets or sets the value. + [XmlElement("Value")] + public string Value { get; set; } + /// Gets or sets the value. + [XmlElement("ReadOnlyProperty")] + public string ReadOnlyProperty { get; } + + void IXmlSerializable.Write(XmlWriter writer, string nameHint) => + Serialize(writer, ModelSerializerOptions.AzureServiceDefault, nameHint); + + void IXmlModelSerializable.Serialize(XmlWriter writer, ModelSerializerOptions options) + => Serialize(writer, options, null); + + internal static XmlModelForCombinedInterface DeserializeXmlModelForCombinedInterface(XElement element, ModelSerializerOptions options = default) + { + string key = default; + string value = default; + string readOnlyProperty = default; + if (element.Element("Key") is XElement keyElement) + { + key = (string)keyElement; + } + if (element.Element("Value") is XElement valueElement) + { + value = (string)valueElement; + } + if (element.Element("ReadOnlyProperty") is XElement readOnlyPropertyElement) + { + readOnlyProperty = (string)readOnlyPropertyElement; + } + return new XmlModelForCombinedInterface(key, value, readOnlyProperty); + } + + private void Serialize(XmlWriter writer, ModelSerializerOptions options, string nameHint) + { + writer.WriteStartElement(nameHint ?? "Tag"); + writer.WriteStartElement("Key"); + writer.WriteValue(Key); + writer.WriteEndElement(); + writer.WriteStartElement("Value"); + writer.WriteValue(Value); + writer.WriteEndElement(); + if (options.Format == ModelSerializerFormat.Json) + { + writer.WriteStartElement("ReadOnlyProperty"); + writer.WriteValue(ReadOnlyProperty); + writer.WriteEndElement(); + } + writer.WriteEndElement(); + } + + private void Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + writer.WriteStartObject(); + writer.WritePropertyName("key"u8); + writer.WriteStringValue(Key); + writer.WritePropertyName("value"u8); + writer.WriteStringValue(Value); + if (options.Format == ModelSerializerFormat.Json) + { + writer.WritePropertyName("readOnlyProperty"u8); + writer.WriteStringValue(ReadOnlyProperty); + } + writer.WriteEndObject(); + } + + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) + { + if (options.Format == ModelSerializerFormat.Json) + { + return ModelSerializerHelper.SerializeToBinaryData(writer => Serialize(writer, options)); + } + if (options.Format == ModelSerializerFormat.Wire) + { + return ModelSerializerHelper.SerializeToBinaryData((writer) => { Serialize(writer, options, null); }); + } + throw new InvalidOperationException($"Unsupported format '{options.Format}' request for '{GetType().Name}'"); + } + + internal static XmlModelForCombinedInterface DeserializeXmlModelForCombinedInterface(JsonElement element, ModelSerializerOptions options) + { + string key = default; + string value = default; + string readOnlyProperty = default; + + Dictionary rawData = new Dictionary(); + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("key"u8)) + { + key = property.Value.GetString(); + continue; + } + if (property.NameEquals("value"u8)) + { + value = property.Value.GetString(); + continue; + } + if (property.NameEquals("readOnlyProperty"u8)) + { + readOnlyProperty = property.Value.GetString(); + continue; + } + } + return new XmlModelForCombinedInterface(key, value, readOnlyProperty); + } + + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + { + if (options.Format == ModelSerializerFormat.Json) + { + using var doc = JsonDocument.Parse(data); + return DeserializeXmlModelForCombinedInterface(doc.RootElement, options); + } + if (options.Format == ModelSerializerFormat.Wire) + { + return DeserializeXmlModelForCombinedInterface(XElement.Load(data.ToStream()), options); + } + throw new InvalidOperationException($"Unsupported format '{options.Format}' request for '{GetType().Name}'"); + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/NewtonSoftTests.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/NewtonSoftTests.cs new file mode 100644 index 000000000000..200ab02fc0fa --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/NewtonSoftTests.cs @@ -0,0 +1,88 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Azure.Core.Serialization; +using NUnit.Framework; +using Newtonsoft.Json; + +namespace Azure.Core.Tests.Public.ModelSerializationTests +{ + internal class NewtonSoftTests + { + [TestCase("J")] + [TestCase("W")] + public void CanRoundTripFutureVersionWithoutLoss(string format) + { + Stream stream = new MemoryStream(); + string serviceResponse = + "{\"latinName\":\"Animalia\",\"weight\":2.5,\"name\":\"Rabbit\",\"isHungry\":false}"; + + StringBuilder expectedSerialized = new StringBuilder("{"); + expectedSerialized.Append("\"IsHungry\":false,"); + expectedSerialized.Append("\"Weight\":2.5,"); + if (format.Equals(ModelSerializerFormat.Json)) + { + expectedSerialized.Append("\"LatinName\":\"Animalia\","); + } + expectedSerialized.Append("\"Name\":\"Rabbit\""); + expectedSerialized.Append("}"); + var expectedSerializedString = expectedSerialized.ToString(); + + ModelSerializerOptions options = new ModelSerializerOptions(format); + + if (format == ModelSerializerFormat.Wire) + { + JsonSerializerSettings settings = new JsonSerializerSettings + { + ContractResolver = new IgnoreReadOnlyPropertiesResolver() + }; + options.Serializers.Add(typeof(Animal), new NewtonsoftJsonObjectSerializer(settings)); + } + else + options.Serializers.Add(typeof(Animal), new NewtonsoftJsonObjectSerializer()); + + var model = ModelSerializer.Deserialize(new BinaryData(Encoding.UTF8.GetBytes(serviceResponse)), options: options); + + if (format.Equals(ModelSerializerFormat.Json)) + { + Assert.That(model.LatinName, Is.EqualTo("Animalia")); + } + Assert.That(model.Name, Is.EqualTo("Rabbit")); + Assert.IsFalse(model.IsHungry); + Assert.That(model.Weight, Is.EqualTo(2.5)); + + var data = ModelSerializer.Serialize(model, options); + string roundTrip = data.ToString(); + +#if NET6_0_OR_GREATER + Assert.That(roundTrip, Is.EqualTo(expectedSerializedString)); +#endif + + var model2 = ModelSerializer.Deserialize(new BinaryData(Encoding.UTF8.GetBytes(roundTrip)), options: options); + VerifyModels.CheckAnimals(model, model2, options); + } + + // Generate a class that implements the NewtonSoft default contract resolver so that ReadOnly properties are not serialized + // This is used to verify that the ReadOnly properties are not serialized when IgnoreReadOnlyProperties is set to true + private class IgnoreReadOnlyPropertiesResolver : Newtonsoft.Json.Serialization.DefaultContractResolver + { + protected override Newtonsoft.Json.Serialization.JsonProperty CreateProperty(System.Reflection.MemberInfo member, MemberSerialization memberSerialization) + { + Newtonsoft.Json.Serialization.JsonProperty property = base.CreateProperty(member, memberSerialization); + + if (!property.Writable) + { + property.ShouldSerialize = obj => false; + } + + return property; + } + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ResourceProviderDataTests.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ResourceProviderDataTests.cs new file mode 100644 index 000000000000..22db23d0257d --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ResourceProviderDataTests.cs @@ -0,0 +1,134 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Text; +using System.Text.Json; +using Azure.Core.Serialization; +using Azure.Core.Tests.Public.ResourceManager.Resources; +using NUnit.Framework; + +namespace Azure.Core.Tests.Public.ModelSerializationTests +{ + internal class ResourceProviderDataTests + { + [Test] + public void RoundTripTest() => + RoundTripTest(SerializeWithModelSerializer, DeserializeWithModelSerializer); + + [Test] + public void BufferTest() => + RoundTripTest(SerializeWithBuffer, DeserializeWithModelSerializer); + + [Test] + public void JsonReaderTest() => + RoundTripTest(SerializeWithModelSerializer, DeserializeWithJsonReader); + + [Test] + public void UsingSequence() => + RoundTripTest(SerializeWithModelSerializer, DeserializeWithSequence); + + [Test] + public void UsingNonGeneric() => + RoundTripTest(SerializeWithModelSerializerNonGeneric, DeserializeWithModelSerializerNonGeneric); + + [Test] + public void UsingInternal() => + RoundTripTest(SerializeWithInternal, DeserializeWithInternal); + + private void RoundTripTest(Func serialize, Func deserialize) + { + string serviceResponse = File.ReadAllText(Path.Combine(Directory.GetParent(Assembly.GetExecutingAssembly().Location).FullName, "ModelSerializationTests", "TestData", "ResourceProviderData.json")).TrimEnd(); + + var expectedSerializedString = serviceResponse; + + ResourceProviderData model = deserialize(serviceResponse); + + string roundTrip = serialize(model); + + Assert.That(roundTrip, Is.EqualTo(expectedSerializedString)); + + ResourceProviderData model2 = deserialize(roundTrip); + } + + private ResourceProviderData DeserializeWithJsonReader(string json) + { + Utf8JsonReader reader = new Utf8JsonReader(Encoding.UTF8.GetBytes(json)); + var model = Activator.CreateInstance(typeof(ResourceProviderData), true) as IJsonModelSerializable; + return (ResourceProviderData)model.Deserialize(ref reader, new ModelSerializerOptions()); + } + + private ResourceProviderData DeserializeWithSequence(string json) + { + using var content = WriteStringToBuffer(json, new ModelSerializerOptions()); + using var doc = JsonDocument.Parse(content.GetReadOnlySequence()); + return ResourceProviderData.DeserializeResourceProviderData(doc.RootElement); + } + + private ResourceProviderData DeserializeWithModelSerializer(string json) + { + return ModelSerializer.Deserialize(new BinaryData(Encoding.UTF8.GetBytes(json))); + } + + private ResourceProviderData DeserializeWithInternal(string json) + { + using var doc = JsonDocument.Parse(json); + return ResourceProviderData.DeserializeResourceProviderData(doc.RootElement); + } + + private ResourceProviderData DeserializeWithModelSerializerNonGeneric(string json) + { + return (ResourceProviderData)ModelSerializer.Deserialize(new BinaryData(Encoding.UTF8.GetBytes(json)), typeof(ResourceProviderData)); + } + + private SequenceWriter WriteStringToBuffer(string json, ModelSerializerOptions options) + { + var model = ModelSerializer.Deserialize(new BinaryData(Encoding.UTF8.GetBytes(json))); + SequenceWriter sequenceWriter = new SequenceWriter(); + using var writer = new Utf8JsonWriter(sequenceWriter); + ((IJsonModelSerializable)model).Serialize(writer, options); + writer.Flush(); + return sequenceWriter; + } + + private string SerializeWithModelSerializer(ResourceProviderData model) + { + var data = ModelSerializer.Serialize(model); + return data.ToString(); + } + + private string SerializeWithModelSerializerNonGeneric(object model) + { + var data = ModelSerializer.Serialize(model); + return data.ToString(); + } + + private string SerializeWithInternal(ResourceProviderData model) + { + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream); + model.Serialize(writer); + writer.Flush(); + stream.Position = 0; + using var reader = new StreamReader(stream); + return reader.ReadToEnd(); + } + + private string SerializeWithBuffer(ResourceProviderData model) + { + using var sequenceWriter = new SequenceWriter(bufferSize: 4048); + var writer = new Utf8JsonWriter(sequenceWriter); + model.Serialize(writer); + writer.Flush(); + RequestContent content = RequestContent.Create(sequenceWriter); + using var stream = new MemoryStream(); + content.WriteTo(stream, default); + stream.Position = 0; + using var reader = new StreamReader(stream); + return reader.ReadToEnd(); + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ApiProfile.Serialization.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ApiProfile.Serialization.cs new file mode 100644 index 000000000000..0f199fc243f5 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ApiProfile.Serialization.cs @@ -0,0 +1,108 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.IO; +using System.Text.Json; +using Azure.Core; +using Azure.Core.Serialization; + +namespace Azure.Core.Tests.Public.ResourceManager.Resources.Models +{ + public partial class ApiProfile : IUtf8JsonSerializable, IJsonModelSerializable + { + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => ((IJsonModelSerializable)this).Serialize(writer, ModelSerializerOptions.AzureServiceDefault); + + internal static ApiProfile DeserializeApiProfile(JsonElement element, ModelSerializerOptions? options = default) + { + options ??= ModelSerializerOptions.AzureServiceDefault; + + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + Optional profileVersion = default; + Optional apiVersion = default; + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("profileVersion"u8)) + { + profileVersion = property.Value.GetString(); + continue; + } + if (property.NameEquals("apiVersion"u8)) + { + apiVersion = property.Value.GetString(); + continue; + } + } + return new ApiProfile(profileVersion.Value, apiVersion.Value); + } + + void IJsonModelSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) => Serialize(writer, options); + + private void Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + writer.WriteStartObject(); + if (Optional.IsDefined(ProfileVersion)) + { + writer.WritePropertyName("profileVersion"u8); + writer.WriteStringValue(ProfileVersion); + } + if (Optional.IsDefined(ApiVersion)) + { + writer.WritePropertyName("apiVersion"u8); + writer.WriteStringValue(ApiVersion); + } + writer.WriteEndObject(); + } + + private struct ApiProfileProperties + { + public Optional ProfileVersion { get; set; } + public Optional ApiVersion { get; set; } + } + + object IJsonModelSerializable.Deserialize(ref Utf8JsonReader reader, ModelSerializerOptions options) + { + if (!reader.TryDeserialize(options, SetProperty, out var properties)) + return null; + + return new ApiProfile(properties.ProfileVersion.Value, properties.ApiVersion.Value); + } + + private static void SetProperty(ReadOnlySpan propertyName, ref ApiProfileProperties properties, ref Utf8JsonReader reader, ModelSerializerOptions options) + { + if (propertyName.SequenceEqual("profileVersion"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.ProfileVersion = reader.GetString(); + return; + } + if (propertyName.SequenceEqual("apiVersion"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.ApiVersion = reader.GetString(); + return; + } + reader.Skip(); + } + + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + { + using var doc = JsonDocument.Parse(data); + return DeserializeApiProfile(doc.RootElement, options); + } + + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) + { + return ModelSerializerHelper.SerializeToBinaryData((writer) => { Serialize(writer, options); }); + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ApiProfile.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ApiProfile.cs new file mode 100644 index 000000000000..1c39af00a962 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ApiProfile.cs @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +namespace Azure.Core.Tests.Public.ResourceManager.Resources.Models +{ + /// The ApiProfile. + public partial class ApiProfile + { + /// Initializes a new instance of ApiProfile. + internal ApiProfile() + { + } + + /// Initializes a new instance of ApiProfile. + /// The profile version. + /// The API version. + internal ApiProfile(string profileVersion, string apiVersion) + { + ProfileVersion = profileVersion; + ApiVersion = apiVersion; + } + + /// The profile version. + public string ProfileVersion { get; } + /// The API version. + public string ApiVersion { get; } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/AvailabilitySetData.Serialization.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/AvailabilitySetData.Serialization.cs new file mode 100644 index 000000000000..48c938c3f563 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/AvailabilitySetData.Serialization.cs @@ -0,0 +1,402 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; +using System.Diagnostics.Tracing; +using System.IO; +using System.Runtime.CompilerServices; +using System.Text; +using System.Text.Json; +using Azure.Core; +using Azure.Core.Serialization; +using Azure.Core.Tests.Public.ResourceManager.Compute.Models; +using Azure.Core.Tests.Public.ResourceManager.Models; +using Azure.Core.Tests.Public.ResourceManager.Resources.Models; +using Newtonsoft.Json.Linq; + +namespace Azure.Core.Tests.Public.ResourceManager.Compute +{ + public partial class AvailabilitySetData : IUtf8JsonSerializable, IJsonModelSerializable + { + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => ((IJsonModelSerializable)this).Serialize(writer, ModelSerializerOptions.AzureServiceDefault); + + void IJsonModelSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) => Serialize(writer, options); + + private void Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + writer.WriteStartObject(); + if (options.Format == ModelSerializerFormat.Json) + { + writer.WritePropertyName("name"u8); + writer.WriteStringValue(Name); + } + if (options.Format == ModelSerializerFormat.Json) + { + writer.WritePropertyName("id"u8); + writer.WriteStringValue(Id.ToString()); + } + if (options.Format == ModelSerializerFormat.Json) + { + writer.WritePropertyName("type"u8); + writer.WriteStringValue(ResourceType.ToString()); + } + if (Optional.IsDefined(Sku)) + { + writer.WritePropertyName("sku"u8); + writer.WriteObjectValue(Sku); + } + if (Optional.IsCollectionDefined(Tags)) + { + writer.WritePropertyName("tags"u8); + writer.WriteStartObject(); + foreach (var item in Tags) + { + writer.WritePropertyName(item.Key); + writer.WriteStringValue(item.Value); + } + writer.WriteEndObject(); + } + writer.WritePropertyName("location"u8); + writer.WriteStringValue(Location); + writer.WritePropertyName("properties"u8); + writer.WriteStartObject(); + if (Optional.IsDefined(PlatformUpdateDomainCount)) + { + writer.WritePropertyName("platformUpdateDomainCount"u8); + writer.WriteNumberValue(PlatformUpdateDomainCount.Value); + } + if (Optional.IsDefined(PlatformFaultDomainCount)) + { + writer.WritePropertyName("platformFaultDomainCount"u8); + writer.WriteNumberValue(PlatformFaultDomainCount.Value); + } + if (Optional.IsCollectionDefined(VirtualMachines)) + { + writer.WritePropertyName("virtualMachines"u8); + writer.WriteStartArray(); + foreach (var item in VirtualMachines) + { + JsonSerializer.Serialize(writer, item); + } + writer.WriteEndArray(); + } + if (Optional.IsDefined(ProximityPlacementGroup)) + { + writer.WritePropertyName("proximityPlacementGroup"u8); + JsonSerializer.Serialize(writer, ProximityPlacementGroup); + } + writer.WriteEndObject(); + writer.WriteEndObject(); + } + + public static AvailabilitySetData DeserializeAvailabilitySetData(JsonElement element, ModelSerializerOptions? options = default) + { + options ??= ModelSerializerOptions.AzureServiceDefault; + + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + Optional sku = default; + Optional> tags = default; + AzureLocation location = default; + ResourceIdentifier id = default; + string name = default; + ResourceType type = default; + Optional systemData = default; + Optional platformUpdateDomainCount = default; + Optional platformFaultDomainCount = default; + Optional> virtualMachines = default; + Optional proximityPlacementGroup = default; + Optional> statuses = default; + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("sku"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + sku = ComputeSku.DeserializeComputeSku(property.Value); + continue; + } + if (property.NameEquals("tags"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + Dictionary dictionary = new Dictionary(); + foreach (var property0 in property.Value.EnumerateObject()) + { + dictionary.Add(property0.Name, property0.Value.GetString()); + } + tags = dictionary; + continue; + } + if (property.NameEquals("location"u8)) + { + location = new AzureLocation(property.Value.GetString()); + continue; + } + if (property.NameEquals("id"u8)) + { + id = new ResourceIdentifier(property.Value.GetString()); + continue; + } + if (property.NameEquals("name"u8)) + { + name = property.Value.GetString(); + continue; + } + if (property.NameEquals("type"u8)) + { + type = new ResourceType(property.Value.GetString()); + continue; + } + if (property.NameEquals("systemData"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + systemData = JsonSerializer.Deserialize(property.Value.GetRawText()); + continue; + } + if (property.NameEquals("properties"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + property.ThrowNonNullablePropertyIsNull(); + continue; + } + foreach (var property0 in property.Value.EnumerateObject()) + { + if (property0.NameEquals("platformUpdateDomainCount"u8)) + { + if (property0.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + platformUpdateDomainCount = property0.Value.GetInt32(); + continue; + } + if (property0.NameEquals("platformFaultDomainCount"u8)) + { + if (property0.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + platformFaultDomainCount = property0.Value.GetInt32(); + continue; + } + if (property0.NameEquals("virtualMachines"u8)) + { + if (property0.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + List array = new List(); + foreach (var item in property0.Value.EnumerateArray()) + { + array.Add(JsonSerializer.Deserialize(item.GetRawText())); + } + virtualMachines = array; + continue; + } + if (property0.NameEquals("proximityPlacementGroup"u8)) + { + if (property0.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + proximityPlacementGroup = JsonSerializer.Deserialize(property0.Value.GetRawText()); + continue; + } + if (property0.NameEquals("statuses"u8)) + { + if (property0.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + List array = new List(); + foreach (var item in property0.Value.EnumerateArray()) + { + array.Add(InstanceViewStatus.DeserializeInstanceViewStatus(item)); + } + statuses = array; + continue; + } + } + continue; + } + } + return new AvailabilitySetData(id, name, type, systemData.Value, Optional.ToDictionary(tags), location, sku.Value, Optional.ToNullable(platformUpdateDomainCount), Optional.ToNullable(platformFaultDomainCount), Optional.ToList(virtualMachines), proximityPlacementGroup, Optional.ToList(statuses)); + } + + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + { + using var doc = JsonDocument.Parse(data); + return DeserializeAvailabilitySetData(doc.RootElement, options); + } + + // only used for public access to internal serialize + public void Serialize(Utf8JsonWriter writer) => ((IUtf8JsonSerializable)this).Write(writer); + + private struct AvailabilitySetDataProperties + { + public Optional Sku { get; set; } + public Optional> Tags { get; set; } + public AzureLocation Location { get; set; } + public ResourceIdentifier Id { get; set; } + public string Name { get; set; } + public ResourceType ResourceType { get; set; } + public Optional SystemData { get; set; } + public Optional PlatformUpdateDomainCount { get; set; } + public Optional PlatformFaultDomainCount { get; set; } + public Optional> VirtualMachines { get; set; } + public Optional ProximityPlacementGroup { get; set; } + public Optional> Statuses { get; set; } + } + + object IJsonModelSerializable.Deserialize(ref Utf8JsonReader reader, ModelSerializerOptions options) + { + if (!reader.TryDeserialize(options, SetProperty, out var properties)) + return null; + + return new AvailabilitySetData( + properties.Id, + properties.Name, + properties.ResourceType, + properties.SystemData.Value, + Optional.ToDictionary(properties.Tags), + properties.Location, + properties.Sku.Value, + Optional.ToNullable(properties.PlatformUpdateDomainCount), + Optional.ToNullable(properties.PlatformFaultDomainCount), + Optional.ToList(properties.VirtualMachines), + properties.ProximityPlacementGroup, + Optional.ToList(properties.Statuses)); + } + + private static void SetProperty(ReadOnlySpan propertyName, ref AvailabilitySetDataProperties properties, ref Utf8JsonReader reader, ModelSerializerOptions options) + { + if (propertyName.SequenceEqual("tags"u8)) + { + properties.Tags = reader.GetDictionary(options); + return; + } + if (propertyName.SequenceEqual("sku"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.Sku = reader.GetObject(options); + return; + } + if (propertyName.SequenceEqual("location"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.Location = new AzureLocation(reader.GetString()); + return; + } + if (propertyName.SequenceEqual("id"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.Id = new ResourceIdentifier(reader.GetString()); + return; + } + if (propertyName.SequenceEqual("name"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.Name = reader.GetString(); + return; + } + if (propertyName.SequenceEqual("type"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.ResourceType = new ResourceType(reader.GetString()); + return; + } + if (propertyName.SequenceEqual("systemData"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.SystemData = reader.GetObject(options); + return; + } + if (propertyName.SequenceEqual("properties"u8)) + { + //this is an inline object (from flatten?) + reader.Read(); + if (reader.TokenType != JsonTokenType.StartObject) + throw new FormatException("Expected StartObject token"); + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndObject) + break; + + if (reader.TokenType != JsonTokenType.PropertyName) + throw new FormatException("Expected PropertyName token"); + + var innerPropertyName = reader.ValueSpan; + SetProperty(innerPropertyName, ref properties, ref reader, options); + } + return; + } + if (propertyName.SequenceEqual("platformUpdateDomainCount"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.PlatformUpdateDomainCount = reader.GetInt32(); + return; + } + if (propertyName.SequenceEqual("platformFaultDomainCount"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.PlatformFaultDomainCount = reader.GetInt32(); + return; + } + if (propertyName.SequenceEqual("virtualMachines"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + { + properties.VirtualMachines = reader.GetList(options); + } + return; + } + if (propertyName.SequenceEqual("proximityPlacementGroup"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.ProximityPlacementGroup = reader.GetObject(options); + return; + } + if (propertyName.SequenceEqual("statuses"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + { + properties.Statuses = reader.GetList(options); + } + return; + } + reader.Skip(); + } + + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) + { + return ModelSerializerHelper.SerializeToBinaryData((writer) => { Serialize(writer, options); }); + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/AvailabilitySetData.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/AvailabilitySetData.cs new file mode 100644 index 000000000000..2659e6557ca4 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/AvailabilitySetData.cs @@ -0,0 +1,91 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System.Collections.Generic; +using System.Text.Json; +using Azure.Core.Tests.Public.ResourceManager.Compute.Models; +using Azure.Core.Tests.Public.ResourceManager.Models; +using Azure.Core.Tests.Public.ResourceManager.Resources.Models; + +namespace Azure.Core.Tests.Public.ResourceManager.Compute +{ + /// + /// A class representing the AvailabilitySet data model. + /// Specifies information about the availability set that the virtual machine should be assigned to. Virtual machines specified in the same availability set are allocated to different nodes to maximize availability. For more information about availability sets, see [Availability sets overview](https://docs.microsoft.com/azure/virtual-machines/availability-set-overview). For more information on Azure planned maintenance, see [Maintenance and updates for Virtual Machines in Azure](https://docs.microsoft.com/azure/virtual-machines/maintenance-and-updates). Currently, a VM can only be added to an availability set at creation time. An existing VM cannot be added to an availability set. + /// + public partial class AvailabilitySetData : TrackedResourceData + { + internal AvailabilitySetData() { } + + public static implicit operator RequestContent(AvailabilitySetData availabilitySetData) + { + return new Utf8JsonDelayedRequestContent(availabilitySetData); + } + + public static explicit operator AvailabilitySetData(Response response) + { + using JsonDocument jsonDocument = JsonDocument.Parse(response.ContentStream); + return DeserializeAvailabilitySetData(jsonDocument.RootElement); + } + + /// Initializes a new instance of AvailabilitySetData. + /// The location. + public AvailabilitySetData(AzureLocation location) : base(location) + { + VirtualMachines = new ChangeTrackingList(); + Statuses = new ChangeTrackingList(); + } + + /// Initializes a new instance of AvailabilitySetData. + /// The id. + /// The name. + /// The resourceType. + /// The systemData. + /// The tags. + /// The location. + /// Sku of the availability set, only name is required to be set. See AvailabilitySetSkuTypes for possible set of values. Use 'Aligned' for virtual machines with managed disks and 'Classic' for virtual machines with unmanaged disks. Default value is 'Classic'. + /// Update Domain count. + /// Fault Domain count. + /// A list of references to all virtual machines in the availability set. + /// Specifies information about the proximity placement group that the availability set should be assigned to. Minimum api-version: 2018-04-01. + /// The resource status information. + internal AvailabilitySetData(ResourceIdentifier id, string name, ResourceType resourceType, SystemData systemData, IDictionary tags, AzureLocation location, ComputeSku sku, int? platformUpdateDomainCount, int? platformFaultDomainCount, IList virtualMachines, WritableSubResource proximityPlacementGroup, IReadOnlyList statuses) : base(id, name, resourceType, systemData, tags, location) + { + Sku = sku; + PlatformUpdateDomainCount = platformUpdateDomainCount; + PlatformFaultDomainCount = platformFaultDomainCount; + VirtualMachines = virtualMachines; + ProximityPlacementGroup = proximityPlacementGroup; + Statuses = statuses; + } + + /// Sku of the availability set, only name is required to be set. See AvailabilitySetSkuTypes for possible set of values. Use 'Aligned' for virtual machines with managed disks and 'Classic' for virtual machines with unmanaged disks. Default value is 'Classic'. + public ComputeSku Sku { get; set; } + /// Update Domain count. + public int? PlatformUpdateDomainCount { get; set; } + /// Fault Domain count. + public int? PlatformFaultDomainCount { get; set; } + /// A list of references to all virtual machines in the availability set. + public IList VirtualMachines { get; } + /// Specifies information about the proximity placement group that the availability set should be assigned to. Minimum api-version: 2018-04-01. + internal WritableSubResource ProximityPlacementGroup { get; set; } + /// Gets or sets Id. + public ResourceIdentifier ProximityPlacementGroupId + { + get => ProximityPlacementGroup is null ? default : ProximityPlacementGroup.Id; + set + { + if (ProximityPlacementGroup is null) + ProximityPlacementGroup = new WritableSubResource(); + ProximityPlacementGroup.Id = value; + } + } + + /// The resource status information. + public IReadOnlyList Statuses { get; } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ComputeSku.Serialization.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ComputeSku.Serialization.cs new file mode 100644 index 000000000000..4a3bd85eafbf --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ComputeSku.Serialization.cs @@ -0,0 +1,131 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Linq; +using System.Text.Json; +using Azure.Core; +using Azure.Core.Serialization; + +namespace Azure.Core.Tests.Public.ResourceManager.Compute.Models +{ + public partial class ComputeSku : IUtf8JsonSerializable, IJsonModelSerializable + { + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => ((IJsonModelSerializable)this).Serialize(writer, ModelSerializerOptions.AzureServiceDefault); + + void IJsonModelSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) => Serialize(writer, options); + + private void Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + writer.WriteStartObject(); + if (Optional.IsDefined(Name)) + { + writer.WritePropertyName("name"u8); + writer.WriteStringValue(Name); + } + if (Optional.IsDefined(Tier)) + { + writer.WritePropertyName("tier"u8); + writer.WriteStringValue(Tier); + } + if (Optional.IsDefined(Capacity)) + { + writer.WritePropertyName("capacity"u8); + writer.WriteNumberValue(Capacity.Value); + } + writer.WriteEndObject(); + } + + internal static ComputeSku DeserializeComputeSku(JsonElement element, ModelSerializerOptions? options = default) + { + options ??= ModelSerializerOptions.AzureServiceDefault; + + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + Optional name = default; + Optional tier = default; + Optional capacity = default; + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("name"u8)) + { + name = property.Value.GetString(); + continue; + } + if (property.NameEquals("tier"u8)) + { + tier = property.Value.GetString(); + continue; + } + if (property.NameEquals("capacity"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + capacity = property.Value.GetInt64(); + continue; + } + } + return new ComputeSku(name.Value, tier.Value, Optional.ToNullable(capacity)); + } + + private struct ComputeSkuProperties + { + public Optional Name { get; set; } + public Optional Tier { get; set; } + public Optional Capacity { get; set; } + } + + object IJsonModelSerializable.Deserialize(ref Utf8JsonReader reader, ModelSerializerOptions options) + { + if (!reader.TryDeserialize(options, SetProperty, out var properties)) + return null; + + return new ComputeSku(properties.Name.Value, properties.Tier.Value, Optional.ToNullable(properties.Capacity)); + } + + private static void SetProperty(ReadOnlySpan propertyName, ref ComputeSkuProperties properties, ref Utf8JsonReader reader, ModelSerializerOptions options) + { + if (propertyName.SequenceEqual("name"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.Name = reader.GetString(); + return; + } + if (propertyName.SequenceEqual("tier"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.Tier = reader.GetString(); + return; + } + if (propertyName.SequenceEqual("capacity"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.Capacity = reader.GetInt64(); + return; + } + reader.Skip(); + } + + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + { + using var doc = JsonDocument.Parse(data); + return DeserializeComputeSku(doc.RootElement, options); + } + + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) + { + return ModelSerializerHelper.SerializeToBinaryData((writer) => { Serialize(writer, options); }); + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ComputeSku.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ComputeSku.cs new file mode 100644 index 000000000000..6cac54af6d39 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ComputeSku.cs @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +namespace Azure.Core.Tests.Public.ResourceManager.Compute.Models +{ + /// Describes a virtual machine scale set sku. NOTE: If the new VM SKU is not supported on the hardware the scale set is currently on, you need to deallocate the VMs in the scale set before you modify the SKU name. + public partial class ComputeSku + { + /// Initializes a new instance of ComputeSku. + public ComputeSku() + { + } + + /// Initializes a new instance of ComputeSku. + /// The sku name. + /// Specifies the tier of virtual machines in a scale set.<br /><br /> Possible Values:<br /><br /> **Standard**<br /><br /> **Basic**. + /// Specifies the number of virtual machines in the scale set. + internal ComputeSku(string name, string tier, long? capacity) + { + Name = name; + Tier = tier; + Capacity = capacity; + } + + /// The sku name. + public string Name { get; set; } + /// Specifies the tier of virtual machines in a scale set.<br /><br /> Possible Values:<br /><br /> **Standard**<br /><br /> **Basic**. + public string Tier { get; set; } + /// Specifies the number of virtual machines in the scale set. + public long? Capacity { get; set; } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ComputeStatusLevelType.Serialization.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ComputeStatusLevelType.Serialization.cs new file mode 100644 index 000000000000..b6887869d02a --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ComputeStatusLevelType.Serialization.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; + +namespace Azure.Core.Tests.Public.ResourceManager.Compute.Models +{ + internal static partial class ComputeStatusLevelTypeExtensions + { + public static string ToSerialString(this ComputeStatusLevelType value) => value switch + { + ComputeStatusLevelType.Info => "Info", + ComputeStatusLevelType.Warning => "Warning", + ComputeStatusLevelType.Error => "Error", + _ => throw new ArgumentOutOfRangeException(nameof(value), value, "Unknown ComputeStatusLevelType value.") + }; + + public static ComputeStatusLevelType ToComputeStatusLevelType(this string value) + { + if (StringComparer.OrdinalIgnoreCase.Equals(value, "Info")) return ComputeStatusLevelType.Info; + if (StringComparer.OrdinalIgnoreCase.Equals(value, "Warning")) return ComputeStatusLevelType.Warning; + if (StringComparer.OrdinalIgnoreCase.Equals(value, "Error")) return ComputeStatusLevelType.Error; + throw new ArgumentOutOfRangeException(nameof(value), value, "Unknown ComputeStatusLevelType value."); + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ComputeStatusLevelType.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ComputeStatusLevelType.cs new file mode 100644 index 000000000000..9b5ffecea2a4 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ComputeStatusLevelType.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +namespace Azure.Core.Tests.Public.ResourceManager.Compute.Models +{ + /// The level code. + public enum ComputeStatusLevelType + { + /// Info. + Info, + /// Warning. + Warning, + /// Error. + Error + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/CreatedByType.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/CreatedByType.cs new file mode 100644 index 000000000000..d7b423adc162 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/CreatedByType.cs @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.ComponentModel; + +namespace Azure.Core.Tests.Public.ResourceManager.Models +{ + /// The type of identity that created the resource. + public readonly partial struct CreatedByType : IEquatable + { + private readonly string _value; + + /// Initializes a new instance of . + /// is null. + public CreatedByType(string value) + { + _value = value ?? throw new ArgumentNullException(nameof(value)); + } + + private const string UserValue = "User"; + private const string ApplicationValue = "Application"; + private const string ManagedIdentityValue = "ManagedIdentity"; + private const string KeyValue = "Key"; + + /// User. + public static CreatedByType User { get; } = new CreatedByType(UserValue); + /// Application. + public static CreatedByType Application { get; } = new CreatedByType(ApplicationValue); + /// ManagedIdentity. + public static CreatedByType ManagedIdentity { get; } = new CreatedByType(ManagedIdentityValue); + /// Key. + public static CreatedByType Key { get; } = new CreatedByType(KeyValue); + /// Determines if two values are the same. + public static bool operator ==(CreatedByType left, CreatedByType right) => left.Equals(right); + /// Determines if two values are not the same. + public static bool operator !=(CreatedByType left, CreatedByType right) => !left.Equals(right); + /// Converts a string to a . + public static implicit operator CreatedByType(string value) => new CreatedByType(value); + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) => obj is CreatedByType other && Equals(other); + /// + public bool Equals(CreatedByType other) => string.Equals(_value, other._value, StringComparison.InvariantCultureIgnoreCase); + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() => _value?.GetHashCode() ?? 0; + /// + public override string ToString() => _value; + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/InstanceViewStatus.Serialization.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/InstanceViewStatus.Serialization.cs new file mode 100644 index 000000000000..80aff8be4a61 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/InstanceViewStatus.Serialization.cs @@ -0,0 +1,179 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Globalization; +using System.Reflection.Emit; +using System.Text.Json; +using Azure.Core; +using Azure.Core.Serialization; + +namespace Azure.Core.Tests.Public.ResourceManager.Compute.Models +{ + public partial class InstanceViewStatus : IUtf8JsonSerializable, IJsonModelSerializable + { + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => ((IJsonModelSerializable)this).Serialize(writer, ModelSerializerOptions.AzureServiceDefault); + + void IJsonModelSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) => Serialize(writer, options); + + private void Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + writer.WriteStartObject(); + if (Optional.IsDefined(Code)) + { + writer.WritePropertyName("code"u8); + writer.WriteStringValue(Code); + } + if (Optional.IsDefined(Level)) + { + writer.WritePropertyName("level"u8); + writer.WriteStringValue(Level.Value.ToSerialString()); + } + if (Optional.IsDefined(DisplayStatus)) + { + writer.WritePropertyName("displayStatus"u8); + writer.WriteStringValue(DisplayStatus); + } + if (Optional.IsDefined(Message)) + { + writer.WritePropertyName("message"u8); + writer.WriteStringValue(Message); + } + if (Optional.IsDefined(Time)) + { + writer.WritePropertyName("time"u8); + writer.WriteStringValue(Time.Value, "O"); + } + writer.WriteEndObject(); + } + + internal static InstanceViewStatus DeserializeInstanceViewStatus(JsonElement element, ModelSerializerOptions? options = default) + { + options ??= ModelSerializerOptions.AzureServiceDefault; + + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + Optional code = default; + Optional level = default; + Optional displayStatus = default; + Optional message = default; + Optional time = default; + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("code"u8)) + { + code = property.Value.GetString(); + continue; + } + if (property.NameEquals("level"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + level = property.Value.GetString().ToComputeStatusLevelType(); + continue; + } + if (property.NameEquals("displayStatus"u8)) + { + displayStatus = property.Value.GetString(); + continue; + } + if (property.NameEquals("message"u8)) + { + message = property.Value.GetString(); + continue; + } + if (property.NameEquals("time"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + time = property.Value.GetDateTimeOffset("O"); + continue; + } + } + return new InstanceViewStatus(code.Value, Optional.ToNullable(level), displayStatus.Value, message.Value, Optional.ToNullable(time)); + } + + private struct InstanceViewStatusProperties + { + public Optional Code { get; set; } + public Optional Level { get; set; } + public Optional DisplayStatus { get; set; } + public Optional Message { get; set; } + public Optional Time { get; set; } + } + + object IJsonModelSerializable.Deserialize(ref Utf8JsonReader reader, ModelSerializerOptions options) + { + if (!reader.TryDeserialize(options, SetProperty, out var properties)) + return null; + + return new InstanceViewStatus( + properties.Code.Value, + Optional.ToNullable(properties.Level), + properties.DisplayStatus.Value, + properties.Message.Value, + Optional.ToNullable(properties.Time)); + } + + private static void SetProperty(ReadOnlySpan propertyName, ref InstanceViewStatusProperties properties, ref Utf8JsonReader reader, ModelSerializerOptions options) + { + if (propertyName.SequenceEqual("code"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.Code = reader.GetString(); + return; + } + if (propertyName.SequenceEqual("level"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.Level = reader.GetString().ToComputeStatusLevelType(); + return; + } + if (propertyName.SequenceEqual("displayStatus"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.DisplayStatus = reader.GetString(); + return; + } + if (propertyName.SequenceEqual("message"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.Message = reader.GetString(); + return; + } + if (propertyName.SequenceEqual("time"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.Time = DateTimeOffset.Parse(reader.GetString(), CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal); + return; + } + reader.Skip(); + } + + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + { + using var doc = JsonDocument.Parse(data); + return DeserializeInstanceViewStatus(doc.RootElement, options); + } + + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) + { + return ModelSerializerHelper.SerializeToBinaryData((writer) => { Serialize(writer, options); }); + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/InstanceViewStatus.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/InstanceViewStatus.cs new file mode 100644 index 000000000000..b1b72f27c854 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/InstanceViewStatus.cs @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; + +namespace Azure.Core.Tests.Public.ResourceManager.Compute.Models +{ + /// Instance view status. + public partial class InstanceViewStatus + { + /// Initializes a new instance of InstanceViewStatus. + public InstanceViewStatus() + { + } + + /// Initializes a new instance of InstanceViewStatus. + /// The status code. + /// The level code. + /// The short localizable label for the status. + /// The detailed status message, including for alerts and error messages. + /// The time of the status. + internal InstanceViewStatus(string code, ComputeStatusLevelType? level, string displayStatus, string message, DateTimeOffset? time) + { + Code = code; + Level = level; + DisplayStatus = displayStatus; + Message = message; + Time = time; + } + + /// The status code. + public string Code { get; set; } + /// The level code. + public ComputeStatusLevelType? Level { get; set; } + /// The short localizable label for the status. + public string DisplayStatus { get; set; } + /// The detailed status message, including for alerts and error messages. + public string Message { get; set; } + /// The time of the status. + public DateTimeOffset? Time { get; set; } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ProviderAuthorizationConsentState.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ProviderAuthorizationConsentState.cs new file mode 100644 index 000000000000..bbd451764eb2 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ProviderAuthorizationConsentState.cs @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.ComponentModel; + +namespace Azure.Core.Tests.Public.ResourceManager.Resources.Models +{ + /// The provider authorization consent state. + public readonly partial struct ProviderAuthorizationConsentState : IEquatable + { + private readonly string _value; + + /// Initializes a new instance of . + /// is null. + public ProviderAuthorizationConsentState(string value) + { + _value = value ?? throw new ArgumentNullException(nameof(value)); + } + + private const string NotSpecifiedValue = "NotSpecified"; + private const string RequiredValue = "Required"; + private const string NotRequiredValue = "NotRequired"; + private const string ConsentedValue = "Consented"; + + /// NotSpecified. + public static ProviderAuthorizationConsentState NotSpecified { get; } = new ProviderAuthorizationConsentState(NotSpecifiedValue); + /// Required. + public static ProviderAuthorizationConsentState Required { get; } = new ProviderAuthorizationConsentState(RequiredValue); + /// NotRequired. + public static ProviderAuthorizationConsentState NotRequired { get; } = new ProviderAuthorizationConsentState(NotRequiredValue); + /// Consented. + public static ProviderAuthorizationConsentState Consented { get; } = new ProviderAuthorizationConsentState(ConsentedValue); + /// Determines if two values are the same. + public static bool operator ==(ProviderAuthorizationConsentState left, ProviderAuthorizationConsentState right) => left.Equals(right); + /// Determines if two values are not the same. + public static bool operator !=(ProviderAuthorizationConsentState left, ProviderAuthorizationConsentState right) => !left.Equals(right); + /// Converts a string to a . + public static implicit operator ProviderAuthorizationConsentState(string value) => new ProviderAuthorizationConsentState(value); + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) => obj is ProviderAuthorizationConsentState other && Equals(other); + /// + public bool Equals(ProviderAuthorizationConsentState other) => string.Equals(_value, other._value, StringComparison.InvariantCultureIgnoreCase); + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() => _value?.GetHashCode() ?? 0; + /// + public override string ToString() => _value; + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ProviderExtendedLocation.Serialization.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ProviderExtendedLocation.Serialization.cs new file mode 100644 index 000000000000..5bc7bc70c0ba --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ProviderExtendedLocation.Serialization.cs @@ -0,0 +1,145 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; +using System.Text.Json; +using Azure.Core; +using Azure.Core.Serialization; + +namespace Azure.Core.Tests.Public.ResourceManager.Resources.Models +{ + public partial class ProviderExtendedLocation : IUtf8JsonSerializable, IJsonModelSerializable + { + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => ((IJsonModelSerializable)this).Serialize(writer, ModelSerializerOptions.AzureServiceDefault); + + internal static ProviderExtendedLocation DeserializeProviderExtendedLocation(JsonElement element, ModelSerializerOptions? options = default) + { + options ??= ModelSerializerOptions.AzureServiceDefault; + + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + Optional location = default; + Optional type = default; + Optional> extendedLocations = default; + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("location"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + location = new AzureLocation(property.Value.GetString()); + continue; + } + if (property.NameEquals("type"u8)) + { + type = property.Value.GetString(); + continue; + } + if (property.NameEquals("extendedLocations"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + List array = new List(); + foreach (var item in property.Value.EnumerateArray()) + { + array.Add(item.GetString()); + } + extendedLocations = array; + continue; + } + } + return new ProviderExtendedLocation(Optional.ToNullable(location), type.Value, Optional.ToList(extendedLocations)); + } + + void IJsonModelSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) => Serialize(writer, options); + + private void Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + writer.WriteStartObject(); + if (Optional.IsDefined(Location)) + { + writer.WritePropertyName("location"u8); + writer.WriteStringValue(Location.Value.DisplayName); + } + if (Optional.IsDefined(ProviderExtendedLocationType)) + { + writer.WritePropertyName("type"u8); + writer.WriteStringValue(ProviderExtendedLocationType); + } + if (Optional.IsCollectionDefined(ExtendedLocations)) + { + writer.WritePropertyName("extendedLocations"u8); + writer.WriteStartArray(); + foreach (var item in ExtendedLocations) + { + writer.WriteStringValue(item); + } + writer.WriteEndArray(); + } + writer.WriteEndObject(); + } + + private struct ProviderExtendedLocationProperties + { + public Optional Location { get; set; } + public Optional ProviderExtendedLocationType { get; set; } + public Optional> ExtendedLocations { get; set; } + } + + object IJsonModelSerializable.Deserialize(ref Utf8JsonReader reader, ModelSerializerOptions options) + { + if (!reader.TryDeserialize(options, SetProperty, out var properties)) + return null; + + return new ProviderExtendedLocation(Optional.ToNullable(properties.Location), properties.ProviderExtendedLocationType.Value, Optional.ToList(properties.ExtendedLocations)); + } + + private static void SetProperty(ReadOnlySpan propertyName, ref ProviderExtendedLocationProperties properties, ref Utf8JsonReader reader, ModelSerializerOptions options) + { + if (propertyName.SequenceEqual("location"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.Location = new AzureLocation(reader.GetString()); + return; + } + if (propertyName.SequenceEqual("type"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.ProviderExtendedLocationType = reader.GetString(); + return; + } + if (propertyName.SequenceEqual("extendedLocations"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.ExtendedLocations = reader.GetList(options); + return; + } + reader.Skip(); + } + + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + { + using var doc = JsonDocument.Parse(data); + return DeserializeProviderExtendedLocation(doc.RootElement, options); + } + + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) + { + return ModelSerializerHelper.SerializeToBinaryData((writer) => { Serialize(writer, options); }); + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ProviderExtendedLocation.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ProviderExtendedLocation.cs new file mode 100644 index 000000000000..365c3b07e864 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ProviderExtendedLocation.cs @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System.Collections.Generic; +using Azure.Core; + +namespace Azure.Core.Tests.Public.ResourceManager.Resources.Models +{ + /// The provider extended location. + public partial class ProviderExtendedLocation + { + /// Initializes a new instance of ProviderExtendedLocation. + internal ProviderExtendedLocation() + { + ExtendedLocations = new ChangeTrackingList(); + } + + /// Initializes a new instance of ProviderExtendedLocation. + /// The azure location. + /// The extended location type. + /// The extended locations for the azure location. + internal ProviderExtendedLocation(AzureLocation? location, string providerExtendedLocationType, IReadOnlyList extendedLocations) + { + Location = location; + ProviderExtendedLocationType = providerExtendedLocationType; + ExtendedLocations = extendedLocations; + } + + /// The azure location. + public AzureLocation? Location { get; } + /// The extended location type. + public string ProviderExtendedLocationType { get; } + /// The extended locations for the azure location. + public IReadOnlyList ExtendedLocations { get; } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ProviderResourceType.Serialization.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ProviderResourceType.Serialization.cs new file mode 100644 index 000000000000..c536d162a7c3 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ProviderResourceType.Serialization.cs @@ -0,0 +1,368 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; +using System.Security.AccessControl; +using System.Text.Json; +using Azure.Core; +using Azure.Core.Serialization; + +namespace Azure.Core.Tests.Public.ResourceManager.Resources.Models +{ + public partial class ProviderResourceType : IUtf8JsonSerializable, IJsonModelSerializable + { + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => ((IJsonModelSerializable)this).Serialize(writer, ModelSerializerOptions.AzureServiceDefault); + + internal static ProviderResourceType DeserializeProviderResourceType(JsonElement element, ModelSerializerOptions? options = default) + { + options ??= ModelSerializerOptions.AzureServiceDefault; + + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + Optional resourceType = default; + Optional> locations = default; + Optional> locationMappings = default; + Optional> aliases = default; + Optional> apiVersions = default; + Optional defaultApiVersion = default; + Optional> zoneMappings = default; + Optional> apiProfiles = default; + Optional capabilities = default; + Optional> properties = default; + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("resourceType"u8)) + { + resourceType = property.Value.GetString(); + continue; + } + if (property.NameEquals("locations"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + List array = new List(); + foreach (var item in property.Value.EnumerateArray()) + { + array.Add(item.GetString()); + } + locations = array; + continue; + } + if (property.NameEquals("locationMappings"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + List array = new List(); + foreach (var item in property.Value.EnumerateArray()) + { + array.Add(ProviderExtendedLocation.DeserializeProviderExtendedLocation(item)); + } + locationMappings = array; + continue; + } + if (property.NameEquals("aliases"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + List array = new List(); + foreach (var item in property.Value.EnumerateArray()) + { + array.Add(ResourceTypeAlias.DeserializeResourceTypeAlias(item)); + } + aliases = array; + continue; + } + if (property.NameEquals("apiVersions"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + List array = new List(); + foreach (var item in property.Value.EnumerateArray()) + { + array.Add(item.GetString()); + } + apiVersions = array; + continue; + } + if (property.NameEquals("defaultApiVersion"u8)) + { + defaultApiVersion = property.Value.GetString(); + continue; + } + if (property.NameEquals("zoneMappings"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + List array = new List(); + foreach (var item in property.Value.EnumerateArray()) + { + array.Add(ZoneMapping.DeserializeZoneMapping(item)); + } + zoneMappings = array; + continue; + } + if (property.NameEquals("apiProfiles"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + List array = new List(); + foreach (var item in property.Value.EnumerateArray()) + { + array.Add(ApiProfile.DeserializeApiProfile(item)); + } + apiProfiles = array; + continue; + } + if (property.NameEquals("capabilities"u8)) + { + capabilities = property.Value.GetString(); + continue; + } + if (property.NameEquals("properties"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + Dictionary dictionary = new Dictionary(); + foreach (var property0 in property.Value.EnumerateObject()) + { + dictionary.Add(property0.Name, property0.Value.GetString()); + } + properties = dictionary; + continue; + } + } + return new ProviderResourceType(resourceType.Value, Optional.ToList(locations), Optional.ToList(locationMappings), Optional.ToList(aliases), Optional.ToList(apiVersions), defaultApiVersion.Value, Optional.ToList(zoneMappings), Optional.ToList(apiProfiles), capabilities.Value, Optional.ToDictionary(properties)); + } + + void IJsonModelSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) => Serialize(writer, options); + + private void Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + writer.WriteStartObject(); + if(Optional.IsDefined(ResourceType)) + { + writer.WritePropertyName("resourceType"u8); + writer.WriteStringValue(ResourceType); + } + if (Optional.IsCollectionDefined(Locations)) + { + writer.WritePropertyName("locations"u8); + writer.WriteStartArray(); + foreach (var item in Locations) + { + writer.WriteStringValue(item); + } + writer.WriteEndArray(); + } + if (Optional.IsCollectionDefined(ApiVersions)) + { + writer.WritePropertyName("apiVersions"u8); + writer.WriteStartArray(); + foreach (var item in ApiVersions) + { + writer.WriteStringValue(item); + } + writer.WriteEndArray(); + } + if (Optional.IsDefined(DefaultApiVersion)) + { + writer.WritePropertyName("defaultApiVersion"u8); + writer.WriteStringValue(DefaultApiVersion); + } + if (Optional.IsCollectionDefined(ApiProfiles)) + { + writer.WritePropertyName("apiProfiles"u8); + writer.WriteStartArray(); + foreach (var item in ApiProfiles) + { + writer.WriteObjectValue(item); + } + writer.WriteEndArray(); + } + if (Optional.IsCollectionDefined(ZoneMappings)) + { + writer.WritePropertyName("zoneMappings"u8); + writer.WriteStartArray(); + foreach (var item in ZoneMappings) + { + writer.WriteObjectValue(item); + } + writer.WriteEndArray(); + } + if (Optional.IsCollectionDefined(LocationMappings)) + { + writer.WritePropertyName("locationMappings"u8); + writer.WriteStartArray(); + foreach (var item in LocationMappings) + { + writer.WriteObjectValue(item); + } + writer.WriteEndArray(); + } + if (Optional.IsDefined(Capabilities)) + { + writer.WritePropertyName("capabilities"u8); + writer.WriteStringValue(Capabilities); + } + if (Optional.IsCollectionDefined(Aliases)) + { + writer.WritePropertyName("aliases"u8); + writer.WriteStartArray(); + foreach (var item in Aliases) + { + writer.WriteObjectValue(item); + } + writer.WriteEndArray(); + } + if (Optional.IsCollectionDefined(Properties)) + { + writer.WritePropertyName("properties"u8); + writer.WriteStartObject(); + foreach (var item in Properties) + { + writer.WritePropertyName(item.Key); + writer.WriteStringValue(item.Value); + } + writer.WriteEndObject(); + } + writer.WriteEndObject(); + } + + private struct ProviderResourceTypeProperties + { + public Optional ResourceType { get; set; } + public Optional> Locations { get; set; } + public Optional> LocationMappings { get; set; } + public Optional> Aliases { get; set; } + public Optional> ApiVersions { get; set; } + public Optional DefaultApiVersion { get; set; } + public Optional> ZoneMappings { get; set; } + public Optional> ApiProfiles { get; set; } + public Optional Capabilities { get; set; } + public Optional> Properties { get; set; } + } + + object IJsonModelSerializable.Deserialize(ref Utf8JsonReader reader, ModelSerializerOptions options) + { + if (!reader.TryDeserialize(options, SetProperty, out var properties)) + return null; + + return new ProviderResourceType( + properties.ResourceType.Value, + Optional.ToList(properties.Locations), + Optional.ToList(properties.LocationMappings), + Optional.ToList(properties.Aliases), + Optional.ToList(properties.ApiVersions), + properties.DefaultApiVersion.Value, + Optional.ToList(properties.ZoneMappings), + Optional.ToList(properties.ApiProfiles), + properties.Capabilities.Value, + Optional.ToDictionary(properties.Properties)); + } + + private static void SetProperty(ReadOnlySpan propertyName, ref ProviderResourceTypeProperties properties, ref Utf8JsonReader reader, ModelSerializerOptions options) + { + if (propertyName.SequenceEqual("resourceType"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.ResourceType = reader.GetString(); + return; + } + if (propertyName.SequenceEqual("locations"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.Locations = reader.GetList(options); + return; + } + if (propertyName.SequenceEqual("locationMappings"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.LocationMappings = reader.GetList(options); + return; + } + if (propertyName.SequenceEqual("aliases"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.Aliases = reader.GetList(options); + return; + } + if (propertyName.SequenceEqual("apiVersions"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.ApiVersions = reader.GetList(options); + return; + } + if (propertyName.SequenceEqual("defaultApiVersion"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.DefaultApiVersion = reader.GetString(); + return; + } + if (propertyName.SequenceEqual("zoneMappings"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.ZoneMappings = reader.GetList(options); + return; + } + if (propertyName.SequenceEqual("apiProfiles"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.ApiProfiles = reader.GetList(options); + return; + } + if (propertyName.SequenceEqual("capabilities"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.Capabilities = reader.GetString(); + return; + } + if (propertyName.SequenceEqual("properties"u8)) + { + properties.Properties = reader.GetDictionary(options); + return; + } + reader.Skip(); + } + + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + { + using var doc = JsonDocument.Parse(data); + return DeserializeProviderResourceType(doc.RootElement, options); + } + + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) + { + return ModelSerializerHelper.SerializeToBinaryData((writer) => { Serialize(writer, options); }); + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ProviderResourceType.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ProviderResourceType.cs new file mode 100644 index 000000000000..89a6ecb9e9fa --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ProviderResourceType.cs @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System.Collections.Generic; +using Azure.Core; + +namespace Azure.Core.Tests.Public.ResourceManager.Resources.Models +{ + /// Resource type managed by the resource provider. + public partial class ProviderResourceType + { + /// Initializes a new instance of ProviderResourceType. + internal ProviderResourceType() + { + Locations = new ChangeTrackingList(); + LocationMappings = new ChangeTrackingList(); + Aliases = new ChangeTrackingList(); + ApiVersions = new ChangeTrackingList(); + ZoneMappings = new ChangeTrackingList(); + ApiProfiles = new ChangeTrackingList(); + Properties = new ChangeTrackingDictionary(); + } + + /// Initializes a new instance of ProviderResourceType. + /// The resource type. + /// The collection of locations where this resource type can be created. + /// The location mappings that are supported by this resource type. + /// The aliases that are supported by this resource type. + /// The API version. + /// The default API version. + /// + /// The API profiles for the resource provider. + /// The additional capabilities offered by this resource type. + /// The properties. + internal ProviderResourceType(string resourceType, IReadOnlyList locations, IReadOnlyList locationMappings, IReadOnlyList aliases, IReadOnlyList apiVersions, string defaultApiVersion, IReadOnlyList zoneMappings, IReadOnlyList apiProfiles, string capabilities, IReadOnlyDictionary properties) + { + ResourceType = resourceType; + Locations = locations; + LocationMappings = locationMappings; + Aliases = aliases; + ApiVersions = apiVersions; + DefaultApiVersion = defaultApiVersion; + ZoneMappings = zoneMappings; + ApiProfiles = apiProfiles; + Capabilities = capabilities; + Properties = properties; + } + + /// The resource type. + public string ResourceType { get; } + /// The collection of locations where this resource type can be created. + public IReadOnlyList Locations { get; } + /// The location mappings that are supported by this resource type. + public IReadOnlyList LocationMappings { get; } + /// The aliases that are supported by this resource type. + public IReadOnlyList Aliases { get; } + /// The API version. + public IReadOnlyList ApiVersions { get; } + /// The default API version. + public string DefaultApiVersion { get; } + /// Gets the zone mappings. + public IReadOnlyList ZoneMappings { get; } + /// The API profiles for the resource provider. + public IReadOnlyList ApiProfiles { get; } + /// The additional capabilities offered by this resource type. + public string Capabilities { get; } + /// The properties. + public IReadOnlyDictionary Properties { get; } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceData.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceData.cs new file mode 100644 index 000000000000..d642db96d26e --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceData.cs @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#nullable disable + +using Azure.Core; + +namespace Azure.Core.Tests.Public.ResourceManager.Models +{ + /// Common fields that are returned in the response for all Azure Resource Manager resources. + public abstract partial class ResourceData + { + /// Initializes a new instance of Resource. + protected ResourceData() + { + } + + /// Initializes a new instance of Resource. + /// Fully qualified resource ID for the resource. Ex - /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/{resourceProviderNamespace}/{resourceType}/{resourceName}. + /// The name of the resource. + /// The type of the resource. E.g. "Microsoft.Compute/virtualMachines" or "Microsoft.Storage/storageAccounts". + /// Azure Resource Manager metadata containing createdBy and modifiedBy information. + protected ResourceData(ResourceIdentifier id, string name, ResourceType resourceType, SystemData systemData) + { + Id = id; + Name = name; + ResourceType = resourceType; + SystemData = systemData; + } + + /// Fully qualified resource ID for the resource. Ex - /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/{resourceProviderNamespace}/{resourceType}/{resourceName}. + public ResourceIdentifier Id { get; } + /// The name of the resource. + public string Name { get; } + /// The type of the resource. E.g. "Microsoft.Compute/virtualMachines" or "Microsoft.Storage/storageAccounts". + public ResourceType ResourceType { get; } + /// Azure Resource Manager metadata containing createdBy and modifiedBy information. + public SystemData SystemData { get; } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceProviderData.Serialization.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceProviderData.Serialization.cs new file mode 100644 index 000000000000..cfc89074387f --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceProviderData.Serialization.cs @@ -0,0 +1,219 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; +using System.IO; +using System.Security.AccessControl; +using System.Text.Json; +using Azure.Core; +using Azure.Core.Serialization; +using Azure.Core.Tests.Public.ResourceManager.Resources.Models; +using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; + +namespace Azure.Core.Tests.Public.ResourceManager.Resources +{ + public partial class ResourceProviderData : IUtf8JsonSerializable, IJsonModelSerializable + { + public static ResourceProviderData DeserializeResourceProviderData(JsonElement element, ModelSerializerOptions? options = default) + { + options ??= ModelSerializerOptions.AzureServiceDefault; + + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + Optional id = default; + Optional @namespace = default; + Optional registrationState = default; + Optional registrationPolicy = default; + Optional> resourceTypes = default; + Optional providerAuthorizationConsentState = default; + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("id"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + id = new ResourceIdentifier(property.Value.GetString()); + continue; + } + if (property.NameEquals("namespace"u8)) + { + @namespace = property.Value.GetString(); + continue; + } + if (property.NameEquals("registrationState"u8)) + { + registrationState = property.Value.GetString(); + continue; + } + if (property.NameEquals("registrationPolicy"u8)) + { + registrationPolicy = property.Value.GetString(); + continue; + } + if (property.NameEquals("resourceTypes"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + List array = new List(); + foreach (var item in property.Value.EnumerateArray()) + { + array.Add(ProviderResourceType.DeserializeProviderResourceType(item)); + } + resourceTypes = array; + continue; + } + if (property.NameEquals("providerAuthorizationConsentState"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + providerAuthorizationConsentState = new ProviderAuthorizationConsentState(property.Value.GetString()); + continue; + } + } + return new ResourceProviderData(id.Value, @namespace.Value, registrationState.Value, registrationPolicy.Value, Optional.ToList(resourceTypes), Optional.ToNullable(providerAuthorizationConsentState)); + } + + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + { + using var doc = JsonDocument.Parse(data); + return DeserializeResourceProviderData(doc.RootElement, options); + } + + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => ((IJsonModelSerializable)this).Serialize(writer, ModelSerializerOptions.AzureServiceDefault); + + // only used for public access to internal serialize + public void Serialize(Utf8JsonWriter writer) => ((IUtf8JsonSerializable)this).Write(writer); + + void IJsonModelSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) => Serialize(writer, options); + + private void Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + writer.WriteStartObject(); + if (Optional.IsDefined(Id)) + { + writer.WritePropertyName("id"u8); + writer.WriteStringValue(Id); + } + if (Optional.IsDefined(Namespace)) + { + writer.WritePropertyName("namespace"u8); + writer.WriteStringValue(Namespace); + } + if (Optional.IsCollectionDefined(ResourceTypes)) + { + writer.WritePropertyName("resourceTypes"u8); + writer.WriteStartArray(); + foreach (var item in ResourceTypes) + { + writer.WriteObjectValue(item); + } + writer.WriteEndArray(); + } + if (Optional.IsDefined(RegistrationState)) + { + writer.WritePropertyName("registrationState"u8); + writer.WriteStringValue(RegistrationState); + } + if (Optional.IsDefined(RegistrationPolicy)) + { + writer.WritePropertyName("registrationPolicy"u8); + writer.WriteStringValue(RegistrationPolicy); + } + if (Optional.IsDefined(ProviderAuthorizationConsentState)) + { + writer.WritePropertyName("providerAuthorizationConsentState"u8); + writer.WriteStringValue(ProviderAuthorizationConsentState.ToString()); + } + writer.WriteEndObject(); + } + + private struct ResourceProviderDataProperties + { + public Optional Id { get; set; } + public Optional Namespace { get; set; } + public Optional RegistrationState { get; set; } + public Optional RegistrationPolicy { get; set; } + public Optional> ResourceTypes { get; set; } + public Optional ProviderAuthorizationConsentState { get; set; } + } + + object IJsonModelSerializable.Deserialize(ref Utf8JsonReader reader, ModelSerializerOptions options) + { + if (!reader.TryDeserialize< ResourceProviderDataProperties>(options, SetProperty, out var properties)) + return null; + + return new ResourceProviderData( + properties.Id.Value, + properties.Namespace.Value, + properties.RegistrationState.Value, + properties.RegistrationPolicy.Value, + Optional.ToList(properties.ResourceTypes), + Optional.ToNullable(properties.ProviderAuthorizationConsentState)); + } + + private static void SetProperty(ReadOnlySpan propertyName, ref ResourceProviderDataProperties properties, ref Utf8JsonReader reader, ModelSerializerOptions options) + { + if (propertyName.SequenceEqual("id"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.Id = new ResourceIdentifier(reader.GetString()); + return; + } + if (propertyName.SequenceEqual("namespace"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.Namespace = reader.GetString(); + return; + } + if (propertyName.SequenceEqual("registrationState"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.RegistrationState = reader.GetString(); + return; + } + if (propertyName.SequenceEqual("registrationPolicy"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.RegistrationPolicy = reader.GetString(); + return; + } + if (propertyName.SequenceEqual("resourceTypes"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.ResourceTypes = reader.GetList(options); + return; + } + if (propertyName.SequenceEqual("providerAuthorizationConsentState"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.ProviderAuthorizationConsentState = new ProviderAuthorizationConsentState(reader.GetString()); + return; + } + reader.Skip(); + } + + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) + { + return ModelSerializerHelper.SerializeToBinaryData((writer) => { Serialize(writer, options); }); + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceProviderData.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceProviderData.cs new file mode 100644 index 000000000000..c7e80b4383a9 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceProviderData.cs @@ -0,0 +1,85 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; +using System.Text.Json.Serialization; +using System.Text.Json; +using Azure.Core; +using Azure.Core.Tests.Public.ResourceManager.Resources.Models; +using Azure.Core.Serialization; + +namespace Azure.Core.Tests.Public.ResourceManager.Resources +{ + /// + /// A class representing the ResourceProvider data model. + /// Resource provider information. + /// + public partial class ResourceProviderData + { + public static implicit operator RequestContent(ResourceProviderData resourceProviderData) + { + return new Utf8JsonDelayedRequestContent(resourceProviderData); + } + + public static explicit operator ResourceProviderData(Response response) + { + using JsonDocument jsonDocument = JsonDocument.Parse(response.ContentStream); + return DeserializeResourceProviderData(jsonDocument.RootElement); + } + + /// Initializes a new instance of ProviderData. + public ResourceProviderData() + { + ResourceTypes = new ChangeTrackingList(); + } + + /// Initializes a new instance of ProviderData. + /// The provider ID. + /// The namespace of the resource provider. + /// The registration state of the resource provider. + /// The registration policy of the resource provider. + /// The collection of provider resource types. + /// The provider authorization consent state. + internal ResourceProviderData(ResourceIdentifier id, string @namespace, string registrationState, string registrationPolicy, IReadOnlyList resourceTypes, ProviderAuthorizationConsentState? providerAuthorizationConsentState) + { + Id = id; + Namespace = @namespace; + RegistrationState = registrationState; + RegistrationPolicy = registrationPolicy; + ResourceTypes = resourceTypes; + ProviderAuthorizationConsentState = providerAuthorizationConsentState; + } + + /// The provider ID. + public ResourceIdentifier Id { get; } + + internal partial class ProviderDataConverter : JsonConverter + { + public override void Write(Utf8JsonWriter writer, ResourceProviderData providerData, JsonSerializerOptions options) + { + writer.WriteObjectValue(providerData); + } + public override ResourceProviderData Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var document = JsonDocument.ParseValue(ref reader); + return DeserializeResourceProviderData(document.RootElement); + } + } + + /// The namespace of the resource provider. + public string Namespace { get; } + /// The registration state of the resource provider. + public string RegistrationState { get; } + /// The registration policy of the resource provider. + public string RegistrationPolicy { get; } + /// The collection of provider resource types. + public IReadOnlyList ResourceTypes { get; } + /// The provider authorization consent state. + public ProviderAuthorizationConsentState? ProviderAuthorizationConsentState { get; } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAlias.Serialization.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAlias.Serialization.cs new file mode 100644 index 000000000000..9c6aaeb90166 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAlias.Serialization.cs @@ -0,0 +1,217 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.Json; +using Azure.Core; +using Azure.Core.Serialization; + +namespace Azure.Core.Tests.Public.ResourceManager.Resources.Models +{ + public partial class ResourceTypeAlias : IUtf8JsonSerializable, IJsonModelSerializable + { + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => ((IJsonModelSerializable)this).Serialize(writer, ModelSerializerOptions.AzureServiceDefault); + + internal static ResourceTypeAlias DeserializeResourceTypeAlias(JsonElement element, ModelSerializerOptions? options = default) + { + options ??= ModelSerializerOptions.AzureServiceDefault; + + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + Optional name = default; + Optional> paths = default; + Optional type = default; + Optional defaultPath = default; + Optional defaultPattern = default; + Optional defaultMetadata = default; + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("name"u8)) + { + name = property.Value.GetString(); + continue; + } + if (property.NameEquals("paths"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + List array = new List(); + foreach (var item in property.Value.EnumerateArray()) + { + array.Add(ResourceTypeAliasPath.DeserializeResourceTypeAliasPath(item)); + } + paths = array; + continue; + } + if (property.NameEquals("type"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + type = property.Value.GetString().ToResourceTypeAliasType(); + continue; + } + if (property.NameEquals("defaultPath"u8)) + { + defaultPath = property.Value.GetString(); + continue; + } + if (property.NameEquals("defaultPattern"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + defaultPattern = ResourceTypeAliasPattern.DeserializeResourceTypeAliasPattern(property.Value); + continue; + } + if (property.NameEquals("defaultMetadata"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + defaultMetadata = ResourceTypeAliasPathMetadata.DeserializeResourceTypeAliasPathMetadata(property.Value); + continue; + } + } + return new ResourceTypeAlias(name.Value, Optional.ToList(paths), Optional.ToNullable(type), defaultPath.Value, defaultPattern.Value, defaultMetadata.Value); + } + + void IJsonModelSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) => Serialize(writer, options); + + private void Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + writer.WriteStartObject(); + if (Optional.IsDefined(Name)) + { + writer.WritePropertyName("name"u8); + writer.WriteStringValue(Name); + } + if (Optional.IsCollectionDefined(Paths)) + { + writer.WritePropertyName("paths"u8); + writer.WriteStartArray(); + foreach (var item in Paths) + { + writer.WriteObjectValue(item); + } + writer.WriteEndArray(); + } + if (Optional.IsDefined(AliasType)) + { + writer.WritePropertyName("type"u8); + writer.WriteStringValue(AliasType.ToString()); + } + if (Optional.IsDefined(DefaultPath)) + { + writer.WritePropertyName("defaultPath"u8); + writer.WriteStringValue(DefaultPath); + } + if (Optional.IsDefined(DefaultPattern)) + { + writer.WritePropertyName("defaultPattern"u8); + writer.WriteObjectValue(DefaultPattern); + } + if (Optional.IsDefined(DefaultMetadata)) + { + writer.WritePropertyName("defaultMetadata"u8); + writer.WriteObjectValue(DefaultMetadata); + } + writer.WriteEndObject(); + } + + private struct ResourceTypeAliasProperties + { + public Optional Name { get; set; } + public Optional> Paths { get; set; } + public Optional AliasType { get; set; } + public Optional DefaultPath { get; set; } + public Optional DefaultPattern { get; set; } + public Optional DefaultMetadata { get; set; } + } + + object IJsonModelSerializable.Deserialize(ref Utf8JsonReader reader, ModelSerializerOptions options) + { + if (!reader.TryDeserialize(options, SetProperty, out var properties)) + return null; + + return new ResourceTypeAlias( + properties.Name.Value, + Optional.ToList(properties.Paths), + Optional.ToNullable(properties.AliasType), + properties.DefaultPath.Value, + properties.DefaultPattern.Value, + properties.DefaultMetadata.Value); + } + + private static void SetProperty(ReadOnlySpan propertyName, ref ResourceTypeAliasProperties properties, ref Utf8JsonReader reader, ModelSerializerOptions options) + { + if (propertyName.SequenceEqual("name"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.Name = reader.GetString(); + return; + } + if (propertyName.SequenceEqual("paths"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.Paths = reader.GetList(options); + return; + } + if (propertyName.SequenceEqual("type"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.AliasType = reader.GetString().ToResourceTypeAliasType(); + return; + } + if (propertyName.SequenceEqual("defaultPath"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.DefaultPath = reader.GetString(); + return; + } + if (propertyName.SequenceEqual("defaultPattern"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.DefaultPattern = reader.GetObject(options); + return; + } + if (propertyName.SequenceEqual("defaultMetadata"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.DefaultMetadata = reader.GetObject(options); + return; + } + reader.Skip(); + } + + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + { + using var doc = JsonDocument.Parse(data); + return DeserializeResourceTypeAlias(doc.RootElement, options); + } + + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) + { + return ModelSerializerHelper.SerializeToBinaryData((writer) => { Serialize(writer, options); }); + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAlias.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAlias.cs new file mode 100644 index 000000000000..6002b848e3c2 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAlias.cs @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System.Collections.Generic; +using Azure.Core; + +namespace Azure.Core.Tests.Public.ResourceManager.Resources.Models +{ + /// The alias type. + public partial class ResourceTypeAlias + { + /// Initializes a new instance of ResourceTypeAlias. + internal ResourceTypeAlias() + { + Paths = new ChangeTrackingList(); + } + + /// Initializes a new instance of ResourceTypeAlias. + /// The alias name. + /// The paths for an alias. + /// The type of the alias. + /// The default path for an alias. + /// The default pattern for an alias. + /// The default alias path metadata. Applies to the default path and to any alias path that doesn't have metadata. + internal ResourceTypeAlias(string name, IReadOnlyList paths, ResourceTypeAliasType? aliasType, string defaultPath, ResourceTypeAliasPattern defaultPattern, ResourceTypeAliasPathMetadata defaultMetadata) + { + Name = name; + Paths = paths; + AliasType = aliasType; + DefaultPath = defaultPath; + DefaultPattern = defaultPattern; + DefaultMetadata = defaultMetadata; + } + + /// The alias name. + public string Name { get; } + /// The paths for an alias. + public IReadOnlyList Paths { get; } + /// The type of the alias. + public ResourceTypeAliasType? AliasType { get; } + /// The default path for an alias. + public string DefaultPath { get; } + /// The default pattern for an alias. + public ResourceTypeAliasPattern DefaultPattern { get; } + /// The default alias path metadata. Applies to the default path and to any alias path that doesn't have metadata. + public ResourceTypeAliasPathMetadata DefaultMetadata { get; } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAliasPath.Serialization.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAliasPath.Serialization.cs new file mode 100644 index 000000000000..c3cdf6540675 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAliasPath.Serialization.cs @@ -0,0 +1,169 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.Json; +using Azure.Core; +using Azure.Core.Serialization; + +namespace Azure.Core.Tests.Public.ResourceManager.Resources.Models +{ + public partial class ResourceTypeAliasPath : IUtf8JsonSerializable, IJsonModelSerializable + { + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => ((IJsonModelSerializable)this).Serialize(writer, ModelSerializerOptions.AzureServiceDefault); + + internal static ResourceTypeAliasPath DeserializeResourceTypeAliasPath(JsonElement element, ModelSerializerOptions? options = default) + { + options ??= ModelSerializerOptions.AzureServiceDefault; + + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + Optional path = default; + Optional> apiVersions = default; + Optional pattern = default; + Optional metadata = default; + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("path"u8)) + { + path = property.Value.GetString(); + continue; + } + if (property.NameEquals("apiVersions"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + List array = new List(); + foreach (var item in property.Value.EnumerateArray()) + { + array.Add(item.GetString()); + } + apiVersions = array; + continue; + } + if (property.NameEquals("pattern"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + pattern = ResourceTypeAliasPattern.DeserializeResourceTypeAliasPattern(property.Value); + continue; + } + if (property.NameEquals("metadata"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + metadata = ResourceTypeAliasPathMetadata.DeserializeResourceTypeAliasPathMetadata(property.Value); + continue; + } + } + return new ResourceTypeAliasPath(path.Value, Optional.ToList(apiVersions), pattern.Value, metadata.Value); + } + + void IJsonModelSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) => Serialize(writer, options); + + private void Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + writer.WriteStartObject(); + if (Optional.IsDefined(Path)) + { + writer.WritePropertyName("path"u8); + writer.WriteStringValue(Path); + } + if (Optional.IsCollectionDefined(ApiVersions)) + { + writer.WritePropertyName("apiVersions"u8); + writer.WriteStartArray(); + foreach (var item in ApiVersions) + { + writer.WriteStringValue(item); + } + writer.WriteEndArray(); + } + if (Optional.IsDefined(Pattern)) + { + writer.WritePropertyName("pattern"u8); + writer.WriteObjectValue(Pattern); + } + if (Optional.IsDefined(Metadata)) + { + writer.WritePropertyName("metadata"u8); + writer.WriteObjectValue(Metadata); + } + writer.WriteEndObject(); + } + + private struct ResourceTypeAliasPathProperties + { + public Optional Path { get; set; } + public Optional> ApiVersions { get; set; } + public Optional Pattern { get; set; } + public Optional Metadata { get; set; } + } + + object IJsonModelSerializable.Deserialize(ref Utf8JsonReader reader, ModelSerializerOptions options) + { + if (!reader.TryDeserialize(options, SetProperty, out var properties)) + return null; + + return new ResourceTypeAliasPath(properties.Path.Value, Optional.ToList(properties.ApiVersions), properties.Pattern.Value, properties.Metadata.Value); + } + + private static void SetProperty(ReadOnlySpan propertyName, ref ResourceTypeAliasPathProperties properties, ref Utf8JsonReader reader, ModelSerializerOptions options) + { + if (propertyName.SequenceEqual("path"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.Path = reader.GetString(); + return; + } + if (propertyName.SequenceEqual("apiVersions"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.ApiVersions = reader.GetList(options); + return; + } + if (propertyName.SequenceEqual("pattern"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.Pattern = reader.GetObject(options); + return; + } + if (propertyName.SequenceEqual("metadata"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.Metadata = reader.GetObject(options); + return; + } + reader.Skip(); + } + + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + { + using var doc = JsonDocument.Parse(data); + return DeserializeResourceTypeAliasPath(doc.RootElement, options); + } + + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) + { + return ModelSerializerHelper.SerializeToBinaryData((writer) => { Serialize(writer, options); }); + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAliasPath.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAliasPath.cs new file mode 100644 index 000000000000..598ec7979720 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAliasPath.cs @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System.Collections.Generic; +using Azure.Core; + +namespace Azure.Core.Tests.Public.ResourceManager.Resources.Models +{ + /// The type of the paths for alias. + public partial class ResourceTypeAliasPath + { + /// Initializes a new instance of ResourceTypeAliasPath. + internal ResourceTypeAliasPath() + { + ApiVersions = new ChangeTrackingList(); + } + + /// Initializes a new instance of ResourceTypeAliasPath. + /// The path of an alias. + /// The API versions. + /// The pattern for an alias path. + /// The metadata of the alias path. If missing, fall back to the default metadata of the alias. + internal ResourceTypeAliasPath(string path, IReadOnlyList apiVersions, ResourceTypeAliasPattern pattern, ResourceTypeAliasPathMetadata metadata) + { + Path = path; + ApiVersions = apiVersions; + Pattern = pattern; + Metadata = metadata; + } + + /// The path of an alias. + public string Path { get; } + /// The API versions. + public IReadOnlyList ApiVersions { get; } + /// The pattern for an alias path. + public ResourceTypeAliasPattern Pattern { get; } + /// The metadata of the alias path. If missing, fall back to the default metadata of the alias. + public ResourceTypeAliasPathMetadata Metadata { get; } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAliasPathAttributes.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAliasPathAttributes.cs new file mode 100644 index 000000000000..cf6b04b3aca8 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAliasPathAttributes.cs @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.ComponentModel; + +namespace Azure.Core.Tests.Public.ResourceManager.Resources.Models +{ + /// The attributes of the token that the alias path is referring to. + public readonly partial struct ResourceTypeAliasPathAttributes : IEquatable + { + private readonly string _value; + + /// Initializes a new instance of . + /// is null. + public ResourceTypeAliasPathAttributes(string value) + { + _value = value ?? throw new ArgumentNullException(nameof(value)); + } + + private const string NoneValue = "None"; + private const string ModifiableValue = "Modifiable"; + + /// The token that the alias path is referring to has no attributes. + public static ResourceTypeAliasPathAttributes None { get; } = new ResourceTypeAliasPathAttributes(NoneValue); + /// The token that the alias path is referring to is modifiable by policies with 'modify' effect. + public static ResourceTypeAliasPathAttributes Modifiable { get; } = new ResourceTypeAliasPathAttributes(ModifiableValue); + /// Determines if two values are the same. + public static bool operator ==(ResourceTypeAliasPathAttributes left, ResourceTypeAliasPathAttributes right) => left.Equals(right); + /// Determines if two values are not the same. + public static bool operator !=(ResourceTypeAliasPathAttributes left, ResourceTypeAliasPathAttributes right) => !left.Equals(right); + /// Converts a string to a . + public static implicit operator ResourceTypeAliasPathAttributes(string value) => new ResourceTypeAliasPathAttributes(value); + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) => obj is ResourceTypeAliasPathAttributes other && Equals(other); + /// + public bool Equals(ResourceTypeAliasPathAttributes other) => string.Equals(_value, other._value, StringComparison.InvariantCultureIgnoreCase); + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() => _value?.GetHashCode() ?? 0; + /// + public override string ToString() => _value; + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAliasPathMetadata.Serialization.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAliasPathMetadata.Serialization.cs new file mode 100644 index 000000000000..3460f6feffe6 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAliasPathMetadata.Serialization.cs @@ -0,0 +1,117 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.IO; +using System.Text.Json; +using System.Xml.Linq; +using Azure.Core; +using Azure.Core.Serialization; + +namespace Azure.Core.Tests.Public.ResourceManager.Resources.Models +{ + public partial class ResourceTypeAliasPathMetadata : IUtf8JsonSerializable, IJsonModelSerializable + { + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => ((IJsonModelSerializable)this).Serialize(writer, ModelSerializerOptions.AzureServiceDefault); + + internal static ResourceTypeAliasPathMetadata DeserializeResourceTypeAliasPathMetadata(JsonElement element, ModelSerializerOptions? options = default) + { + options ??= ModelSerializerOptions.AzureServiceDefault; + + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + Optional type = default; + Optional attributes = default; + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("type"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + type = new ResourceTypeAliasPathTokenType(property.Value.GetString()); + continue; + } + if (property.NameEquals("attributes"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + attributes = new ResourceTypeAliasPathAttributes(property.Value.GetString()); + continue; + } + } + return new ResourceTypeAliasPathMetadata(Optional.ToNullable(type), Optional.ToNullable(attributes)); + } + + void IJsonModelSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) => Serialize(writer, options); + + private void Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + writer.WriteStartObject(); + if (Optional.IsDefined(TokenType)) + { + writer.WritePropertyName("type"u8); + writer.WriteStringValue(TokenType.ToString()); + } + if (Optional.IsDefined(Attributes)) + { + writer.WritePropertyName("attributes"u8); + writer.WriteStringValue(Attributes.ToString()); + } + writer.WriteEndObject(); + } + + private struct ResourceTypeAliasPathMetadataProperties + { + public Optional TokenType { get; set; } + public Optional Attributes { get; set; } + } + + object IJsonModelSerializable.Deserialize(ref Utf8JsonReader reader, ModelSerializerOptions options) + { + if (!reader.TryDeserialize(options, SetProperty, out var properties)) + return null; + + return new ResourceTypeAliasPathMetadata(Optional.ToNullable(properties.TokenType), Optional.ToNullable(properties.Attributes)); + } + + private static void SetProperty(ReadOnlySpan propertyName, ref ResourceTypeAliasPathMetadataProperties properties, ref Utf8JsonReader reader, ModelSerializerOptions options) + { + if (propertyName.SequenceEqual("type"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.TokenType = new ResourceTypeAliasPathTokenType(reader.GetString()); + return; + } + if (propertyName.SequenceEqual("attributes"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.Attributes = new ResourceTypeAliasPathAttributes(reader.GetString()); + return; + } + reader.Skip(); + } + + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + { + using var doc = JsonDocument.Parse(data); + return DeserializeResourceTypeAliasPathMetadata(doc.RootElement, options); + } + + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) + { + return ModelSerializerHelper.SerializeToBinaryData((writer) => { Serialize(writer, options); }); + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAliasPathMetadata.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAliasPathMetadata.cs new file mode 100644 index 000000000000..579c7ed3095b --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAliasPathMetadata.cs @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +namespace Azure.Core.Tests.Public.ResourceManager.Resources.Models +{ + /// The ResourceTypeAliasPathMetadata. + public partial class ResourceTypeAliasPathMetadata + { + /// Initializes a new instance of ResourceTypeAliasPathMetadata. + internal ResourceTypeAliasPathMetadata() + { + } + + /// Initializes a new instance of ResourceTypeAliasPathMetadata. + /// The type of the token that the alias path is referring to. + /// The attributes of the token that the alias path is referring to. + internal ResourceTypeAliasPathMetadata(ResourceTypeAliasPathTokenType? tokenType, ResourceTypeAliasPathAttributes? attributes) + { + TokenType = tokenType; + Attributes = attributes; + } + + /// The type of the token that the alias path is referring to. + public ResourceTypeAliasPathTokenType? TokenType { get; } + /// The attributes of the token that the alias path is referring to. + public ResourceTypeAliasPathAttributes? Attributes { get; } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAliasPathTokenType.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAliasPathTokenType.cs new file mode 100644 index 000000000000..b565085b808d --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAliasPathTokenType.cs @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.ComponentModel; + +namespace Azure.Core.Tests.Public.ResourceManager.Resources.Models +{ + /// The type of the token that the alias path is referring to. + public readonly partial struct ResourceTypeAliasPathTokenType : IEquatable + { + private readonly string _value; + + /// Initializes a new instance of . + /// is null. + public ResourceTypeAliasPathTokenType(string value) + { + _value = value ?? throw new ArgumentNullException(nameof(value)); + } + + private const string NotSpecifiedValue = "NotSpecified"; + private const string AnyValue = "Any"; + private const string StringValue = "String"; + private const string ObjectValue = "Object"; + private const string ArrayValue = "Array"; + private const string IntegerValue = "Integer"; + private const string NumberValue = "Number"; + private const string BooleanValue = "Boolean"; + + /// The token type is not specified. + public static ResourceTypeAliasPathTokenType NotSpecified { get; } = new ResourceTypeAliasPathTokenType(NotSpecifiedValue); + /// The token type can be anything. + public static ResourceTypeAliasPathTokenType Any { get; } = new ResourceTypeAliasPathTokenType(AnyValue); + /// The token type is string. + public static ResourceTypeAliasPathTokenType String { get; } = new ResourceTypeAliasPathTokenType(StringValue); + /// The token type is object. + public static ResourceTypeAliasPathTokenType Object { get; } = new ResourceTypeAliasPathTokenType(ObjectValue); + /// The token type is array. + public static ResourceTypeAliasPathTokenType Array { get; } = new ResourceTypeAliasPathTokenType(ArrayValue); + /// The token type is integer. + public static ResourceTypeAliasPathTokenType Integer { get; } = new ResourceTypeAliasPathTokenType(IntegerValue); + /// The token type is number. + public static ResourceTypeAliasPathTokenType Number { get; } = new ResourceTypeAliasPathTokenType(NumberValue); + /// The token type is boolean. + public static ResourceTypeAliasPathTokenType Boolean { get; } = new ResourceTypeAliasPathTokenType(BooleanValue); + /// Determines if two values are the same. + public static bool operator ==(ResourceTypeAliasPathTokenType left, ResourceTypeAliasPathTokenType right) => left.Equals(right); + /// Determines if two values are not the same. + public static bool operator !=(ResourceTypeAliasPathTokenType left, ResourceTypeAliasPathTokenType right) => !left.Equals(right); + /// Converts a string to a . + public static implicit operator ResourceTypeAliasPathTokenType(string value) => new ResourceTypeAliasPathTokenType(value); + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) => obj is ResourceTypeAliasPathTokenType other && Equals(other); + /// + public bool Equals(ResourceTypeAliasPathTokenType other) => string.Equals(_value, other._value, StringComparison.InvariantCultureIgnoreCase); + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() => _value?.GetHashCode() ?? 0; + /// + public override string ToString() => _value; + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAliasPattern.Serialization.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAliasPattern.Serialization.cs new file mode 100644 index 000000000000..d00d6150622f --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAliasPattern.Serialization.cs @@ -0,0 +1,130 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Text.Json; +using Azure.Core; +using Azure.Core.Serialization; + +namespace Azure.Core.Tests.Public.ResourceManager.Resources.Models +{ + public partial class ResourceTypeAliasPattern : IUtf8JsonSerializable, IJsonModelSerializable + { + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => ((IJsonModelSerializable)this).Serialize(writer, ModelSerializerOptions.AzureServiceDefault); + + internal static ResourceTypeAliasPattern DeserializeResourceTypeAliasPattern(JsonElement element, ModelSerializerOptions? options = default) + { + options ??= ModelSerializerOptions.AzureServiceDefault; + + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + Optional phrase = default; + Optional variable = default; + Optional type = default; + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("phrase"u8)) + { + phrase = property.Value.GetString(); + continue; + } + if (property.NameEquals("variable"u8)) + { + variable = property.Value.GetString(); + continue; + } + if (property.NameEquals("type"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + type = property.Value.GetString().ToResourceTypeAliasPatternType(); + continue; + } + } + return new ResourceTypeAliasPattern(phrase.Value, variable.Value, Optional.ToNullable(type)); + } + + void IJsonModelSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) => Serialize(writer, options); + + private void Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + writer.WriteStartObject(); + if (Optional.IsDefined(Phrase)) + { + writer.WritePropertyName("phrase"u8); + writer.WriteStringValue(Phrase); + } + if (Optional.IsDefined(Variable)) + { + writer.WritePropertyName("variable"u8); + writer.WriteStringValue(Variable); + } + if (Optional.IsDefined(PatternType)) + { + writer.WritePropertyName("type"u8); + writer.WriteStringValue(PatternType.Value.ToSerialString()); + } + writer.WriteEndObject(); + } + + private struct ResourceTypeAliasPatternProperites + { + public Optional Phrase { get; set; } + public Optional Variable { get; set; } + public Optional PatternType { get; set; } + } + + object IJsonModelSerializable.Deserialize(ref Utf8JsonReader reader, ModelSerializerOptions options) + { + if (!reader.TryDeserialize(options, SetProperty, out var properties)) + return null; + + return new ResourceTypeAliasPattern(properties.Phrase.Value, properties.Variable.Value, Optional.ToNullable(properties.PatternType)); + } + + private static void SetProperty(ReadOnlySpan propertyName, ref ResourceTypeAliasPatternProperites properties, ref Utf8JsonReader reader, ModelSerializerOptions options) + { + if (propertyName.SequenceEqual("phrase"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.Phrase = reader.GetString(); + return; + } + if (propertyName.SequenceEqual("variable"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.Variable = reader.GetString(); + return; + } + if (propertyName.SequenceEqual("type"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.PatternType = reader.GetString().ToResourceTypeAliasPatternType(); + return; + } + reader.Skip(); + } + + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + { + using var doc = JsonDocument.Parse(data); + return DeserializeResourceTypeAliasPattern(doc.RootElement, options); + } + + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) + { + return ModelSerializerHelper.SerializeToBinaryData((writer) => { Serialize(writer, options); }); + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAliasPattern.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAliasPattern.cs new file mode 100644 index 000000000000..1e4c23c9e606 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAliasPattern.cs @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +namespace Azure.Core.Tests.Public.ResourceManager.Resources.Models +{ + /// The type of the pattern for an alias path. + public partial class ResourceTypeAliasPattern + { + /// Initializes a new instance of ResourceTypeAliasPattern. + internal ResourceTypeAliasPattern() + { + } + + /// Initializes a new instance of ResourceTypeAliasPattern. + /// The alias pattern phrase. + /// The alias pattern variable. + /// The type of alias pattern. + internal ResourceTypeAliasPattern(string phrase, string variable, ResourceTypeAliasPatternType? patternType) + { + Phrase = phrase; + Variable = variable; + PatternType = patternType; + } + + /// The alias pattern phrase. + public string Phrase { get; } + /// The alias pattern variable. + public string Variable { get; } + /// The type of alias pattern. + public ResourceTypeAliasPatternType? PatternType { get; } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAliasPatternType.Serialization.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAliasPatternType.Serialization.cs new file mode 100644 index 000000000000..2a37a2428dac --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAliasPatternType.Serialization.cs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; + +namespace Azure.Core.Tests.Public.ResourceManager.Resources.Models +{ + internal static partial class ResourceTypeAliasPatternTypeExtensions + { + public static string ToSerialString(this ResourceTypeAliasPatternType value) => value switch + { + ResourceTypeAliasPatternType.NotSpecified => "NotSpecified", + ResourceTypeAliasPatternType.Extract => "Extract", + _ => throw new ArgumentOutOfRangeException(nameof(value), value, "Unknown ResourceTypeAliasPatternType value.") + }; + + public static ResourceTypeAliasPatternType ToResourceTypeAliasPatternType(this string value) + { + if (StringComparer.OrdinalIgnoreCase.Equals(value, "NotSpecified")) return ResourceTypeAliasPatternType.NotSpecified; + if (StringComparer.OrdinalIgnoreCase.Equals(value, "Extract")) return ResourceTypeAliasPatternType.Extract; + throw new ArgumentOutOfRangeException(nameof(value), value, "Unknown ResourceTypeAliasPatternType value."); + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAliasPatternType.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAliasPatternType.cs new file mode 100644 index 000000000000..5e63dd139268 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAliasPatternType.cs @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +namespace Azure.Core.Tests.Public.ResourceManager.Resources.Models +{ + /// The type of alias pattern. + public enum ResourceTypeAliasPatternType + { + /// NotSpecified is not allowed. + NotSpecified, + /// Extract is the only allowed value. + Extract + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAliasType.Serialization.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAliasType.Serialization.cs new file mode 100644 index 000000000000..f147fdd11862 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAliasType.Serialization.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; + +namespace Azure.Core.Tests.Public.ResourceManager.Resources.Models +{ + internal static partial class ResourceTypeAliasTypeExtensions + { + public static string ToSerialString(this ResourceTypeAliasType value) => value switch + { + ResourceTypeAliasType.NotSpecified => "NotSpecified", + ResourceTypeAliasType.PlainText => "PlainText", + ResourceTypeAliasType.Mask => "Mask", + _ => throw new ArgumentOutOfRangeException(nameof(value), value, "Unknown ResourceTypeAliasType value.") + }; + + public static ResourceTypeAliasType ToResourceTypeAliasType(this string value) + { + if (StringComparer.OrdinalIgnoreCase.Equals(value, "NotSpecified")) return ResourceTypeAliasType.NotSpecified; + if (StringComparer.OrdinalIgnoreCase.Equals(value, "PlainText")) return ResourceTypeAliasType.PlainText; + if (StringComparer.OrdinalIgnoreCase.Equals(value, "Mask")) return ResourceTypeAliasType.Mask; + throw new ArgumentOutOfRangeException(nameof(value), value, "Unknown ResourceTypeAliasType value."); + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAliasType.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAliasType.cs new file mode 100644 index 000000000000..fb101269ad68 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ResourceTypeAliasType.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +namespace Azure.Core.Tests.Public.ResourceManager.Resources.Models +{ + /// The type of the alias. + public enum ResourceTypeAliasType + { + /// Alias type is unknown (same as not providing alias type). + NotSpecified, + /// Alias value is not secret. + PlainText, + /// Alias value is secret. + Mask + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/SystemData.Serialization.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/SystemData.Serialization.cs new file mode 100644 index 000000000000..b4acabf59de4 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/SystemData.Serialization.cs @@ -0,0 +1,192 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Globalization; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; +using Azure.Core; +using Azure.Core.Serialization; + +namespace Azure.Core.Tests.Public.ResourceManager.Models +{ + [JsonConverter(typeof(SystemDataConverter))] + public partial class SystemData : IUtf8JsonSerializable, IJsonModelSerializable + { + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => ((IJsonModelSerializable)this).Serialize(writer, ModelSerializerOptions.AzureServiceDefault); + + void IJsonModelSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) => Serialize(writer, options); + + private void Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + writer.WriteStartObject(); + writer.WriteEndObject(); + } + + internal static SystemData DeserializeSystemData(JsonElement element, ModelSerializerOptions? options = default) + { + options ??= ModelSerializerOptions.AzureServiceDefault; + + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + Optional createdBy = default; + Optional createdByType = default; + Optional createdAt = default; + Optional lastModifiedBy = default; + Optional lastModifiedByType = default; + Optional lastModifiedAt = default; + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("createdBy"u8)) + { + createdBy = property.Value.GetString(); + continue; + } + if (property.NameEquals("createdByType"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + createdByType = new CreatedByType(property.Value.GetString()); + continue; + } + if (property.NameEquals("createdAt"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + createdAt = property.Value.GetDateTimeOffset("O"); + continue; + } + if (property.NameEquals("lastModifiedBy"u8)) + { + lastModifiedBy = property.Value.GetString(); + continue; + } + if (property.NameEquals("lastModifiedByType"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + lastModifiedByType = new CreatedByType(property.Value.GetString()); + continue; + } + if (property.NameEquals("lastModifiedAt"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + lastModifiedAt = property.Value.GetDateTimeOffset("O"); + continue; + } + } + return new SystemData(createdBy.Value, Optional.ToNullable(createdByType), Optional.ToNullable(createdAt), lastModifiedBy.Value, Optional.ToNullable(lastModifiedByType), Optional.ToNullable(lastModifiedAt)); + } + + private struct SystemDataProperties + { + public Optional CreatedBy { get; set; } + public Optional CreatedByType { get; set; } + public Optional CreatedOn { get; set; } + public Optional LastModifiedBy { get; set; } + public Optional LastModifiedByType { get; set; } + public Optional LastModifiedOn { get; set; } + } + + object IJsonModelSerializable.Deserialize(ref Utf8JsonReader reader, ModelSerializerOptions options) + { + if (!reader.TryDeserialize(options, SetProperty, out var properties)) + return null; + + return new SystemData( + properties.CreatedBy.Value, + Optional.ToNullable(properties.CreatedByType), + Optional.ToNullable(properties.CreatedOn), + properties.LastModifiedBy.Value, + Optional.ToNullable(properties.LastModifiedByType), + Optional.ToNullable(properties.LastModifiedOn)); + } + + private static void SetProperty(ReadOnlySpan propertyName, ref SystemDataProperties properties, ref Utf8JsonReader reader, ModelSerializerOptions options) + { + if (propertyName.SequenceEqual("createdBy"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.CreatedBy = reader.GetString(); + return; + } + if (propertyName.SequenceEqual("createdByType"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.CreatedByType = new CreatedByType(reader.GetString()); + return; + } + if (propertyName.SequenceEqual("createdAt"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.CreatedOn = DateTimeOffset.Parse(reader.GetString(), CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal); + return; + } + if (propertyName.SequenceEqual("lastModifiedBy"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.LastModifiedBy = reader.GetString(); + return; + } + if (propertyName.SequenceEqual("lastModifiedByType"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.LastModifiedByType = new CreatedByType(reader.GetString()); + return; + } + if (propertyName.SequenceEqual("lastModifiedAt"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.LastModifiedOn = DateTimeOffset.Parse(reader.GetString(), CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal); + return; + } + reader.Skip(); + } + + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + { + using var doc = JsonDocument.Parse(data); + return DeserializeSystemData(doc.RootElement, options); + } + + internal partial class SystemDataConverter : JsonConverter + { + public override void Write(Utf8JsonWriter writer, SystemData model, JsonSerializerOptions options) + { + writer.WriteObjectValue(model); + } + public override SystemData Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var document = JsonDocument.ParseValue(ref reader); + return DeserializeSystemData(document.RootElement); + } + } + + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) + { + return ModelSerializerHelper.SerializeToBinaryData((writer) => { Serialize(writer, options); }); + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/SystemData.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/SystemData.cs new file mode 100644 index 000000000000..f54c02c39f5a --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/SystemData.cs @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using Azure.Core; + +namespace Azure.Core.Tests.Public.ResourceManager.Models +{ + /// Metadata pertaining to creation and last modification of the resource. + public partial class SystemData + { + /// Initializes a new instance of SystemData. + public SystemData() + { + } + + /// Initializes a new instance of SystemData. + /// The identity that created the resource. + /// The type of identity that created the resource. + /// The timestamp of resource creation (UTC). + /// The identity that last modified the resource. + /// The type of identity that last modified the resource. + /// The timestamp of resource last modification (UTC). + internal SystemData(string createdBy, CreatedByType? createdByType, DateTimeOffset? createdOn, string lastModifiedBy, CreatedByType? lastModifiedByType, DateTimeOffset? lastModifiedOn) + { + CreatedBy = createdBy; + CreatedByType = createdByType; + CreatedOn = createdOn; + LastModifiedBy = lastModifiedBy; + LastModifiedByType = lastModifiedByType; + LastModifiedOn = lastModifiedOn; + } + + /// The identity that created the resource. + public string CreatedBy { get; } + /// The type of identity that created the resource. + public CreatedByType? CreatedByType { get; } + /// The timestamp of resource creation (UTC). + public DateTimeOffset? CreatedOn { get; } + /// The identity that last modified the resource. + public string LastModifiedBy { get; } + /// The type of identity that last modified the resource. + public CreatedByType? LastModifiedByType { get; } + /// The timestamp of resource last modification (UTC). + public DateTimeOffset? LastModifiedOn { get; } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/TrackedResourceData.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/TrackedResourceData.cs new file mode 100644 index 000000000000..185956511930 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/TrackedResourceData.cs @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#nullable disable + +using System.Collections.Generic; +using Azure.Core; + +namespace Azure.Core.Tests.Public.ResourceManager.Models +{ + /// The resource model definition for an Azure Resource Manager tracked top level resource which has 'tags' and a 'location'. + public abstract partial class TrackedResourceData : ResourceData + { + internal TrackedResourceData() { } + + /// Initializes a new instance of TrackedResource. + /// The geo-location where the resource lives. + protected TrackedResourceData(AzureLocation location) + { + Tags = new ChangeTrackingDictionary(); + Location = location; + } + + /// Initializes a new instance of TrackedResource. + /// Fully qualified resource ID for the resource. Ex - /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/{resourceProviderNamespace}/{resourceType}/{resourceName}. + /// The name of the resource. + /// The type of the resource. E.g. "Microsoft.Compute/virtualMachines" or "Microsoft.Storage/storageAccounts". + /// Azure Resource Manager metadata containing createdBy and modifiedBy information. + /// Resource tags. + /// The geo-location where the resource lives. + protected TrackedResourceData(ResourceIdentifier id, string name, ResourceType resourceType, SystemData systemData, IDictionary tags, AzureLocation location) : base(id, name, resourceType, systemData) + { + Tags = tags; + Location = location; + } + + /// Resource tags. + public IDictionary Tags { get; } + /// The geo-location where the resource lives. + public AzureLocation Location { get; set; } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/WritableSubResource.Serialization.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/WritableSubResource.Serialization.cs new file mode 100644 index 000000000000..27404159cd89 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/WritableSubResource.Serialization.cs @@ -0,0 +1,117 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Globalization; +using System.Text.Json; +using System.Text.Json.Serialization; +using Azure.Core; +using Azure.Core.Serialization; + +namespace Azure.Core.Tests.Public.ResourceManager.Resources.Models +{ + /// + /// A class representing a sub-resource that contains only the ID. + /// + [JsonConverter(typeof(WritableSubResourceConverter))] + public partial class WritableSubResource : IUtf8JsonSerializable, IJsonModelSerializable + { + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => ((IJsonModelSerializable)this).Serialize(writer, ModelSerializerOptions.AzureServiceDefault); + + void IJsonModelSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) => Serialize(writer, options); + + /// + /// Serialize the input WritableSubResource object. + /// + /// Input Json writer. + private void Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + if (Optional.IsDefined(Id)) + { + writer.WritePropertyName("id"); + writer.WriteStringValue(Id); + } + writer.WriteEndObject(); + } + + /// + /// Deserialize the input JSON element to a WritableSubResource object. + /// + /// The JSON element to be deserialized. + /// Deserialized WritableSubResource object. + internal static WritableSubResource DeserializeWritableSubResource(JsonElement element, ModelSerializerOptions? options = default) + { + options ??= ModelSerializerOptions.AzureServiceDefault; + + ResourceIdentifier id = default; + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("id")) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + id = new ResourceIdentifier(property.Value.GetString()); + continue; + } + } + return new WritableSubResource(id); + } + + private struct WritableSubResourceProperties + { + public ResourceIdentifier Id { get; set; } + } + + object IJsonModelSerializable.Deserialize(ref Utf8JsonReader reader, ModelSerializerOptions options) + { + if (!reader.TryDeserialize(options, SetProperty, out var properties)) + return null; + + return new WritableSubResource(properties.Id); + } + + private static void SetProperty(ReadOnlySpan propertyName, ref WritableSubResourceProperties properties, ref Utf8JsonReader reader, ModelSerializerOptions options) + { + if (propertyName.SequenceEqual("id"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.Id = new ResourceIdentifier(reader.GetString()); + return; + } + reader.Skip(); + } + + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + { + using var doc = JsonDocument.Parse(data); + return DeserializeWritableSubResource(doc.RootElement, options); + } + + internal partial class WritableSubResourceConverter : JsonConverter + { + public override void Write(Utf8JsonWriter writer, WritableSubResource model, JsonSerializerOptions options) + { + writer.WriteObjectValue(model); + } + public override WritableSubResource Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var document = JsonDocument.ParseValue(ref reader); + return DeserializeWritableSubResource(document.RootElement); + } + } + + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) + { + return ModelSerializerHelper.SerializeToBinaryData((writer) => { Serialize(writer, options); }); + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/WritableSubResource.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/WritableSubResource.cs new file mode 100644 index 000000000000..7ef35f24b26b --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/WritableSubResource.cs @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Azure.Core; + +namespace Azure.Core.Tests.Public.ResourceManager.Resources.Models +{ + /// + /// A class representing a sub-resource that contains only the ID. + /// + public partial class WritableSubResource + { + /// + /// Initializes an empty instance of for mocking. + /// + public WritableSubResource() + { + } + + /// Initializes a new instance of . + /// ARM resource Id. + protected internal WritableSubResource(ResourceIdentifier id) + { + Id = id; + } + + /// + /// Gets or sets the ARM resource identifier. + /// + /// + public ResourceIdentifier Id { get; set; } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ZoneMapping.Serialization.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ZoneMapping.Serialization.cs new file mode 100644 index 000000000000..fa7fb4e22cc5 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ZoneMapping.Serialization.cs @@ -0,0 +1,127 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.Json; +using Azure.Core; +using Azure.Core.Serialization; + +namespace Azure.Core.Tests.Public.ResourceManager.Resources.Models +{ + public partial class ZoneMapping : IUtf8JsonSerializable, IJsonModelSerializable + { + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) => ((IJsonModelSerializable)this).Serialize(writer, ModelSerializerOptions.AzureServiceDefault); + + internal static ZoneMapping DeserializeZoneMapping(JsonElement element, ModelSerializerOptions? options = default) + { + options ??= ModelSerializerOptions.AzureServiceDefault; + + if (element.ValueKind == JsonValueKind.Null) + { + return null; + } + Optional location = default; + Optional> zones = default; + foreach (var property in element.EnumerateObject()) + { + if (property.NameEquals("location"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + location = new AzureLocation(property.Value.GetString()); + continue; + } + if (property.NameEquals("zones"u8)) + { + if (property.Value.ValueKind == JsonValueKind.Null) + { + continue; + } + List array = new List(); + foreach (var item in property.Value.EnumerateArray()) + { + array.Add(item.GetString()); + } + zones = array; + continue; + } + } + return new ZoneMapping(Optional.ToNullable(location), Optional.ToList(zones)); + } + + void IJsonModelSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) => Serialize(writer, options); + + private void Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + writer.WriteStartObject(); + if (Optional.IsDefined(Location)) + { + writer.WritePropertyName("location"u8); + writer.WriteStringValue(Location.Value.DisplayName); + } + if (Optional.IsCollectionDefined(Zones)) + { + writer.WritePropertyName("zones"u8); + writer.WriteStartArray(); + foreach (var item in Zones) + { + writer.WriteStringValue(item); + } + writer.WriteEndArray(); + } + writer.WriteEndObject(); + } + + private struct ZoneMappingProperties + { + public Optional Location { get; set; } + public Optional> Zones { get; set; } + } + + object IJsonModelSerializable.Deserialize(ref Utf8JsonReader reader, ModelSerializerOptions options) + { + if (!reader.TryDeserialize(options, SetProperty, out var properties)) + return null; + + return new ZoneMapping(Optional.ToNullable(properties.Location), Optional.ToList(properties.Zones)); + } + + private static void SetProperty(ReadOnlySpan propertyName, ref ZoneMappingProperties properties, ref Utf8JsonReader reader, ModelSerializerOptions options) + { + if (propertyName.SequenceEqual("location"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.Location = new AzureLocation(reader.GetString()); + return; + } + if (propertyName.SequenceEqual("zones"u8)) + { + reader.Read(); + if (reader.TokenType != JsonTokenType.Null) + properties.Zones = reader.GetList(options); + return; + } + reader.Skip(); + } + + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + { + using var doc = JsonDocument.Parse(data); + return DeserializeZoneMapping(doc.RootElement, options); + } + + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) + { + return ModelSerializerHelper.SerializeToBinaryData((writer) => { Serialize(writer, options); }); + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ZoneMapping.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ZoneMapping.cs new file mode 100644 index 000000000000..2a2261078b54 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/ServiceModels/ZoneMapping.cs @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System.Collections.Generic; +using Azure.Core; + +namespace Azure.Core.Tests.Public.ResourceManager.Resources.Models +{ + /// The ZoneMapping. + public partial class ZoneMapping + { + /// Initializes a new instance of ZoneMapping. + internal ZoneMapping() + { + Zones = new ChangeTrackingList(); + } + + /// Initializes a new instance of ZoneMapping. + /// The location of the zone mapping. + /// + internal ZoneMapping(AzureLocation? location, IReadOnlyList zones) + { + Location = location; + Zones = zones; + } + + /// The location of the zone mapping. + public AzureLocation? Location { get; } + /// Gets the zones. + public IReadOnlyList Zones { get; } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/TestData/ResourceProviderData.json b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/TestData/ResourceProviderData.json new file mode 100644 index 000000000000..174ff6cdbe4d --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/TestData/ResourceProviderData.json @@ -0,0 +1 @@ +{"id":"/subscriptions/e37510d7-33b6-4676-886f-ee75bcc01871/providers/Microsoft.Network","namespace":"Microsoft.Network","resourceTypes":[{"resourceType":"virtualNetworks","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01","2018-03-01","2018-02-01","2018-01-01","2017-11-01","2017-10-01","2017-09-01","2017-08-01","2017-06-01","2017-04-01","2017-03-01","2016-12-01","2016-11-01","2016-10-01","2016-09-01","2016-08-01","2016-07-01","2016-06-01","2016-03-30","2015-06-15","2015-05-01-preview","2014-12-01-preview"],"defaultApiVersion":"2020-03-01","apiProfiles":[{"profileVersion":"2017-03-09-profile","apiVersion":"2015-06-15"},{"profileVersion":"2018-03-01-hybrid","apiVersion":"2017-10-01"},{"profileVersion":"2019-03-01-hybrid","apiVersion":"2017-10-01"}],"locationMappings":[{"location":"East US 2 EUAP","type":"EdgeZone","extendedLocations":["microsoftrrdclab1","microsoftrrdclab3"]},{"location":"West US","type":"EdgeZone","extendedLocations":["microsoftlosangeles1"]},{"location":"East US 2","type":"EdgeZone","extendedLocations":["microsoftmiami1"]}],"capabilities":"CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation"},{"resourceType":"virtualNetworks/taggedTrafficConsumers","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01","2018-03-01","2018-02-01","2018-01-01","2017-11-01","2017-10-01","2017-09-01","2017-08-01","2017-06-01","2017-04-01","2017-03-01","2016-12-01","2016-11-01","2016-10-01","2016-09-01","2016-08-01","2016-07-01","2016-06-01","2016-03-30","2015-06-15","2015-05-01-preview","2014-12-01-preview"],"defaultApiVersion":"2020-03-01","apiProfiles":[{"profileVersion":"2017-03-09-profile","apiVersion":"2015-06-15"},{"profileVersion":"2018-03-01-hybrid","apiVersion":"2017-10-01"},{"profileVersion":"2019-03-01-hybrid","apiVersion":"2017-10-01"}],"capabilities":"None"},{"resourceType":"natGateways","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01"],"defaultApiVersion":"2020-03-01","zoneMappings":[{"location":"Australia East","zones":["2","1","3"]},{"location":"Brazil South","zones":["2","1","3"]},{"location":"Canada Central","zones":["2","1","3"]},{"location":"Central India","zones":["2","1","3"]},{"location":"Central US","zones":["2","1","3"]},{"location":"Central US EUAP","zones":["1","2"]},{"location":"East Asia","zones":["2","1","3"]},{"location":"East US","zones":["2","1","3"]},{"location":"East US 2","zones":["2","1","3"]},{"location":"East US 2 EUAP","zones":["2","1","3"]},{"location":"France Central","zones":["2","1","3"]},{"location":"Germany West Central","zones":["2","1","3"]},{"location":"Japan East","zones":["2","1","3"]},{"location":"Korea Central","zones":["2","1","3"]},{"location":"North Central US","zones":[]},{"location":"North Europe","zones":["2","1","3"]},{"location":"Norway East","zones":["2","1","3"]},{"location":"Qatar Central","zones":["2","1","3"]},{"location":"South Africa North","zones":["2","1","3"]},{"location":"South Central US","zones":["2","1","3"]},{"location":"Southeast Asia","zones":["2","1","3"]},{"location":"Sweden Central","zones":["2","1","3"]},{"location":"Switzerland North","zones":["2","1","3"]},{"location":"UAE North","zones":["2","1","3"]},{"location":"UK South","zones":["2","1","3"]},{"location":"West Europe","zones":["2","1","3"]},{"location":"West US","zones":[]},{"location":"West US 2","zones":["2","1","3"]},{"location":"West US 3","zones":["2","1","3"]},{"location":"Poland Central","zones":["2","1","3"]}],"capabilities":"CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation"},{"resourceType":"publicIPAddresses","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01","2018-03-01","2018-02-01","2018-01-01","2017-11-01","2017-10-01","2017-09-01","2017-08-01","2017-06-01","2017-04-01","2017-03-01","2016-12-01","2016-11-01","2016-10-01","2016-09-01","2016-08-01","2016-07-01","2016-06-01","2016-03-30","2015-06-15","2015-05-01-preview","2014-12-01-preview"],"defaultApiVersion":"2020-03-01","apiProfiles":[{"profileVersion":"2017-03-09-profile","apiVersion":"2015-06-15"},{"profileVersion":"2018-03-01-hybrid","apiVersion":"2017-10-01"},{"profileVersion":"2019-03-01-hybrid","apiVersion":"2017-10-01"}],"zoneMappings":[{"location":"Australia East","zones":["2","1","3"]},{"location":"Brazil South","zones":["2","1","3"]},{"location":"Canada Central","zones":["2","1","3"]},{"location":"Central India","zones":["2","1","3"]},{"location":"Central US","zones":["2","1","3"]},{"location":"Central US EUAP","zones":["1","2"]},{"location":"East Asia","zones":["2","1","3"]},{"location":"East US","zones":["2","1","3"]},{"location":"East US 2","zones":["2","1","3"]},{"location":"East US 2 EUAP","zones":["2","1","3"]},{"location":"France Central","zones":["2","1","3"]},{"location":"Germany West Central","zones":["2","1","3"]},{"location":"Japan East","zones":["2","1","3"]},{"location":"Korea Central","zones":["2","1","3"]},{"location":"North Central US","zones":[]},{"location":"North Europe","zones":["2","1","3"]},{"location":"Norway East","zones":["2","1","3"]},{"location":"Qatar Central","zones":["2","1","3"]},{"location":"South Africa North","zones":["2","1","3"]},{"location":"South Central US","zones":["2","1","3"]},{"location":"Southeast Asia","zones":["2","1","3"]},{"location":"Sweden Central","zones":["2","1","3"]},{"location":"Switzerland North","zones":["2","1","3"]},{"location":"UAE North","zones":["2","1","3"]},{"location":"UK South","zones":["2","1","3"]},{"location":"West Europe","zones":["2","1","3"]},{"location":"West US","zones":[]},{"location":"West US 2","zones":["2","1","3"]},{"location":"West US 3","zones":["2","1","3"]},{"location":"Poland Central","zones":["2","1","3"]}],"locationMappings":[{"location":"East US 2 EUAP","type":"EdgeZone","extendedLocations":["microsoftrrdclab1","microsoftrrdclab3"]},{"location":"West US","type":"EdgeZone","extendedLocations":["microsoftlosangeles1"]},{"location":"East US 2","type":"EdgeZone","extendedLocations":["microsoftmiami1"]}],"capabilities":"CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation"},{"resourceType":"internalPublicIpAddresses","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01"],"capabilities":"None"},{"resourceType":"customIpPrefixes","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01"],"defaultApiVersion":"2020-06-01","zoneMappings":[{"location":"Australia East","zones":["2","1","3"]},{"location":"Brazil South","zones":["2","1","3"]},{"location":"Canada Central","zones":["2","1","3"]},{"location":"Central India","zones":["2","1","3"]},{"location":"Central US","zones":["2","1","3"]},{"location":"Central US EUAP","zones":["1","2"]},{"location":"East Asia","zones":["2","1","3"]},{"location":"East US","zones":["2","1","3"]},{"location":"East US 2","zones":["2","1","3"]},{"location":"East US 2 EUAP","zones":["2","1","3"]},{"location":"France Central","zones":["2","1","3"]},{"location":"Germany West Central","zones":["2","1","3"]},{"location":"Japan East","zones":["2","1","3"]},{"location":"Korea Central","zones":["2","1","3"]},{"location":"North Central US","zones":[]},{"location":"North Europe","zones":["2","1","3"]},{"location":"Norway East","zones":["2","1","3"]},{"location":"Qatar Central","zones":["2","1","3"]},{"location":"South Africa North","zones":["2","1","3"]},{"location":"South Central US","zones":["2","1","3"]},{"location":"Southeast Asia","zones":["2","1","3"]},{"location":"Sweden Central","zones":["2","1","3"]},{"location":"Switzerland North","zones":["2","1","3"]},{"location":"UAE North","zones":["2","1","3"]},{"location":"UK South","zones":["2","1","3"]},{"location":"West Europe","zones":["2","1","3"]},{"location":"West US","zones":[]},{"location":"West US 2","zones":["2","1","3"]},{"location":"West US 3","zones":["2","1","3"]},{"location":"Poland Central","zones":["2","1","3"]}],"locationMappings":[{"location":"East US 2 EUAP","type":"EdgeZone","extendedLocations":["microsoftrrdclab1","microsoftrrdclab3"]},{"location":"West US","type":"EdgeZone","extendedLocations":["microsoftlosangeles1"]},{"location":"East US 2","type":"EdgeZone","extendedLocations":["microsoftmiami1"]}],"capabilities":"CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation"},{"resourceType":"networkInterfaces","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01","2018-03-01","2018-02-01","2018-01-01","2017-11-01","2017-10-01","2017-09-01","2017-08-01","2017-06-01","2017-04-01","2017-03-01","2016-12-01","2016-11-01","2016-10-01","2016-09-01","2016-08-01","2016-07-01","2016-06-01","2016-03-30","2015-06-15","2015-05-01-preview","2014-12-01-preview"],"defaultApiVersion":"2020-03-01","apiProfiles":[{"profileVersion":"2017-03-09-profile","apiVersion":"2015-06-15"},{"profileVersion":"2018-03-01-hybrid","apiVersion":"2017-10-01"},{"profileVersion":"2019-03-01-hybrid","apiVersion":"2017-10-01"}],"locationMappings":[{"location":"East US 2 EUAP","type":"EdgeZone","extendedLocations":["microsoftrrdclab1","microsoftrrdclab3"]},{"location":"West US","type":"EdgeZone","extendedLocations":["microsoftlosangeles1"]},{"location":"East US 2","type":"EdgeZone","extendedLocations":["microsoftmiami1"]}],"capabilities":"CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation"},{"resourceType":"dscpConfigurations","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01"],"defaultApiVersion":"2020-06-01","capabilities":"CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation"},{"resourceType":"privateEndpoints","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01"],"defaultApiVersion":"2020-03-01","locationMappings":[{"location":"East US 2 EUAP","type":"EdgeZone","extendedLocations":["microsoftrrdclab1","microsoftrrdclab3"]},{"location":"West US","type":"EdgeZone","extendedLocations":["microsoftlosangeles1"]},{"location":"East US 2","type":"EdgeZone","extendedLocations":["microsoftmiami1"]}],"capabilities":"CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation"},{"resourceType":"privateEndpoints/privateLinkServiceProxies","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01"],"defaultApiVersion":"2020-03-01","capabilities":"None"},{"resourceType":"privateEndpointRedirectMaps","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01"],"defaultApiVersion":"2020-03-01","capabilities":"SupportsTags, SupportsLocation"},{"resourceType":"loadBalancers","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01","2018-03-01","2018-02-01","2018-01-01","2017-11-01","2017-10-01","2017-09-01","2017-08-01","2017-06-01","2017-04-01","2017-03-01","2016-12-01","2016-11-01","2016-10-01","2016-09-01","2016-08-01","2016-07-01","2016-06-01","2016-03-30","2015-06-15","2015-05-01-preview","2014-12-01-preview"],"defaultApiVersion":"2020-03-01","apiProfiles":[{"profileVersion":"2017-03-09-profile","apiVersion":"2015-06-15"},{"profileVersion":"2018-03-01-hybrid","apiVersion":"2017-10-01"},{"profileVersion":"2019-03-01-hybrid","apiVersion":"2017-10-01"}],"locationMappings":[{"location":"East US 2 EUAP","type":"EdgeZone","extendedLocations":["microsoftrrdclab1","microsoftrrdclab3"]},{"location":"West US","type":"EdgeZone","extendedLocations":["microsoftlosangeles1"]},{"location":"East US 2","type":"EdgeZone","extendedLocations":["microsoftmiami1"]}],"capabilities":"CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation"},{"resourceType":"networkSecurityGroups","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01","2018-03-01","2018-02-01","2018-01-01","2017-11-01","2017-10-01","2017-09-01","2017-08-01","2017-06-01","2017-04-01","2017-03-01","2016-12-01","2016-11-01","2016-10-01","2016-09-01","2016-08-01","2016-07-01","2016-06-01","2016-03-30","2015-06-15","2015-05-01-preview","2014-12-01-preview"],"defaultApiVersion":"2020-03-01","apiProfiles":[{"profileVersion":"2017-03-09-profile","apiVersion":"2015-06-15"},{"profileVersion":"2018-03-01-hybrid","apiVersion":"2017-10-01"},{"profileVersion":"2019-03-01-hybrid","apiVersion":"2017-10-01"}],"capabilities":"CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation"},{"resourceType":"applicationSecurityGroups","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01","2018-03-01","2018-02-01","2018-01-01","2017-11-01","2017-10-01","2017-09-01"],"defaultApiVersion":"2020-03-01","apiProfiles":[{"profileVersion":"2017-03-09-profile","apiVersion":"2017-09-01"},{"profileVersion":"2018-03-01-hybrid","apiVersion":"2017-10-01"}],"capabilities":"CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation"},{"resourceType":"serviceEndpointPolicies","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01","2018-03-01","2018-02-01","2018-01-01"],"defaultApiVersion":"2020-03-01","capabilities":"CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation"},{"resourceType":"networkIntentPolicies","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01"],"defaultApiVersion":"2020-03-01","capabilities":"CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation"},{"resourceType":"routeTables","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01","2018-03-01","2018-02-01","2018-01-01","2017-11-01","2017-10-01","2017-09-01","2017-08-01","2017-06-01","2017-04-01","2017-03-01","2016-12-01","2016-11-01","2016-10-01","2016-09-01","2016-08-01","2016-07-01","2016-06-01","2016-03-30","2015-06-15","2015-05-01-preview","2014-12-01-preview"],"defaultApiVersion":"2020-03-01","apiProfiles":[{"profileVersion":"2017-03-09-profile","apiVersion":"2015-06-15"},{"profileVersion":"2018-03-01-hybrid","apiVersion":"2017-10-01"},{"profileVersion":"2019-03-01-hybrid","apiVersion":"2017-10-01"}],"capabilities":"CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation"},{"resourceType":"publicIPPrefixes","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01"],"defaultApiVersion":"2020-03-01","zoneMappings":[{"location":"Australia East","zones":["2","1","3"]},{"location":"Brazil South","zones":["2","1","3"]},{"location":"Canada Central","zones":["2","1","3"]},{"location":"Central India","zones":["2","1","3"]},{"location":"Central US","zones":["2","1","3"]},{"location":"Central US EUAP","zones":["1","2"]},{"location":"East Asia","zones":["2","1","3"]},{"location":"East US","zones":["2","1","3"]},{"location":"East US 2","zones":["2","1","3"]},{"location":"East US 2 EUAP","zones":["2","1","3"]},{"location":"France Central","zones":["2","1","3"]},{"location":"Germany West Central","zones":["2","1","3"]},{"location":"Japan East","zones":["2","1","3"]},{"location":"Korea Central","zones":["2","1","3"]},{"location":"North Central US","zones":[]},{"location":"North Europe","zones":["2","1","3"]},{"location":"Norway East","zones":["2","1","3"]},{"location":"Qatar Central","zones":["2","1","3"]},{"location":"South Africa North","zones":["2","1","3"]},{"location":"South Central US","zones":["2","1","3"]},{"location":"Southeast Asia","zones":["2","1","3"]},{"location":"Sweden Central","zones":["2","1","3"]},{"location":"Switzerland North","zones":["2","1","3"]},{"location":"UAE North","zones":["2","1","3"]},{"location":"UK South","zones":["2","1","3"]},{"location":"West Europe","zones":["2","1","3"]},{"location":"West US","zones":[]},{"location":"West US 2","zones":["2","1","3"]},{"location":"West US 3","zones":["2","1","3"]},{"location":"Poland Central","zones":["2","1","3"]}],"locationMappings":[{"location":"East US 2 EUAP","type":"EdgeZone","extendedLocations":["microsoftrrdclab1","microsoftrrdclab3"]},{"location":"West US","type":"EdgeZone","extendedLocations":["microsoftlosangeles1"]},{"location":"East US 2","type":"EdgeZone","extendedLocations":["microsoftmiami1"]}],"capabilities":"CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation"},{"resourceType":"networkWatchers","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01","2018-03-01","2018-02-01","2018-01-01","2017-11-01","2017-10-01","2017-09-01","2017-08-01","2017-06-01","2017-04-01","2017-03-01","2016-12-01","2016-11-01","2016-10-01","2016-09-01","2016-08-01","2016-07-01","2016-06-01","2016-03-30","2015-06-15","2015-05-01-preview","2014-12-01-preview"],"defaultApiVersion":"2020-03-01","capabilities":"CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation"},{"resourceType":"networkWatchers/connectionMonitors","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01","2018-03-01","2018-02-01","2018-01-01","2017-11-01","2017-10-01","2017-09-01"],"defaultApiVersion":"2020-03-01","capabilities":"CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation"},{"resourceType":"networkWatchers/flowLogs","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01","2018-03-01","2018-02-01","2018-01-01","2017-11-01","2017-10-01","2017-09-01"],"defaultApiVersion":"2020-03-01","capabilities":"CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation"},{"resourceType":"networkWatchers/pingMeshes","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01","2018-03-01","2018-02-01","2018-01-01","2017-11-01","2017-10-01","2017-09-01"],"defaultApiVersion":"2020-03-01","capabilities":"CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation"},{"resourceType":"virtualNetworkGateways","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01","2018-03-01","2018-02-01","2018-01-01","2017-11-01","2017-10-01","2017-09-01","2017-08-01","2017-06-01","2017-04-01","2017-03-01","2016-12-01","2016-11-01","2016-10-01","2016-09-01","2016-08-01","2016-07-01","2016-06-01","2016-03-30","2015-06-15","2015-05-01-preview","2014-12-01-preview"],"defaultApiVersion":"2020-03-01","apiProfiles":[{"profileVersion":"2017-03-09-profile","apiVersion":"2015-06-15"},{"profileVersion":"2018-03-01-hybrid","apiVersion":"2017-03-01"},{"profileVersion":"2019-03-01-hybrid","apiVersion":"2017-10-01"}],"locationMappings":[{"location":"East US 2 EUAP","type":"EdgeZone","extendedLocations":["microsoftrrdclab1","microsoftrrdclab3"]},{"location":"West US","type":"EdgeZone","extendedLocations":["microsoftlosangeles1"]},{"location":"East US 2","type":"EdgeZone","extendedLocations":["microsoftmiami1"]}],"capabilities":"CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation"},{"resourceType":"localNetworkGateways","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01","2018-03-01","2018-02-01","2018-01-01","2017-11-01","2017-10-01","2017-09-01","2017-08-01","2017-06-01","2017-04-01","2017-03-01","2016-12-01","2016-11-01","2016-10-01","2016-09-01","2016-08-01","2016-07-01","2016-06-01","2016-03-30","2015-06-15","2015-05-01-preview","2014-12-01-preview"],"defaultApiVersion":"2020-03-01","apiProfiles":[{"profileVersion":"2017-03-09-profile","apiVersion":"2015-06-15"},{"profileVersion":"2018-03-01-hybrid","apiVersion":"2017-03-01"},{"profileVersion":"2019-03-01-hybrid","apiVersion":"2017-10-01"}],"capabilities":"CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation"},{"resourceType":"connections","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01","2018-03-01","2018-02-01","2018-01-01","2017-11-01","2017-10-01","2017-09-01","2017-08-01","2017-06-01","2017-04-01","2017-03-01","2016-12-01","2016-11-01","2016-10-01","2016-09-01","2016-08-01","2016-07-01","2016-06-01","2016-03-30","2015-06-15","2015-05-01-preview","2014-12-01-preview"],"defaultApiVersion":"2020-03-01","apiProfiles":[{"profileVersion":"2017-03-09-profile","apiVersion":"2015-06-15"},{"profileVersion":"2018-03-01-hybrid","apiVersion":"2017-03-01"},{"profileVersion":"2019-03-01-hybrid","apiVersion":"2017-10-01"}],"capabilities":"CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation"},{"resourceType":"applicationGateways","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01","2018-03-01","2018-02-01","2018-01-01","2017-11-01","2017-10-01","2017-09-01","2017-08-01","2017-06-01","2017-04-01","2017-03-01","2016-12-01","2016-11-01","2016-10-01","2016-09-01","2016-08-01","2016-07-01","2016-06-01","2016-03-30","2015-06-15","2015-05-01-preview","2014-12-01-preview"],"defaultApiVersion":"2020-03-01","zoneMappings":[{"location":"Australia East","zones":["2","1","3"]},{"location":"Brazil South","zones":["2","1","3"]},{"location":"Canada Central","zones":["2","1","3"]},{"location":"Central India","zones":["2","1","3"]},{"location":"Central US","zones":["2","1","3"]},{"location":"Central US EUAP","zones":["1","2"]},{"location":"East Asia","zones":["2","1","3"]},{"location":"East US","zones":["2","1","3"]},{"location":"East US 2","zones":["2","1","3"]},{"location":"East US 2 EUAP","zones":["2","1","3"]},{"location":"France Central","zones":["2","1","3"]},{"location":"Germany West Central","zones":["2","1","3"]},{"location":"Japan East","zones":["2","1","3"]},{"location":"Korea Central","zones":["2","1","3"]},{"location":"North Central US","zones":[]},{"location":"North Europe","zones":["2","1","3"]},{"location":"Norway East","zones":["2","1","3"]},{"location":"Qatar Central","zones":["2","1","3"]},{"location":"South Africa North","zones":["2","1","3"]},{"location":"South Central US","zones":["2","1","3"]},{"location":"Southeast Asia","zones":["2","1","3"]},{"location":"Sweden Central","zones":["2","1","3"]},{"location":"Switzerland North","zones":["2","1","3"]},{"location":"UAE North","zones":["2","1","3"]},{"location":"UK South","zones":["2","1","3"]},{"location":"West Europe","zones":["2","1","3"]},{"location":"West US","zones":[]},{"location":"West US 2","zones":["2","1","3"]},{"location":"West US 3","zones":["2","1","3"]},{"location":"Poland Central","zones":["2","1","3"]}],"capabilities":"SupportsTags, SupportsLocation"},{"resourceType":"applicationGatewayWebApplicationFirewallPolicies","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01"],"defaultApiVersion":"2020-03-01","capabilities":"SupportsTags, SupportsLocation"},{"resourceType":"locations","locations":[],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01","2018-03-01","2018-02-01","2018-01-01","2017-11-01","2017-10-01","2017-09-01","2017-08-01","2017-06-01","2017-04-01","2017-03-01","2016-12-01","2016-11-01","2016-10-01","2016-09-01","2016-08-01","2016-07-01","2016-06-01","2016-03-30","2015-06-15","2015-05-01-preview","2014-12-01-preview"],"apiProfiles":[{"profileVersion":"2017-03-09-profile","apiVersion":"2015-06-15"},{"profileVersion":"2018-03-01-hybrid","apiVersion":"2017-10-01"},{"profileVersion":"2019-03-01-hybrid","apiVersion":"2017-10-01"}],"capabilities":"None"},{"resourceType":"locations/operations","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01","2018-03-01","2018-02-01","2018-01-01","2017-11-01","2017-10-01","2017-09-01","2017-08-01","2017-06-01","2017-04-01","2017-03-01","2016-12-01","2016-11-01","2016-10-01","2016-09-01","2016-08-01","2016-07-01","2016-06-01","2016-03-30","2015-06-15","2015-05-01-preview","2014-12-01-preview"],"apiProfiles":[{"profileVersion":"2017-03-09-profile","apiVersion":"2015-06-15"},{"profileVersion":"2018-03-01-hybrid","apiVersion":"2017-10-01"},{"profileVersion":"2019-03-01-hybrid","apiVersion":"2017-10-01"}],"capabilities":"None"},{"resourceType":"locations/operationResults","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01","2018-03-01","2018-02-01","2018-01-01","2017-11-01","2017-10-01","2017-09-01","2017-08-01","2017-06-01","2017-04-01","2017-03-01","2016-12-01","2016-11-01","2016-10-01","2016-09-01","2016-08-01","2016-07-01","2016-06-01","2016-03-30","2015-06-15","2015-05-01-preview","2014-12-01-preview"],"apiProfiles":[{"profileVersion":"2017-03-09-profile","apiVersion":"2015-06-15"},{"profileVersion":"2018-03-01-hybrid","apiVersion":"2017-10-01"},{"profileVersion":"2019-03-01-hybrid","apiVersion":"2017-10-01"}],"capabilities":"None"},{"resourceType":"locations/CheckDnsNameAvailability","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01","2018-03-01","2018-02-01","2018-01-01","2017-11-01","2017-10-01","2017-09-01","2017-08-01","2017-06-01","2017-04-01","2017-03-01","2016-12-01","2016-11-01","2016-10-01","2016-09-01","2016-08-01","2016-07-01","2016-06-01","2016-03-30","2015-06-15","2015-05-01-preview","2014-12-01-preview"],"capabilities":"None"},{"resourceType":"locations/setLoadBalancerFrontendPublicIpAddresses","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01"],"capabilities":"None"},{"resourceType":"cloudServiceSlots","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01"],"capabilities":"SupportsExtension"},{"resourceType":"locations/usages","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01","2018-03-01","2018-02-01","2018-01-01","2017-11-01","2017-10-01","2017-09-01","2017-08-01","2017-06-01","2017-04-01","2017-03-01","2016-12-01","2016-11-01","2016-10-01","2016-09-01","2016-08-01","2016-07-01","2016-06-01","2016-03-30","2015-06-15","2015-05-01-preview","2014-12-01-preview"],"apiProfiles":[{"profileVersion":"2017-03-09-profile","apiVersion":"2015-06-15"},{"profileVersion":"2018-03-01-hybrid","apiVersion":"2017-10-01"},{"profileVersion":"2019-03-01-hybrid","apiVersion":"2017-10-01"}],"capabilities":"None"},{"resourceType":"locations/virtualNetworkAvailableEndpointServices","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01","2018-03-01","2018-02-01","2018-01-01","2017-11-01","2017-10-01","2017-09-01","2017-08-01","2017-06-01","2017-04-01"],"capabilities":"None"},{"resourceType":"locations/availableDelegations","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01"],"capabilities":"None"},{"resourceType":"locations/ApplicationGatewayWafDynamicManifests","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01"],"capabilities":"None"},{"resourceType":"locations/serviceTags","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01"],"capabilities":"None"},{"resourceType":"locations/availablePrivateEndpointTypes","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01"],"capabilities":"None"},{"resourceType":"locations/availableServiceAliases","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01"],"capabilities":"None"},{"resourceType":"locations/checkPrivateLinkServiceVisibility","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01"],"capabilities":"None"},{"resourceType":"locations/autoApprovedPrivateLinkServices","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01"],"capabilities":"None"},{"resourceType":"locations/batchValidatePrivateEndpointsForResourceMove","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01"],"capabilities":"None"},{"resourceType":"locations/batchNotifyPrivateEndpointsForResourceMove","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01"],"capabilities":"None"},{"resourceType":"locations/supportedVirtualMachineSizes","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01"],"capabilities":"None"},{"resourceType":"locations/setAzureNetworkManagerConfiguration","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01"],"capabilities":"None"},{"resourceType":"locations/publishResources","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01"],"capabilities":"None"},{"resourceType":"locations/getAzureNetworkManagerConfiguration","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01"],"capabilities":"None"},{"resourceType":"locations/checkAcceleratedNetworkingSupport","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01"],"capabilities":"None"},{"resourceType":"locations/validateResourceOwnership","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01"],"capabilities":"None"},{"resourceType":"locations/setResourceOwnership","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01"],"capabilities":"None"},{"resourceType":"locations/effectiveResourceOwnership","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01"],"capabilities":"None"},{"resourceType":"operations","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01","2018-03-01","2018-02-01","2018-01-01","2017-11-01","2017-10-01","2017-09-01","2017-08-01","2017-06-01","2017-04-01","2017-03-01","2016-12-01","2016-11-01","2016-10-01","2016-09-01","2016-08-01","2016-07-01","2016-06-01","2016-03-30","2015-06-15","2015-05-01-preview","2014-12-01-preview"],"capabilities":"None"},{"resourceType":"dnszones","locations":["global"],"apiVersions":["2018-05-01","2018-03-01-preview","2017-10-01","2017-09-15-preview","2017-09-01","2016-04-01","2015-05-04-preview"],"defaultApiVersion":"2018-05-01","apiProfiles":[{"profileVersion":"2017-03-09-profile","apiVersion":"2016-04-01"},{"profileVersion":"2018-03-01-hybrid","apiVersion":"2016-04-01"},{"profileVersion":"2019-03-01-hybrid","apiVersion":"2016-04-01"}],"capabilities":"CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation"},{"resourceType":"dnsOperationResults","locations":["global"],"apiVersions":["2018-05-01","2018-03-01-preview","2017-10-01","2017-09-15-preview","2017-09-01","2016-04-01"],"defaultApiVersion":"2018-05-01","capabilities":"None"},{"resourceType":"dnsOperationStatuses","locations":["global"],"apiVersions":["2018-05-01","2018-03-01-preview","2017-10-01","2017-09-15-preview","2017-09-01","2016-04-01"],"defaultApiVersion":"2018-05-01","capabilities":"None"},{"resourceType":"getDnsResourceReference","locations":["global"],"apiVersions":["2018-05-01"],"defaultApiVersion":"2018-05-01","capabilities":"None"},{"resourceType":"internalNotify","locations":["global"],"apiVersions":["2018-05-01"],"defaultApiVersion":"2018-05-01","capabilities":"None"},{"resourceType":"dnszones/A","locations":["global"],"apiVersions":["2018-05-01","2018-03-01-preview","2017-10-01","2017-09-15-preview","2017-09-01","2016-04-01","2015-05-04-preview"],"defaultApiVersion":"2018-05-01","capabilities":"None"},{"resourceType":"dnszones/AAAA","locations":["global"],"apiVersions":["2018-05-01","2018-03-01-preview","2017-10-01","2017-09-15-preview","2017-09-01","2016-04-01","2015-05-04-preview"],"defaultApiVersion":"2018-05-01","capabilities":"None"},{"resourceType":"dnszones/CNAME","locations":["global"],"apiVersions":["2018-05-01","2018-03-01-preview","2017-10-01","2017-09-15-preview","2017-09-01","2016-04-01","2015-05-04-preview"],"defaultApiVersion":"2018-05-01","capabilities":"None"},{"resourceType":"dnszones/PTR","locations":["global"],"apiVersions":["2018-05-01","2018-03-01-preview","2017-10-01","2017-09-15-preview","2017-09-01","2016-04-01","2015-05-04-preview"],"defaultApiVersion":"2018-05-01","capabilities":"None"},{"resourceType":"dnszones/MX","locations":["global"],"apiVersions":["2018-05-01","2018-03-01-preview","2017-10-01","2017-09-15-preview","2017-09-01","2016-04-01","2015-05-04-preview"],"defaultApiVersion":"2018-05-01","capabilities":"None"},{"resourceType":"dnszones/TXT","locations":["global"],"apiVersions":["2018-05-01","2018-03-01-preview","2017-10-01","2017-09-15-preview","2017-09-01","2016-04-01","2015-05-04-preview"],"defaultApiVersion":"2018-05-01","capabilities":"None"},{"resourceType":"dnszones/SRV","locations":["global"],"apiVersions":["2018-05-01","2018-03-01-preview","2017-10-01","2017-09-15-preview","2017-09-01","2016-04-01","2015-05-04-preview"],"defaultApiVersion":"2018-05-01","capabilities":"None"},{"resourceType":"dnszones/SOA","locations":["global"],"apiVersions":["2018-05-01","2018-03-01-preview","2017-10-01","2017-09-15-preview","2017-09-01","2016-04-01","2015-05-04-preview"],"defaultApiVersion":"2018-05-01","capabilities":"None"},{"resourceType":"dnszones/NS","locations":["global"],"apiVersions":["2018-05-01","2018-03-01-preview","2017-10-01","2017-09-15-preview","2017-09-01","2016-04-01","2015-05-04-preview"],"defaultApiVersion":"2018-05-01","capabilities":"None"},{"resourceType":"dnszones/CAA","locations":["global"],"apiVersions":["2018-05-01","2018-03-01-preview","2017-10-01","2017-09-15-preview","2017-09-01"],"defaultApiVersion":"2018-05-01","capabilities":"None"},{"resourceType":"dnszones/recordsets","locations":["global"],"apiVersions":["2018-05-01","2018-03-01-preview","2017-10-01","2017-09-15-preview","2017-09-01","2016-04-01","2015-05-04-preview"],"defaultApiVersion":"2018-05-01","capabilities":"None"},{"resourceType":"dnszones/all","locations":["global"],"apiVersions":["2018-05-01","2018-03-01-preview","2017-10-01","2017-09-15-preview","2017-09-01","2016-04-01","2015-05-04-preview"],"defaultApiVersion":"2018-05-01","capabilities":"None"},{"resourceType":"privateDnsZones","locations":["global"],"apiVersions":["2020-06-01","2020-01-01","2018-09-01"],"defaultApiVersion":"2018-09-01","capabilities":"CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation"},{"resourceType":"privateDnsZones/virtualNetworkLinks","locations":["global"],"apiVersions":["2020-06-01","2020-01-01","2018-09-01"],"defaultApiVersion":"2018-09-01","capabilities":"CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation"},{"resourceType":"privateDnsOperationResults","locations":["global"],"apiVersions":["2020-06-01","2020-01-01","2018-09-01"],"defaultApiVersion":"2018-09-01","capabilities":"None"},{"resourceType":"privateDnsOperationStatuses","locations":["global"],"apiVersions":["2020-06-01","2020-01-01","2018-09-01"],"defaultApiVersion":"2018-09-01","capabilities":"None"},{"resourceType":"privateDnsZonesInternal","locations":["global"],"apiVersions":["2020-06-01","2020-01-01"],"defaultApiVersion":"2020-01-01","capabilities":"None"},{"resourceType":"privateDnsZones/A","locations":["global"],"apiVersions":["2020-06-01","2020-01-01","2018-09-01"],"defaultApiVersion":"2018-09-01","capabilities":"None"},{"resourceType":"privateDnsZones/AAAA","locations":["global"],"apiVersions":["2020-06-01","2020-01-01","2018-09-01"],"defaultApiVersion":"2018-09-01","capabilities":"None"},{"resourceType":"privateDnsZones/CNAME","locations":["global"],"apiVersions":["2020-06-01","2020-01-01","2018-09-01"],"defaultApiVersion":"2018-09-01","capabilities":"None"},{"resourceType":"privateDnsZones/PTR","locations":["global"],"apiVersions":["2020-06-01","2020-01-01","2018-09-01"],"defaultApiVersion":"2018-09-01","capabilities":"None"},{"resourceType":"privateDnsZones/MX","locations":["global"],"apiVersions":["2020-06-01","2020-01-01","2018-09-01"],"defaultApiVersion":"2018-09-01","capabilities":"None"},{"resourceType":"privateDnsZones/TXT","locations":["global"],"apiVersions":["2020-06-01","2020-01-01","2018-09-01"],"defaultApiVersion":"2018-09-01","capabilities":"None"},{"resourceType":"privateDnsZones/SRV","locations":["global"],"apiVersions":["2020-06-01","2020-01-01","2018-09-01"],"defaultApiVersion":"2018-09-01","capabilities":"None"},{"resourceType":"privateDnsZones/SOA","locations":["global"],"apiVersions":["2020-06-01","2020-01-01","2018-09-01"],"defaultApiVersion":"2018-09-01","capabilities":"None"},{"resourceType":"privateDnsZones/all","locations":["global"],"apiVersions":["2020-06-01","2020-01-01","2018-09-01"],"defaultApiVersion":"2018-09-01","capabilities":"None"},{"resourceType":"virtualNetworks/privateDnsZoneLinks","locations":["global"],"apiVersions":["2020-06-01"],"defaultApiVersion":"2020-06-01","capabilities":"None"},{"resourceType":"dnsResolvers","locations":["West Central US","East US 2","West Europe","North Europe","Australia East","UK South","South Central US","East US","North Central US","West US 2","West US 3","Southeast Asia","Central India","Canada Central","Central US","France Central","Japan East","Germany West Central","South Africa North","Korea Central","Sweden Central","East Asia","Switzerland North","Brazil South","West US","Norway East","UAE North","Australia Southeast","Canada East","Japan West","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2022-07-01","2020-04-01-preview"],"defaultApiVersion":"2020-04-01-preview","capabilities":"CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation"},{"resourceType":"dnsResolvers/inboundEndpoints","locations":["West Central US","East US 2","West Europe","North Europe","Australia East","UK South","South Central US","East US","North Central US","West US 2","West US 3","Southeast Asia","Central India","Canada Central","Central US","France Central","Japan East","Germany West Central","South Africa North","Korea Central","Sweden Central","East Asia","Switzerland North","Brazil South","West US","Norway East","UAE North","Australia Southeast","Canada East","Japan West","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2022-07-01","2020-04-01-preview"],"defaultApiVersion":"2020-04-01-preview","capabilities":"CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation"},{"resourceType":"dnsResolvers/outboundEndpoints","locations":["West Central US","East US 2","West Europe","North Europe","Australia East","UK South","South Central US","East US","North Central US","West US 2","West US 3","Southeast Asia","Central India","Canada Central","Central US","France Central","Japan East","Germany West Central","South Africa North","Korea Central","Sweden Central","East Asia","Switzerland North","Brazil South","West US","Norway East","UAE North","Australia Southeast","Canada East","Japan West","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2022-07-01","2020-04-01-preview"],"defaultApiVersion":"2020-04-01-preview","capabilities":"CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation"},{"resourceType":"dnsForwardingRulesets","locations":["West Central US","East US 2","West Europe","North Europe","Australia East","UK South","South Central US","East US","North Central US","West US 2","West US 3","Southeast Asia","Central India","Canada Central","Central US","France Central","Japan East","Germany West Central","South Africa North","Korea Central","Sweden Central","East Asia","Switzerland North","Brazil South","West US","Norway East","UAE North","Australia Southeast","Canada East","Japan West","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2022-07-01","2020-04-01-preview"],"defaultApiVersion":"2020-04-01-preview","capabilities":"CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation"},{"resourceType":"dnsForwardingRulesets/forwardingRules","locations":["West Central US","East US 2","West Europe","North Europe","Australia East","UK South","South Central US","East US","North Central US","West US 2","West US 3","Southeast Asia","Central India","Canada Central","Central US","France Central","Japan East","Germany West Central","South Africa North","Korea Central","Sweden Central","East Asia","Switzerland North","Brazil South","West US","Norway East","UAE North","Australia Southeast","Canada East","Japan West","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2022-07-01","2020-04-01-preview"],"defaultApiVersion":"2020-04-01-preview","capabilities":"None"},{"resourceType":"dnsForwardingRulesets/virtualNetworkLinks","locations":["West Central US","East US 2","West Europe","North Europe","Australia East","UK South","South Central US","East US","North Central US","West US 2","West US 3","Southeast Asia","Central India","Canada Central","Central US","France Central","Japan East","Germany West Central","South Africa North","Korea Central","Sweden Central","East Asia","Switzerland North","Brazil South","West US","Norway East","UAE North","Australia Southeast","Canada East","Japan West","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2022-07-01","2020-04-01-preview"],"defaultApiVersion":"2020-04-01-preview","capabilities":"None"},{"resourceType":"virtualNetworks/listDnsResolvers","locations":["West Central US","East US 2","West Europe","North Europe","Australia East","UK South","South Central US","East US","North Central US","West US 2","West US 3","Southeast Asia","Central India","Canada Central","Central US","France Central","Japan East","Germany West Central","South Africa North","Korea Central","Sweden Central","East Asia","Switzerland North","Brazil South","West US","Norway East","UAE North","Australia Southeast","Canada East","Japan West","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2022-07-01","2020-04-01-preview"],"defaultApiVersion":"2020-04-01-preview","capabilities":"None"},{"resourceType":"virtualNetworks/listDnsForwardingRulesets","locations":["West Central US","East US 2","West Europe","North Europe","Australia East","UK South","South Central US","East US","North Central US","West US 2","West US 3","Southeast Asia","Central India","Canada Central","Central US","France Central","Japan East","Germany West Central","South Africa North","Korea Central","Sweden Central","East Asia","Switzerland North","Brazil South","West US","Norway East","UAE North","Australia Southeast","Canada East","Japan West","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2022-07-01","2020-04-01-preview"],"defaultApiVersion":"2020-04-01-preview","capabilities":"None"},{"resourceType":"locations/dnsResolverOperationResults","locations":[],"apiVersions":["2022-07-01","2020-04-01-preview"],"defaultApiVersion":"2020-04-01-preview","capabilities":"None"},{"resourceType":"locations/dnsResolverOperationStatuses","locations":[],"apiVersions":["2022-07-01","2020-04-01-preview"],"defaultApiVersion":"2020-04-01-preview","capabilities":"None"},{"resourceType":"trafficmanagerprofiles","locations":["global"],"apiVersions":["2022-12-01-preview","2022-04-01-preview","2018-08-01","2018-04-01","2018-03-01","2018-02-01","2017-05-01","2017-03-01","2015-11-01","2015-04-28-preview"],"defaultApiVersion":"2018-08-01","capabilities":"CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation"},{"resourceType":"trafficmanagerprofiles/heatMaps","locations":["global"],"apiVersions":["2022-04-01-preview","2018-08-01","2018-04-01","2018-03-01","2018-02-01","2017-09-01-preview"],"defaultApiVersion":"2018-08-01","capabilities":"None"},{"resourceType":"trafficmanagerprofiles/azureendpoints","locations":["global"],"apiVersions":["2022-12-01-preview","2022-04-01-preview","2018-08-01","2018-04-01","2018-03-01","2018-02-01","2017-05-01","2017-03-01","2015-11-01","2015-04-28-preview"],"defaultApiVersion":"2018-08-01","capabilities":"None"},{"resourceType":"trafficmanagerprofiles/externalendpoints","locations":["global"],"apiVersions":["2022-12-01-preview","2022-04-01-preview","2018-08-01","2018-04-01","2018-03-01","2018-02-01","2017-05-01","2017-03-01","2015-11-01","2015-04-28-preview"],"defaultApiVersion":"2018-08-01","capabilities":"None"},{"resourceType":"trafficmanagerprofiles/nestedendpoints","locations":["global"],"apiVersions":["2022-12-01-preview","2022-04-01-preview","2018-08-01","2018-04-01","2018-03-01","2018-02-01","2017-05-01","2017-03-01","2015-11-01","2015-04-28-preview"],"defaultApiVersion":"2018-08-01","capabilities":"None"},{"resourceType":"checkTrafficManagerNameAvailability","locations":["global"],"apiVersions":["2022-12-01-preview","2022-04-01-preview","2018-08-01","2018-04-01","2018-03-01","2018-02-01","2017-05-01","2017-03-01","2015-11-01","2015-04-28-preview"],"defaultApiVersion":"2018-08-01","capabilities":"None"},{"resourceType":"checkTrafficManagerNameAvailabilityV2","locations":["global"],"apiVersions":["2022-12-01-preview"],"defaultApiVersion":"2022-12-01-preview","capabilities":"None"},{"resourceType":"trafficManagerUserMetricsKeys","locations":["global"],"apiVersions":["2022-04-01-preview","2018-08-01","2018-04-01","2017-09-01-preview"],"defaultApiVersion":"2018-08-01","capabilities":"None"},{"resourceType":"trafficManagerGeographicHierarchies","locations":["global"],"apiVersions":["2022-12-01-preview","2022-04-01-preview","2018-08-01","2018-04-01","2018-03-01","2018-02-01","2017-05-01","2017-03-01"],"defaultApiVersion":"2018-08-01","capabilities":"None"},{"resourceType":"expressRouteCircuits","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01","2018-03-01","2018-02-01","2018-01-01","2017-11-01","2017-10-01","2017-09-01","2017-08-01","2017-06-01","2017-04-01","2017-03-01","2016-12-01","2016-11-01","2016-10-01","2016-09-01","2016-08-01","2016-07-01","2016-06-01","2016-03-30","2015-06-15","2015-05-01-preview","2014-12-01-preview"],"defaultApiVersion":"2020-03-01","capabilities":"SupportsTags, SupportsLocation"},{"resourceType":"expressRouteServiceProviders","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01","2018-03-01","2018-02-01","2018-01-01","2017-11-01","2017-10-01","2017-09-01","2017-08-01","2017-06-01","2017-04-01","2017-03-01","2016-12-01","2016-11-01","2016-10-01","2016-09-01","2016-08-01","2016-07-01","2016-06-01","2016-03-30","2015-06-15","2015-05-01-preview","2014-12-01-preview"],"capabilities":"None"},{"resourceType":"applicationGatewayAvailableWafRuleSets","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01","2018-03-01","2018-02-01","2018-01-01","2017-11-01","2017-10-01","2017-09-01","2017-08-01","2017-06-01","2017-04-01","2017-03-01"],"capabilities":"None"},{"resourceType":"applicationGatewayAvailableSslOptions","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01","2018-03-01","2018-02-01","2018-01-01","2017-11-01","2017-10-01","2017-09-01","2017-08-01","2017-06-01"],"capabilities":"None"},{"resourceType":"applicationGatewayAvailableServerVariables","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01"],"capabilities":"None"},{"resourceType":"applicationGatewayAvailableRequestHeaders","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01"],"capabilities":"None"},{"resourceType":"applicationGatewayAvailableResponseHeaders","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01"],"capabilities":"None"},{"resourceType":"routeFilters","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01","2018-03-01","2018-02-01","2018-01-01","2017-11-01","2017-10-01","2017-09-01","2017-08-01","2017-06-01","2017-04-01","2017-03-01","2016-12-01","2016-11-01","2016-10-01","2016-09-01","2016-08-01","2016-07-01","2016-06-01","2016-03-30","2015-06-15","2015-05-01-preview","2014-12-01-preview"],"defaultApiVersion":"2020-03-01","capabilities":"SupportsTags, SupportsLocation"},{"resourceType":"bgpServiceCommunities","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01","2018-03-01","2018-02-01","2018-01-01","2017-11-01","2017-10-01","2017-09-01","2017-08-01","2017-06-01","2017-04-01","2017-03-01","2016-12-01"],"capabilities":"None"},{"resourceType":"virtualWans","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01","2018-03-01","2018-02-01","2018-01-01","2017-11-01","2017-10-01","2017-09-01"],"defaultApiVersion":"2020-03-01","capabilities":"SupportsTags, SupportsLocation"},{"resourceType":"vpnSites","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01","2018-03-01","2018-02-01","2018-01-01","2017-11-01","2017-10-01","2017-09-01"],"defaultApiVersion":"2020-03-01","capabilities":"SupportsTags, SupportsLocation"},{"resourceType":"vpnServerConfigurations","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","South Africa North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01"],"defaultApiVersion":"2020-03-01","capabilities":"SupportsTags, SupportsLocation"},{"resourceType":"virtualHubs","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01","2018-03-01","2018-02-01","2018-01-01","2017-11-01","2017-10-01","2017-09-01"],"defaultApiVersion":"2020-03-01","capabilities":"SupportsTags, SupportsLocation"},{"resourceType":"vpnGateways","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01","2018-03-01","2018-02-01","2018-01-01","2017-11-01","2017-10-01","2017-09-01"],"defaultApiVersion":"2020-03-01","capabilities":"SupportsTags, SupportsLocation"},{"resourceType":"p2sVpnGateways","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","UAE North","South Africa North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01"],"defaultApiVersion":"2020-03-01","capabilities":"SupportsTags, SupportsLocation"},{"resourceType":"expressRouteGateways","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01"],"defaultApiVersion":"2020-03-01","capabilities":"SupportsTags, SupportsLocation"},{"resourceType":"locations/hybridEdgeZone","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01"],"capabilities":"None"},{"resourceType":"expressRoutePortsLocations","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01"],"capabilities":"None"},{"resourceType":"expressRoutePorts","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","UAE North","South Africa North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01"],"defaultApiVersion":"2020-03-01","capabilities":"SupportsTags, SupportsLocation"},{"resourceType":"firewallPolicies","locations":["Qatar Central","UAE North","Australia Central 2","UAE Central","Germany North","Central India","Korea South","Switzerland North","Switzerland West","Japan West","France South","South Africa West","West India","Canada East","South India","Germany West Central","Norway East","Norway West","South Africa North","East Asia","Southeast Asia","Korea Central","Brazil South","Brazil Southeast","West US 3","Jio India West","Sweden Central","Japan East","UK West","West US","East US","North Europe","West Europe","West Central US","South Central US","Australia East","Australia Central","Australia Southeast","UK South","East US 2","West US 2","North Central US","Canada Central","France Central","Central US","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01"],"defaultApiVersion":"2020-04-01","capabilities":"SupportsTags, SupportsLocation"},{"resourceType":"ipGroups","locations":["Qatar Central","UAE North","Australia Central 2","UAE Central","Germany North","Central India","Korea South","Switzerland North","Switzerland West","Japan West","France South","South Africa West","West India","Canada East","South India","Germany West Central","Norway East","Norway West","South Africa North","East Asia","Southeast Asia","Korea Central","Brazil South","Brazil Southeast","West US 3","Jio India West","Sweden Central","Japan East","UK West","West US","East US","North Europe","West Europe","South Central US","Australia East","Australia Central","Australia Southeast","UK South","East US 2","West US 2","North Central US","Canada Central","France Central","West Central US","Central US","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01"],"defaultApiVersion":"2020-04-01","capabilities":"SupportsTags, SupportsLocation"},{"resourceType":"azureWebCategories","locations":[],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01"],"defaultApiVersion":"2020-08-01","capabilities":"None"},{"resourceType":"locations/nfvOperations","locations":[],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01","2018-03-01","2018-02-01","2018-01-01","2017-11-01","2017-10-01","2017-09-01"],"capabilities":"None"},{"resourceType":"locations/nfvOperationResults","locations":[],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01","2018-03-01","2018-02-01","2018-01-01","2017-11-01","2017-10-01","2017-09-01"],"capabilities":"None"},{"resourceType":"securityPartnerProviders","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01"],"defaultApiVersion":"2020-03-01","capabilities":"SupportsTags, SupportsLocation"},{"resourceType":"azureFirewalls","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","France Central","Australia Central","Japan West","Japan East","Korea Central","Korea South","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01"],"defaultApiVersion":"2020-03-01","zoneMappings":[{"location":"Australia East","zones":["2","1","3"]},{"location":"Brazil South","zones":["2","1","3"]},{"location":"Canada Central","zones":["2","1","3"]},{"location":"Central India","zones":["2","1","3"]},{"location":"Central US","zones":["2","1","3"]},{"location":"Central US EUAP","zones":["1","2"]},{"location":"East Asia","zones":["2","1","3"]},{"location":"East US","zones":["2","1","3"]},{"location":"East US 2","zones":["2","1","3"]},{"location":"East US 2 EUAP","zones":["2","1","3"]},{"location":"France Central","zones":["2","1","3"]},{"location":"Germany West Central","zones":["2","1","3"]},{"location":"Japan East","zones":["2","1","3"]},{"location":"Korea Central","zones":["2","1","3"]},{"location":"North Central US","zones":[]},{"location":"North Europe","zones":["2","1","3"]},{"location":"Norway East","zones":["2","1","3"]},{"location":"Qatar Central","zones":["2","1","3"]},{"location":"South Africa North","zones":["2","1","3"]},{"location":"South Central US","zones":["2","1","3"]},{"location":"Southeast Asia","zones":["2","1","3"]},{"location":"Sweden Central","zones":["2","1","3"]},{"location":"Switzerland North","zones":["2","1","3"]},{"location":"UAE North","zones":["2","1","3"]},{"location":"UK South","zones":["2","1","3"]},{"location":"West Europe","zones":["2","1","3"]},{"location":"West US","zones":[]},{"location":"West US 2","zones":["2","1","3"]},{"location":"West US 3","zones":["2","1","3"]},{"location":"Poland Central","zones":["2","1","3"]}],"capabilities":"SupportsTags, SupportsLocation"},{"resourceType":"azureFirewallFqdnTags","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01"],"capabilities":"None"},{"resourceType":"virtualNetworkTaps","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01"],"defaultApiVersion":"2020-03-01","capabilities":"SupportsTags, SupportsLocation"},{"resourceType":"privateLinkServices","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01"],"defaultApiVersion":"2020-03-01","locationMappings":[{"location":"East US 2 EUAP","type":"EdgeZone","extendedLocations":["microsoftrrdclab1","microsoftrrdclab3"]},{"location":"West US","type":"EdgeZone","extendedLocations":["microsoftlosangeles1"]},{"location":"East US 2","type":"EdgeZone","extendedLocations":["microsoftmiami1"]}],"capabilities":"SupportsTags, SupportsLocation"},{"resourceType":"locations/privateLinkServices","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01"],"capabilities":"None"},{"resourceType":"ddosProtectionPlans","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01","2018-03-01","2018-02-01"],"defaultApiVersion":"2020-03-01","apiProfiles":[{"profileVersion":"2017-03-09-profile","apiVersion":"2018-02-01"}],"capabilities":"SupportsTags, SupportsLocation"},{"resourceType":"networkProfiles","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01"],"defaultApiVersion":"2020-03-01","capabilities":"SupportsTags, SupportsLocation"},{"resourceType":"checkFrontdoorNameAvailability","locations":["global","Central US","East US","East US 2","North Central US","South Central US","West US","North Europe","West Europe","East Asia","Southeast Asia","Japan East","Japan West","Brazil South","Australia East","Australia Southeast"],"apiVersions":["2021-06-01","2020-07-01","2020-05-01","2020-01-01","2019-08-01","2019-05-01","2019-04-01","2018-08-01"],"defaultApiVersion":"2020-07-01","capabilities":"None"},{"resourceType":"frontdoorWebApplicationFirewallManagedRuleSets","locations":["global","Central US","East US","East US 2","North Central US","South Central US","West US","North Europe","West Europe","East Asia","Southeast Asia","Japan East","Japan West","Brazil South","Australia East","Australia Southeast"],"apiVersions":["2022-05-01","2020-11-01","2020-04-01","2019-10-01","2019-03-01"],"defaultApiVersion":"2020-11-01","capabilities":"None"},{"resourceType":"locations/bareMetalTenants","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01"],"capabilities":"None"},{"resourceType":"bastionHosts","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01"],"defaultApiVersion":"2020-03-01","capabilities":"CrossResourceGroupResourceMove, SupportsTags, SupportsLocation"},{"resourceType":"virtualRouters","locations":["Qatar Central","UAE North","Australia Central 2","UAE Central","Germany North","Central India","Korea South","Switzerland North","Switzerland West","Japan West","France South","South Africa West","West India","Canada East","South India","Germany West Central","Norway East","Norway West","South Africa North","East Asia","Southeast Asia","Korea Central","Brazil South","Brazil Southeast","West US 3","Jio India West","Sweden Central","Japan East","UK West","West US","East US","North Europe","West Europe","West Central US","South Central US","Australia East","Australia Central","Australia Southeast","UK South","East US 2","West US 2","North Central US","Canada Central","France Central","Central US","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01"],"defaultApiVersion":"2020-04-01","capabilities":"SupportsTags, SupportsLocation"},{"resourceType":"networkVirtualAppliances","locations":["Qatar Central","Brazil Southeast","West US 3","Jio India West","Sweden Central","UAE North","Australia Central 2","UAE Central","Germany North","Central India","Korea South","Switzerland North","Switzerland West","Japan West","France South","South Africa West","West India","Canada East","South India","Germany West Central","Norway East","Norway West","South Africa North","East Asia","Southeast Asia","Korea Central","Brazil South","Japan East","UK West","West US","East US","North Europe","West Europe","West Central US","South Central US","Australia East","Australia Central","Australia Southeast","UK South","East US 2","West US 2","North Central US","Canada Central","France Central","Central US","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01"],"defaultApiVersion":"2020-04-01","capabilities":"SystemAssignedResourceIdentity, SupportsTags, SupportsLocation"},{"resourceType":"ipAllocations","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01"],"defaultApiVersion":"2020-03-01","capabilities":"CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation"},{"resourceType":"networkManagers","locations":["West Central US","North Central US","West US","West Europe","UAE Central","Germany North","East US","West India","East US 2","Australia Central","Australia Central 2","South Africa West","Brazil South","UK West","North Europe","Central US","UAE North","Germany West Central","Switzerland West","East Asia","Jio India West","South Africa North","UK South","South India","Australia Southeast","France South","West US 2","Sweden Central","Japan West","Norway East","France Central","West US 3","Central India","Korea South","Brazil Southeast","Korea Central","Southeast Asia","South Central US","Norway West","Australia East","Japan East","Canada East","Canada Central","Switzerland North","Qatar Central","Poland Central","East US 2 EUAP","Central US EUAP"],"apiVersions":["2023-03-01-preview","2022-11-01","2022-09-01","2022-07-01","2022-06-01-preview","2022-05-01","2022-04-01-preview","2022-01-01"],"defaultApiVersion":"2022-05-01","capabilities":"CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation"},{"resourceType":"networkManagerConnections","locations":[],"apiVersions":["2023-03-01-preview","2022-11-01","2022-09-01","2022-07-01","2022-06-01-preview","2022-05-01","2022-04-01-preview","2022-01-01"],"defaultApiVersion":"2022-05-01","capabilities":"SupportsExtension"},{"resourceType":"locations/queryNetworkSecurityPerimeter","locations":["West Central US","Jio India West","North Central US","West US","West Europe","UAE Central","Germany North","East US","West India","East US 2","Australia Central","Australia Central 2","South Africa West","Brazil South","UK West","North Europe","Central US","UAE North","Germany West Central","Switzerland West","East Asia","South Africa North","UK South","South India","Australia Southeast","France South","West US 2","Sweden Central","Japan West","Norway East","France Central","West US 3","Central India","Korea South","Brazil Southeast","Korea Central","Southeast Asia","South Central US","Norway West","Australia East","Japan East","Canada East","Canada Central","Switzerland North","Qatar Central","Poland Central","East US 2 EUAP","Central US EUAP"],"apiVersions":["2022-02-01-preview","2021-05-01-preview","2021-02-01-preview"],"defaultApiVersion":"2021-02-01-preview","capabilities":"None"},{"resourceType":"virtualNetworks/listNetworkManagerEffectiveConnectivityConfigurations","locations":["West Central US","North Central US","West US","West Europe","UAE Central","Germany North","East US","West India","East US 2","Australia Central","Australia Central 2","South Africa West","Brazil South","UK West","North Europe","Central US","UAE North","Germany West Central","Switzerland West","East Asia","Jio India West","South Africa North","UK South","South India","Australia Southeast","France South","West US 2","Sweden Central","Japan West","Norway East","France Central","West US 3","Central India","Korea South","Brazil Southeast","Korea Central","Southeast Asia","South Central US","Norway West","Australia East","Japan East","Canada East","Canada Central","Switzerland North","Qatar Central","Poland Central","East US 2 EUAP","Central US EUAP"],"apiVersions":["2023-03-01-preview","2022-11-01","2022-09-01","2022-07-01","2022-06-01-preview","2022-05-01","2022-04-01-preview","2022-01-01"],"defaultApiVersion":"2022-05-01","capabilities":"None"},{"resourceType":"virtualNetworks/listNetworkManagerEffectiveSecurityAdminRules","locations":["West Central US","North Central US","West US","West Europe","UAE Central","Germany North","East US","West India","East US 2","Australia Central","Australia Central 2","South Africa West","Brazil South","UK West","North Europe","Central US","UAE North","Germany West Central","Switzerland West","East Asia","Jio India West","South Africa North","UK South","South India","Australia Southeast","France South","West US 2","Sweden Central","Japan West","Norway East","France Central","West US 3","Central India","Korea South","Brazil Southeast","Korea Central","Southeast Asia","South Central US","Norway West","Australia East","Japan East","Canada East","Canada Central","Switzerland North","Qatar Central","Poland Central","East US 2 EUAP","Central US EUAP"],"apiVersions":["2023-03-01-preview","2022-11-01","2022-09-01","2022-07-01","2022-06-01-preview","2022-05-01","2022-04-01-preview","2022-01-01"],"defaultApiVersion":"2022-05-01","capabilities":"None"},{"resourceType":"networkGroupMemberships","locations":["West Central US","North Central US","West US","West Europe","UAE Central","Germany North","East US","West India","East US 2","Australia Central","Australia Central 2","South Africa West","Brazil South","UK West","North Europe","Central US","UAE North","Germany West Central","Switzerland West","East Asia","Jio India West","South Africa North","UK South","South India","Australia Southeast","France South","West US 2","Sweden Central","Japan West","Norway East","France Central","West US 3","Central India","Korea South","Brazil Southeast","Korea Central","Southeast Asia","South Central US","Norway West","Australia East","Japan East","Canada East","Canada Central","Switzerland North","Qatar Central","Poland Central","East US 2 EUAP","Central US EUAP"],"apiVersions":["2022-06-01-preview"],"defaultApiVersion":"2022-06-01-preview","capabilities":"SupportsExtension"},{"resourceType":"locations/commitInternalAzureNetworkManagerConfiguration","locations":["West Central US","North Central US","West US","West Europe","UAE Central","Germany North","East US","West India","East US 2","Australia Central","Australia Central 2","South Africa West","Brazil South","UK West","North Europe","Central US","UAE North","Germany West Central","Switzerland West","East Asia","Jio India West","South Africa North","UK South","South India","Australia Southeast","France South","West US 2","Sweden Central","Japan West","Norway East","France Central","West US 3","Central India","Korea South","Brazil Southeast","Korea Central","Southeast Asia","South Central US","Norway West","Australia East","Japan East","Canada East","Canada Central","Switzerland North","Qatar Central","Poland Central","East US 2 EUAP","Central US EUAP"],"apiVersions":["2023-03-01-preview","2022-11-01","2022-09-01","2022-07-01","2022-06-01-preview","2022-05-01","2022-04-01-preview","2022-01-01"],"capabilities":"None"},{"resourceType":"locations/internalAzureVirtualNetworkManagerOperation","locations":["West Central US","North Central US","West US","West Europe","UAE Central","Germany North","East US","West India","East US 2","Australia Central","Australia Central 2","South Africa West","Brazil South","UK West","North Europe","Central US","UAE North","Germany West Central","Switzerland West","East Asia","Jio India West","South Africa North","UK South","South India","Australia Southeast","France South","West US 2","Sweden Central","Japan West","Norway East","France Central","West US 3","Central India","Korea South","Brazil Southeast","Korea Central","Southeast Asia","South Central US","Norway West","Australia East","Japan East","Canada East","Canada Central","Switzerland North","Qatar Central","Poland Central","East US 2 EUAP","Central US EUAP"],"apiVersions":["2023-03-01-preview","2022-11-01","2022-09-01","2022-07-01","2022-06-01-preview","2022-05-01","2022-04-01-preview","2022-01-01"],"capabilities":"None"},{"resourceType":"networkVirtualApplianceSkus","locations":[],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01"],"defaultApiVersion":"2020-04-01","capabilities":"None"},{"resourceType":"locations/serviceTagDetails","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01"],"capabilities":"None"},{"resourceType":"locations/dataTasks","locations":["West US","East US","North Europe","West Europe","East Asia","Southeast Asia","North Central US","South Central US","Central US","East US 2","Japan East","Japan West","Brazil South","Australia East","Australia Southeast","Central India","South India","West India","Canada Central","Canada East","West Central US","West US 2","UK West","UK South","Korea Central","Korea South","France Central","Australia Central","South Africa North","UAE North","Switzerland North","Germany West Central","Norway East","West US 3","Jio India West","Sweden Central","Qatar Central","Poland Central","Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01"],"capabilities":"None"},{"resourceType":"networkWatchers/lenses","locations":["Central US EUAP","East US 2 EUAP"],"apiVersions":["2023-02-01","2022-11-01","2022-09-01","2022-07-01","2022-05-01","2022-01-01","2021-12-01","2021-08-01","2021-06-01","2021-05-01","2021-04-01","2021-03-01","2021-02-01","2021-01-01","2020-11-01","2020-08-01","2020-07-01","2020-06-01","2020-05-01","2020-04-01","2020-03-01","2020-01-01","2019-12-01","2019-11-01","2019-09-01","2019-08-01","2019-07-01","2019-06-01","2019-04-01","2019-02-01","2018-12-01","2018-11-01","2018-10-01","2018-08-01","2018-07-01","2018-06-01","2018-05-01","2018-04-01","2018-03-01","2018-02-01","2018-01-01","2017-11-01","2017-10-01","2017-09-01"],"defaultApiVersion":"2020-03-01","capabilities":"CrossResourceGroupResourceMove, CrossSubscriptionResourceMove, SupportsTags, SupportsLocation"},{"resourceType":"frontdoorOperationResults","locations":["global"],"apiVersions":["2022-05-01","2021-06-01","2020-11-01","2020-07-01","2020-05-01","2020-04-01","2020-01-01","2019-11-01","2019-10-01","2019-08-01","2019-05-01","2019-04-01","2019-03-01","2018-08-01"],"defaultApiVersion":"2020-07-01","capabilities":"None"},{"resourceType":"frontdoors","locations":["Central US EUAP","East US 2 EUAP","global","Central US","East US","East US 2","North Central US","South Central US","West US","North Europe","West Europe","East Asia","Southeast Asia","Japan East","Japan West","Brazil South","Australia East","Australia Southeast"],"apiVersions":["2021-06-01","2020-07-01","2020-05-01","2020-04-01","2020-01-01","2019-08-01","2019-05-01","2019-04-01","2018-08-01"],"defaultApiVersion":"2020-07-01","capabilities":"SupportsTags, SupportsLocation"},{"resourceType":"frontdoors/frontendEndpoints","locations":["Central US EUAP","East US 2 EUAP","global","Central US","East US","East US 2","North Central US","South Central US","West US","North Europe","West Europe","East Asia","Southeast Asia","Japan East","Japan West","Brazil South","Australia East","Australia Southeast"],"apiVersions":["2021-06-01","2020-07-01","2020-05-01","2020-04-01","2020-01-01","2019-08-01","2019-05-01","2019-04-01","2018-08-01"],"defaultApiVersion":"2020-07-01","capabilities":"None"},{"resourceType":"frontdoors/frontendEndpoints/customHttpsConfiguration","locations":["Central US EUAP","East US 2 EUAP","global","Central US","East US","East US 2","North Central US","South Central US","West US","North Europe","West Europe","East Asia","Southeast Asia","Japan East","Japan West","Brazil South","Australia East","Australia Southeast"],"apiVersions":["2021-06-01","2020-07-01","2020-05-01","2020-04-01","2020-01-01","2019-08-01","2019-05-01","2019-04-01","2018-08-01"],"defaultApiVersion":"2020-07-01","capabilities":"None"},{"resourceType":"frontdoorWebApplicationFirewallPolicies","locations":["East US 2 EUAP","global","Central US","East US","East US 2","North Central US","South Central US","West US","North Europe","West Europe","East Asia","Southeast Asia","Japan East","Japan West","Brazil South","Australia East","Australia Southeast"],"apiVersions":["2022-05-01","2020-11-01","2020-04-01","2019-10-01","2019-03-01","2018-08-01"],"defaultApiVersion":"2020-11-01","capabilities":"SupportsTags, SupportsLocation"},{"resourceType":"networkExperimentProfiles","locations":["Central US EUAP","East US 2 EUAP","global","Central US","East US","East US 2","North Central US","South Central US","West US","West US 2","North Europe","West Europe","East Asia","Southeast Asia","Japan East","Japan West","Brazil South","Australia East","Australia Southeast"],"apiVersions":["2019-11-01"],"defaultApiVersion":"2019-11-01","capabilities":"SupportsTags, SupportsLocation"}],"registrationState":"Registered","registrationPolicy":"RegistrationRequired"} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/UsingJsonSerializerTests.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/UsingJsonSerializerTests.cs new file mode 100644 index 000000000000..1f4deb927d53 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/UsingJsonSerializerTests.cs @@ -0,0 +1,84 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Text.Json; +using Azure.Developer.LoadTesting.Models; +using NUnit.Framework; + +namespace Azure.Core.Tests.Public.ModelSerializationTests +{ + public class UsingJsonSerializerTests + { + [Test] + public void CastToIUtf8JsonSerializable() + { + Test t = new Test(); + IUtf8JsonSerializable serializable = t; + Assert.IsNotNull(serializable); + } + + [TestCase(true)] + [TestCase(false)] + public void SerializeTest(bool ignoreReadonlyProperties) + { + string expected = "{"; + expected += "\"name\":\"Doggo\",\"isHungry\":false,"; +#if NETFRAMEWORK + expected += "\"weight\":1.1000000000000001,"; +#else + expected += "\"weight\":1.1,"; +#endif + expected += "\"foodConsumed\":[\"kibble\",\"egg\",\"peanut butter\"]}"; + var dog = new DogListProperty + { + Name = "Doggo", + IsHungry = false, + Weight = 1.1, + FoodConsumed = { "kibble", "egg", "peanut butter" }, + }; + var options = new JsonSerializerOptions() + { + IgnoreReadOnlyProperties = ignoreReadonlyProperties + }; + var actual = JsonSerializer.Serialize(dog, options); + Assert.AreEqual(expected, actual); + } + + [TestCase(true)] + [TestCase(false)] + public void DeserializeTest(bool ignoreReadonlyProperties) + { + string serviceResponse = "{\"latinName\":\"Animalia\",\"weight\":1.1,\"name\":\"Doggo\",\"isHungry\":false,\"foodConsumed\":[\"kibble\",\"egg\",\"peanut butter\"],\"numberOfLegs\":4}"; + + var options = new JsonSerializerOptions() + { + IgnoreReadOnlyProperties = ignoreReadonlyProperties + }; + var dog = JsonSerializer.Deserialize(serviceResponse, options); + + Assert.AreEqual("Doggo", dog.Name); + Assert.AreEqual(false, dog.IsHungry); + Assert.AreEqual(1.1, dog.Weight); + CollectionAssert.AreEquivalent(new List { "kibble", "egg", "peanut butter" }, dog.FoodConsumed); + Assert.AreEqual("Animalia", dog.LatinName); + + var additionalProperties = typeof(Animal).GetProperty("RawData", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(dog) as Dictionary; + Assert.IsNotNull(additionalProperties); + Assert.IsFalse(additionalProperties.ContainsKey("numberOfLegs")); + + string expected = "{"; + expected += "\"name\":\"Doggo\",\"isHungry\":false,"; +#if NETFRAMEWORK + expected += "\"weight\":1.1000000000000001,"; +#else + expected += "\"weight\":1.1,"; +#endif + expected += "\"foodConsumed\":[\"kibble\",\"egg\",\"peanut butter\"]}"; + + var actual = JsonSerializer.Serialize(dog, options); + Assert.AreEqual(expected, actual); + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/VerifyModels.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/VerifyModels.cs new file mode 100644 index 000000000000..3c8aea67019e --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/VerifyModels.cs @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using Azure.Core.Serialization; +using NUnit.Framework; + +namespace Azure.Core.Tests.Public.ModelSerializationTests +{ + internal class VerifyModels + { + public static void CheckAnimals(Animal x, Animal y, ModelSerializerOptions options) + { + VerifyProperties(x, y, options); + } + + private static void VerifyProperties(Animal x, Animal y, ModelSerializerOptions options) + { + if (options.Format == ModelSerializerFormat.Json) + Assert.That(x.LatinName, Is.EqualTo(y.LatinName)); + Assert.That(x.Name, Is.EqualTo(y.Name)); + Assert.That(x.Weight, Is.EqualTo(y.Weight)); + + if (options.Format == ModelSerializerFormat.Json) + { + var additionalPropertiesX = typeof(Animal).GetProperty("RawData", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(x) as Dictionary; + var additionalPropertiesY = typeof(Animal).GetProperty("RawData", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(y) as Dictionary; + + Assert.AreEqual(additionalPropertiesX.Count, additionalPropertiesY.Count); + + foreach (var additionalProperty in additionalPropertiesX) + { + Assert.IsTrue(additionalPropertiesY.ContainsKey(additionalProperty.Key)); + Assert.AreEqual(additionalProperty.Value.ToString(), additionalPropertiesY[additionalProperty.Key].ToString()); + } + foreach (var additionalProperty in additionalPropertiesY) + { + Assert.IsTrue(additionalPropertiesX.ContainsKey(additionalProperty.Key)); + Assert.AreEqual(additionalProperty.Value.ToString(), additionalPropertiesX[additionalProperty.Key].ToString()); + } + } + } + + public static void CheckCats(CatReadOnlyProperty x, CatReadOnlyProperty y, ModelSerializerOptions options) + { + VerifyProperties(x, y, options); + Assert.That(x.HasWhiskers, Is.EqualTo(y.HasWhiskers)); + } + + public static void CheckDogs(DogListProperty x, DogListProperty y, ModelSerializerOptions options) + { + VerifyProperties(x, y, options); + Assert.That(x.FoodConsumed, Is.EqualTo(y.FoodConsumed)); + } + + public static string NormalizeNewLines(string value) + { + return value + .Replace("\r\n", "\n") + .Replace("\n", Environment.NewLine); + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/Samples/SerializationSamples.cs b/sdk/core/Azure.Core/tests/public/Samples/SerializationSamples.cs new file mode 100644 index 000000000000..5a8488d3a7e8 --- /dev/null +++ b/sdk/core/Azure.Core/tests/public/Samples/SerializationSamples.cs @@ -0,0 +1,178 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.IO; +using System.Text; +using System.Text.Json; +using Azure.Core.Experimental.Tests; +using Azure.Core.Serialization; +using Azure.Core.TestFramework; +using Azure.Core.Tests.Public.ModelSerializationTests; +using Azure.Core.Tests.Public.ModelSerializationTests.Models; +using Newtonsoft.Json; +using NUnit.Framework; + +namespace Azure.Core.Samples +{ + public class SerializationSamples + { + [Test] + [Ignore("Only verifying that the sample builds")] + public void ExplicitCastSerialize() + { + #region Snippet:ExplicitCast_Serialize + PetStoreClient client = new PetStoreClient(new Uri("http://somewhere.com"), new MockCredential()); + DogListProperty dog = new DogListProperty("myPet"); + Response response = client.CreatePet("myPet", (RequestContent)dog); + var response2 = client.CreatePet("myPet", RequestContent.Create(dog)); + #endregion + } + + [Test] + [Ignore("Only verifying that the sample builds")] + public void ExplicitCastDeserialize() + { + #region Snippet:ExplicitCast_Deserialize + PetStoreClient client = new PetStoreClient(new Uri("http://somewhere.com"), new MockCredential()); + Response response = client.GetPet("myPet"); + DogListProperty dog = (DogListProperty)response; + Console.WriteLine(dog.IsHungry); + #endregion + } + + [Test] + [Ignore("Only verifying that the sample builds")] + public void NewtonSoftSerialize() + { + #region Snippet:NewtonSoft_Serialize + DogListProperty dog = new DogListProperty + { + Name = "Doggo", + IsHungry = true, + Weight = 1.1, + FoodConsumed = { "kibble", "egg", "peanut butter" }, + }; + ModelSerializerOptions options = new ModelSerializerOptions(); + options.Serializers.Add(typeof(DogListProperty), new NewtonsoftJsonObjectSerializer()); + + BinaryData data = ModelSerializer.Serialize(dog, options); + #endregion + } + + [Test] + [Ignore("Only verifying that the sample builds")] + public void NewtonSoftDeserialize() + { + #region Snippet:NewtonSoft_Deserialize + ModelSerializerOptions options = new ModelSerializerOptions(); + options.Serializers.Add(typeof(DogListProperty), new NewtonsoftJsonObjectSerializer()); + string json = @"[{""LatinName"":""Animalia"",""Weight"":1.1,""Name"":""Doggo"",""IsHungry"":false,""FoodConsumed"":[""kibble"",""egg"",""peanut butter""],""NumberOfLegs"":4}]"; + + DogListProperty dog = ModelSerializer.Deserialize(BinaryData.FromString(json), options); + #endregion + } + + [Test] + [Ignore("Only verifying that the sample builds")] + public void ModelConverterSerialize() + { + #region Snippet:ModelConverter_Serialize + DogListProperty dog = new DogListProperty + { + Name = "Doggo", + IsHungry = true, + Weight = 1.1, + FoodConsumed = { "kibble", "egg", "peanut butter" }, + }; + + JsonSerializerOptions options = new JsonSerializerOptions(); + options.Converters.Add(new ModelJsonConverter()); + + string json = System.Text.Json.JsonSerializer.Serialize(dog, options); + #endregion + } + + [Test] + [Ignore("Only verifying that the sample builds")] + public void ModelConverterDeserialize() + { + #region Snippet:ModelConverter_Deserialize + string json = @"[{""LatinName"":""Animalia"",""Weight"":1.1,""Name"":""Doggo"",""IsHungry"":false,""FoodConsumed"":[""kibble"",""egg"",""peanut butter""],""NumberOfLegs"":4}]"; + + JsonSerializerOptions options = new JsonSerializerOptions(); + options.Converters.Add(new ModelJsonConverter()); + + DogListProperty dog = System.Text.Json.JsonSerializer.Deserialize(json, options); + #endregion + } + + [Test] + [Ignore("Only verifying that the sample builds")] + public void BYOMWithNewtonsoftSerialize() + { + #region Snippet:BYOMWithNewtonsoftSerialize + Envelope envelope = new Envelope(); + envelope.ModelA = new CatReadOnlyProperty(); + envelope.ModelT = new ModelT { Name = "Fluffy", Age = 10 }; + ModelSerializerOptions options = new ModelSerializerOptions(); + options.Serializers.Add(typeof(ModelT), new NewtonsoftJsonObjectSerializer()); + BinaryData data = ModelSerializer.Serialize(envelope, options); + #endregion + } + + [Test] + [Ignore("Only verifying that the sample builds")] + public void BYOMWithNewtonsoftDeserialize() + { + #region Snippet:BYOMWithNewtonsoftDeserialize + string serviceResponse = + "{\"readOnlyProperty\":\"read\"," + + "\"modelA\":{\"name\":\"Cat\",\"isHungry\":false,\"weight\":2.5}," + + "\"modelT\":{\"Name\":\"hello\",\"Age\":1}" + + "}"; + + ModelSerializerOptions options = new ModelSerializerOptions(); + options.Serializers.Add(typeof(ModelT), new NewtonsoftJsonObjectSerializer()); + + Envelope model = ModelSerializer.Deserialize>(new BinaryData(Encoding.UTF8.GetBytes(serviceResponse)), options: options); + #endregion + } + + [Test] + [Ignore("Only verifying that the sample builds")] + public void IModelSerializableSerialize() + { + #region Snippet:ModelSerializer_IModelSerializable_Serialize + XmlModelForCombinedInterface xmlModel = new XmlModelForCombinedInterface("Color", "Red", "ReadOnly"); + var data = ModelSerializer.Serialize(xmlModel); + string xmlString = data.ToString(); + + JsonModelForCombinedInterface jsonModel = new JsonModelForCombinedInterface("Color", "Red", "ReadOnly"); + data = ModelSerializer.Serialize(jsonModel); + string jsonString = data.ToString(); + #endregion + } + + [Test] + [Ignore("Only verifying that the sample builds")] + public void IModelSerializableDeserialize() + { + #region Snippet:ModelSerializer_IModelSerializable_Deserialize + string xmlResponse = "ColorRed"; + XmlModelForCombinedInterface xmlModel = ModelSerializer.Deserialize(new BinaryData(Encoding.UTF8.GetBytes(xmlResponse))); + + string jsonResponse = "{\"key\":\"Color\",\"value\":\"Red\",\"readOnlyProperty\":\"ReadOnly\",\"x\":\"extra\"}"; + JsonModelForCombinedInterface jsonModel = ModelSerializer.Deserialize(new BinaryData(Encoding.UTF8.GetBytes(jsonResponse))); + #endregion + } + + #region Snippet:Example_Model + private class ModelT + { + public string Name { get; set; } + public int Age { get; set; } + } + #endregion + } +} diff --git a/sdk/loadtestservice/Azure.Developer.LoadTesting/Azure.Developer.LoadTesting.sln b/sdk/loadtestservice/Azure.Developer.LoadTesting/Azure.Developer.LoadTesting.sln index 606ba56f6d8d..14c4dd1428f6 100644 --- a/sdk/loadtestservice/Azure.Developer.LoadTesting/Azure.Developer.LoadTesting.sln +++ b/sdk/loadtestservice/Azure.Developer.LoadTesting/Azure.Developer.LoadTesting.sln @@ -13,6 +13,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Developer.LoadTesting EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Test.Perf", "..\..\..\common\Perf\Azure.Test.Perf\Azure.Test.Perf.csproj", "{1FEEF7F8-FE15-4A10-9E00-13F9DBC07D4B}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Core", "..\..\core\Azure.Core\src\Azure.Core.csproj", "{D2EFFAEB-5CFE-4C50-AC4C-A58627F6653D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -39,6 +41,10 @@ Global {1FEEF7F8-FE15-4A10-9E00-13F9DBC07D4B}.Debug|Any CPU.Build.0 = Debug|Any CPU {1FEEF7F8-FE15-4A10-9E00-13F9DBC07D4B}.Release|Any CPU.ActiveCfg = Release|Any CPU {1FEEF7F8-FE15-4A10-9E00-13F9DBC07D4B}.Release|Any CPU.Build.0 = Release|Any CPU + {D2EFFAEB-5CFE-4C50-AC4C-A58627F6653D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D2EFFAEB-5CFE-4C50-AC4C-A58627F6653D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D2EFFAEB-5CFE-4C50-AC4C-A58627F6653D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D2EFFAEB-5CFE-4C50-AC4C-A58627F6653D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/sdk/loadtestservice/Azure.Developer.LoadTesting/api/Azure.Developer.LoadTesting.netstandard2.0.cs b/sdk/loadtestservice/Azure.Developer.LoadTesting/api/Azure.Developer.LoadTesting.netstandard2.0.cs index 9b6fafbf3afe..0728e5cae29a 100644 --- a/sdk/loadtestservice/Azure.Developer.LoadTesting/api/Azure.Developer.LoadTesting.netstandard2.0.cs +++ b/sdk/loadtestservice/Azure.Developer.LoadTesting/api/Azure.Developer.LoadTesting.netstandard2.0.cs @@ -22,7 +22,9 @@ public LoadTestAdministrationClient(System.Uri endpoint, Azure.Core.TokenCredent public virtual System.Threading.Tasks.Task CreateOrUpdateAppComponentsAsync(string testId, Azure.Core.RequestContent content, Azure.RequestContext context = null) { throw null; } public virtual Azure.Response CreateOrUpdateServerMetricsConfig(string testId, Azure.Core.RequestContent content, Azure.RequestContext context = null) { throw null; } public virtual System.Threading.Tasks.Task CreateOrUpdateServerMetricsConfigAsync(string testId, Azure.Core.RequestContent content, Azure.RequestContext context = null) { throw null; } + public virtual Azure.Response CreateOrUpdateTest(Azure.Developer.LoadTesting.Models.Test test, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Response CreateOrUpdateTest(string testId, Azure.Core.RequestContent content, Azure.RequestContext context = null) { throw null; } + public virtual System.Threading.Tasks.Task> CreateOrUpdateTestAsync(Azure.Developer.LoadTesting.Models.Test test, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task CreateOrUpdateTestAsync(string testId, Azure.Core.RequestContent content, Azure.RequestContext context = null) { throw null; } public virtual Azure.Response DeleteTest(string testId, Azure.RequestContext context = null) { throw null; } public virtual System.Threading.Tasks.Task DeleteTestAsync(string testId, Azure.RequestContext context = null) { throw null; } @@ -99,6 +101,252 @@ public TestRunResultOperation(string testRunId, Azure.Developer.LoadTesting.Load public override System.Threading.Tasks.ValueTask UpdateStatusAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } } } +namespace Azure.Developer.LoadTesting.Models +{ + public partial class CertificateMetadata + { + public CertificateMetadata() { } + public string Name { get { throw null; } set { } } + public Azure.Developer.LoadTesting.Models.CertificateType? Type { get { throw null; } set { } } + public string Value { get { throw null; } set { } } + } + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] + public readonly partial struct CertificateType : System.IEquatable + { + private readonly object _dummy; + private readonly int _dummyPrimitive; + public CertificateType(string value) { throw null; } + public static Azure.Developer.LoadTesting.Models.CertificateType AKVCertURI { get { throw null; } } + public bool Equals(Azure.Developer.LoadTesting.Models.CertificateType other) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override bool Equals(object obj) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override int GetHashCode() { throw null; } + public static bool operator ==(Azure.Developer.LoadTesting.Models.CertificateType left, Azure.Developer.LoadTesting.Models.CertificateType right) { throw null; } + public static implicit operator Azure.Developer.LoadTesting.Models.CertificateType (string value) { throw null; } + public static bool operator !=(Azure.Developer.LoadTesting.Models.CertificateType left, Azure.Developer.LoadTesting.Models.CertificateType right) { throw null; } + public override string ToString() { throw null; } + } + public partial class FileInfo + { + internal FileInfo() { } + public System.DateTimeOffset? ExpireDateTime { get { throw null; } } + public string FileName { get { throw null; } } + public Azure.Developer.LoadTesting.Models.FileType? FileType { get { throw null; } } + public System.Uri Url { get { throw null; } } + public string ValidationFailureDetails { get { throw null; } } + public Azure.Developer.LoadTesting.Models.FileStatus? ValidationStatus { get { throw null; } } + } + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] + public readonly partial struct FileStatus : System.IEquatable + { + private readonly object _dummy; + private readonly int _dummyPrimitive; + public FileStatus(string value) { throw null; } + public static Azure.Developer.LoadTesting.Models.FileStatus NOTValidated { get { throw null; } } + public static Azure.Developer.LoadTesting.Models.FileStatus ValidationFailure { get { throw null; } } + public static Azure.Developer.LoadTesting.Models.FileStatus ValidationInitiated { get { throw null; } } + public static Azure.Developer.LoadTesting.Models.FileStatus ValidationNOTRequired { get { throw null; } } + public static Azure.Developer.LoadTesting.Models.FileStatus ValidationSuccess { get { throw null; } } + public bool Equals(Azure.Developer.LoadTesting.Models.FileStatus other) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override bool Equals(object obj) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override int GetHashCode() { throw null; } + public static bool operator ==(Azure.Developer.LoadTesting.Models.FileStatus left, Azure.Developer.LoadTesting.Models.FileStatus right) { throw null; } + public static implicit operator Azure.Developer.LoadTesting.Models.FileStatus (string value) { throw null; } + public static bool operator !=(Azure.Developer.LoadTesting.Models.FileStatus left, Azure.Developer.LoadTesting.Models.FileStatus right) { throw null; } + public override string ToString() { throw null; } + } + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] + public readonly partial struct FileType : System.IEquatable + { + private readonly object _dummy; + private readonly int _dummyPrimitive; + public FileType(string value) { throw null; } + public static Azure.Developer.LoadTesting.Models.FileType AdditionalArtifacts { get { throw null; } } + public static Azure.Developer.LoadTesting.Models.FileType JMXFile { get { throw null; } } + public static Azure.Developer.LoadTesting.Models.FileType UserProperties { get { throw null; } } + public bool Equals(Azure.Developer.LoadTesting.Models.FileType other) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override bool Equals(object obj) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override int GetHashCode() { throw null; } + public static bool operator ==(Azure.Developer.LoadTesting.Models.FileType left, Azure.Developer.LoadTesting.Models.FileType right) { throw null; } + public static implicit operator Azure.Developer.LoadTesting.Models.FileType (string value) { throw null; } + public static bool operator !=(Azure.Developer.LoadTesting.Models.FileType left, Azure.Developer.LoadTesting.Models.FileType right) { throw null; } + public override string ToString() { throw null; } + } + public partial class LoadTestConfiguration + { + public LoadTestConfiguration() { } + public int? EngineInstances { get { throw null; } set { } } + public Azure.Developer.LoadTesting.Models.OptionalLoadTestConfig OptionalLoadTestConfig { get { throw null; } set { } } + public bool? QuickStartTest { get { throw null; } set { } } + public bool? SplitAllCSVs { get { throw null; } set { } } + } + public partial class OptionalLoadTestConfig + { + public OptionalLoadTestConfig() { } + public int? Duration { get { throw null; } set { } } + public string EndpointUrl { get { throw null; } set { } } + public int? RampUpTime { get { throw null; } set { } } + public int? VirtualUsers { get { throw null; } set { } } + } + public partial class PassFailCriteria + { + public PassFailCriteria() { } + public System.Collections.Generic.IDictionary PassFailMetrics { get { throw null; } } + } + public partial class PassFailMetric + { + public PassFailMetric() { } + public Azure.Developer.LoadTesting.Models.PFAction? Action { get { throw null; } set { } } + public double? ActualValue { get { throw null; } } + public Azure.Developer.LoadTesting.Models.PFAgFunc? Aggregate { get { throw null; } set { } } + public Azure.Developer.LoadTesting.Models.PFMetrics? ClientMetric { get { throw null; } set { } } + public string Condition { get { throw null; } set { } } + public string RequestName { get { throw null; } set { } } + public Azure.Developer.LoadTesting.Models.PFResult? Result { get { throw null; } } + public double? Value { get { throw null; } set { } } + } + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] + public readonly partial struct PFAction : System.IEquatable + { + private readonly object _dummy; + private readonly int _dummyPrimitive; + public PFAction(string value) { throw null; } + public static Azure.Developer.LoadTesting.Models.PFAction Continue { get { throw null; } } + public static Azure.Developer.LoadTesting.Models.PFAction Stop { get { throw null; } } + public bool Equals(Azure.Developer.LoadTesting.Models.PFAction other) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override bool Equals(object obj) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override int GetHashCode() { throw null; } + public static bool operator ==(Azure.Developer.LoadTesting.Models.PFAction left, Azure.Developer.LoadTesting.Models.PFAction right) { throw null; } + public static implicit operator Azure.Developer.LoadTesting.Models.PFAction (string value) { throw null; } + public static bool operator !=(Azure.Developer.LoadTesting.Models.PFAction left, Azure.Developer.LoadTesting.Models.PFAction right) { throw null; } + public override string ToString() { throw null; } + } + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] + public readonly partial struct PFAgFunc : System.IEquatable + { + private readonly object _dummy; + private readonly int _dummyPrimitive; + public PFAgFunc(string value) { throw null; } + public static Azure.Developer.LoadTesting.Models.PFAgFunc Avg { get { throw null; } } + public static Azure.Developer.LoadTesting.Models.PFAgFunc Count { get { throw null; } } + public static Azure.Developer.LoadTesting.Models.PFAgFunc Max { get { throw null; } } + public static Azure.Developer.LoadTesting.Models.PFAgFunc Min { get { throw null; } } + public static Azure.Developer.LoadTesting.Models.PFAgFunc P50 { get { throw null; } } + public static Azure.Developer.LoadTesting.Models.PFAgFunc P90 { get { throw null; } } + public static Azure.Developer.LoadTesting.Models.PFAgFunc P95 { get { throw null; } } + public static Azure.Developer.LoadTesting.Models.PFAgFunc P99 { get { throw null; } } + public static Azure.Developer.LoadTesting.Models.PFAgFunc Percentage { get { throw null; } } + public bool Equals(Azure.Developer.LoadTesting.Models.PFAgFunc other) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override bool Equals(object obj) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override int GetHashCode() { throw null; } + public static bool operator ==(Azure.Developer.LoadTesting.Models.PFAgFunc left, Azure.Developer.LoadTesting.Models.PFAgFunc right) { throw null; } + public static implicit operator Azure.Developer.LoadTesting.Models.PFAgFunc (string value) { throw null; } + public static bool operator !=(Azure.Developer.LoadTesting.Models.PFAgFunc left, Azure.Developer.LoadTesting.Models.PFAgFunc right) { throw null; } + public override string ToString() { throw null; } + } + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] + public readonly partial struct PFMetrics : System.IEquatable + { + private readonly object _dummy; + private readonly int _dummyPrimitive; + public PFMetrics(string value) { throw null; } + public static Azure.Developer.LoadTesting.Models.PFMetrics Error { get { throw null; } } + public static Azure.Developer.LoadTesting.Models.PFMetrics Latency { get { throw null; } } + public static Azure.Developer.LoadTesting.Models.PFMetrics Requests { get { throw null; } } + public static Azure.Developer.LoadTesting.Models.PFMetrics RequestsPerSec { get { throw null; } } + public static Azure.Developer.LoadTesting.Models.PFMetrics ResponseTimeMs { get { throw null; } } + public bool Equals(Azure.Developer.LoadTesting.Models.PFMetrics other) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override bool Equals(object obj) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override int GetHashCode() { throw null; } + public static bool operator ==(Azure.Developer.LoadTesting.Models.PFMetrics left, Azure.Developer.LoadTesting.Models.PFMetrics right) { throw null; } + public static implicit operator Azure.Developer.LoadTesting.Models.PFMetrics (string value) { throw null; } + public static bool operator !=(Azure.Developer.LoadTesting.Models.PFMetrics left, Azure.Developer.LoadTesting.Models.PFMetrics right) { throw null; } + public override string ToString() { throw null; } + } + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] + public readonly partial struct PFResult : System.IEquatable + { + private readonly object _dummy; + private readonly int _dummyPrimitive; + public PFResult(string value) { throw null; } + public static Azure.Developer.LoadTesting.Models.PFResult Failed { get { throw null; } } + public static Azure.Developer.LoadTesting.Models.PFResult Passed { get { throw null; } } + public static Azure.Developer.LoadTesting.Models.PFResult Undetermined { get { throw null; } } + public bool Equals(Azure.Developer.LoadTesting.Models.PFResult other) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override bool Equals(object obj) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override int GetHashCode() { throw null; } + public static bool operator ==(Azure.Developer.LoadTesting.Models.PFResult left, Azure.Developer.LoadTesting.Models.PFResult right) { throw null; } + public static implicit operator Azure.Developer.LoadTesting.Models.PFResult (string value) { throw null; } + public static bool operator !=(Azure.Developer.LoadTesting.Models.PFResult left, Azure.Developer.LoadTesting.Models.PFResult right) { throw null; } + public override string ToString() { throw null; } + } + public partial class Secret + { + public Secret() { } + public Azure.Developer.LoadTesting.Models.SecretType? Type { get { throw null; } set { } } + public string Value { get { throw null; } set { } } + } + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] + public readonly partial struct SecretType : System.IEquatable + { + private readonly object _dummy; + private readonly int _dummyPrimitive; + public SecretType(string value) { throw null; } + public static Azure.Developer.LoadTesting.Models.SecretType AKVSecretURI { get { throw null; } } + public static Azure.Developer.LoadTesting.Models.SecretType SecretValue { get { throw null; } } + public bool Equals(Azure.Developer.LoadTesting.Models.SecretType other) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override bool Equals(object obj) { throw null; } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public override int GetHashCode() { throw null; } + public static bool operator ==(Azure.Developer.LoadTesting.Models.SecretType left, Azure.Developer.LoadTesting.Models.SecretType right) { throw null; } + public static implicit operator Azure.Developer.LoadTesting.Models.SecretType (string value) { throw null; } + public static bool operator !=(Azure.Developer.LoadTesting.Models.SecretType left, Azure.Developer.LoadTesting.Models.SecretType right) { throw null; } + public override string ToString() { throw null; } + } + public partial class Test + { + public Test() { } + public Azure.Developer.LoadTesting.Models.CertificateMetadata Certificate { get { throw null; } set { } } + public string CreatedBy { get { throw null; } } + public System.DateTimeOffset? CreatedDateTime { get { throw null; } } + public string Description { get { throw null; } set { } } + public string DisplayName { get { throw null; } set { } } + public System.Collections.Generic.IDictionary EnvironmentVariables { get { throw null; } } + public Azure.Developer.LoadTesting.Models.TestInputArtifacts InputArtifacts { get { throw null; } } + public string KeyvaultReferenceIdentityId { get { throw null; } set { } } + public string KeyvaultReferenceIdentityType { get { throw null; } set { } } + public string LastModifiedBy { get { throw null; } } + public System.DateTimeOffset? LastModifiedDateTime { get { throw null; } } + public Azure.Developer.LoadTesting.Models.LoadTestConfiguration LoadTestConfiguration { get { throw null; } set { } } + public Azure.Developer.LoadTesting.Models.PassFailCriteria PassFailCriteria { get { throw null; } set { } } + public System.Collections.Generic.IDictionary Secrets { get { throw null; } } + public string SubnetId { get { throw null; } set { } } + public string TestId { get { throw null; } set { } } + } + public partial class TestInputArtifacts + { + internal TestInputArtifacts() { } + public System.Collections.Generic.IReadOnlyList AdditionalFileInfo { get { throw null; } } + public Azure.Developer.LoadTesting.Models.FileInfo ConfigFileInfo { get { throw null; } } + public Azure.Developer.LoadTesting.Models.FileInfo InputArtifactsZipFileInfo { get { throw null; } } + public Azure.Developer.LoadTesting.Models.FileInfo TestScriptFileInfo { get { throw null; } } + public Azure.Developer.LoadTesting.Models.FileInfo UserPropFileInfo { get { throw null; } } + } +} namespace Microsoft.Extensions.Azure { public static partial class AzureLoadTestingClientBuilderExtensions diff --git a/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Azure.Developer.LoadTesting.csproj b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Azure.Developer.LoadTesting.csproj index 7909176cc8ce..a1c4d75675eb 100644 --- a/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Azure.Developer.LoadTesting.csproj +++ b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Azure.Developer.LoadTesting.csproj @@ -1,4 +1,4 @@ - + This is the LoadTestService client library for developing .NET applications with rich experience. Azure SDK Code Generation LoadTestService for Azure Data Plane @@ -8,6 +8,7 @@ Azure LoadTestService $(RequiredTargetFrameworks) true + AZC0012;AZC0001 @@ -15,9 +16,23 @@ + + + + + + + + + + + + + - + + diff --git a/sdk/loadtestservice/Azure.Developer.LoadTesting/src/LoadTestAdministrationClient.cs b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/LoadTestAdministrationClient.cs index 0a8725651190..bae205f1fcf5 100644 --- a/sdk/loadtestservice/Azure.Developer.LoadTesting/src/LoadTestAdministrationClient.cs +++ b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/LoadTestAdministrationClient.cs @@ -2,9 +2,13 @@ // Licensed under the MIT License. using System; +using System.IO; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Azure.Core; +using Azure.Core.Serialization; +using Azure.Developer.LoadTesting.Models; namespace Azure.Developer.LoadTesting { @@ -127,5 +131,62 @@ public virtual Pageable GetTests(string orderby = null, string searc HttpMessage NextPageRequest(int? pageSizeHint, string nextLink) => CreateGetTestsNextPageRequest(nextLink, orderby, search, lastModifiedStartTime, lastModifiedEndTime, pageSizeHint, context); return PageableHelpers.CreatePageable(FirstPageRequest, NextPageRequest, e => BinaryData.FromString(e.GetRawText()), ClientDiagnostics, _pipeline, "LoadTestAdministrationClient.GetTests", "value", "nextLink", context); } + + /// + /// + /// + /// + /// + public virtual async Task> CreateOrUpdateTestAsync(Test test, CancellationToken cancellationToken = default) + { + if (test is not IJsonModelSerializable serializable) + { + throw new InvalidCastException("model is not serializable"); + } + + using Stream stream = new MemoryStream(); + using (Utf8JsonWriter writer = new(stream)) + { + serializable.Serialize(writer, new ModelSerializerOptions("P")); + } + + stream.Position = 0; + RequestContent content = RequestContent.Create(stream); + + // TODO: was there a good way to get RequestContext without creating it new? + RequestContext context = new() { CancellationToken = cancellationToken }; + + Response response = await CreateOrUpdateTestAsync(test.TestId, content, context).ConfigureAwait(false); + + return Response.FromValue((Test)response, response); + } + + /// + /// + /// + /// + /// + public virtual Response CreateOrUpdateTest(Test test, CancellationToken cancellationToken = default) + { + if (test is not IJsonModelSerializable serializable) + { + throw new InvalidCastException("model is not serializable"); + } + + using Stream stream = new MemoryStream(); + using (Utf8JsonWriter writer = new(stream)) + { + serializable.Serialize(writer, new ModelSerializerOptions("P")); + } + + stream.Position = 0; + RequestContent content = RequestContent.Create(stream); + + RequestContext context = new() { CancellationToken = cancellationToken }; + + Response response = CreateOrUpdateTest(test.TestId, content, context); + + return Response.FromValue((Test)response, response); + } } } diff --git a/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/CertificateMetadata.Serialization.cs b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/CertificateMetadata.Serialization.cs new file mode 100644 index 000000000000..761004046507 --- /dev/null +++ b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/CertificateMetadata.Serialization.cs @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Text.Json; +using Azure.Core; +using Azure.Core.Json; +using Azure.Core.Serialization; + +namespace Azure.Developer.LoadTesting.Models +{ + public partial class CertificateMetadata : IUtf8JsonSerializable, IJsonModelSerializable + { + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) + { + writer.WriteStartObject(); + if (Optional.IsDefined(Value)) + { + writer.WritePropertyName("value"u8); + writer.WriteStringValue(Value); + } + if (Optional.IsDefined(Type)) + { + writer.WritePropertyName("type"u8); + writer.WriteStringValue(Type.Value.ToString()); + } + if (Optional.IsDefined(Name)) + { + writer.WritePropertyName("name"u8); + writer.WriteStringValue(Name); + } + writer.WriteEndObject(); + } + + void IJsonModelSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + if (options.Format == "P") + { + _element.WriteTo(writer, 'P'); + return; + } + + ((IUtf8JsonSerializable)this).Write(writer); + } + + object IJsonModelSerializable.Deserialize(ref Utf8JsonReader reader, ModelSerializerOptions options) + { + JsonDocument doc = JsonDocument.ParseValue(ref reader); + MutableJsonDocument mdoc = new MutableJsonDocument(doc, new JsonSerializerOptions()); + return new CertificateMetadata(mdoc.RootElement); + } + + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + { + MutableJsonDocument jsonDocument = MutableJsonDocument.Parse(data); + return new CertificateMetadata(jsonDocument.RootElement); + } + + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) => ModelSerializerHelper.SerializeToBinaryData(writer => ((IJsonModelSerializable)this).Serialize(writer, options)); + } +} diff --git a/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/CertificateMetadata.cs b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/CertificateMetadata.cs new file mode 100644 index 000000000000..d4ab6d5bcf2d --- /dev/null +++ b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/CertificateMetadata.cs @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using Azure.Core.Json; + +namespace Azure.Developer.LoadTesting.Models +{ + /// Certificates metadata. + public partial class CertificateMetadata + { + private MutableJsonElement _element; + + /// Initializes a new instance of CertificateMetadata. + public CertificateMetadata() + { + _element = MutableJsonDocument.Parse(MutableJsonDocument.EmptyJson).RootElement; + } + + internal CertificateMetadata(MutableJsonElement element) + { + _element = element; + } + + /// The value of the certificate for respective type. + public string Value + { + get => _element.GetProperty("value").GetString(); + set => _element.SetProperty("value", value); + } + + /// Type of certificate. + public CertificateType? Type + { + get => _element.GetProperty("type").GetString(); + set => _element.SetProperty("type", value); + } + + /// Name of the certificate. + public string Name + { + get => _element.GetProperty("name").GetString(); + set => _element.SetProperty("name", value); + } + } +} diff --git a/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/CertificateType.cs b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/CertificateType.cs new file mode 100644 index 000000000000..a6bd365c443f --- /dev/null +++ b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/CertificateType.cs @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.ComponentModel; + +namespace Azure.Developer.LoadTesting.Models +{ + /// Type of certificate. + public readonly partial struct CertificateType : IEquatable + { + private readonly string _value; + + /// Initializes a new instance of . + /// is null. + public CertificateType(string value) + { + _value = value ?? throw new ArgumentNullException(nameof(value)); + } + + private const string AKVCertURIValue = "AKV_CERT_URI"; + + /// If the certificate is stored in an Azure Key Vault. + public static CertificateType AKVCertURI { get; } = new CertificateType(AKVCertURIValue); + /// Determines if two values are the same. + public static bool operator ==(CertificateType left, CertificateType right) => left.Equals(right); + /// Determines if two values are not the same. + public static bool operator !=(CertificateType left, CertificateType right) => !left.Equals(right); + /// Converts a string to a . + public static implicit operator CertificateType(string value) => new CertificateType(value); + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) => obj is CertificateType other && Equals(other); + /// + public bool Equals(CertificateType other) => string.Equals(_value, other._value, StringComparison.InvariantCultureIgnoreCase); + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() => _value?.GetHashCode() ?? 0; + /// + public override string ToString() => _value; + } +} diff --git a/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/FileInfo.Serialization.cs b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/FileInfo.Serialization.cs new file mode 100644 index 000000000000..de0ae62bc18d --- /dev/null +++ b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/FileInfo.Serialization.cs @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Text.Json; +using Azure.Core; +using Azure.Core.Json; +using Azure.Core.Serialization; + +namespace Azure.Developer.LoadTesting.Models +{ + public partial class FileInfo : IUtf8JsonSerializable, IJsonModelSerializable + { + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) + { + throw new NotImplementedException(); + } + + void IJsonModelSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + if (options.Format == "P") + { + _element.WriteTo(writer, 'P'); + return; + } + + ((IUtf8JsonSerializable)this).Write(writer); + } + + object IJsonModelSerializable.Deserialize(ref Utf8JsonReader reader, ModelSerializerOptions options) + { + JsonDocument doc = JsonDocument.ParseValue(ref reader); + MutableJsonDocument mdoc = new MutableJsonDocument(doc, new JsonSerializerOptions()); + return new FileInfo(mdoc.RootElement); + } + + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + { + MutableJsonDocument jsonDocument = MutableJsonDocument.Parse(data); + return new FileInfo(jsonDocument.RootElement); + } + + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) => ModelSerializerHelper.SerializeToBinaryData(writer => ((IJsonModelSerializable)this).Serialize(writer, options)); + } +} diff --git a/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/FileInfo.cs b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/FileInfo.cs new file mode 100644 index 000000000000..ab3e920404f9 --- /dev/null +++ b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/FileInfo.cs @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using Azure.Core.Json; + +namespace Azure.Developer.LoadTesting.Models +{ + /// File info. + public partial class FileInfo + { + private MutableJsonElement _element; + + /// Initializes a new instance of FileInfo. + internal FileInfo() + { + _element = MutableJsonDocument.Parse(MutableJsonDocument.EmptyJson).RootElement; + } + + internal FileInfo(MutableJsonElement element) + { + _element = element; + } + + /// File URL. + public Uri Url + { + get => _element.GetProperty("url").ConvertTo(); + } + + /// Name of the file. + public string FileName + { + get => _element.GetProperty("fileName").GetString(); + } + + /// File type. + public FileType? FileType + { + get => _element.GetProperty("fileType").GetString(); + } + + /// Expiry time of the file (ISO 8601 literal format). + public DateTimeOffset? ExpireDateTime + { + get => _element.GetProperty("expireDateTime").GetDateTimeOffset(); + } + + /// Validation status of the file. + public FileStatus? ValidationStatus + { + get => _element.GetProperty("validationStatus").GetString(); + } + + /// Validation failure error details. + public string ValidationFailureDetails + { + get => _element.GetProperty("validationFailureDetails").GetString(); + } + } +} diff --git a/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/FileStatus.cs b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/FileStatus.cs new file mode 100644 index 000000000000..8596a7328469 --- /dev/null +++ b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/FileStatus.cs @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.ComponentModel; + +namespace Azure.Developer.LoadTesting.Models +{ + /// Validation status of the file. + public readonly partial struct FileStatus : IEquatable + { + private readonly string _value; + + /// Initializes a new instance of . + /// is null. + public FileStatus(string value) + { + _value = value ?? throw new ArgumentNullException(nameof(value)); + } + + private const string NOTValidatedValue = "NOT_VALIDATED"; + private const string ValidationSuccessValue = "VALIDATION_SUCCESS"; + private const string ValidationFailureValue = "VALIDATION_FAILURE"; + private const string ValidationInitiatedValue = "VALIDATION_INITIATED"; + private const string ValidationNOTRequiredValue = "VALIDATION_NOT_REQUIRED"; + + /// File is not validated. + public static FileStatus NOTValidated { get; } = new FileStatus(NOTValidatedValue); + /// File is validated. + public static FileStatus ValidationSuccess { get; } = new FileStatus(ValidationSuccessValue); + /// File validation is failed. + public static FileStatus ValidationFailure { get; } = new FileStatus(ValidationFailureValue); + /// File validation is in progress. + public static FileStatus ValidationInitiated { get; } = new FileStatus(ValidationInitiatedValue); + /// Validation is not required. + public static FileStatus ValidationNOTRequired { get; } = new FileStatus(ValidationNOTRequiredValue); + /// Determines if two values are the same. + public static bool operator ==(FileStatus left, FileStatus right) => left.Equals(right); + /// Determines if two values are not the same. + public static bool operator !=(FileStatus left, FileStatus right) => !left.Equals(right); + /// Converts a string to a . + public static implicit operator FileStatus(string value) => new FileStatus(value); + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) => obj is FileStatus other && Equals(other); + /// + public bool Equals(FileStatus other) => string.Equals(_value, other._value, StringComparison.InvariantCultureIgnoreCase); + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() => _value?.GetHashCode() ?? 0; + /// + public override string ToString() => _value; + } +} diff --git a/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/FileType.cs b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/FileType.cs new file mode 100644 index 000000000000..5a84716042bb --- /dev/null +++ b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/FileType.cs @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.ComponentModel; + +namespace Azure.Developer.LoadTesting.Models +{ + /// File type. + public readonly partial struct FileType : IEquatable + { + private readonly string _value; + + /// Initializes a new instance of . + /// is null. + public FileType(string value) + { + _value = value ?? throw new ArgumentNullException(nameof(value)); + } + + private const string JMXFileValue = "JMX_FILE"; + private const string UserPropertiesValue = "USER_PROPERTIES"; + private const string AdditionalArtifactsValue = "ADDITIONAL_ARTIFACTS"; + + /// If file is jmx script. + public static FileType JMXFile { get; } = new FileType(JMXFileValue); + /// If file is user properties. + public static FileType UserProperties { get; } = new FileType(UserPropertiesValue); + /// If file is not any of other supported type. + public static FileType AdditionalArtifacts { get; } = new FileType(AdditionalArtifactsValue); + /// Determines if two values are the same. + public static bool operator ==(FileType left, FileType right) => left.Equals(right); + /// Determines if two values are not the same. + public static bool operator !=(FileType left, FileType right) => !left.Equals(right); + /// Converts a string to a . + public static implicit operator FileType(string value) => new FileType(value); + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) => obj is FileType other && Equals(other); + /// + public bool Equals(FileType other) => string.Equals(_value, other._value, StringComparison.InvariantCultureIgnoreCase); + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() => _value?.GetHashCode() ?? 0; + /// + public override string ToString() => _value; + } +} diff --git a/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/LoadTestConfiguration.Serialization.cs b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/LoadTestConfiguration.Serialization.cs new file mode 100644 index 000000000000..e9a132ffe662 --- /dev/null +++ b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/LoadTestConfiguration.Serialization.cs @@ -0,0 +1,70 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Text.Json; +using Azure.Core; +using Azure.Core.Json; +using Azure.Core.Serialization; + +namespace Azure.Developer.LoadTesting.Models +{ + public partial class LoadTestConfiguration : IUtf8JsonSerializable, IJsonModelSerializable + { + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) + { + writer.WriteStartObject(); + if (Optional.IsDefined(EngineInstances)) + { + writer.WritePropertyName("engineInstances"u8); + writer.WriteNumberValue(EngineInstances.Value); + } + if (Optional.IsDefined(SplitAllCSVs)) + { + writer.WritePropertyName("splitAllCSVs"u8); + writer.WriteBooleanValue(SplitAllCSVs.Value); + } + if (Optional.IsDefined(QuickStartTest)) + { + writer.WritePropertyName("quickStartTest"u8); + writer.WriteBooleanValue(QuickStartTest.Value); + } + if (Optional.IsDefined(OptionalLoadTestConfig)) + { + writer.WritePropertyName("optionalLoadTestConfig"u8); + writer.WriteObjectValue(OptionalLoadTestConfig); + } + writer.WriteEndObject(); + } + + void IJsonModelSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + if (options.Format == "P") + { + _element.WriteTo(writer, 'P'); + return; + } + + ((IUtf8JsonSerializable)this).Write(writer); + } + + object IJsonModelSerializable.Deserialize(ref Utf8JsonReader reader, ModelSerializerOptions options) + { + JsonDocument doc = JsonDocument.ParseValue(ref reader); + MutableJsonDocument mdoc = new MutableJsonDocument(doc, new JsonSerializerOptions()); + return new LoadTestConfiguration(mdoc.RootElement); + } + + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + { + MutableJsonDocument jsonDocument = MutableJsonDocument.Parse(data); + return new LoadTestConfiguration(jsonDocument.RootElement); + } + + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) => ModelSerializerHelper.SerializeToBinaryData(writer => ((IJsonModelSerializable)this).Serialize(writer, options)); + } +} diff --git a/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/LoadTestConfiguration.cs b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/LoadTestConfiguration.cs new file mode 100644 index 000000000000..f9b72cd7a512 --- /dev/null +++ b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/LoadTestConfiguration.cs @@ -0,0 +1,101 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Text.Json; +using System.Xml.Linq; +using Azure.Core.Json; + +namespace Azure.Developer.LoadTesting.Models +{ + /// The load test configuration. + public partial class LoadTestConfiguration + { + private MutableJsonElement _element; + + /// Initializes a new instance of LoadTestConfiguration. + public LoadTestConfiguration() + { + _element = MutableJsonDocument.Parse(MutableJsonDocument.EmptyJson).RootElement; + } + + internal LoadTestConfiguration(MutableJsonElement element) + { + _element = element; + } + + /// The number of engine instances to execute load test. Supported values are in range of 1-45. Required for creating a new test. + public int? EngineInstances + { + get + { + // TODO: encapsulate this boilerplate code for handling a nullable value type + if (!_element.TryGetProperty("engineInstances", out MutableJsonElement value)) + { + return null; + } + + if (!value.TryGetInt32(out int i)) + { + return null; + } + + return i; + } + + set => _element.SetProperty("engineInstances", value); + } + + /// If false, Azure Load Testing copies and processes your input files unmodified across all test engine instances. If true, Azure Load Testing splits the CSV input data evenly across all engine instances. If you provide multiple CSV files, each file will be split evenly. + public bool? SplitAllCSVs + { + get + { + // TODO: encapsulate this boilerplate code for handling a nullable bool + if (!_element.TryGetProperty("splitAllCSVs", out MutableJsonElement value)) + { + return null; + } + + return value.ValueKind switch + { + JsonValueKind.True => true, + JsonValueKind.False => false, + JsonValueKind.Null => null, + _ => throw new InvalidCastException("JsonElement at 'splitAllCSVs' is not a 'bool'.") + }; + } + + set => _element.SetProperty("splitAllCSVs", value); + } + + /// If true, optionalLoadTestConfig is required and JMX script for the load test is not required to upload. + public bool? QuickStartTest + { + get + { + if (!_element.TryGetProperty("splitAllCSVs", out MutableJsonElement value)) + { + return null; + } + + return value.ValueKind switch + { + JsonValueKind.True => true, + JsonValueKind.False => false, + JsonValueKind.Null => null, + _ => throw new InvalidCastException("JsonElement at 'splitAllCSVs' is not a 'bool'.") + }; + } + + set => _element.SetProperty("splitAllCSVs", value); + } + + /// Optional load test config. + public OptionalLoadTestConfig OptionalLoadTestConfig { get; set; } + } +} diff --git a/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/OptionalLoadTestConfig.Serialization.cs b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/OptionalLoadTestConfig.Serialization.cs new file mode 100644 index 000000000000..a5b1df8197e5 --- /dev/null +++ b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/OptionalLoadTestConfig.Serialization.cs @@ -0,0 +1,70 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Text.Json; +using Azure.Core; +using Azure.Core.Json; +using Azure.Core.Serialization; + +namespace Azure.Developer.LoadTesting.Models +{ + public partial class OptionalLoadTestConfig : IUtf8JsonSerializable, IJsonModelSerializable + { + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) + { + writer.WriteStartObject(); + if (Optional.IsDefined(EndpointUrl)) + { + writer.WritePropertyName("endpointUrl"u8); + writer.WriteStringValue(EndpointUrl.AbsoluteUri); + } + if (Optional.IsDefined(VirtualUsers)) + { + writer.WritePropertyName("virtualUsers"u8); + writer.WriteNumberValue(VirtualUsers.Value); + } + if (Optional.IsDefined(RampUpTime)) + { + writer.WritePropertyName("rampUpTime"u8); + writer.WriteNumberValue(RampUpTime.Value); + } + if (Optional.IsDefined(Duration)) + { + writer.WritePropertyName("duration"u8); + writer.WriteNumberValue(Duration.Value); + } + writer.WriteEndObject(); + } + + void IJsonModelSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + if (options.Format == "P") + { + _element.WriteTo(writer, 'P'); + return; + } + + ((IUtf8JsonSerializable)this).Write(writer); + } + + object IJsonModelSerializable.Deserialize(ref Utf8JsonReader reader, ModelSerializerOptions options) + { + JsonDocument doc = JsonDocument.ParseValue(ref reader); + MutableJsonDocument mdoc = new MutableJsonDocument(doc, new JsonSerializerOptions()); + return new OptionalLoadTestConfig(mdoc.RootElement); + } + + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + { + MutableJsonDocument jsonDocument = MutableJsonDocument.Parse(data); + return new OptionalLoadTestConfig(jsonDocument.RootElement); + } + + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) => ModelSerializerHelper.SerializeToBinaryData(writer => ((IJsonModelSerializable)this).Serialize(writer, options)); + } +} diff --git a/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/OptionalLoadTestConfig.cs b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/OptionalLoadTestConfig.cs new file mode 100644 index 000000000000..d4b19bb6fb7f --- /dev/null +++ b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/OptionalLoadTestConfig.cs @@ -0,0 +1,99 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using Azure.Core.Json; + +namespace Azure.Developer.LoadTesting.Models +{ + /// Optional load test config. + public partial class OptionalLoadTestConfig + { + private MutableJsonElement _element; + + /// Initializes a new instance of OptionalLoadTestConfig. + public OptionalLoadTestConfig() + { + _element = MutableJsonDocument.Parse(MutableJsonDocument.EmptyJson).RootElement; + } + + internal OptionalLoadTestConfig(MutableJsonElement element) + { + _element = element; + } + + /// Test URL. Provide the complete HTTP URL. For example, http://contoso-app.azurewebsites.net/login. + public Uri EndpointUrl + { + get => _element.GetProperty("endpointUrl").ConvertTo(); + set => _element.SetProperty("endpointUrl", value); + } + + /// No of concurrent virtual users. + public int? VirtualUsers + { + get + { + if (!_element.TryGetProperty("virtualUsers", out MutableJsonElement value)) + { + return null; + } + + if (!value.TryGetInt32(out int i)) + { + return null; + } + + return i; + } + + set => _element.SetProperty("virtualUsers", value); + } + + /// Ramp up time. + public int? RampUpTime + { + get + { + if (!_element.TryGetProperty("rampUpTime", out MutableJsonElement value)) + { + return null; + } + + if (!value.TryGetInt32(out int i)) + { + return null; + } + + return i; + } + + set => _element.SetProperty("rampUpTime", value); + } + + /// Test run duration. + public int? Duration + { + get + { + if (!_element.TryGetProperty("duration", out MutableJsonElement value)) + { + return null; + } + + if (!value.TryGetInt32(out int i)) + { + return null; + } + + return i; + } + + set => _element.SetProperty("duration", value); + } + } +} diff --git a/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/PFAction.cs b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/PFAction.cs new file mode 100644 index 000000000000..aa2d4695259a --- /dev/null +++ b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/PFAction.cs @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.ComponentModel; + +namespace Azure.Developer.LoadTesting.Models +{ + /// Action taken after the threshold is met. Default is ‘continue’. + public readonly partial struct PFAction : IEquatable + { + private readonly string _value; + + /// Initializes a new instance of . + /// is null. + public PFAction(string value) + { + _value = value ?? throw new ArgumentNullException(nameof(value)); + } + + private const string ContinueValue = "continue"; + private const string StopValue = "stop"; + + /// Test will continue to run even if pass fail metric criteria metric gets failed. + public static PFAction Continue { get; } = new PFAction(ContinueValue); + /// Test run will stop if pass fail criteria metric is not passed. + public static PFAction Stop { get; } = new PFAction(StopValue); + /// Determines if two values are the same. + public static bool operator ==(PFAction left, PFAction right) => left.Equals(right); + /// Determines if two values are not the same. + public static bool operator !=(PFAction left, PFAction right) => !left.Equals(right); + /// Converts a string to a . + public static implicit operator PFAction(string value) => new PFAction(value); + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) => obj is PFAction other && Equals(other); + /// + public bool Equals(PFAction other) => string.Equals(_value, other._value, StringComparison.InvariantCultureIgnoreCase); + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() => _value?.GetHashCode() ?? 0; + /// + public override string ToString() => _value; + } +} diff --git a/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/PFAgFunc.cs b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/PFAgFunc.cs new file mode 100644 index 000000000000..27a706b49e08 --- /dev/null +++ b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/PFAgFunc.cs @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.ComponentModel; + +namespace Azure.Developer.LoadTesting.Models +{ + /// The aggregation function to be applied on the client metric. Allowed functions - ‘percentage’ - for error metric , ‘avg’, ‘p50’, ‘p90’, ‘p95’, ‘p99’, ‘min’, ‘max’ - for response_time_ms and latency metric, ‘avg’ - for requests_per_sec, ‘count’ - for requests. + public readonly partial struct PFAgFunc : IEquatable + { + private readonly string _value; + + /// Initializes a new instance of . + /// is null. + public PFAgFunc(string value) + { + _value = value ?? throw new ArgumentNullException(nameof(value)); + } + + private const string CountValue = "count"; + private const string PercentageValue = "percentage"; + private const string AvgValue = "avg"; + private const string P50Value = "p50"; + private const string P90Value = "p90"; + private const string P95Value = "p95"; + private const string P99Value = "p99"; + private const string MinValue = "min"; + private const string MaxValue = "max"; + + /// Criteria applies for count value. + public static PFAgFunc Count { get; } = new PFAgFunc(CountValue); + /// Criteria applies for given percentage value. + public static PFAgFunc Percentage { get; } = new PFAgFunc(PercentageValue); + /// Criteria applies for avg value. + public static PFAgFunc Avg { get; } = new PFAgFunc(AvgValue); + /// Criteria applies for 50th percentile value. + public static PFAgFunc P50 { get; } = new PFAgFunc(P50Value); + /// Criteria applies for 90th percentile value. + public static PFAgFunc P90 { get; } = new PFAgFunc(P90Value); + /// Criteria applies for 95th percentile value. + public static PFAgFunc P95 { get; } = new PFAgFunc(P95Value); + /// Criteria applies for 99th percentile value. + public static PFAgFunc P99 { get; } = new PFAgFunc(P99Value); + /// Criteria applies for minimum value. + public static PFAgFunc Min { get; } = new PFAgFunc(MinValue); + /// Criteria applies for maximum value. + public static PFAgFunc Max { get; } = new PFAgFunc(MaxValue); + /// Determines if two values are the same. + public static bool operator ==(PFAgFunc left, PFAgFunc right) => left.Equals(right); + /// Determines if two values are not the same. + public static bool operator !=(PFAgFunc left, PFAgFunc right) => !left.Equals(right); + /// Converts a string to a . + public static implicit operator PFAgFunc(string value) => new PFAgFunc(value); + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) => obj is PFAgFunc other && Equals(other); + /// + public bool Equals(PFAgFunc other) => string.Equals(_value, other._value, StringComparison.InvariantCultureIgnoreCase); + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() => _value?.GetHashCode() ?? 0; + /// + public override string ToString() => _value; + } +} diff --git a/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/PFMetrics.cs b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/PFMetrics.cs new file mode 100644 index 000000000000..a4f5049a1bce --- /dev/null +++ b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/PFMetrics.cs @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.ComponentModel; + +namespace Azure.Developer.LoadTesting.Models +{ + /// The client metric on which the criteria should be applied. + public readonly partial struct PFMetrics : IEquatable + { + private readonly string _value; + + /// Initializes a new instance of . + /// is null. + public PFMetrics(string value) + { + _value = value ?? throw new ArgumentNullException(nameof(value)); + } + + private const string ResponseTimeMsValue = "response_time_ms"; + private const string LatencyValue = "latency"; + private const string ErrorValue = "error"; + private const string RequestsValue = "requests"; + private const string RequestsPerSecValue = "requests_per_sec"; + + /// Pass fail criteria for response time metric. + public static PFMetrics ResponseTimeMs { get; } = new PFMetrics(ResponseTimeMsValue); + /// Pass fail criteria for response time metric. + public static PFMetrics Latency { get; } = new PFMetrics(LatencyValue); + /// Pass fail criteria for error metric. + public static PFMetrics Error { get; } = new PFMetrics(ErrorValue); + /// Pass fail criteria for total requests. + public static PFMetrics Requests { get; } = new PFMetrics(RequestsValue); + /// Pass fail criteria for request rate. + public static PFMetrics RequestsPerSec { get; } = new PFMetrics(RequestsPerSecValue); + /// Determines if two values are the same. + public static bool operator ==(PFMetrics left, PFMetrics right) => left.Equals(right); + /// Determines if two values are not the same. + public static bool operator !=(PFMetrics left, PFMetrics right) => !left.Equals(right); + /// Converts a string to a . + public static implicit operator PFMetrics(string value) => new PFMetrics(value); + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) => obj is PFMetrics other && Equals(other); + /// + public bool Equals(PFMetrics other) => string.Equals(_value, other._value, StringComparison.InvariantCultureIgnoreCase); + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() => _value?.GetHashCode() ?? 0; + /// + public override string ToString() => _value; + } +} diff --git a/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/PFResult.cs b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/PFResult.cs new file mode 100644 index 000000000000..535132072c80 --- /dev/null +++ b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/PFResult.cs @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.ComponentModel; + +namespace Azure.Developer.LoadTesting.Models +{ + /// Outcome of the test run. + public readonly partial struct PFResult : IEquatable + { + private readonly string _value; + + /// Initializes a new instance of . + /// is null. + public PFResult(string value) + { + _value = value ?? throw new ArgumentNullException(nameof(value)); + } + + private const string PassedValue = "passed"; + private const string UndeterminedValue = "undetermined"; + private const string FailedValue = "failed"; + + /// Given pass fail criteria metric has passed. + public static PFResult Passed { get; } = new PFResult(PassedValue); + /// Given pass fail criteria metric couldn't determine. + public static PFResult Undetermined { get; } = new PFResult(UndeterminedValue); + /// Given pass fail criteria metric has failed. + public static PFResult Failed { get; } = new PFResult(FailedValue); + /// Determines if two values are the same. + public static bool operator ==(PFResult left, PFResult right) => left.Equals(right); + /// Determines if two values are not the same. + public static bool operator !=(PFResult left, PFResult right) => !left.Equals(right); + /// Converts a string to a . + public static implicit operator PFResult(string value) => new PFResult(value); + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) => obj is PFResult other && Equals(other); + /// + public bool Equals(PFResult other) => string.Equals(_value, other._value, StringComparison.InvariantCultureIgnoreCase); + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() => _value?.GetHashCode() ?? 0; + /// + public override string ToString() => _value; + } +} diff --git a/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/PassFailCriteria.Serialization.cs b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/PassFailCriteria.Serialization.cs new file mode 100644 index 000000000000..5fd97c0795bc --- /dev/null +++ b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/PassFailCriteria.Serialization.cs @@ -0,0 +1,61 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Text.Json; +using Azure.Core; +using Azure.Core.Json; +using Azure.Core.Serialization; + +namespace Azure.Developer.LoadTesting.Models +{ + public partial class PassFailCriteria : IUtf8JsonSerializable, IJsonModelSerializable + { + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) + { + writer.WriteStartObject(); + if (Optional.IsCollectionDefined(PassFailMetrics)) + { + writer.WritePropertyName("passFailMetrics"u8); + writer.WriteStartObject(); + foreach (var item in PassFailMetrics) + { + writer.WritePropertyName(item.Key); + writer.WriteObjectValue(item.Value); + } + writer.WriteEndObject(); + } + writer.WriteEndObject(); + } + + void IJsonModelSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + if (options.Format == "P") + { + _element.WriteTo(writer, 'P'); + return; + } + + ((IUtf8JsonSerializable)this).Write(writer); + } + + object IJsonModelSerializable.Deserialize(ref Utf8JsonReader reader, ModelSerializerOptions options) + { + JsonDocument doc = JsonDocument.ParseValue(ref reader); + MutableJsonDocument mdoc = new MutableJsonDocument(doc, new JsonSerializerOptions()); + return new PassFailCriteria(mdoc.RootElement); + } + + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + { + MutableJsonDocument jsonDocument = MutableJsonDocument.Parse(data); + return new PassFailCriteria(jsonDocument.RootElement); + } + + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) => ModelSerializerHelper.SerializeToBinaryData(writer => ((IJsonModelSerializable)this).Serialize(writer, options)); + } +} diff --git a/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/PassFailCriteria.cs b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/PassFailCriteria.cs new file mode 100644 index 000000000000..7ab14a5621c6 --- /dev/null +++ b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/PassFailCriteria.cs @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; +using Azure.Core.Json; + +namespace Azure.Developer.LoadTesting.Models +{ + /// Pass fail criteria for a test. + public partial class PassFailCriteria + { + private MutableJsonElement _element; + + /// Initializes a new instance of PassFailCriteria. + public PassFailCriteria() + { + _element = MutableJsonDocument.Parse(MutableJsonDocument.EmptyJson).RootElement; + } + + internal PassFailCriteria(MutableJsonElement element) + { + _element = element; + + if (_element.TryGetProperty("passFailMetrics", out MutableJsonElement value)) + { + _passFailMetrics = new MutableJsonDictionary(value); + } + } + + private IDictionary _passFailMetrics; + /// Map of id and pass fail metrics { id : pass fail metrics }. + public IDictionary PassFailMetrics + { + get + { + if (_passFailMetrics == null) + { + _element.SetProperty("passFailMetrics", new { }); + _passFailMetrics = new MutableJsonDictionary(_element.GetProperty("passFailMetrics")); + } + + return _passFailMetrics; + } + } + } +} diff --git a/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/PassFailMetric.Serialization.cs b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/PassFailMetric.Serialization.cs new file mode 100644 index 000000000000..cd8d5fd134bf --- /dev/null +++ b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/PassFailMetric.Serialization.cs @@ -0,0 +1,80 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Text.Json; +using Azure.Core; +using Azure.Core.Json; +using Azure.Core.Serialization; + +namespace Azure.Developer.LoadTesting.Models +{ + public partial class PassFailMetric : IUtf8JsonSerializable, IJsonModelSerializable + { + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) + { + writer.WriteStartObject(); + if (Optional.IsDefined(ClientMetric)) + { + writer.WritePropertyName("clientMetric"u8); + writer.WriteStringValue(ClientMetric.Value.ToString()); + } + if (Optional.IsDefined(Aggregate)) + { + writer.WritePropertyName("aggregate"u8); + writer.WriteStringValue(Aggregate.Value.ToString()); + } + if (Optional.IsDefined(Condition)) + { + writer.WritePropertyName("condition"u8); + writer.WriteStringValue(Condition); + } + if (Optional.IsDefined(RequestName)) + { + writer.WritePropertyName("requestName"u8); + writer.WriteStringValue(RequestName); + } + if (Optional.IsDefined(Value)) + { + writer.WritePropertyName("value"u8); + writer.WriteNumberValue(Value.Value); + } + if (Optional.IsDefined(Action)) + { + writer.WritePropertyName("action"u8); + writer.WriteStringValue(Action.Value.ToString()); + } + writer.WriteEndObject(); + } + + void IJsonModelSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + if (options.Format == "P") + { + _element.WriteTo(writer, 'P'); + return; + } + + ((IUtf8JsonSerializable)this).Write(writer); + } + + object IJsonModelSerializable.Deserialize(ref Utf8JsonReader reader, ModelSerializerOptions options) + { + JsonDocument doc = JsonDocument.ParseValue(ref reader); + MutableJsonDocument mdoc = new MutableJsonDocument(doc, new JsonSerializerOptions()); + return new PassFailMetric(mdoc.RootElement); + } + + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + { + MutableJsonDocument jsonDocument = MutableJsonDocument.Parse(data); + return new PassFailMetric(jsonDocument.RootElement); + } + + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) => ModelSerializerHelper.SerializeToBinaryData(writer => ((IJsonModelSerializable)this).Serialize(writer, options)); + } +} diff --git a/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/PassFailMetric.cs b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/PassFailMetric.cs new file mode 100644 index 000000000000..6632c6f8a641 --- /dev/null +++ b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/PassFailMetric.cs @@ -0,0 +1,163 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using Azure.Core.Json; +using System; + +namespace Azure.Developer.LoadTesting.Models +{ + /// Pass fail metric. + public partial class PassFailMetric + { + private MutableJsonElement _element; + + /// Initializes a new instance of PassFailMetric. + public PassFailMetric() + { + _element = MutableJsonDocument.Parse(MutableJsonDocument.EmptyJson).RootElement; + } + + internal PassFailMetric(MutableJsonElement element) + { + _element = element; + } + + /// The client metric on which the criteria should be applied. + public PFMetrics? ClientMetric + { + get + { + if (_element.TryGetProperty("clientMetric", out MutableJsonElement value)) + { + return value.GetString(); + } + + return null; + } + + set => _element.SetProperty("clientMetric", value); + } + + /// The aggregation function to be applied on the client metric. Allowed functions - ‘percentage’ - for error metric , ‘avg’, ‘p50’, ‘p90’, ‘p95’, ‘p99’, ‘min’, ‘max’ - for response_time_ms and latency metric, ‘avg’ - for requests_per_sec, ‘count’ - for requests. + public PFAgFunc? Aggregate + { + get + { + if (_element.TryGetProperty("aggregate", out MutableJsonElement value)) + { + return value.GetString(); + } + + return null; + } + + set => _element.SetProperty("aggregate", value); + } + + /// The comparison operator. Supported types ‘>’, ‘<’. + public string Condition + { + get + { + if (_element.TryGetProperty("condition", out MutableJsonElement value)) + { + return value.GetString(); + } + + return null; + } + + set => _element.SetProperty("condition", value); + } + + /// Request name for which the Pass fail criteria has to be applied. + public string RequestName + { + get + { + if (_element.TryGetProperty("requestName", out MutableJsonElement value)) + { + return value.GetString(); + } + + return null; + } + + set => _element.SetProperty("requestName", value); + } + + /// The value to compare with the client metric. Allowed values - ‘error : [0.0 , 100.0] unit- % ’, response_time_ms and latency : any integer value unit- ms. + public double? Value + { + get + { + if (!_element.TryGetProperty("value", out MutableJsonElement value)) + { + return null; + } + + if (!value.TryGetDouble(out double d)) + { + return null; + } + + return d; + } + + set => _element.SetProperty("value", value); + } + + /// Action taken after the threshold is met. Default is ‘continue’. + public PFAction? Action + { + get + { + if (_element.TryGetProperty("action", out MutableJsonElement value)) + { + return value.GetString(); + } + + return null; + } + + set => _element.SetProperty("action", value); + } + + /// The actual value of the client metric for the test run. + public double? ActualValue + { + get + { + if (!_element.TryGetProperty("actualValue", out MutableJsonElement value)) + { + return null; + } + + if (!value.TryGetDouble(out double d)) + { + return null; + } + + return d; + } + } + + /// Outcome of the test run. + public PFResult? Result + { + get + { + if (_element.TryGetProperty("result", out MutableJsonElement value)) + { + return value.GetString(); + } + + return null; + } + } + } +} diff --git a/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/Secret.Serialization.cs b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/Secret.Serialization.cs new file mode 100644 index 000000000000..f14931118358 --- /dev/null +++ b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/Secret.Serialization.cs @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Text.Json; +using Azure.Core; +using Azure.Core.Json; +using Azure.Core.Serialization; + +namespace Azure.Developer.LoadTesting.Models +{ + public partial class Secret : IUtf8JsonSerializable, IJsonModelSerializable + { + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) + { + writer.WriteStartObject(); + if (Optional.IsDefined(Value)) + { + writer.WritePropertyName("value"u8); + writer.WriteStringValue(Value); + } + if (Optional.IsDefined(Type)) + { + writer.WritePropertyName("type"u8); + writer.WriteStringValue(Type.Value.ToString()); + } + writer.WriteEndObject(); + } + + void IJsonModelSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + if (options.Format == "P") + { + _element.WriteTo(writer, 'P'); + return; + } + + ((IUtf8JsonSerializable)this).Write(writer); + } + + object IJsonModelSerializable.Deserialize(ref Utf8JsonReader reader, ModelSerializerOptions options) + { + JsonDocument doc = JsonDocument.ParseValue(ref reader); + MutableJsonDocument mdoc = new MutableJsonDocument(doc, new JsonSerializerOptions()); + return new Secret(mdoc.RootElement); + } + + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + { + MutableJsonDocument jsonDocument = MutableJsonDocument.Parse(data); + return new Secret(jsonDocument.RootElement); + } + + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) => ModelSerializerHelper.SerializeToBinaryData(writer => ((IJsonModelSerializable)this).Serialize(writer, options)); + } +} diff --git a/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/Secret.cs b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/Secret.cs new file mode 100644 index 000000000000..6731d7035a5d --- /dev/null +++ b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/Secret.cs @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using Azure.Core.Json; + +namespace Azure.Developer.LoadTesting.Models +{ + /// Secret. + public partial class Secret + { + private MutableJsonElement _element; + + /// Initializes a new instance of Secret. + public Secret() + { + _element = MutableJsonDocument.Parse(MutableJsonDocument.EmptyJson).RootElement; + } + + internal Secret(MutableJsonElement element) + { + _element = element; + } + + /// The value of the secret for the respective type. + public string Value + { + get => _element.GetProperty("value").GetString(); + set => _element.SetProperty("value", value); + } + + /// Type of secret. + public SecretType? Type + { + get => _element.GetProperty("type").GetString(); + set => _element.SetProperty("type", value); + } + } +} diff --git a/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/SecretType.cs b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/SecretType.cs new file mode 100644 index 000000000000..9e6457ce07dd --- /dev/null +++ b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/SecretType.cs @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.ComponentModel; + +namespace Azure.Developer.LoadTesting.Models +{ + /// Type of secret. + public readonly partial struct SecretType : IEquatable + { + private readonly string _value; + + /// Initializes a new instance of . + /// is null. + public SecretType(string value) + { + _value = value ?? throw new ArgumentNullException(nameof(value)); + } + + private const string AKVSecretURIValue = "AKV_SECRET_URI"; + private const string SecretValueValue = "SECRET_VALUE"; + + /// If the secret is stored in an Azure Key Vault. + public static SecretType AKVSecretURI { get; } = new SecretType(AKVSecretURIValue); + /// If the Plain text secret value provided. + public static SecretType SecretValue { get; } = new SecretType(SecretValueValue); + /// Determines if two values are the same. + public static bool operator ==(SecretType left, SecretType right) => left.Equals(right); + /// Determines if two values are not the same. + public static bool operator !=(SecretType left, SecretType right) => !left.Equals(right); + /// Converts a string to a . + public static implicit operator SecretType(string value) => new SecretType(value); + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) => obj is SecretType other && Equals(other); + /// + public bool Equals(SecretType other) => string.Equals(_value, other._value, StringComparison.InvariantCultureIgnoreCase); + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() => _value?.GetHashCode() ?? 0; + /// + public override string ToString() => _value; + } +} diff --git a/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/Test.Serialization.cs b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/Test.Serialization.cs new file mode 100644 index 000000000000..664067faf412 --- /dev/null +++ b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/Test.Serialization.cs @@ -0,0 +1,140 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Text.Json; +using Azure.Core; +using Azure.Core.Json; +using Azure.Core.Serialization; + +namespace Azure.Developer.LoadTesting.Models +{ + public partial class Test : IUtf8JsonSerializable, IJsonModelSerializable + { + /// + /// + /// + public static implicit operator RequestContent(Test test) + { + return new Utf8JsonDelayedRequestContent(test); + } + + /// + /// + /// + public static explicit operator Test(Response response) + { + MutableJsonDocument jsonDocument = MutableJsonDocument.Parse(response.Content); + return new Test(jsonDocument.RootElement); + } + + // only used for public access to internal serialize + /// + /// + /// +#pragma warning disable AZC0014 // don't use STJ types + public void Serialize(Utf8JsonWriter writer) => ((IUtf8JsonSerializable)this).Write(writer); +#pragma warning restore AZC0014 // don't use STJ types + + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) + { + writer.WriteStartObject(); + if (Optional.IsDefined(PassFailCriteria)) + { + writer.WritePropertyName("passFailCriteria"u8); + writer.WriteObjectValue(PassFailCriteria); + } + if (Optional.IsCollectionDefined(Secrets)) + { + writer.WritePropertyName("secrets"u8); + writer.WriteStartObject(); + foreach (var item in Secrets) + { + writer.WritePropertyName(item.Key); + writer.WriteObjectValue(item.Value); + } + writer.WriteEndObject(); + } + if (Optional.IsDefined(Certificate)) + { + writer.WritePropertyName("certificate"u8); + writer.WriteObjectValue(Certificate); + } + if (Optional.IsCollectionDefined(EnvironmentVariables)) + { + writer.WritePropertyName("environmentVariables"u8); + writer.WriteStartObject(); + foreach (var item in EnvironmentVariables) + { + writer.WritePropertyName(item.Key); + writer.WriteStringValue(item.Value); + } + writer.WriteEndObject(); + } + if (Optional.IsDefined(LoadTestConfiguration)) + { + writer.WritePropertyName("loadTestConfiguration"u8); + writer.WriteObjectValue(LoadTestConfiguration); + } + if (Optional.IsDefined(Description)) + { + writer.WritePropertyName("description"u8); + writer.WriteStringValue(Description); + } + if (Optional.IsDefined(DisplayName)) + { + writer.WritePropertyName("displayName"u8); + writer.WriteStringValue(DisplayName); + } + if (Optional.IsDefined(SubnetId)) + { + writer.WritePropertyName("subnetId"u8); + writer.WriteStringValue(SubnetId); + } + if (Optional.IsDefined(KeyvaultReferenceIdentityType)) + { + writer.WritePropertyName("keyvaultReferenceIdentityType"u8); + writer.WriteStringValue(KeyvaultReferenceIdentityType); + } + if (Optional.IsDefined(KeyvaultReferenceIdentityId)) + { + writer.WritePropertyName("keyvaultReferenceIdentityId"u8); + writer.WriteStringValue(KeyvaultReferenceIdentityId); + } + writer.WriteEndObject(); + } + + void IJsonModelSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + // TODO: it would be nice to standardize on the type of Format. + if (options.Format == "P") + { + _element.WriteTo(writer, 'P'); + return; + } + + ((IUtf8JsonSerializable)this).Write(writer); + } + + object IJsonModelSerializable.Deserialize(ref Utf8JsonReader reader, ModelSerializerOptions options) + { + JsonDocument doc = JsonDocument.ParseValue(ref reader); + MutableJsonDocument mdoc = new MutableJsonDocument(doc, new JsonSerializerOptions()); + return new Test(mdoc.RootElement); + } + + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + { + // TODO: Use options? + + MutableJsonDocument jsonDocument = MutableJsonDocument.Parse(data); + return new Test(jsonDocument.RootElement); + } + + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) => ModelSerializerHelper.SerializeToBinaryData(writer => ((IJsonModelSerializable)this).Serialize(writer, options)); + } +} diff --git a/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/Test.cs b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/Test.cs new file mode 100644 index 000000000000..3071001718c8 --- /dev/null +++ b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/Test.cs @@ -0,0 +1,264 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.Json; +using Azure.Core.Json; + +namespace Azure.Developer.LoadTesting.Models +{ + /// Load test model. + public partial class Test + { + private MutableJsonElement _element; + + /// Initializes a new instance of Test. + public Test(string testId) + { + // testId is required for the client to create the request URI + // but it is not needed in the body of a PATCH request. + + // Parse the initial element with this + BinaryData utf8Json; + using (MemoryStream stream = new()) + { + using Utf8JsonWriter writer = new Utf8JsonWriter(stream); + writer.WriteStartObject(); + writer.WritePropertyName("testId"); + writer.WriteStringValue(testId.AsSpan()); + writer.WriteEndObject(); + writer.Flush(); + stream.Position = 0; + utf8Json = BinaryData.FromStream(stream); + } + + _element = MutableJsonDocument.Parse(utf8Json).RootElement; + + // TODO: If we are suggesting a wholesale replacement of ChangeTrackingDictionary, + // we will need to confirm that MJD provides the complete set of features this type provides. + } + + internal Test(MutableJsonElement element) + { + _element = element; + + if (_element.TryGetProperty("passFailCriteria", out MutableJsonElement passFailCriteriaElement)) + { + _passFailCriteria = new PassFailCriteria(passFailCriteriaElement); + } + + if (_element.TryGetProperty("secrets", out MutableJsonElement secretsElement)) + { + _secrets = new MutableJsonDictionary(secretsElement); + } + + if (_element.TryGetProperty("environmentVariables", out MutableJsonElement environmentVariablesElement)) + { + _environmentVariables = new MutableJsonDictionary(environmentVariablesElement); + } + } + + private PassFailCriteria _passFailCriteria; + /// Pass fail criteria for a test. + public PassFailCriteria PassFailCriteria + { + get + { + if (_passFailCriteria == null) + { + _element.SetProperty("passFailCriteria", new { }); + _passFailCriteria = new PassFailCriteria(_element.GetProperty("passFailCriteria")); + } + + return _passFailCriteria; + } + + // TODO: need to test what happens if caller sets this, how the interaction + // across MJEs in the MJD works. + set { _passFailCriteria = value; } + } + + private IDictionary _secrets; + /// Secrets can be stored in an Azure Key Vault or any other secret store. If the secret is stored in an Azure Key Vault, the value should be the secret identifier and the type should be AKV_SECRET_URI. If the secret is stored elsewhere, the secret value should be provided directly and the type should be SECRET_VALUE. + public IDictionary Secrets + { + get + { + if (_secrets == null) + { + _element.SetProperty("secrets", new { }); + _secrets = new MutableJsonDictionary(_element.GetProperty("secrets")); + } + + return _secrets; + } + } + + /// Certificates metadata. + public CertificateMetadata Certificate { get; set; } + + private IDictionary _environmentVariables; + /// Environment variables which are defined as a set of <name,value> pairs. + public IDictionary EnvironmentVariables + { + get + { + if (_environmentVariables == null) + { + _element.SetProperty("environmentVariables", new { }); + _environmentVariables = new MutableJsonDictionary(_element.GetProperty("environmentVariables")); + } + + return _environmentVariables; + } + } + + /// The load test configuration. + public LoadTestConfiguration LoadTestConfiguration { get; set; } + + /// The input artifacts for the test. + public TestInputArtifacts InputArtifacts { get; } + + /// Unique test name as identifier. + public string TestId + { + get + { + if (_element.TryGetProperty("testId", out MutableJsonElement value)) + { + return value.GetString(); + } + return null; + } + set => _element.SetProperty("testId", value); + } + + /// The test description. + public string Description + { + get + { + if (_element.TryGetProperty("description", out MutableJsonElement value)) + { + return value.GetString(); + } + return null; + } + set => _element.SetProperty("description", value); + } + + /// Display name of a test. + public string DisplayName + { + get + { + if (_element.TryGetProperty("displayName", out MutableJsonElement value)) + { + return value.GetString(); + } + return null; + } + set => _element.SetProperty("displayName", value); + } + + /// Subnet ID on which the load test instances should run. + public string SubnetId + { + get + { + if (_element.TryGetProperty("subnetId", out MutableJsonElement value)) + { + return value.GetString(); + } + return null; + } + set => _element.SetProperty("subnetId", value); + } + + /// Type of the managed identity referencing the Key vault. + public string KeyvaultReferenceIdentityType + { + get + { + if (_element.TryGetProperty("keyvaultReferenceIdentityType", out MutableJsonElement value)) + { + return value.GetString(); + } + return null; + } + set => _element.SetProperty("keyvaultReferenceIdentityType", value); + } + + /// Resource Id of the managed identity referencing the Key vault. + public string KeyvaultReferenceIdentityId + { + get + { + if (_element.TryGetProperty("keyvaultReferenceIdentityId", out MutableJsonElement value)) + { + return value.GetString(); + } + return null; + } + set => _element.SetProperty("keyvaultReferenceIdentityId", value); + } + + /// The creation datetime(ISO 8601 literal format). + public DateTimeOffset? CreatedDateTime + { + get + { + if (_element.TryGetProperty("createdDateTime", out MutableJsonElement value)) + { + return value.GetDateTimeOffset(); + } + return null; + } + } + + /// The user that created. + public string CreatedBy + { + get + { + if (_element.TryGetProperty("createdBy", out MutableJsonElement value)) + { + return value.GetString(); + } + return null; + } + } + + /// The last Modified datetime(ISO 8601 literal format). + public DateTimeOffset? LastModifiedDateTime + { + get + { + if (_element.TryGetProperty("lastModifiedDateTime", out MutableJsonElement value)) + { + return value.GetDateTimeOffset(); + } + return null; + } + } + + /// The user that last modified. + public string LastModifiedBy + { + get + { + if (_element.TryGetProperty("lastModifiedBy", out MutableJsonElement value)) + { + return value.GetString(); + } + return null; + } + } + } +} diff --git a/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/TestInputArtifacts.Serialization.cs b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/TestInputArtifacts.Serialization.cs new file mode 100644 index 000000000000..2fe8b6a82e5f --- /dev/null +++ b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/TestInputArtifacts.Serialization.cs @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Text.Json; +using Azure.Core; +using Azure.Core.Json; +using Azure.Core.Serialization; + +namespace Azure.Developer.LoadTesting.Models +{ + public partial class TestInputArtifacts : IUtf8JsonSerializable, IJsonModelSerializable + { + void IUtf8JsonSerializable.Write(Utf8JsonWriter writer) + { + throw new NotImplementedException(); + } + + void IJsonModelSerializable.Serialize(Utf8JsonWriter writer, ModelSerializerOptions options) + { + if (options.Format == "P") + { + _element.WriteTo(writer, 'P'); + return; + } + + ((IUtf8JsonSerializable)this).Write(writer); + } + + object IJsonModelSerializable.Deserialize(ref Utf8JsonReader reader, ModelSerializerOptions options) + { + JsonDocument doc = JsonDocument.ParseValue(ref reader); + MutableJsonDocument mdoc = new MutableJsonDocument(doc, new JsonSerializerOptions()); + return new TestInputArtifacts(mdoc.RootElement); + } + + object IModelSerializable.Deserialize(BinaryData data, ModelSerializerOptions options) + { + MutableJsonDocument jsonDocument = MutableJsonDocument.Parse(data); + return new TestInputArtifacts(jsonDocument.RootElement); + } + + BinaryData IModelSerializable.Serialize(ModelSerializerOptions options) => ModelSerializerHelper.SerializeToBinaryData(writer => ((IJsonModelSerializable)this).Serialize(writer, options)); + } +} diff --git a/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/TestInputArtifacts.cs b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/TestInputArtifacts.cs new file mode 100644 index 000000000000..f174ac10fee2 --- /dev/null +++ b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/Models/TestInputArtifacts.cs @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// + +#nullable disable + +using System; +using System.Collections.Generic; +using Azure.Core.Json; + +namespace Azure.Developer.LoadTesting.Models +{ + /// The input artifacts for the test. + public partial class TestInputArtifacts + { + private MutableJsonElement _element; + + /// Initializes a new instance of TestInputArtifacts. + internal TestInputArtifacts() + { + _element = MutableJsonDocument.Parse(MutableJsonDocument.EmptyJson).RootElement; + + AdditionalFileInfo = new MutableJsonReadOnlyList(_element.GetProperty("additionalFileInfo")); + } + + internal TestInputArtifacts(MutableJsonElement element) + { + _element = element; + + // TODO: TryGetProperty + + ConfigFileInfo = new FileInfo(_element.GetProperty("configFileInfo")); + TestScriptFileInfo = new FileInfo(_element.GetProperty("testScriptFileInfo")); + UserPropFileInfo = new FileInfo(_element.GetProperty("userPropFileInfo")); + InputArtifactsZipFileInfo = new FileInfo(_element.GetProperty("inputArtifactsZipFileInfo")); + + AdditionalFileInfo = new MutableJsonReadOnlyList(_element.GetProperty("additionalFileInfo")); + } + + /// File info. + public FileInfo ConfigFileInfo { get; } + + /// File info. + public FileInfo TestScriptFileInfo { get; } + + /// File info. + public FileInfo UserPropFileInfo { get; } + + /// File info. + public FileInfo InputArtifactsZipFileInfo { get; } + + /// Additional supported files for the test run. + public IReadOnlyList AdditionalFileInfo { get; } + } +} diff --git a/sdk/loadtestservice/Azure.Developer.LoadTesting/src/properties/AssemblyInfo.cs b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/properties/AssemblyInfo.cs index 432f56df6382..062fb4e5c920 100644 --- a/sdk/loadtestservice/Azure.Developer.LoadTesting/src/properties/AssemblyInfo.cs +++ b/sdk/loadtestservice/Azure.Developer.LoadTesting/src/properties/AssemblyInfo.cs @@ -4,3 +4,4 @@ using System.Runtime.CompilerServices; [assembly: Azure.Core.AzureResourceProviderNamespace("Microsoft.Loadtestservice")] +[assembly: InternalsVisibleTo("Azure.Developer.LoadTesting.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100d15ddcb29688295338af4b7686603fe614abd555e09efba8fb88ee09e1f7b1ccaeed2e8f823fa9eef3fdd60217fc012ea67d2479751a0b8c087a4185541b851bd8b16f8d91b840e51b1cb0ba6fe647997e57429265e85ef62d565db50a69ae1647d54d7bd855e4db3d8a91510e5bcbd0edfbbecaa20a7bd9ae74593daa7b11b4")] diff --git a/sdk/loadtestservice/Azure.Developer.LoadTesting/tests/LoadTestAdministrationClientTest.cs b/sdk/loadtestservice/Azure.Developer.LoadTesting/tests/LoadTestAdministrationClientTest.cs index 4c3004ccbc8f..d84c4b275ba3 100644 --- a/sdk/loadtestservice/Azure.Developer.LoadTesting/tests/LoadTestAdministrationClientTest.cs +++ b/sdk/loadtestservice/Azure.Developer.LoadTesting/tests/LoadTestAdministrationClientTest.cs @@ -9,13 +9,12 @@ using System.Threading.Tasks; using Azure.Core; using Azure.Core.TestFramework; -using Azure.Core.TestFramework.Models; -using Azure.Developer.LoadTesting.Tests.Helper; +using Azure.Developer.LoadTesting.Models; using NUnit.Framework; namespace Azure.Developer.LoadTesting.Tests { - public class LoadTestAdministrationClientTest: LoadTestTestsBase + public class LoadTestAdministrationClientTest : LoadTestTestsBase { public LoadTestAdministrationClientTest(bool isAsync) : base(isAsync) { } @@ -44,6 +43,61 @@ public async Task TearDown() } } + [Test] + [Category(SKIP_SET_UP)] + [RecordedTest] + public async Task CreateOrUpdateTestConveninence() + { + Test test = new Test(_testId); + test.Description = "This test was created through loadtesting C# SDK"; + test.DisplayName = "Dotnet Testing Framework Loadtest"; + test.LoadTestConfiguration = new LoadTestConfiguration(); + test.LoadTestConfiguration.EngineInstances = 1; + test.LoadTestConfiguration.SplitAllCSVs = false; + test.Secrets.Clear(); + test.EnvironmentVariables.Clear(); + test.PassFailCriteria = new PassFailCriteria(); + test.PassFailCriteria.PassFailMetrics.Clear(); + + Response response = await _loadTestAdministrationClient.CreateOrUpdateTestAsync(test); + Assert.AreEqual(_testId, response.Value.TestId); + } + + private Test GetTest() + { + Test test = new Test(_testId); + test.Description = "This test was created through loadtesting C# SDK"; + test.DisplayName = "Dotnet Testing Framework Loadtest"; + test.LoadTestConfiguration = new LoadTestConfiguration(); + test.LoadTestConfiguration.EngineInstances = 1; + test.LoadTestConfiguration.SplitAllCSVs = false; + test.Secrets.Clear(); + test.EnvironmentVariables.Clear(); + test.PassFailCriteria = new PassFailCriteria(); + test.PassFailCriteria.PassFailMetrics.Clear(); + return test; + } + + [Test] + [Category(SKIP_SET_UP)] + [RecordedTest] + public async Task RoundTripTestValueConvenience() + { + Test orig = GetTest(); + + // Retrieve the value from the service. + Test test = await _loadTestAdministrationClient.CreateOrUpdateTestAsync(orig); + + // Change a value on the model. + test.EnvironmentVariables["NewEnvVar"] = "NewValue"; + + // Update the value on the service. + Test modified = await _loadTestAdministrationClient.CreateOrUpdateTestAsync(test); + + // Confirm update. + Assert.AreEqual("NewValue", modified.EnvironmentVariables["NewEnvVar"]); + } + [Test] [Category(SKIP_SET_UP)] public async Task CreateOrUpdateTest() @@ -115,13 +169,13 @@ public async Task ListLoadTest() { count++; - foreach (var value in page.Values) - { + foreach (var value in page.Values) + { JsonDocument jsonDocument = JsonDocument.Parse(value.ToString()); Assert.NotNull(jsonDocument.RootElement.GetProperty("testId").ToString()); Console.WriteLine(value.ToString()); - } + } } int i = 0; @@ -129,7 +183,7 @@ public async Task ListLoadTest() { i++; - if (i