From ddf31a675875208c6034758b92d04fa7544ffaa6 Mon Sep 17 00:00:00 2001
From: ShivangiReja <45216704+ShivangiReja@users.noreply.github.com>
Date: Tue, 29 Jun 2021 12:41:55 -0700
Subject: [PATCH] Add PropertyReferenceType attribute in ResourceIdentity
(#22255)
* Add PropertyReferenceTypeAttribute attribute in ResourceIdentity
* Feedback
---
.../PropertyReferenceTypeAttribute.cs | 36 ++++++++++++
.../src/Resources/ResourceIdentity.cs | 7 ++-
.../src/Resources/UserAssignedIdentity.cs | 4 +-
.../tests/Unit/IdentityTests.cs | 20 +++----
.../tests/Unit/PropertyReferenceTypeTests.cs | 58 +++++++++++++++++++
5 files changed, 112 insertions(+), 13 deletions(-)
create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/PropertyReferenceTypeAttribute.cs
create mode 100644 sdk/resourcemanager/Azure.ResourceManager.Core/tests/Unit/PropertyReferenceTypeTests.cs
diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/PropertyReferenceTypeAttribute.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/PropertyReferenceTypeAttribute.cs
new file mode 100644
index 000000000000..f8a755b9aa9d
--- /dev/null
+++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/PropertyReferenceTypeAttribute.cs
@@ -0,0 +1,36 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+
+namespace Azure.ResourceManager.Core
+{
+ ///
+ /// An attribute class indicating a reference type for code generation.
+ ///
+ [AttributeUsage(AttributeTargets.Class)]
+ public class PropertyReferenceTypeAttribute : Attribute
+ {
+ ///
+ /// Instatiate a new reference type attribute.
+ ///
+ /// An array of types to skip for this reference type.
+ public PropertyReferenceTypeAttribute(Type[] skipTypes)
+ {
+ SkipTypes = skipTypes;
+ }
+
+ ///
+ /// Instatiate a new reference type attribute.
+ ///
+ public PropertyReferenceTypeAttribute() : this(null)
+ {
+ }
+
+ ///
+ /// Get an array of types to skip for this reference type.
+ ///
+ public Type[] SkipTypes { get; }
+ }
+}
diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/ResourceIdentity.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/ResourceIdentity.cs
index 9102f5db7b4d..12599e56aad6 100644
--- a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/ResourceIdentity.cs
+++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/ResourceIdentity.cs
@@ -11,6 +11,7 @@ namespace Azure.ResourceManager.Core
///
/// Represents a managed identity
///
+ [PropertyReferenceType(new Type[] { typeof(ResourceIdentityType) })]
public class ResourceIdentity : IEquatable
{
private const string SystemAssigned = "SystemAssigned";
@@ -20,6 +21,7 @@ public class ResourceIdentity : IEquatable
///
/// Initializes a new instance of the class.
///
+ [InitializationConstructor]
public ResourceIdentity()
: this(null, false)
{
@@ -30,6 +32,7 @@ public ResourceIdentity()
///
/// Dictionary with a key and a object value.
/// Flag for using or not.
+ [SerializationConstructor]
public ResourceIdentity(Dictionary user, bool useSystemAssigned)
{
// check for combination of user and system on the impact to type value
@@ -78,7 +81,9 @@ public ResourceIdentity(SystemAssignedIdentity systemAssigned, IDictionary
/// A containing an .
/// New Identity object with JSON values.
- internal static ResourceIdentity Deserialize(JsonElement element)
+#pragma warning disable AZC0014 // Avoid using banned types in public API
+ public static ResourceIdentity DeserializeResourceIdentity(JsonElement element)
+#pragma warning restore AZC0014 // Avoid using banned types in public API
{
if (element.ValueKind == JsonValueKind.Undefined)
{
diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/UserAssignedIdentity.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/UserAssignedIdentity.cs
index 3450deff2ff7..88ed8932bddf 100644
--- a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/UserAssignedIdentity.cs
+++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/UserAssignedIdentity.cs
@@ -26,12 +26,12 @@ public UserAssignedIdentity(Guid clientId, Guid principalId)
///
/// Gets or sets the Client ID.
///
- public Guid ClientId { get; set; }
+ public Guid ClientId { get; }
///
/// Gets or sets the Principal ID.
///
- public Guid PrincipalId { get; set; }
+ public Guid PrincipalId { get; }
///
/// Converts a into an object.
diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/Unit/IdentityTests.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/Unit/IdentityTests.cs
index a725b9130f75..88419932480c 100644
--- a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/Unit/IdentityTests.cs
+++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/Unit/IdentityTests.cs
@@ -151,7 +151,7 @@ public void EqualsTestFalseSameKey()
public void TestDeserializerInvalidDefaultJson()
{
JsonElement invalid = default(JsonElement);
- Assert.Throws(delegate { ResourceIdentity.Deserialize(invalid); });
+ Assert.Throws(delegate { ResourceIdentity.DeserializeResourceIdentity(invalid); });
}
public JsonProperty DeserializerHelper(string filename)
@@ -166,14 +166,14 @@ public JsonProperty DeserializerHelper(string filename)
public void TestDeserializerInvalidNullType()
{
var identityJsonProperty = DeserializerHelper("InvalidTypeIsNull.json");
- Assert.Throws(delegate { ResourceIdentity.Deserialize(identityJsonProperty.Value); });
+ Assert.Throws(delegate { ResourceIdentity.DeserializeResourceIdentity(identityJsonProperty.Value); });
}
[TestCase]
public void TestDeserializerValidSystemAndUserAssigned()
{
var identityJsonProperty = DeserializerHelper("SystemAndUserAssignedValid.json");
- ResourceIdentity back = ResourceIdentity.Deserialize(identityJsonProperty.Value);
+ ResourceIdentity back = ResourceIdentity.DeserializeResourceIdentity(identityJsonProperty.Value);
Assert.IsTrue("22fdaec1-8b9f-49dc-bd72-ddaf8f215577".Equals(back.SystemAssignedIdentity.PrincipalId.ToString()));
Assert.IsTrue("72f988af-86f1-41af-91ab-2d7cd011db47".Equals(back.SystemAssignedIdentity.TenantId.ToString()));
var user = back.UserAssignedIdentities;
@@ -186,7 +186,7 @@ public void TestDeserializerValidSystemAndUserAssigned()
public void TestDeserializerInvalidType()
{
var identityJsonProperty = DeserializerHelper("InvalidType.json");
- ResourceIdentity back = ResourceIdentity.Deserialize(identityJsonProperty.Value);
+ ResourceIdentity back = ResourceIdentity.DeserializeResourceIdentity(identityJsonProperty.Value);
var user = back.UserAssignedIdentities;
Assert.AreEqual("/subscriptions/d96407f5-db8f-4325-b582-84ad21310bd8/resourceGroups/tester/providers/Microsoft.ManagedIdentity/userAssignedIdentities/testidentity", user.Keys.First().ToString());
Assert.AreEqual("9a2eaa6a-b49c-4a63-afb5-3b72e3e65422", user.Values.First().ClientId.ToString());
@@ -197,7 +197,7 @@ public void TestDeserializerInvalidType()
public void TestDeserializerValidInnerExtraField()
{
var identityJsonProperty = DeserializerHelper("SystemAndUserAssignedInnerExtraField.json");
- ResourceIdentity back = ResourceIdentity.Deserialize(identityJsonProperty.Value);
+ ResourceIdentity back = ResourceIdentity.DeserializeResourceIdentity(identityJsonProperty.Value);
Assert.IsTrue("22fddec1-8b9f-49dc-bd72-ddaf8f215577".Equals(back.SystemAssignedIdentity.PrincipalId.ToString()));
Assert.IsTrue("72f988bf-86f1-41af-91ab-2d7cd011db47".Equals(back.SystemAssignedIdentity.TenantId.ToString()));
var user = back.UserAssignedIdentities;
@@ -210,7 +210,7 @@ public void TestDeserializerValidInnerExtraField()
public void TestDeserializerValidMiddleExtraField()
{
var identityJsonProperty = DeserializerHelper("SystemAndUserAssignedMiddleExtraField.json");
- ResourceIdentity back = ResourceIdentity.Deserialize(identityJsonProperty.Value);
+ ResourceIdentity back = ResourceIdentity.DeserializeResourceIdentity(identityJsonProperty.Value);
Assert.IsTrue("22fddec1-8b9f-49dc-bd72-ddaf8f215577".Equals(back.SystemAssignedIdentity.PrincipalId.ToString()));
Assert.IsTrue("72f988bf-86f1-41af-91ab-2d7cd011db47".Equals(back.SystemAssignedIdentity.TenantId.ToString()));
var user = back.UserAssignedIdentities;
@@ -226,7 +226,7 @@ public void TestDeserializerValidOuterExtraField()
JsonDocument document = JsonDocument.Parse(json);
JsonElement rootElement = document.RootElement;
var identityJsonProperty = rootElement.EnumerateObject().ElementAt(1);
- ResourceIdentity back = ResourceIdentity.Deserialize(identityJsonProperty.Value);
+ ResourceIdentity back = ResourceIdentity.DeserializeResourceIdentity(identityJsonProperty.Value);
Assert.IsTrue("22fddec1-8b9f-49dc-bd72-ddaf8f215577".Equals(back.SystemAssignedIdentity.PrincipalId.ToString()));
Assert.IsTrue("72f988bf-86f1-41af-91ab-2d7cd011db47".Equals(back.SystemAssignedIdentity.TenantId.ToString()));
var user = back.UserAssignedIdentities;
@@ -239,7 +239,7 @@ public void TestDeserializerValidOuterExtraField()
public void TestDeserializerValidSystemAndMultUser()
{
var identityJsonProperty = DeserializerHelper("SystemAndUserAssignedValidMultIdentities.json");
- ResourceIdentity back = ResourceIdentity.Deserialize(identityJsonProperty.Value);
+ ResourceIdentity back = ResourceIdentity.DeserializeResourceIdentity(identityJsonProperty.Value);
Assert.IsTrue("22fddec1-8b9f-49dc-bd72-ddaf8f215570".Equals(back.SystemAssignedIdentity.PrincipalId.ToString()));
Assert.IsTrue("72f988bf-86f1-41af-91ab-2d7cd011db40".Equals(back.SystemAssignedIdentity.TenantId.ToString()));
var user = back.UserAssignedIdentities;
@@ -255,7 +255,7 @@ public void TestDeserializerValidSystemAndMultUser()
public void TestDeserializerValidSystemAssigned()
{
var identityJsonProperty = DeserializerHelper("SystemAssigned.json");
- ResourceIdentity back = ResourceIdentity.Deserialize(identityJsonProperty.Value);
+ ResourceIdentity back = ResourceIdentity.DeserializeResourceIdentity(identityJsonProperty.Value);
Assert.IsTrue("22fddec1-8b9f-49dc-bd72-ddaf8f215577".Equals(back.SystemAssignedIdentity.PrincipalId.ToString()));
Assert.IsTrue("72f988bf-86f1-41af-91ab-2d7cd011db47".Equals(back.SystemAssignedIdentity.TenantId.ToString()));
Assert.IsTrue(back.UserAssignedIdentities.Count == 0);
@@ -265,7 +265,7 @@ public void TestDeserializerValidSystemAssigned()
public void TestDeserializerValidUserAssigned()
{
var identityJsonProperty = DeserializerHelper("UserAssigned.json");
- ResourceIdentity back = ResourceIdentity.Deserialize(identityJsonProperty.Value);
+ ResourceIdentity back = ResourceIdentity.DeserializeResourceIdentity(identityJsonProperty.Value);
Assert.IsNull(back.SystemAssignedIdentity);
var user = back.UserAssignedIdentities;
Assert.AreEqual("/subscriptions/db1ab6f0-4769-4b2e-930e-01e2ef9c123c/resourceGroups/tester-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/testidentity", user.Keys.First().ToString());
diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/Unit/PropertyReferenceTypeTests.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/Unit/PropertyReferenceTypeTests.cs
new file mode 100644
index 000000000000..e78c36d909b7
--- /dev/null
+++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/Unit/PropertyReferenceTypeTests.cs
@@ -0,0 +1,58 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using NUnit.Framework;
+
+namespace Azure.ResourceManager.Core.Tests
+{
+ public class PropertyReferenceTypeTests
+ {
+ private static readonly Type PropertyReferenceTypeAttribute = typeof(PropertyReferenceTypeAttribute);
+ private static readonly Type SerializationConstructor = typeof(SerializationConstructorAttribute);
+ private static readonly Type InitializationConstructor = typeof(InitializationConstructorAttribute);
+ private static readonly IEnumerable AssemblyTypes = typeof(ArmClient).Assembly.GetTypes();
+
+ [Test]
+ public void ValidatePropertyReferenceTypeAttribute()
+ {
+ var type = typeof(PropertyReferenceTypeAttribute);
+ var fieldInfo = type.GetProperties(BindingFlags.Instance | BindingFlags.Public).FirstOrDefault(p => p.Name == "SkipTypes");
+ Assert.NotNull(fieldInfo, $"Field 'SkipTypes' is not found");
+ Assert.AreEqual(fieldInfo.PropertyType, typeof(Type[]));
+ Assert.True(fieldInfo.CanRead);
+ Assert.False(fieldInfo.CanWrite);
+ }
+
+ [Test]
+ public void ValidateSerializationConstructor()
+ {
+ foreach (var refType in AssemblyTypes.Where(t => HasAttribute(t.GetCustomAttributes(false), PropertyReferenceTypeAttribute)))
+ {
+ var serializationCtor = refType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
+ .Where(c => HasAttribute(c.GetCustomAttributes(false), SerializationConstructor)).FirstOrDefault();
+ Assert.IsNotNull(serializationCtor);
+ Assert.IsTrue(serializationCtor.IsPublic, $"Serialization ctor for {refType.Name} should not be public");
+ }
+ }
+
+ [Test]
+ public void ValidateInitializationConstructor()
+ {
+ foreach (var refType in AssemblyTypes.Where(t => HasAttribute(t.GetCustomAttributes(false), PropertyReferenceTypeAttribute)))
+ {
+ var initializationCtor = refType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
+ .Where(c => HasAttribute(c.GetCustomAttributes(false), InitializationConstructor)).FirstOrDefault();
+ Assert.IsNotNull(initializationCtor);
+ Assert.IsTrue(refType.IsAbstract == initializationCtor.IsFamily, $"If {refType.Name} is abstract then its initialization ctor should be protected");
+ Assert.IsTrue(refType.IsAbstract != initializationCtor.IsPublic, $"If {refType.Name} is abstract then its initialization ctor should be public");
+ Assert.IsFalse(initializationCtor.IsAssembly, $"Initialization ctor for {refType.Name} should not be internal");
+ }
+ }
+
+ public bool HasAttribute(IEnumerable list, Type attributeType)
+ {
+ return list.FirstOrDefault(a => a.GetType() == attributeType) is not null;
+ }
+ }
+}