Skip to content

Commit f0b3777

Browse files
authored
Use MRW for ManagedServiceIdentity serialization (Azure#50705)
1 parent 0a9e9e6 commit f0b3777

14 files changed

+187
-179
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using System;
5+
using System.ClientModel.Primitives;
6+
using System.Collections.Generic;
7+
using Azure.ResourceManager.Models;
8+
9+
namespace Azure.ResourceManager
10+
{
11+
public partial class AzureResourceManagerContext
12+
{
13+
partial void AddAdditionalFactories(Dictionary<Type, Func<ModelReaderWriterTypeBuilder>> factories)
14+
{
15+
factories.Add(typeof(ManagedServiceIdentity), () => new ManagedServiceIdentityTypeBuilder());
16+
}
17+
18+
private class ManagedServiceIdentityTypeBuilder : ModelReaderWriterTypeBuilder
19+
{
20+
protected override Type BuilderType => typeof(ManagedServiceIdentity);
21+
22+
protected override object CreateInstance()
23+
{
24+
return new ManagedServiceIdentity(ManagedServiceIdentityType.None);
25+
}
26+
}
27+
}
28+
}

sdk/resourcemanager/Azure.ResourceManager/src/Common/Custom/Models/ManagedServiceIdentity.Serialization.cs

Lines changed: 79 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,55 @@ namespace Azure.ResourceManager.Models
1818
[JsonConverter(typeof(ManagedServiceIdentityConverter))]
1919
public partial class ManagedServiceIdentity : IJsonModel<ManagedServiceIdentity>
2020
{
21-
internal void Write(Utf8JsonWriter writer, ModelReaderWriterOptions options, JsonSerializerOptions jOptions = default)
21+
private const string SystemAssignedUserAssignedV3Value = "SystemAssigned,UserAssigned";
22+
23+
// This method checks if the format string in options.Format ends with the "|v3" suffix.
24+
// The "|v3" suffix indicates that the ManagedServiceIdentityType format is version 3.
25+
// If the suffix is present, it is removed, and the base format is returned via the 'format' parameter.
26+
// This allows the method to handle version-specific logic while preserving the base format.
27+
private static bool UseManagedServiceIdentityV3(ModelReaderWriterOptions options, out string format)
28+
{
29+
var originalFormat = options.Format.AsSpan();
30+
if (originalFormat.Length > 3)
31+
{
32+
var v3Format = "|v3".AsSpan();
33+
if (originalFormat.EndsWith(v3Format))
34+
{
35+
format = originalFormat.Slice(0, originalFormat.Length - v3Format.Length).ToString();
36+
return true;
37+
}
38+
}
39+
40+
format = options.Format;
41+
return false;
42+
}
43+
44+
void IJsonModel<ManagedServiceIdentity>.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options)
2245
{
23-
var format = options.Format == "W" ? ((IPersistableModel<ManagedServiceIdentity>)this).GetFormatFromOptions(options) : options.Format;
46+
var useManagedServiceIdentityV3 = UseManagedServiceIdentityV3(options, out string optionsFormat);
47+
var format = optionsFormat == "W" ? ((IPersistableModel<ManagedServiceIdentity>)this).GetFormatFromOptions(options) : optionsFormat;
2448
if (format != "J")
2549
{
2650
throw new FormatException($"The model {nameof(ManagedServiceIdentity)} does not support '{format}' format.");
2751
}
2852

2953
writer.WriteStartObject();
30-
JsonSerializer.Serialize(writer, ManagedServiceIdentityType, jOptions);
31-
if (options.Format != "W" && Optional.IsDefined(PrincipalId))
54+
writer.WritePropertyName("type"u8);
55+
if (useManagedServiceIdentityV3 && ManagedServiceIdentityType == ManagedServiceIdentityType.SystemAssignedUserAssigned)
56+
{
57+
writer.WriteStringValue(SystemAssignedUserAssignedV3Value);
58+
}
59+
else
60+
{
61+
writer.WriteStringValue(ManagedServiceIdentityType.ToString());
62+
}
63+
64+
if (optionsFormat != "W" && Optional.IsDefined(PrincipalId))
3265
{
3366
writer.WritePropertyName("principalId"u8);
3467
writer.WriteStringValue(PrincipalId.Value);
3568
}
36-
if (options.Format != "W" && Optional.IsDefined(TenantId))
69+
if (optionsFormat != "W" && Optional.IsDefined(TenantId))
3770
{
3871
writer.WritePropertyName("tenantId"u8);
3972
writer.WriteStringValue(TenantId.Value);
@@ -45,21 +78,24 @@ internal void Write(Utf8JsonWriter writer, ModelReaderWriterOptions options, Jso
4578
foreach (var item in UserAssignedIdentities)
4679
{
4780
writer.WritePropertyName(item.Key);
48-
JsonSerializer.Serialize(writer, item.Value);
81+
if (item.Value is null)
82+
{
83+
writer.WriteNullValue();
84+
}
85+
else
86+
{
87+
((IJsonModel<UserAssignedIdentity>)item.Value).Write(writer, new ModelReaderWriterOptions(optionsFormat));
88+
}
4989
}
5090
writer.WriteEndObject();
5191
}
5292
writer.WriteEndObject();
5393
}
5494

55-
void IJsonModel<ManagedServiceIdentity>.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options)
56-
{
57-
Write(writer, options, null);
58-
}
59-
6095
ManagedServiceIdentity IJsonModel<ManagedServiceIdentity>.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options)
6196
{
62-
var format = options.Format == "W" ? ((IPersistableModel<ManagedServiceIdentity>)this).GetFormatFromOptions(options) : options.Format;
97+
UseManagedServiceIdentityV3(options, out string optionsFormat);
98+
var format = optionsFormat == "W" ? ((IPersistableModel<ManagedServiceIdentity>)this).GetFormatFromOptions(options) : optionsFormat;
6399
if (format != "J")
64100
{
65101
throw new FormatException($"The model {nameof(ManagedServiceIdentity)} does not support '{format}' format.");
@@ -71,7 +107,8 @@ ManagedServiceIdentity IJsonModel<ManagedServiceIdentity>.Create(ref Utf8JsonRea
71107

72108
BinaryData IPersistableModel<ManagedServiceIdentity>.Write(ModelReaderWriterOptions options)
73109
{
74-
var format = options.Format == "W" ? ((IPersistableModel<ManagedServiceIdentity>)this).GetFormatFromOptions(options) : options.Format;
110+
UseManagedServiceIdentityV3(options, out string optionsFormat);
111+
var format = optionsFormat == "W" ? ((IPersistableModel<ManagedServiceIdentity>)this).GetFormatFromOptions(options) : optionsFormat;
75112

76113
switch (format)
77114
{
@@ -80,7 +117,7 @@ BinaryData IPersistableModel<ManagedServiceIdentity>.Write(ModelReaderWriterOpti
80117
case "bicep":
81118
return SerializeBicep(options);
82119
default:
83-
throw new FormatException($"The model {nameof(ManagedServiceIdentity)} does not support '{options.Format}' format.");
120+
throw new FormatException($"The model {nameof(ManagedServiceIdentity)} does not support '{format}' format.");
84121
}
85122
}
86123

@@ -168,14 +205,17 @@ private void AppendChildObject(StringBuilder stringBuilder, object childObject,
168205
}
169206
}
170207

171-
internal static ManagedServiceIdentity DeserializeManagedServiceIdentity(JsonElement element, ModelReaderWriterOptions options, JsonSerializerOptions jOptions)
208+
internal static ManagedServiceIdentity DeserializeManagedServiceIdentity(JsonElement element, ModelReaderWriterOptions options = null)
172209
{
173210
options ??= new ModelReaderWriterOptions("W");
211+
var useManagedServiceIdentityV3 = UseManagedServiceIdentityV3(options, out string format);
212+
options = new ModelReaderWriterOptions(format);
174213

175214
if (element.ValueKind == JsonValueKind.Null)
176215
{
177216
return null;
178217
}
218+
179219
Guid? principalId = default;
180220
Guid? tenantId = default;
181221
ManagedServiceIdentityType type = default;
@@ -202,7 +242,15 @@ internal static ManagedServiceIdentity DeserializeManagedServiceIdentity(JsonEle
202242
}
203243
if (property.NameEquals("type"u8))
204244
{
205-
type = JsonSerializer.Deserialize<ManagedServiceIdentityType>($"{{{property}}}", jOptions);
245+
var propertyValue = property.Value.GetString();
246+
if (useManagedServiceIdentityV3 && propertyValue == SystemAssignedUserAssignedV3Value)
247+
{
248+
type = ManagedServiceIdentityType.SystemAssignedUserAssigned;
249+
}
250+
else
251+
{
252+
type = new ManagedServiceIdentityType(propertyValue);
253+
}
206254
continue;
207255
}
208256
if (property.NameEquals("userAssignedIdentities"u8))
@@ -214,7 +262,7 @@ internal static ManagedServiceIdentity DeserializeManagedServiceIdentity(JsonEle
214262
Dictionary<ResourceIdentifier, UserAssignedIdentity> dictionary = new Dictionary<ResourceIdentifier, UserAssignedIdentity>();
215263
foreach (var property0 in property.Value.EnumerateObject())
216264
{
217-
dictionary.Add(new ResourceIdentifier(property0.Name), JsonSerializer.Deserialize<UserAssignedIdentity>(property0.Value.GetRawText()));
265+
dictionary.Add(new ResourceIdentifier(property0.Name), ModelReaderWriter.Read<UserAssignedIdentity>(new BinaryData(Encoding.UTF8.GetBytes(property0.Value.GetRawText())), options, AzureResourceManagerContext.Default));
218266
}
219267
userAssignedIdentities = dictionary;
220268
continue;
@@ -223,14 +271,11 @@ internal static ManagedServiceIdentity DeserializeManagedServiceIdentity(JsonEle
223271
return new ManagedServiceIdentity(principalId, tenantId, type, userAssignedIdentities ?? new ChangeTrackingDictionary<ResourceIdentifier, UserAssignedIdentity>());
224272
}
225273

226-
internal static ManagedServiceIdentity DeserializeManagedServiceIdentity(JsonElement element, ModelReaderWriterOptions options = null)
227-
{
228-
return DeserializeManagedServiceIdentity(element, options, null);
229-
}
230-
231274
ManagedServiceIdentity IPersistableModel<ManagedServiceIdentity>.Create(BinaryData data, ModelReaderWriterOptions options)
232275
{
233-
var format = options.Format == "W" ? ((IPersistableModel<ManagedServiceIdentity>)this).GetFormatFromOptions(options) : options.Format;
276+
options ??= new ModelReaderWriterOptions("W");
277+
var useManagedServiceIdentityV3 = UseManagedServiceIdentityV3(options, out string optionsFormat);
278+
var format = optionsFormat == "W" ? ((IPersistableModel<ManagedServiceIdentity>)this).GetFormatFromOptions(options) : optionsFormat;
234279

235280
switch (format)
236281
{
@@ -240,22 +285,30 @@ ManagedServiceIdentity IPersistableModel<ManagedServiceIdentity>.Create(BinaryDa
240285
return DeserializeManagedServiceIdentity(document.RootElement, options);
241286
}
242287
default:
243-
throw new FormatException($"The model {nameof(ManagedServiceIdentity)} does not support '{options.Format}' format.");
288+
throw new FormatException($"The model {nameof(ManagedServiceIdentity)} does not support '{format}' format.");
244289
}
245290
}
246291

247292
string IPersistableModel<ManagedServiceIdentity>.GetFormatFromOptions(ModelReaderWriterOptions options) => "J";
248293

249294
internal partial class ManagedServiceIdentityConverter : JsonConverter<ManagedServiceIdentity>
250295
{
296+
private static readonly ModelReaderWriterOptions V3Options = new ModelReaderWriterOptions("W|v3");
297+
298+
// This method checks if the ManagedServiceIdentityTypeV3Converter exists and it indicates that the ManagedServiceIdentityType format is version 3.
299+
// Then, the format string in options.Format should be "W|v3", otherwise the default options.Format is "W".
300+
// TODO: Remove this method when ManagedServiceIdentityTypeV3Converter is removed from the codebase after we apply the latest genertor changes.
301+
private bool UseManagedServiceIdentityV3(JsonSerializerOptions options)
302+
=> options is not null && options.Converters.Any(x => x.ToString().EndsWith("ManagedServiceIdentityTypeV3Converter"));
303+
251304
public override void Write(Utf8JsonWriter writer, ManagedServiceIdentity model, JsonSerializerOptions options)
252305
{
253-
model.Write(writer, new ModelReaderWriterOptions("W"), options);
306+
((IJsonModel<ManagedServiceIdentity>)model).Write(writer, UseManagedServiceIdentityV3(options) ? V3Options : new ModelReaderWriterOptions("W"));
254307
}
255308
public override ManagedServiceIdentity Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
256309
{
257310
using var document = JsonDocument.ParseValue(ref reader);
258-
return DeserializeManagedServiceIdentity(document.RootElement, null, options);
311+
return DeserializeManagedServiceIdentity(document.RootElement, UseManagedServiceIdentityV3(options) ? V3Options : null);
259312
}
260313
}
261314
}

sdk/resourcemanager/Azure.ResourceManager/tests/Scenario/PolicyAssignmentCollectionTests.cs

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,8 @@
44
using System;
55
using System.Collections.Generic;
66
using System.Threading.Tasks;
7-
using Azure.Core;
87
using Azure.Core.TestFramework;
98
using Azure.ResourceManager.ManagementGroups;
10-
using Azure.ResourceManager.ManagementGroups.Models;
11-
using Azure.ResourceManager.Models;
129
using Azure.ResourceManager.Resources;
1310
using Azure.ResourceManager.Resources.Models;
1411
using NUnit.Framework;
@@ -112,45 +109,12 @@ public async Task Get()
112109
Assert.ThrowsAsync<ArgumentNullException>(async () => _ = await rg.GetPolicyAssignments().GetAsync(null));
113110
}
114111

115-
#pragma warning disable CS0618 // This type is obsolete and will be removed in a future release.
116-
[TestCase]
117-
[RecordedTest]
118-
public async Task TestManagedIdentity()
119-
{
120-
SubscriptionResource subscription = await Client.GetDefaultSubscriptionAsync();
121-
string rgName = Recording.GenerateAssetName("testRg-");
122-
ResourceGroupResource rg = await CreateResourceGroup(subscription, rgName);
123-
string policyAssignmentName = Recording.GenerateAssetName("polAssign-");
124-
PolicyAssignmentData input = new PolicyAssignmentData
125-
{
126-
DisplayName = $"Test ${policyAssignmentName}",
127-
PolicyDefinitionId = "/providers/Microsoft.Authorization/policyDefinitions/06a78e20-9358-41c9-923c-fb736d382a4d",
128-
Identity = new SystemAssignedServiceIdentity(SystemAssignedServiceIdentityType.SystemAssigned),
129-
Location = AzureLocation.WestUS
130-
};
131-
Assert.AreEqual(SystemAssignedServiceIdentityType.SystemAssigned, input.Identity.SystemAssignedServiceIdentityType);
132-
Assert.AreEqual(ManagedServiceIdentityType.SystemAssigned, input.ManagedIdentity.ManagedServiceIdentityType);
133-
ArmOperation<PolicyAssignmentResource> lro = await rg.GetPolicyAssignments().CreateOrUpdateAsync(WaitUntil.Completed, policyAssignmentName, input);
134-
PolicyAssignmentResource policyAssignment = lro.Value;
135-
Assert.AreEqual(policyAssignmentName, policyAssignment.Data.Name);
136-
Assert.AreEqual(policyAssignment.Data.Identity.PrincipalId, policyAssignment.Data.ManagedIdentity.PrincipalId);
137-
Assert.AreEqual(policyAssignment.Data.Identity.TenantId, policyAssignment.Data.ManagedIdentity.TenantId);
138-
Assert.AreEqual(ManagedServiceIdentityType.SystemAssigned, policyAssignment.Data.ManagedIdentity.ManagedServiceIdentityType);
139-
Assert.AreEqual(SystemAssignedServiceIdentityType.SystemAssigned, policyAssignment.Data.Identity.SystemAssignedServiceIdentityType);
140-
policyAssignment.Data.ManagedIdentity.ManagedServiceIdentityType = ManagedServiceIdentityType.None;
141-
lro = await policyAssignment.UpdateAsync(WaitUntil.Completed, policyAssignment.Data);
142-
PolicyAssignmentResource updatedPolicyAssignment = lro.Value;
143-
Assert.AreEqual(ManagedServiceIdentityType.None, updatedPolicyAssignment.Data.ManagedIdentity.ManagedServiceIdentityType);
144-
Assert.AreEqual(SystemAssignedServiceIdentityType.None, updatedPolicyAssignment.Data.Identity.SystemAssignedServiceIdentityType);
145-
}
146-
147112
private void AssertValidPolicyAssignment(PolicyAssignmentResource model, PolicyAssignmentResource getResult)
148113
{
149114
Assert.AreEqual(model.Data.Name, getResult.Data.Name);
150115
Assert.AreEqual(model.Data.Id, getResult.Data.Id);
151116
Assert.AreEqual(model.Data.ResourceType, getResult.Data.ResourceType);
152117
Assert.AreEqual(model.Data.Location, getResult.Data.Location);
153-
Assert.AreEqual(model.Data.Identity, getResult.Data.Identity);
154118
Assert.AreEqual(model.Data.DisplayName, getResult.Data.DisplayName);
155119
Assert.AreEqual(model.Data.PolicyDefinitionId, getResult.Data.PolicyDefinitionId);
156120
Assert.AreEqual(model.Data.Scope, getResult.Data.Scope);
@@ -181,6 +145,5 @@ private void AssertValidPolicyAssignment(PolicyAssignmentResource model, PolicyA
181145
}
182146
}
183147
}
184-
#pragma warning restore CS0618 // This type is obsolete and will be removed in a future release.
185148
}
186149
}

sdk/resourcemanager/Azure.ResourceManager/tests/Unit/JsonAsserts.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1+
using System.ClientModel.Primitives;
12
using System.IO;
23
using System.Text;
34
using System.Text.Json;
45
using Azure.Core;
56
using NUnit.Framework;
67

8+
#nullable enable
9+
710
namespace Azure.ResourceManager.Tests
811
{
912
internal static class JsonAsserts
@@ -22,13 +25,14 @@ public static void AssertSerialization(string expected, IUtf8JsonSerializable se
2225
Assert.AreEqual(expected, text);
2326
}
2427

25-
public static void AssertConverterSerialization(string expected, object model, JsonSerializerOptions options = default)
28+
public static void AssertConverterSerialization<T>(string expected, T model, ModelReaderWriterOptions? options = null)
2629
{
2730
using var memoryStream = new MemoryStream();
2831

2932
using (var writer = new Utf8JsonWriter(memoryStream))
3033
{
31-
JsonSerializer.Serialize(writer, model, options);
34+
var jsonModel = model as IJsonModel<T>;
35+
jsonModel?.Write(writer, options ?? new ModelReaderWriterOptions("W"));
3236
}
3337

3438
var text = Encoding.UTF8.GetString(memoryStream.ToArray());

0 commit comments

Comments
 (0)