Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PropertyReferenceType attribute in ResourceIdentity #22255

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// An attribute class indicating a reference type for code generation.
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class PropertyReferenceTypeAttribute : Attribute
{
/// <summary>
/// Instatiate a new reference type attribute.
/// </summary>
/// <param name="skipTypes"> An array of types to skip for this reference type. </param>
public PropertyReferenceTypeAttribute(Type[] skipTypes)
{
SkipTypes = skipTypes;
}

/// <summary>
/// Instatiate a new reference type attribute.
/// </summary>
public PropertyReferenceTypeAttribute() : this(null)
{
}

/// <summary>
/// Get an array of types to skip for this reference type.
/// </summary>
public Type[] SkipTypes { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ namespace Azure.ResourceManager.Core
/// <summary>
/// Represents a managed identity
/// </summary>
[PropertyReferenceType(new Type[] { typeof(ResourceIdentityType) })]
public class ResourceIdentity : IEquatable<ResourceIdentity>
{
private const string SystemAssigned = "SystemAssigned";
Expand All @@ -20,6 +21,7 @@ public class ResourceIdentity : IEquatable<ResourceIdentity>
/// <summary>
/// Initializes a new instance of the <see cref="ResourceIdentity"/> class.
/// </summary>
[InitializationConstructor]
public ResourceIdentity()
: this(null, false)
{
Expand All @@ -30,6 +32,7 @@ public ResourceIdentity()
/// </summary>
/// <param name="user"> Dictionary with a <see cref="ResourceIdentifier"/> key and a <see cref="UserAssignedIdentity"/> object value. </param>
/// <param name="useSystemAssigned"> Flag for using <see cref="SystemAssignedIdentity"/> or not. </param>
[SerializationConstructor]
public ResourceIdentity(Dictionary<ResourceGroupResourceIdentifier, UserAssignedIdentity> user, bool useSystemAssigned)
{
// check for combination of user and system on the impact to type value
Expand Down Expand Up @@ -78,7 +81,9 @@ public ResourceIdentity(SystemAssignedIdentity systemAssigned, IDictionary<Resou
/// </summary>
/// <param name="element"> A <see cref="JsonElement"/> containing an <see cref="ResourceIdentity"/>. </param>
/// <returns> New Identity object with JSON values. </returns>
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)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ public UserAssignedIdentity(Guid clientId, Guid principalId)
/// <summary>
/// Gets or sets the Client ID.
/// </summary>
public Guid ClientId { get; set; }
public Guid ClientId { get; }

/// <summary>
/// Gets or sets the Principal ID.
/// </summary>
public Guid PrincipalId { get; set; }
public Guid PrincipalId { get; }

/// <summary>
/// Converts a <see cref="JsonElement"/> into an <see cref="UserAssignedIdentity"/> object.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ public void EqualsTestFalseSameKey()
public void TestDeserializerInvalidDefaultJson()
{
JsonElement invalid = default(JsonElement);
Assert.Throws<ArgumentException>(delegate { ResourceIdentity.Deserialize(invalid); });
Assert.Throws<ArgumentException>(delegate { ResourceIdentity.DeserializeResourceIdentity(invalid); });
}

public JsonProperty DeserializerHelper(string filename)
Expand All @@ -166,14 +166,14 @@ public JsonProperty DeserializerHelper(string filename)
public void TestDeserializerInvalidNullType()
{
var identityJsonProperty = DeserializerHelper("InvalidTypeIsNull.json");
Assert.Throws<InvalidOperationException>(delegate { ResourceIdentity.Deserialize(identityJsonProperty.Value); });
Assert.Throws<InvalidOperationException>(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;
Expand All @@ -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());
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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);
Expand All @@ -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());
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Type> 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<Attribute>(false), PropertyReferenceTypeAttribute)))
{
var serializationCtor = refType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Where(c => HasAttribute(c.GetCustomAttributes<Attribute>(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<Attribute>(false), PropertyReferenceTypeAttribute)))
{
var initializationCtor = refType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Where(c => HasAttribute(c.GetCustomAttributes<Attribute>(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<Attribute> list, Type attributeType)
{
return list.FirstOrDefault(a => a.GetType() == attributeType) is not null;
}
}
}