From d80bb9e137d0912cc7c8689fd0e32044fc0de820 Mon Sep 17 00:00:00 2001 From: Beisi Zhou Date: Mon, 18 Dec 2023 17:27:47 +0800 Subject: [PATCH 01/30] wip --- .../BuildTime/Cmdlets/ExportProxyCmdlet.cs | 24 +++++++++++++++++ .../BuildTime/Models/PsProxyTypes.cs | 27 ++++++++++++------- .../psruntime/BuildTime/PsExtensions.cs | 16 +++++++++++ 3 files changed, 57 insertions(+), 10 deletions(-) diff --git a/powershell/resources/psruntime/BuildTime/Cmdlets/ExportProxyCmdlet.cs b/powershell/resources/psruntime/BuildTime/Cmdlets/ExportProxyCmdlet.cs index 0a6ff3d27c..39feed050e 100644 --- a/powershell/resources/psruntime/BuildTime/Cmdlets/ExportProxyCmdlet.cs +++ b/powershell/resources/psruntime/BuildTime/Cmdlets/ExportProxyCmdlet.cs @@ -103,6 +103,30 @@ protected override void ProcessRecord() sb.Append("param("); sb.Append($"{(parameterGroups.Any() ? Environment.NewLine : String.Empty)}"); + + + var identityTypeParameterGroup = parameterGroups.Where(pg => pg.IsIdentityTypeParameterGroup()).FirstOrDefault(); + if (identityTypeParameterGroup != null) + { + if (identityTypeParameterGroup.CompleterInfo is PSArgumentCompleterInfo psArgInfo && + (psArgInfo.ResourceTypes.Contains("SystemAssigned") || psArgInfo.ResourceTypes.Contains("SystemAssigned,UserAssigned"))) + { + var enableSystemAssignedIdentityParameterGroup = identityTypeParameterGroup.SallowCopy(); + enableSystemAssignedIdentityParameterGroup.ParameterName = "EnableSystemAssignedIdentity"; + enableSystemAssignedIdentityParameterGroup.ParameterType = typeof(SwitchParameter); + enableSystemAssignedIdentityParameterGroup.CompleterInfo = null; + parameterGroups.Add(enableSystemAssignedIdentityParameterGroup); + }; + identityTypeParameterGroup.Parameters[0].DontShow = true; + identityTypeParameterGroup.DontShow = true; + } + + var userAssignedIdentityParameter = parameterGroups.Where(p => p.IsUserAssignedIdentityParameterGroup()).FirstOrDefault(); + if (userAssignedIdentityParameter != null) + { + userAssignedIdentityParameter.ParameterType = typeof(string[]); + } + foreach (var parameterGroup in parameterGroups) { var parameters = parameterGroup.HasAllVariants ? parameterGroup.Parameters.Take(1) : parameterGroup.Parameters; diff --git a/powershell/resources/psruntime/BuildTime/Models/PsProxyTypes.cs b/powershell/resources/psruntime/BuildTime/Models/PsProxyTypes.cs index 7dda8178ef..af73f9d4fb 100644 --- a/powershell/resources/psruntime/BuildTime/Models/PsProxyTypes.cs +++ b/powershell/resources/psruntime/BuildTime/Models/PsProxyTypes.cs @@ -31,7 +31,7 @@ internal class VariantGroup { public string ModuleName { get; } - public string RootModuleName {get => @"${$project.rootModuleName}";} + public string RootModuleName { get => @"${$project.rootModuleName}"; } public string CmdletName { get; } public string CmdletVerb { get; } public string CmdletNoun { get; } @@ -175,13 +175,13 @@ public Variant(string cmdletName, string variantName, CommandInfo info, CommandM internal class ParameterGroup { - public string ParameterName { get; } + public string ParameterName { get; internal set; } public Parameter[] Parameters { get; } public string[] VariantNames { get; } public string[] AllVariantNames { get; } public bool HasAllVariants { get; } - public Type ParameterType { get; } + public Type ParameterType { get; internal set; } public string Description { get; } public string[] Aliases { get; } @@ -191,7 +191,7 @@ internal class ParameterGroup public DefaultInfo DefaultInfo { get; } public bool HasDefaultInfo { get; } public ParameterCategory OrderCategory { get; } - public bool DontShow { get; } + public bool DontShow { get; internal set; } public bool IsMandatory { get; } public bool SupportsWildcards { get; } public bool IsComplexInterface { get; } @@ -249,6 +249,11 @@ public ParameterGroup(string parameterName, Parameter[] parameters, string[] all ValueFromPipelineByPropertyName = Parameters.Any(p => p.ValueFromPipelineByPropertyName); IsInputType = ValueFromPipeline || ValueFromPipelineByPropertyName; } + + internal ParameterGroup SallowCopy() + { + return (ParameterGroup)this.MemberwiseClone(); + } } internal class Parameter @@ -257,7 +262,7 @@ internal class Parameter public string ParameterName { get; } public ParameterMetadata Metadata { get; } public PsParameterHelpInfo HelpInfo { get; } - public Type ParameterType { get; } + public Type ParameterType { get; internal set; } public Attribute[] Attributes { get; } public ParameterCategory[] Categories { get; } @@ -273,7 +278,7 @@ internal class Parameter public bool ValueFromPipeline { get; } public bool ValueFromPipelineByPropertyName { get; } public int? Position { get; } - public bool DontShow { get; } + public bool DontShow { get; internal set; } public bool IsMandatory { get; set; } public InfoAttribute InfoAttribute { get; } @@ -323,6 +328,7 @@ public Parameter(string variantName, string parameterName, ParameterMetadata met IsComplexInterface = ComplexInterfaceInfo.IsComplexInterface; Description = $"{description}{(IsComplexInterface ? complexMessage : String.Empty)}"; } + } internal class ComplexInterfaceInfo @@ -334,7 +340,7 @@ internal class ComplexInterfaceInfo public bool Required { get; } public bool ReadOnly { get; } public string Description { get; } - + public ComplexInterfaceInfo[] NestedInfos { get; } public bool IsComplexInterface { get; } @@ -351,7 +357,7 @@ public ComplexInterfaceInfo(string name, Type type, InfoAttribute infoAttribute, var unwrappedType = Type.Unwrap(); var hasBeenSeen = seenTypes?.Contains(unwrappedType) ?? false; (seenTypes ?? (seenTypes = new List())).Add(unwrappedType); - NestedInfos = hasBeenSeen ? new ComplexInterfaceInfo[]{} : + NestedInfos = hasBeenSeen ? new ComplexInterfaceInfo[] { } : unwrappedType.GetInterfaces() .Concat(InfoAttribute.PossibleTypes) .SelectMany(pt => pt.GetProperties() @@ -440,7 +446,7 @@ public CompleterInfo(ArgumentCompleterAttribute completerAttribute) } } - internal class PSArgumentCompleterInfo: CompleterInfo + internal class PSArgumentCompleterInfo : CompleterInfo { public string[] ResourceTypes { get; } @@ -511,7 +517,8 @@ public static Parameter[] ToParameters(this Variant variant) parameterHelp = parameterHelp.Where(ph => (!ph.ParameterSetNames.Any() || ph.ParameterSetNames.Any(psn => psn == variant.VariantName || psn == AllParameterSets)) && ph.Name != "IncludeTotalCount"); } var result = parameters.Select(p => new Parameter(variant.VariantName, p.Key, p.Value, parameterHelp.FirstOrDefault(ph => ph.Name == p.Key))); - if (variant.SupportsPaging) { + if (variant.SupportsPaging) + { // If supportsPaging is set, we will need to add First and Skip parameters since they are treated as common parameters which as not contained on Metadata>parameters variant.Info.Parameters["First"].Attributes.OfType().FirstOrDefault(pa => pa.ParameterSetName == variant.VariantName || pa.ParameterSetName == AllParameterSets).HelpMessage = "Gets only the first 'n' objects."; variant.Info.Parameters["Skip"].Attributes.OfType().FirstOrDefault(pa => pa.ParameterSetName == variant.VariantName || pa.ParameterSetName == AllParameterSets).HelpMessage = "Ignores the first 'n' objects and then gets the remaining objects."; diff --git a/powershell/resources/psruntime/BuildTime/PsExtensions.cs b/powershell/resources/psruntime/BuildTime/PsExtensions.cs index 52083232dd..c8ef284b81 100644 --- a/powershell/resources/psruntime/BuildTime/PsExtensions.cs +++ b/powershell/resources/psruntime/BuildTime/PsExtensions.cs @@ -172,5 +172,21 @@ public static bool IsHidden(this Parameter parameter) { return parameter.Attributes.Any(attr => attr is DoNotExportAttribute); } + + public static bool IsIdentityTypeParameterGroup(this ParameterGroup parameterGroup) + { + const string IdentityTypeParameterName = "IdentityType"; + const string IdentityTypeParameterType = "System.String"; + return parameterGroup.ParameterName.Equals(IdentityTypeParameterName, StringComparison.InvariantCultureIgnoreCase) && + parameterGroup.ParameterType.FullName.Equals(IdentityTypeParameterType, StringComparison.InvariantCultureIgnoreCase); + } + + public static bool IsUserAssignedIdentityParameterGroup(this ParameterGroup parameterGroup) + { + const string UserAssignedIdentityParameterName = "UserAssignedIdentity"; + const string UserAssignedIdentityParameterType = "System.Collections.Hashtable"; + return parameterGroup.ParameterName.EndsWith(UserAssignedIdentityParameterName, StringComparison.InvariantCultureIgnoreCase) && + parameterGroup.ParameterType.FullName.Equals(UserAssignedIdentityParameterType, StringComparison.InvariantCultureIgnoreCase); + } } } From 88500ad0e339c22e4103a0cad8d37f431044366c Mon Sep 17 00:00:00 2001 From: Beisi Zhou Date: Wed, 20 Dec 2023 09:34:50 +0800 Subject: [PATCH 02/30] wip --- .../BuildTime/Cmdlets/ExportProxyCmdlet.cs | 3 +-- .../BuildTime/Models/PsProxyOutputs.cs | 26 ++++++++++++++++++- .../BuildTime/Models/PsProxyTypes.cs | 4 +-- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/powershell/resources/psruntime/BuildTime/Cmdlets/ExportProxyCmdlet.cs b/powershell/resources/psruntime/BuildTime/Cmdlets/ExportProxyCmdlet.cs index 39feed050e..07f5179cd5 100644 --- a/powershell/resources/psruntime/BuildTime/Cmdlets/ExportProxyCmdlet.cs +++ b/powershell/resources/psruntime/BuildTime/Cmdlets/ExportProxyCmdlet.cs @@ -115,10 +115,9 @@ protected override void ProcessRecord() enableSystemAssignedIdentityParameterGroup.ParameterName = "EnableSystemAssignedIdentity"; enableSystemAssignedIdentityParameterGroup.ParameterType = typeof(SwitchParameter); enableSystemAssignedIdentityParameterGroup.CompleterInfo = null; + parameterGroups.Remove(identityTypeParameterGroup); parameterGroups.Add(enableSystemAssignedIdentityParameterGroup); }; - identityTypeParameterGroup.Parameters[0].DontShow = true; - identityTypeParameterGroup.DontShow = true; } var userAssignedIdentityParameter = parameterGroups.Where(p => p.IsUserAssignedIdentityParameterGroup()).FirstOrDefault(); diff --git a/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs b/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs index e41b819583..6232687c4c 100644 --- a/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs +++ b/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs @@ -248,7 +248,7 @@ public override string ToString() => $@"begin {{ {Indent}{Indent}}} {Indent}{Indent}$parameterSet = $PSCmdlet.ParameterSetName {GetTelemetry()} -{GetParameterSetToCmdletMapping()}{GetDefaultValuesStatements()} +{GetParameterSetToCmdletMapping()}{GetDefaultValuesStatements()}{GetManagedIdentityMappingStatements()} {GetProcessCustomAttributesAtRuntime()} {Indent}{Indent}$wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand(($mapping[$parameterSet]), [System.Management.Automation.CommandTypes]::Cmdlet) {Indent}{Indent}$scriptCmd = {{& $wrappedCmd @PSBoundParameters}} @@ -295,6 +295,30 @@ private string GetDefaultValuesStatements() } return sb.ToString(); } + + private string GetManagedIdentityMappingStatements() + { + var sb = new StringBuilder(); + sb.AppendLine(); + sb.AppendLine($"{Indent}{Indent}$IdentityType = \"None\""); + sb.AppendLine($"{Indent}{Indent}if ($PSBoundParameters.ContainsKey('UserAssignedIdentity')) {{"); + sb.AppendLine($"{Indent}{Indent}{Indent}$UserAssignedIdentityHashTable = @{{}}"); + sb.AppendLine($"{Indent}{Indent}{Indent}$PSBoundParameters['UserAssignedIdentity'] | foreach {{$UserAssignedIdentityHashTable[$_] = @{{}} }}"); + sb.AppendLine($"{Indent}{Indent}{Indent}$PSBoundParameters['UserAssignedIdentity'] = $UserAssignedIdentityHashTable"); + sb.AppendLine($"{Indent}{Indent}{Indent}$IdentityType = \"UserAssigned\""); + sb.AppendLine($"{Indent}{Indent}}}"); + sb.AppendLine($"{Indent}{Indent}if ($PSBoundParameters.ContainsKey('EnableSystemAssignedIdentity')) {{"); + sb.AppendLine($"{Indent}{Indent}{Indent}if ($IdentityType -eq \"None\") {{"); + sb.AppendLine($"{Indent}{Indent}{Indent}{Indent}$IdentityType = \"SystemAssigned\""); + sb.AppendLine($"{Indent}{Indent}{Indent}}}"); + sb.AppendLine($"{Indent}{Indent}{Indent}else {{"); + sb.AppendLine($"{Indent}{Indent}{Indent}{Indent}$IdentityType = \"SystemAssigned,UserAssigned\""); + sb.AppendLine($"{Indent}{Indent}{Indent}}}"); + sb.AppendLine($"{Indent}{Indent}{Indent}$PSBoundParameters['IdentityType'] = $IdentityType"); + sb.AppendLine($"{Indent}{Indent}{Indent}$null = $PSBoundParameters.Remove('EnableSystemAssignedIdentity')"); + sb.Append($"{Indent}{Indent}}}"); + return sb.ToString(); + } } internal class ProcessOutput : BaseOutput diff --git a/powershell/resources/psruntime/BuildTime/Models/PsProxyTypes.cs b/powershell/resources/psruntime/BuildTime/Models/PsProxyTypes.cs index af73f9d4fb..dd15fb17e5 100644 --- a/powershell/resources/psruntime/BuildTime/Models/PsProxyTypes.cs +++ b/powershell/resources/psruntime/BuildTime/Models/PsProxyTypes.cs @@ -176,7 +176,7 @@ public Variant(string cmdletName, string variantName, CommandInfo info, CommandM internal class ParameterGroup { public string ParameterName { get; internal set; } - public Parameter[] Parameters { get; } + public Parameter[] Parameters { get; internal set; } public string[] VariantNames { get; } public string[] AllVariantNames { get; } @@ -187,7 +187,7 @@ internal class ParameterGroup public string[] Aliases { get; } public bool HasValidateNotNull { get; } public bool HasAllowEmptyArray { get; } - public CompleterInfo CompleterInfo { get; } + public CompleterInfo CompleterInfo { get; internal set; } public DefaultInfo DefaultInfo { get; } public bool HasDefaultInfo { get; } public ParameterCategory OrderCategory { get; } From c274b6cd086c339adb5d9c54df082cbe39584f93 Mon Sep 17 00:00:00 2001 From: Beisi Zhou Date: Wed, 20 Dec 2023 16:43:55 +0800 Subject: [PATCH 03/30] wip --- .../BuildTime/Cmdlets/ExportProxyCmdlet.cs | 22 ------- .../BuildTime/Models/PsProxyOutputs.cs | 65 +++++++++++++++++-- .../BuildTime/Models/PsProxyTypes.cs | 55 +++++++++++++--- .../psruntime/BuildTime/PsExtensions.cs | 4 +- 4 files changed, 107 insertions(+), 39 deletions(-) diff --git a/powershell/resources/psruntime/BuildTime/Cmdlets/ExportProxyCmdlet.cs b/powershell/resources/psruntime/BuildTime/Cmdlets/ExportProxyCmdlet.cs index 07f5179cd5..8cc63b9e80 100644 --- a/powershell/resources/psruntime/BuildTime/Cmdlets/ExportProxyCmdlet.cs +++ b/powershell/resources/psruntime/BuildTime/Cmdlets/ExportProxyCmdlet.cs @@ -104,28 +104,6 @@ protected override void ProcessRecord() sb.Append("param("); sb.Append($"{(parameterGroups.Any() ? Environment.NewLine : String.Empty)}"); - - var identityTypeParameterGroup = parameterGroups.Where(pg => pg.IsIdentityTypeParameterGroup()).FirstOrDefault(); - if (identityTypeParameterGroup != null) - { - if (identityTypeParameterGroup.CompleterInfo is PSArgumentCompleterInfo psArgInfo && - (psArgInfo.ResourceTypes.Contains("SystemAssigned") || psArgInfo.ResourceTypes.Contains("SystemAssigned,UserAssigned"))) - { - var enableSystemAssignedIdentityParameterGroup = identityTypeParameterGroup.SallowCopy(); - enableSystemAssignedIdentityParameterGroup.ParameterName = "EnableSystemAssignedIdentity"; - enableSystemAssignedIdentityParameterGroup.ParameterType = typeof(SwitchParameter); - enableSystemAssignedIdentityParameterGroup.CompleterInfo = null; - parameterGroups.Remove(identityTypeParameterGroup); - parameterGroups.Add(enableSystemAssignedIdentityParameterGroup); - }; - } - - var userAssignedIdentityParameter = parameterGroups.Where(p => p.IsUserAssignedIdentityParameterGroup()).FirstOrDefault(); - if (userAssignedIdentityParameter != null) - { - userAssignedIdentityParameter.ParameterType = typeof(string[]); - } - foreach (var parameterGroup in parameterGroups) { var parameters = parameterGroup.HasAllVariants ? parameterGroup.Parameters.Take(1) : parameterGroup.Parameters; diff --git a/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs b/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs index 6232687c4c..a36b27642b 100644 --- a/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs +++ b/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs @@ -297,22 +297,73 @@ private string GetDefaultValuesStatements() } private string GetManagedIdentityMappingStatements() + { + if (!VariantGroup.IsInternal && IsAzure && + (VariantGroup.CmdletVerb.Equals("New") || VariantGroup.CmdletVerb.Equals("Update"))) + { + return $@" +{Indent}{Indent}$IdentityType = 'None' +{GetUserAssignedIdentityMappingStatements()} +{GetSystemAssignedIdentityMappingStatements()} +"; + } + else + { + return ""; + } + } + + private string GetUserAssignedIdentityMappingStatements() { var sb = new StringBuilder(); - sb.AppendLine(); - sb.AppendLine($"{Indent}{Indent}$IdentityType = \"None\""); sb.AppendLine($"{Indent}{Indent}if ($PSBoundParameters.ContainsKey('UserAssignedIdentity')) {{"); sb.AppendLine($"{Indent}{Indent}{Indent}$UserAssignedIdentityHashTable = @{{}}"); sb.AppendLine($"{Indent}{Indent}{Indent}$PSBoundParameters['UserAssignedIdentity'] | foreach {{$UserAssignedIdentityHashTable[$_] = @{{}} }}"); sb.AppendLine($"{Indent}{Indent}{Indent}$PSBoundParameters['UserAssignedIdentity'] = $UserAssignedIdentityHashTable"); - sb.AppendLine($"{Indent}{Indent}{Indent}$IdentityType = \"UserAssigned\""); + sb.AppendLine($"{Indent}{Indent}{Indent}$IdentityType = 'UserAssigned'"); + sb.AppendLine($"{Indent}{Indent}{Indent}$PSBoundParameters['IdentityType'] = $IdentityType"); sb.AppendLine($"{Indent}{Indent}}}"); - sb.AppendLine($"{Indent}{Indent}if ($PSBoundParameters.ContainsKey('EnableSystemAssignedIdentity')) {{"); - sb.AppendLine($"{Indent}{Indent}{Indent}if ($IdentityType -eq \"None\") {{"); - sb.AppendLine($"{Indent}{Indent}{Indent}{Indent}$IdentityType = \"SystemAssigned\""); + return sb.ToString(); + } + + private string GetSystemAssignedIdentityMappingStatements() + { + if (VariantGroup.CmdletVerb.Equals("New")) + { + return GetSystemAssignedIdentityMappingStatementsForNewVerbCmdlet(); + } + else if (VariantGroup.CmdletVerb.Equals("Update")) + { + return GetSystemAssignedIdentityMappingStatementsForUpdateVerbCmdlet(); + } + return ""; + } + + private string GetSystemAssignedIdentityMappingStatementsForNewVerbCmdlet() + { + var sb = new StringBuilder(); + sb.AppendLine($"{Indent}{Indent}if ($PSBoundParameters.ContainsKey('EnableSystemAssignedIdentity') && $PSBoundParameters['EnableSystemAssignedIdentity'].IsPresent) {{"); + sb.AppendLine($"{Indent}{Indent}{Indent}if ($IdentityType -eq 'None') {{"); + sb.AppendLine($"{Indent}{Indent}{Indent}{Indent}$IdentityType = 'SystemAssigned'"); sb.AppendLine($"{Indent}{Indent}{Indent}}}"); sb.AppendLine($"{Indent}{Indent}{Indent}else {{"); - sb.AppendLine($"{Indent}{Indent}{Indent}{Indent}$IdentityType = \"SystemAssigned,UserAssigned\""); + sb.AppendLine($"{Indent}{Indent}{Indent}{Indent}$IdentityType = 'SystemAssigned,UserAssigned'"); + sb.AppendLine($"{Indent}{Indent}{Indent}}}"); + sb.AppendLine($"{Indent}{Indent}{Indent}$PSBoundParameters['IdentityType'] = $IdentityType"); + sb.AppendLine($"{Indent}{Indent}{Indent}$null = $PSBoundParameters.Remove('EnableSystemAssignedIdentity')"); + sb.Append($"{Indent}{Indent}}}"); + return sb.ToString(); + } + + private string GetSystemAssignedIdentityMappingStatementsForUpdateVerbCmdlet() + { + var sb = new StringBuilder(); + sb.AppendLine($"{Indent}{Indent}if ($PSBoundParameters.ContainsKey('EnableSystemAssignedIdentity')) {{"); + sb.AppendLine($"{Indent}{Indent}{Indent}if ($IdentityType -eq 'None' && $PSBoundParameters['EnableSystemAssignedIdentity']) {{"); + sb.AppendLine($"{Indent}{Indent}{Indent}{Indent}$IdentityType = 'SystemAssigned'"); + sb.AppendLine($"{Indent}{Indent}{Indent}}}"); + sb.AppendLine($"{Indent}{Indent}{Indent}elseif($IdentityType -eq 'UserAssigned' && $PSBoundParameters['EnableSystemAssignedIdentity']) {{"); + sb.AppendLine($"{Indent}{Indent}{Indent}{Indent}$IdentityType = 'SystemAssigned,UserAssigned'"); sb.AppendLine($"{Indent}{Indent}{Indent}}}"); sb.AppendLine($"{Indent}{Indent}{Indent}$PSBoundParameters['IdentityType'] = $IdentityType"); sb.AppendLine($"{Indent}{Indent}{Indent}$null = $PSBoundParameters.Remove('EnableSystemAssignedIdentity')"); diff --git a/powershell/resources/psruntime/BuildTime/Models/PsProxyTypes.cs b/powershell/resources/psruntime/BuildTime/Models/PsProxyTypes.cs index dd15fb17e5..ed4d8a6769 100644 --- a/powershell/resources/psruntime/BuildTime/Models/PsProxyTypes.cs +++ b/powershell/resources/psruntime/BuildTime/Models/PsProxyTypes.cs @@ -64,7 +64,7 @@ public VariantGroup(string moduleName, string cmdletName, Variant[] variants, st CmdletVerb = cmdletNameParts.First(); CmdletNoun = cmdletNameParts.Last(); ProfileName = profileName; - Variants = variants; + Variants = MapManagedIdentityVariant(variants); ParameterGroups = Variants.ToParameterGroups().OrderBy(pg => pg.OrderCategory).ThenByDescending(pg => pg.IsMandatory).ToArray(); var aliasDuplicates = ParameterGroups.SelectMany(pg => pg.Aliases) //https://stackoverflow.com/a/18547390/294804 @@ -125,8 +125,48 @@ private string DetermineDefaultParameterSetName() return defaultParameterSet; } + + private Variant[] MapManagedIdentityVariant(Variant[] variants) + { + if (!CmdletVerb.Equals("New") && !CmdletVerb.Equals("Update")) + { + return variants; + } + var mappedManagedIdentityVariant = new List(); + foreach (var variant in variants) + { + var variantParametersList = variant.Parameters?.ToList(); + var identityTypeParameter = variantParametersList.Where(p => p.IsIdentityTypeParameter()).FirstOrDefault(); + if (identityTypeParameter != null && identityTypeParameter.PSArgumentCompleterAttribute != null + && (identityTypeParameter.PSArgumentCompleterAttribute.ResourceTypes.Contains("SystemAssigned") + || identityTypeParameter.PSArgumentCompleterAttribute.ResourceTypes.Contains("SystemAssigned,UserAssigned"))) + { + variantParametersList?.Remove(identityTypeParameter); + var enableSystemAssignedIdentityParameter = new Parameter(identityTypeParameter.VariantName, "", identityTypeParameter.Metadata, identityTypeParameter.HelpInfo) + { + ParameterType = CmdletVerb.Equals("New") ? typeof(SwitchParameter) : typeof(bool?) + }; + enableSystemAssignedIdentityParameter.ParameterName = "EnableSystemAssignedIdentity"; + enableSystemAssignedIdentityParameter.ParameterType = CmdletVerb.Equals("New") ? typeof(SwitchParameter) : typeof(bool?); + variantParametersList.Add(enableSystemAssignedIdentityParameter); + } + + + var userAssignedIdentityParameter = variantParametersList.Where(p => p.IsUserAssignedIdentityParameter()).FirstOrDefault(); + if (userAssignedIdentityParameter != null) + { + userAssignedIdentityParameter.ParameterType = typeof(string[]); + } + + variant.Parameters = variantParametersList?.ToArray(); + mappedManagedIdentityVariant.Add(variant); + } + return mappedManagedIdentityVariant.ToArray(); + } } + + internal class Variant { public string CmdletName { get; } @@ -142,7 +182,7 @@ internal class Variant public bool SupportsPaging { get; } public Attribute[] Attributes { get; } - public Parameter[] Parameters { get; } + public Parameter[] Parameters { get; internal set; } public Parameter[] CmdletOnlyParameters { get; } public bool IsInternal { get; } public bool IsDoNotExport { get; } @@ -249,17 +289,12 @@ public ParameterGroup(string parameterName, Parameter[] parameters, string[] all ValueFromPipelineByPropertyName = Parameters.Any(p => p.ValueFromPipelineByPropertyName); IsInputType = ValueFromPipeline || ValueFromPipelineByPropertyName; } - - internal ParameterGroup SallowCopy() - { - return (ParameterGroup)this.MemberwiseClone(); - } } internal class Parameter { public string VariantName { get; } - public string ParameterName { get; } + public string ParameterName { get; internal set; } public ParameterMetadata Metadata { get; } public PsParameterHelpInfo HelpInfo { get; } public Type ParameterType { get; internal set; } @@ -328,6 +363,10 @@ public Parameter(string variantName, string parameterName, ParameterMetadata met IsComplexInterface = ComplexInterfaceInfo.IsComplexInterface; Description = $"{description}{(IsComplexInterface ? complexMessage : String.Empty)}"; } + internal Parameter SallowCopy() + { + return (Parameter)this.MemberwiseClone(); + } } diff --git a/powershell/resources/psruntime/BuildTime/PsExtensions.cs b/powershell/resources/psruntime/BuildTime/PsExtensions.cs index c8ef284b81..ffcf343c24 100644 --- a/powershell/resources/psruntime/BuildTime/PsExtensions.cs +++ b/powershell/resources/psruntime/BuildTime/PsExtensions.cs @@ -173,7 +173,7 @@ public static bool IsHidden(this Parameter parameter) return parameter.Attributes.Any(attr => attr is DoNotExportAttribute); } - public static bool IsIdentityTypeParameterGroup(this ParameterGroup parameterGroup) + public static bool IsIdentityTypeParameter(this Parameter parameterGroup) { const string IdentityTypeParameterName = "IdentityType"; const string IdentityTypeParameterType = "System.String"; @@ -181,7 +181,7 @@ public static bool IsIdentityTypeParameterGroup(this ParameterGroup parameterGro parameterGroup.ParameterType.FullName.Equals(IdentityTypeParameterType, StringComparison.InvariantCultureIgnoreCase); } - public static bool IsUserAssignedIdentityParameterGroup(this ParameterGroup parameterGroup) + public static bool IsUserAssignedIdentityParameter(this Parameter parameterGroup) { const string UserAssignedIdentityParameterName = "UserAssignedIdentity"; const string UserAssignedIdentityParameterType = "System.Collections.Hashtable"; From e5cc53d16a0b46aef4a60c54e9270b36c2eda1c8 Mon Sep 17 00:00:00 2001 From: Beisi Zhou Date: Wed, 20 Dec 2023 16:55:59 +0800 Subject: [PATCH 04/30] wip --- .../psruntime/BuildTime/Models/PsProxyTypes.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/powershell/resources/psruntime/BuildTime/Models/PsProxyTypes.cs b/powershell/resources/psruntime/BuildTime/Models/PsProxyTypes.cs index ed4d8a6769..0e217da810 100644 --- a/powershell/resources/psruntime/BuildTime/Models/PsProxyTypes.cs +++ b/powershell/resources/psruntime/BuildTime/Models/PsProxyTypes.cs @@ -215,19 +215,19 @@ public Variant(string cmdletName, string variantName, CommandInfo info, CommandM internal class ParameterGroup { - public string ParameterName { get; internal set; } - public Parameter[] Parameters { get; internal set; } + public string ParameterName { get; } + public Parameter[] Parameters { get; } public string[] VariantNames { get; } public string[] AllVariantNames { get; } public bool HasAllVariants { get; } - public Type ParameterType { get; internal set; } + public Type ParameterType { get; } public string Description { get; } public string[] Aliases { get; } public bool HasValidateNotNull { get; } public bool HasAllowEmptyArray { get; } - public CompleterInfo CompleterInfo { get; internal set; } + public CompleterInfo CompleterInfo { get; } public DefaultInfo DefaultInfo { get; } public bool HasDefaultInfo { get; } public ParameterCategory OrderCategory { get; } @@ -313,7 +313,7 @@ internal class Parameter public bool ValueFromPipeline { get; } public bool ValueFromPipelineByPropertyName { get; } public int? Position { get; } - public bool DontShow { get; internal set; } + public bool DontShow { get; } public bool IsMandatory { get; set; } public InfoAttribute InfoAttribute { get; } From d971685eaf564392cc142cb492d5eb85acdf6a5a Mon Sep 17 00:00:00 2001 From: Beisi Zhou Date: Wed, 20 Dec 2023 18:15:47 +0800 Subject: [PATCH 05/30] wip --- .../psruntime/BuildTime/Models/PsProxyOutputs.cs | 4 ++-- .../resources/psruntime/BuildTime/Models/PsProxyTypes.cs | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs b/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs index a36b27642b..8fa666d979 100644 --- a/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs +++ b/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs @@ -359,10 +359,10 @@ private string GetSystemAssignedIdentityMappingStatementsForUpdateVerbCmdlet() { var sb = new StringBuilder(); sb.AppendLine($"{Indent}{Indent}if ($PSBoundParameters.ContainsKey('EnableSystemAssignedIdentity')) {{"); - sb.AppendLine($"{Indent}{Indent}{Indent}if ($IdentityType -eq 'None' && $PSBoundParameters['EnableSystemAssignedIdentity']) {{"); + sb.AppendLine($"{Indent}{Indent}{Indent}if ($IdentityType -eq 'None' -and $PSBoundParameters['EnableSystemAssignedIdentity']) {{"); sb.AppendLine($"{Indent}{Indent}{Indent}{Indent}$IdentityType = 'SystemAssigned'"); sb.AppendLine($"{Indent}{Indent}{Indent}}}"); - sb.AppendLine($"{Indent}{Indent}{Indent}elseif($IdentityType -eq 'UserAssigned' && $PSBoundParameters['EnableSystemAssignedIdentity']) {{"); + sb.AppendLine($"{Indent}{Indent}{Indent}elseif($IdentityType -eq 'UserAssigned' -and $PSBoundParameters['EnableSystemAssignedIdentity']) {{"); sb.AppendLine($"{Indent}{Indent}{Indent}{Indent}$IdentityType = 'SystemAssigned,UserAssigned'"); sb.AppendLine($"{Indent}{Indent}{Indent}}}"); sb.AppendLine($"{Indent}{Indent}{Indent}$PSBoundParameters['IdentityType'] = $IdentityType"); diff --git a/powershell/resources/psruntime/BuildTime/Models/PsProxyTypes.cs b/powershell/resources/psruntime/BuildTime/Models/PsProxyTypes.cs index 0e217da810..6b53a28c1e 100644 --- a/powershell/resources/psruntime/BuildTime/Models/PsProxyTypes.cs +++ b/powershell/resources/psruntime/BuildTime/Models/PsProxyTypes.cs @@ -148,6 +148,8 @@ private Variant[] MapManagedIdentityVariant(Variant[] variants) }; enableSystemAssignedIdentityParameter.ParameterName = "EnableSystemAssignedIdentity"; enableSystemAssignedIdentityParameter.ParameterType = CmdletVerb.Equals("New") ? typeof(SwitchParameter) : typeof(bool?); + enableSystemAssignedIdentityParameter.Description = "Decides if enable a system assigned identity for the resource."; + enableSystemAssignedIdentityParameter.PSArgumentCompleterAttribute = null; variantParametersList.Add(enableSystemAssignedIdentityParameter); } @@ -155,7 +157,9 @@ private Variant[] MapManagedIdentityVariant(Variant[] variants) var userAssignedIdentityParameter = variantParametersList.Where(p => p.IsUserAssignedIdentityParameter()).FirstOrDefault(); if (userAssignedIdentityParameter != null) { + userAssignedIdentityParameter.ParameterName = "UserAssignedIdentity"; userAssignedIdentityParameter.ParameterType = typeof(string[]); + userAssignedIdentityParameter.Description = "The array of user assigned identities associated with the resource. The elements in array will be ARM resource ids in the form: '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{identityName}."; } variant.Parameters = variantParametersList?.ToArray(); @@ -308,7 +312,7 @@ internal class Parameter public bool SupportsWildcards { get; } public CompleterInfoAttribute CompleterInfoAttribute { get; } public ArgumentCompleterAttribute ArgumentCompleterAttribute { get; } - public PSArgumentCompleterAttribute PSArgumentCompleterAttribute { get; } + public PSArgumentCompleterAttribute PSArgumentCompleterAttribute { get; internal set; } public bool ValueFromPipeline { get; } public bool ValueFromPipelineByPropertyName { get; } @@ -319,7 +323,7 @@ internal class Parameter public InfoAttribute InfoAttribute { get; } public ComplexInterfaceInfo ComplexInterfaceInfo { get; } public bool IsComplexInterface { get; } - public string Description { get; } + public string Description { get; internal set; } public Parameter(string variantName, string parameterName, ParameterMetadata metadata, PsParameterHelpInfo helpInfo = null) { From 7ba8cb52c75dcf2a631835cbe1f7bfd6f12a535e Mon Sep 17 00:00:00 2001 From: Beisi Zhou Date: Tue, 26 Dec 2023 17:54:56 +0800 Subject: [PATCH 06/30] get+put --- powershell/cmdlets/class.ts | 27 ++++++++++++++++++- powershell/plugins/create-commands-v2.ts | 6 +++-- .../plugin-create-inline-properties.ts | 10 +++++-- .../BuildTime/Models/PsProxyOutputs.cs | 10 ++++--- .../psruntime/BuildTime/PsExtensions.cs | 2 +- 5 files changed, 45 insertions(+), 10 deletions(-) diff --git a/powershell/cmdlets/class.ts b/powershell/cmdlets/class.ts index 362bf88eb3..49c6ff302e 100644 --- a/powershell/cmdlets/class.ts +++ b/powershell/cmdlets/class.ts @@ -1069,6 +1069,26 @@ export class CmdletClass extends Class { } } + private * ManagedIdentityParameterPreProcess(cmdlet: CmdletClass, pathParams: Array, nonPathParams: Array, viaIdentity: boolean) { + const $this = cmdlet; + return function* () { + const bodyParameters = $this.properties.filter(each => { + for (const attribute of each.attributes) { + for (const parameter of attribute.parameters) { + if ('global::Microsoft.Rest.ParameterCategory.Body' === valueOf(parameter)) { + return true; + } + } + } + return false; + }); + for (const param of bodyParameters) { + yield If(`(bool)(true == this.MyInvocation?.BoundParameters.ContainsKey("IdentityType"))`, + ``); + } + }; + } + private GetPutPreProcess(cmdlet: CmdletClass, pathParams: Array, nonPathParams: Array, viaIdentity: boolean): Statements { const $this = cmdlet; const updateBodyMethod = new Method(`Update${$this.bodyParameter?.value}`, dotnet.Void, { @@ -1095,6 +1115,11 @@ export class CmdletClass extends Class { } const getPut = function* () { yield `${$this.bodyParameter?.value} = await this.${$this.$('Client').invokeMethod(httpOperationName, ...[...pathParams, ...nonPathParams]).implementation}`; + const isManagedIdentityFeature = true; + const alignManagedIdentityDesign = true; + if (isManagedIdentityFeature && alignManagedIdentityDesign) { + //yield `this.ManagedIdentityParameterPreProcess();`; + } yield `this.${updateBodyMethod.name}();`; }; return new Statements(getPut); @@ -2110,7 +2135,7 @@ export class CmdletClass extends Class { if (operation.details.default.externalDocs) { this.add(new Attribute(ExternalDocsAttribute, { parameters: [`${new StringExpression(this.operation.details.default.externalDocs?.url ?? '')}`, - `${new StringExpression(this.operation.details.default.externalDocs?.description ?? '')}`] + `${new StringExpression(this.operation.details.default.externalDocs?.description ?? '')}`] })); } diff --git a/powershell/plugins/create-commands-v2.ts b/powershell/plugins/create-commands-v2.ts index 915b737aad..e1790db1b3 100644 --- a/powershell/plugins/create-commands-v2.ts +++ b/powershell/plugins/create-commands-v2.ts @@ -168,6 +168,8 @@ export /* @internal */ class Inferrer { parameters: new Dictionary(), }; const disableGetPut = await this.state.getValue('disable-getput', false); + const keepIdentityType = await this.state.getValue('keep-identitytype', false); + const flattenUserAssignedIdentity = await this.state.getValue('flatten-userassignedidentity', false); this.state.message({ Channel: Channel.Debug, Text: 'detecting high level commands...' }); for (const operationGroup of values(model.operationGroups)) { @@ -192,12 +194,12 @@ export /* @internal */ class Inferrer { - there is a get operation - there is a put operation - get operation path is the same as put operation path - - there is only one put reqeust schema + - there is only one put request schema - get operation response schema type is the same as put operation request schema type */ if (this.isAzure && !disableGetPut - && !hasPatch + && (!hasPatch || true) && getOperations && putOperation && putOperation.requests?.length == 1) { diff --git a/powershell/plugins/plugin-create-inline-properties.ts b/powershell/plugins/plugin-create-inline-properties.ts index 2a0d903935..d98b1c222f 100644 --- a/powershell/plugins/plugin-create-inline-properties.ts +++ b/powershell/plugins/plugin-create-inline-properties.ts @@ -53,6 +53,13 @@ function getNameOptions(typeName: string, components: Array) { return [...result.values()]; } +function getProposedNameForObjectInlinedProperty(propertyName: string, inlinedPropertyName: string): string { + const proposedName = getPascalIdentifier(`${propertyName === 'properties' + || propertyName === 'error' + || (propertyName === 'identity' && inlinedPropertyName === 'UserAssignedIdentities') ? '' : pascalCase(fixLeadingNumber(deconstruct(propertyName)).map(each => singularize(each)))} ${inlinedPropertyName}`); + return proposedName; +} + function createVirtualProperties(schema: ObjectSchema, stack: Array, threshold: number, conflicts: Array) { // Some properties should be removed are wrongly kept as null and need to clean them if (schema.properties) { @@ -222,7 +229,7 @@ function createVirtualProperties(schema: ObjectSchema, stack: Array, thr // deeper child properties should be inlined with their parent's name // ie, this.[properties].owner.name should be this.ownerName - const proposedName = getPascalIdentifier(`${propertyName === 'properties' || /*objectProperties.length === 1*/ propertyName === 'error' ? '' : pascalCase(fixLeadingNumber(deconstruct(propertyName)).map(each => singularize(each)))} ${inlinedProperty.name}`); + const proposedName = getProposedNameForObjectInlinedProperty(propertyName, inlinedProperty.name); const components = [...removeSequentialDuplicates([propertyName, ...inlinedProperty.nameComponents])]; let readonly = inlinedProperty.readOnly || property.readOnly; @@ -376,7 +383,6 @@ function createVirtualParameters(operation: CommandOperation) { } else if (operation.operationType === OperationType.Update && !(virtualProperty).update) { continue; } - virtualParameters.body.push({ name: virtualProperty.name, description: virtualProperty.property.language.default.description, diff --git a/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs b/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs index 8fa666d979..1b604813cf 100644 --- a/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs +++ b/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs @@ -359,13 +359,15 @@ private string GetSystemAssignedIdentityMappingStatementsForUpdateVerbCmdlet() { var sb = new StringBuilder(); sb.AppendLine($"{Indent}{Indent}if ($PSBoundParameters.ContainsKey('EnableSystemAssignedIdentity')) {{"); - sb.AppendLine($"{Indent}{Indent}{Indent}if ($IdentityType -eq 'None' -and $PSBoundParameters['EnableSystemAssignedIdentity']) {{"); + sb.AppendLine($"{Indent}{Indent}{Indent}if ($true -eq $PSBoundParameters['EnableSystemAssignedIdentity']) {{"); sb.AppendLine($"{Indent}{Indent}{Indent}{Indent}$IdentityType = 'SystemAssigned'"); sb.AppendLine($"{Indent}{Indent}{Indent}}}"); - sb.AppendLine($"{Indent}{Indent}{Indent}elseif($IdentityType -eq 'UserAssigned' -and $PSBoundParameters['EnableSystemAssignedIdentity']) {{"); - sb.AppendLine($"{Indent}{Indent}{Indent}{Indent}$IdentityType = 'SystemAssigned,UserAssigned'"); + sb.AppendLine($"{Indent}{Indent}{Indent}elseif($false -eq $PSBoundParameters['EnableSystemAssignedIdentity']) {{"); + sb.AppendLine($"{Indent}{Indent}{Indent}{Indent}$IdentityType = 'DisableSystemAssigned'"); + sb.AppendLine($"{Indent}{Indent}{Indent}}}"); + sb.AppendLine($"{Indent}{Indent}{Indent}else{{"); + sb.AppendLine($"{Indent}{Indent}{Indent}{Indent}$IdentityType = 'None'"); sb.AppendLine($"{Indent}{Indent}{Indent}}}"); - sb.AppendLine($"{Indent}{Indent}{Indent}$PSBoundParameters['IdentityType'] = $IdentityType"); sb.AppendLine($"{Indent}{Indent}{Indent}$null = $PSBoundParameters.Remove('EnableSystemAssignedIdentity')"); sb.Append($"{Indent}{Indent}}}"); return sb.ToString(); diff --git a/powershell/resources/psruntime/BuildTime/PsExtensions.cs b/powershell/resources/psruntime/BuildTime/PsExtensions.cs index ffcf343c24..09aa71b465 100644 --- a/powershell/resources/psruntime/BuildTime/PsExtensions.cs +++ b/powershell/resources/psruntime/BuildTime/PsExtensions.cs @@ -185,7 +185,7 @@ public static bool IsUserAssignedIdentityParameter(this Parameter parameterGroup { const string UserAssignedIdentityParameterName = "UserAssignedIdentity"; const string UserAssignedIdentityParameterType = "System.Collections.Hashtable"; - return parameterGroup.ParameterName.EndsWith(UserAssignedIdentityParameterName, StringComparison.InvariantCultureIgnoreCase) && + return parameterGroup.ParameterName.Equals(UserAssignedIdentityParameterName, StringComparison.InvariantCultureIgnoreCase) && parameterGroup.ParameterType.FullName.Equals(UserAssignedIdentityParameterType, StringComparison.InvariantCultureIgnoreCase); } } From 2156c22a774894c9c3eaa30af0249734a308a61c Mon Sep 17 00:00:00 2001 From: Beisi Zhou Date: Wed, 3 Jan 2024 13:36:01 +0800 Subject: [PATCH 07/30] fix --- .../BuildTime/Models/PsProxyOutputs.cs | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs b/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs index 1b604813cf..30f494b173 100644 --- a/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs +++ b/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs @@ -302,7 +302,6 @@ private string GetManagedIdentityMappingStatements() (VariantGroup.CmdletVerb.Equals("New") || VariantGroup.CmdletVerb.Equals("Update"))) { return $@" -{Indent}{Indent}$IdentityType = 'None' {GetUserAssignedIdentityMappingStatements()} {GetSystemAssignedIdentityMappingStatements()} "; @@ -320,8 +319,6 @@ private string GetUserAssignedIdentityMappingStatements() sb.AppendLine($"{Indent}{Indent}{Indent}$UserAssignedIdentityHashTable = @{{}}"); sb.AppendLine($"{Indent}{Indent}{Indent}$PSBoundParameters['UserAssignedIdentity'] | foreach {{$UserAssignedIdentityHashTable[$_] = @{{}} }}"); sb.AppendLine($"{Indent}{Indent}{Indent}$PSBoundParameters['UserAssignedIdentity'] = $UserAssignedIdentityHashTable"); - sb.AppendLine($"{Indent}{Indent}{Indent}$IdentityType = 'UserAssigned'"); - sb.AppendLine($"{Indent}{Indent}{Indent}$PSBoundParameters['IdentityType'] = $IdentityType"); sb.AppendLine($"{Indent}{Indent}}}"); return sb.ToString(); } @@ -342,16 +339,11 @@ private string GetSystemAssignedIdentityMappingStatements() private string GetSystemAssignedIdentityMappingStatementsForNewVerbCmdlet() { var sb = new StringBuilder(); - sb.AppendLine($"{Indent}{Indent}if ($PSBoundParameters.ContainsKey('EnableSystemAssignedIdentity') && $PSBoundParameters['EnableSystemAssignedIdentity'].IsPresent) {{"); - sb.AppendLine($"{Indent}{Indent}{Indent}if ($IdentityType -eq 'None') {{"); - sb.AppendLine($"{Indent}{Indent}{Indent}{Indent}$IdentityType = 'SystemAssigned'"); - sb.AppendLine($"{Indent}{Indent}{Indent}}}"); - sb.AppendLine($"{Indent}{Indent}{Indent}else {{"); - sb.AppendLine($"{Indent}{Indent}{Indent}{Indent}$IdentityType = 'SystemAssigned,UserAssigned'"); - sb.AppendLine($"{Indent}{Indent}{Indent}}}"); + sb.AppendLine($"{Indent}{Indent}if ($PSBoundParameters.ContainsKey('EnableSystemAssignedIdentity') -and $PSBoundParameters['EnableSystemAssignedIdentity'].IsPresent) {{"); + sb.AppendLine($"{Indent}{Indent}{Indent}$IdentityType = 'SystemAssigned'"); sb.AppendLine($"{Indent}{Indent}{Indent}$PSBoundParameters['IdentityType'] = $IdentityType"); - sb.AppendLine($"{Indent}{Indent}{Indent}$null = $PSBoundParameters.Remove('EnableSystemAssignedIdentity')"); - sb.Append($"{Indent}{Indent}}}"); + sb.AppendLine($"{Indent}{Indent}}}"); + sb.AppendLine($"{Indent}{Indent}$null = $PSBoundParameters.Remove('EnableSystemAssignedIdentity')"); return sb.ToString(); } @@ -368,6 +360,7 @@ private string GetSystemAssignedIdentityMappingStatementsForUpdateVerbCmdlet() sb.AppendLine($"{Indent}{Indent}{Indent}else{{"); sb.AppendLine($"{Indent}{Indent}{Indent}{Indent}$IdentityType = 'None'"); sb.AppendLine($"{Indent}{Indent}{Indent}}}"); + sb.AppendLine($"{Indent}{Indent}{Indent}$PSBoundParameters['IdentityType'] = $IdentityType"); sb.AppendLine($"{Indent}{Indent}{Indent}$null = $PSBoundParameters.Remove('EnableSystemAssignedIdentity')"); sb.Append($"{Indent}{Indent}}}"); return sb.ToString(); From d4feffcbe3e6165a4a45c93a00b6d30880727ea1 Mon Sep 17 00:00:00 2001 From: Beisi Zhou Date: Wed, 3 Jan 2024 17:47:27 +0800 Subject: [PATCH 08/30] add preprocess method --- powershell/cmdlets/class.ts | 135 +++++++++++++++++++++++++++--------- 1 file changed, 102 insertions(+), 33 deletions(-) diff --git a/powershell/cmdlets/class.ts b/powershell/cmdlets/class.ts index 49c6ff302e..e9a82f8d47 100644 --- a/powershell/cmdlets/class.ts +++ b/powershell/cmdlets/class.ts @@ -12,7 +12,7 @@ import { escapeString, docComment, serialize, pascalCase, DeepPartial, camelCase import { items, values, Dictionary, length } from '@azure-tools/linq'; import { Access, Attribute, BackedProperty, Catch, Class, ClassType, Constructor, dotnet, Else, Expression, Finally, ForEach, If, LambdaProperty, LiteralExpression, LocalVariable, Method, Modifier, Namespace, OneOrMoreStatements, Parameter, Property, Return, Statements, BlockStatement, StringExpression, - Switch, System, TerminalCase, toExpression, Try, Using, valueOf, Field, IsNull, Or, ExpressionOrLiteral, TerminalDefaultCase, xmlize, TypeDeclaration, And, IsNotNull, PartialMethod, Case, While, LiteralStatement + Switch, System, TerminalCase, toExpression, Try, Using, valueOf, Field, IsNull, Or, ExpressionOrLiteral, TerminalDefaultCase, xmlize, TypeDeclaration, And, IsNotNull, PartialMethod, Case, While, LiteralStatement, Not, ElseIf } from '@azure-tools/codegen-csharp'; import { ClientRuntime, EventListener, Schema, ArrayOf, EnumImplementation } from '../llcsharp/exports'; import { Alias, ArgumentCompleterAttribute, PSArgumentCompleterAttribute, AsyncCommandRuntime, AsyncJob, CmdletAttribute, ErrorCategory, ErrorRecord, Events, InvocationInfo, OutputTypeAttribute, ParameterAttribute, PSCmdlet, PSCredential, SwitchParameter, ValidateNotNull, verbEnum, GeneratedAttribute, DescriptionAttribute, ExternalDocsAttribute, CategoryAttribute, ParameterCategory, ProfileAttribute, PSObject, InternalExportAttribute, ExportAsAttribute, DefaultRunspace, RunspaceFactory, AllowEmptyCollectionAttribute, DoNotExportAttribute, HttpPathAttribute, NotSuggestDefaultParameterSetAttribute } from '../internal/powershell-declarations'; @@ -854,19 +854,24 @@ export class CmdletClass extends Class { yield Try(function* () { // make the call. - let preProcess: PreProcess; + let preProcesses: PreProcess[] = []; switch ($this.operation.commandType) { case CommandType.GetPut: - preProcess = $this.GetPutPreProcess; + preProcesses.push($this.GetPutPreProcess); break; case CommandType.Atomic: default: - preProcess = undefined; + if ($this.operation.details.csharp.verb.toLowerCase() === 'new' + && $this.operation.details.csharp.virtualParameters?.body.map(p => p.name).includes("IdentityType") + && $this.operation.details.csharp.virtualParameters?.body.map(p => p.name).includes("UserAssignedIdentity")) { + preProcesses.push($this.ManagedIdentityPreProcessForNewVerbCmdlet); + } + preProcesses.push(undefined); break; } const actualCall = function* () { yield $this.eventListener.signal(Events.CmdletBeforeAPICall); - yield $this.ImplementCall(preProcess); + yield $this.ImplementCall(preProcesses); yield $this.eventListener.signal(Events.CmdletAfterAPICall); }; @@ -890,7 +895,7 @@ export class CmdletClass extends Class { }); } - private * ImplementCall(preProcess: PreProcess) { + private * ImplementCall(preProcesses: PreProcess[]) { const $this = this; const operation = $this.operation; const apiCall = $this.apiCall; @@ -993,8 +998,12 @@ export class CmdletClass extends Class { parameters.push(serializationMode); } - if (preProcess) { - yield preProcess($this, pathParameters, [...otherParams.map(each => toExpression(each.value)), dotnet.This, pipeline], false); + if (preProcesses) { + for (const preProcess of preProcesses) { + if (preProcess) { + yield preProcess($this, pathParameters, [...otherParams.map(each => toExpression(each.value)), dotnet.This, pipeline], false); + } + } } yield `await this.${$this.$('Client').invokeMethod(httpOperationName, ...parameters).implementation}`; } @@ -1038,8 +1047,12 @@ export class CmdletClass extends Class { if (serializationMode) { parameters.push(serializationMode); } - if (preProcess) { - yield preProcess($this, pathParameters, [...otherParams.map(each => toExpression(each.value)), dotnet.This, pipeline], true); + if (preProcesses) { + for (const preProcess of preProcesses) { + if (preProcess) { + yield preProcess($this, pathParameters, [...otherParams.map(each => toExpression(each.value)), dotnet.This, pipeline], true); + } + } } yield `await this.${$this.$('Client').invokeMethod(`${httpOperationName}ViaIdentity`, ...parameters).implementation}`; }; @@ -1062,31 +1075,67 @@ export class CmdletClass extends Class { const jsonParameter = new Field('_jsonString', System.String); parameters = [...pathParameters, ...nonPathParameters, jsonParameter, ...callbackMethods, dotnet.This, pipeline]; } - if (preProcess) { - yield preProcess($this, pathParameters, [...otherParams.map(each => toExpression(each.value)), dotnet.This, pipeline], false); + if (preProcesses) { + for (const preProcess of preProcesses) { + if (preProcess) { + yield preProcess($this, pathParameters, [...otherParams.map(each => toExpression(each.value)), dotnet.This, pipeline], false); + } + } } yield `await this.${$this.$('Client').invokeMethod(httpOperationName, ...parameters).implementation}`; } } - - private * ManagedIdentityParameterPreProcess(cmdlet: CmdletClass, pathParams: Array, nonPathParams: Array, viaIdentity: boolean) { + private ManagedIdentityPreProcessForUpdateVerbCmdlet(cmdlet: CmdletClass): Statements { const $this = cmdlet; - return function* () { - const bodyParameters = $this.properties.filter(each => { - for (const attribute of each.attributes) { - for (const parameter of attribute.parameters) { - if ('global::Microsoft.Rest.ParameterCategory.Body' === valueOf(parameter)) { - return true; - } - } - } - return false; + const doesSupportUserAssignedIdentityMethod = new Method(`DoesSupportUserAssignedIdentity`, dotnet.Bool, { + access: Access.Private, + parameters: [new Parameter('identityType', dotnet.String)] + }); + if (!$this.hasMethodWithSameDeclaration(doesSupportUserAssignedIdentityMethod)) { + doesSupportUserAssignedIdentityMethod.add(Return(`new System.Collections.Generic.List { "UserAssigned", "SystemAssigned,UserAssigned", "SystemAssigned, UserAssigned"}.Contains(identityType)`)); + $this.add(doesSupportUserAssignedIdentityMethod); + } + const doesSupportSystemAssignedIdentityMethod = new Method(`DoesSupportSystemAssignedIdentityMethod`, dotnet.Bool, { + access: Access.Private, + parameters: [new Parameter('identityType', dotnet.String)] + }); + if (!$this.hasMethodWithSameDeclaration(doesSupportSystemAssignedIdentityMethod)) { + doesSupportSystemAssignedIdentityMethod.add(Return(`new System.Collections.Generic.List { "SystemAssigned", "SystemAssigned,UserAssigned", "SystemAssigned, UserAssigned"}.Contains(identityType)`)); + $this.add(doesSupportSystemAssignedIdentityMethod); + } + const preProcessManagedIdentity = function* () { + const supportsUserAssignedIdentity = new LocalVariable('supportsUserAssignedIdentity', dotnet.Var, { initializer: `${dotnet.False}` }); + const supportsSystemAssignedIdentity = new LocalVariable('supportsSystemAssignedIdentity', dotnet.Var, { initializer: `${dotnet.False}` }); + yield supportsUserAssignedIdentity; + yield supportsSystemAssignedIdentity; + yield If(Or( + And(`(bool)(true == this.MyInvocation?.BoundParameters.ContainsKey("UserAssignedIdentity"))`, + `((${$this.state.project.serviceNamespace.fullName}.Models.IUserAssignedIdentities)this.MyInvocation?.BoundParameters["UserAssignedIdentity"])?.Count > 0`), + And(`!(bool)(true == this.MyInvocation?.BoundParameters.ContainsKey("UserAssignedIdentity"))`, + `DoesSupportUserAssignedIdentity(${$this.bodyParameter?.value}.IdentityType)`)), `supportsUserAssignedIdentity = true;`) + + yield If(Or( + And(`(bool)(true == this.MyInvocation?.BoundParameters.ContainsKey("IdentityType"))`, + `"SystemAssigned".Equals((string)this.MyInvocation?.BoundParameters["IdentityType"])`), + And(And(`(bool)(true == this.MyInvocation?.BoundParameters.ContainsKey("IdentityType"))`, + `"None".Equals((string)this.MyInvocation?.BoundParameters["IdentityType"])`), + `DoesSupportSystemAssignedIdentityMethod(${$this.bodyParameter?.value}.IdentityType)`)), `supportsSystemAssignedIdentity = true;`) + + yield `this.MyInvocation?.BoundParameters.Remove("IdentityType");`; + yield If(And(`supportsUserAssignedIdentity`, `supportsSystemAssignedIdentity`), `this.MyInvocation?.BoundParameters.Add("IdentityType", "SystemAssigned,UserAssigned");`); + yield ElseIf(And(`supportsUserAssignedIdentity`, `!supportsSystemAssignedIdentity`), `this.MyInvocation?.BoundParameters.Add("IdentityType", "UserAssigned");`); + yield ElseIf(And(`!supportsUserAssignedIdentity`, `supportsSystemAssignedIdentity`), + function* () { + yield `this.MyInvocation?.BoundParameters.Add("IdentityType", "SystemAssigned");`; + yield `this.MyInvocation?.BoundParameters.Remove("UserAssignedIdentity");`; + }); + yield Else(function* () { + yield `this.MyInvocation?.BoundParameters.Add("IdentityType", null);`; + yield `this.MyInvocation?.BoundParameters.Remove("UserAssignedIdentity");`; + yield `this.MyInvocation?.BoundParameters.Add("UserAssignedIdentity", null);`; }); - for (const param of bodyParameters) { - yield If(`(bool)(true == this.MyInvocation?.BoundParameters.ContainsKey("IdentityType"))`, - ``); - } }; + return new Statements(preProcessManagedIdentity); } private GetPutPreProcess(cmdlet: CmdletClass, pathParams: Array, nonPathParams: Array, viaIdentity: boolean): Statements { @@ -1113,18 +1162,38 @@ export class CmdletClass extends Class { }); $this.add(updateBodyMethod); } + const preProcessManagedIdentityMethod = new Method(`PreProcessManagedIdentityParameters`, dotnet.Void, { + access: Access.Private + }); + if (!$this.hasMethodWithSameDeclaration(preProcessManagedIdentityMethod)) { + preProcessManagedIdentityMethod.add($this.ManagedIdentityPreProcessForUpdateVerbCmdlet(cmdlet)); + $this.add(preProcessManagedIdentityMethod); + } const getPut = function* () { yield `${$this.bodyParameter?.value} = await this.${$this.$('Client').invokeMethod(httpOperationName, ...[...pathParams, ...nonPathParams]).implementation}`; - const isManagedIdentityFeature = true; - const alignManagedIdentityDesign = true; - if (isManagedIdentityFeature && alignManagedIdentityDesign) { - //yield `this.ManagedIdentityParameterPreProcess();`; - } + // PreProcess body parameter + yield `this.${preProcessManagedIdentityMethod.name}();`; yield `this.${updateBodyMethod.name}();`; + /** Instance: + * _requestBodyParametersBody = await this.Client.GrafanaGetWithResult(SubscriptionId, ResourceGroupName, Name, this, Pipeline); + * this.Update_requestBodyParametersBody(); + * */ + }; return new Statements(getPut); } + private ManagedIdentityPreProcessForNewVerbCmdlet(cmdlet: CmdletClass, pathParams: Array, nonPathParams: Array, viaIdentity: boolean): Statements { + const preProcessManagedIdentity = function* () { + yield If(`this.UserAssignedIdentity?.Count > 0`, + function* () { + yield If(`"SystemAssigned".Equals(this.IdentityType, StringComparison.InvariantCultureIgnoreCase)`, `this.IdentityType = "SystemAssigned,UserAssigned";`); + yield Else(`this.IdentityType = "UserAssigned";`); + }) + }; + return new Statements(preProcessManagedIdentity); + } + private NewImplementResponseMethod() { const $this = this; const apiCall = $this.apiCall; From beeb61676e0a78f54a00ac3d43677f30b79f0e41 Mon Sep 17 00:00:00 2001 From: Beisi Zhou Date: Thu, 4 Jan 2024 14:45:35 +0800 Subject: [PATCH 09/30] remove mi by setting identitytype as none --- powershell/cmdlets/class.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powershell/cmdlets/class.ts b/powershell/cmdlets/class.ts index e9a82f8d47..d8ba2ae19b 100644 --- a/powershell/cmdlets/class.ts +++ b/powershell/cmdlets/class.ts @@ -1130,7 +1130,7 @@ export class CmdletClass extends Class { yield `this.MyInvocation?.BoundParameters.Remove("UserAssignedIdentity");`; }); yield Else(function* () { - yield `this.MyInvocation?.BoundParameters.Add("IdentityType", null);`; + yield `this.MyInvocation?.BoundParameters.Add("IdentityType", "None");`; yield `this.MyInvocation?.BoundParameters.Remove("UserAssignedIdentity");`; yield `this.MyInvocation?.BoundParameters.Add("UserAssignedIdentity", null);`; }); From decfce500a36af13dce9b999130db6ae045af8d7 Mon Sep 17 00:00:00 2001 From: Beisi Zhou Date: Thu, 4 Jan 2024 16:06:14 +0800 Subject: [PATCH 10/30] move handler to a sperate method --- powershell/cmdlets/class.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/powershell/cmdlets/class.ts b/powershell/cmdlets/class.ts index d8ba2ae19b..52f2415b76 100644 --- a/powershell/cmdlets/class.ts +++ b/powershell/cmdlets/class.ts @@ -1184,14 +1184,28 @@ export class CmdletClass extends Class { } private ManagedIdentityPreProcessForNewVerbCmdlet(cmdlet: CmdletClass, pathParams: Array, nonPathParams: Array, viaIdentity: boolean): Statements { - const preProcessManagedIdentity = function* () { + const $this = cmdlet; + + const preProcessManagedIdentityParametersMethod = new Method(`PreProcessManagedIdentityParameters`, dotnet.Void, { + access: Access.Private + }); + + const preProcessManagedIdentityType = function* () { yield If(`this.UserAssignedIdentity?.Count > 0`, function* () { yield If(`"SystemAssigned".Equals(this.IdentityType, StringComparison.InvariantCultureIgnoreCase)`, `this.IdentityType = "SystemAssigned,UserAssigned";`); yield Else(`this.IdentityType = "UserAssigned";`); }) }; - return new Statements(preProcessManagedIdentity); + + if (!$this.hasMethodWithSameDeclaration(preProcessManagedIdentityParametersMethod)) { + preProcessManagedIdentityParametersMethod.add(preProcessManagedIdentityType); + $this.add(preProcessManagedIdentityParametersMethod); + } + + return new Statements(function* () { + yield `this.${preProcessManagedIdentityParametersMethod.name}();`; + }); } private NewImplementResponseMethod() { From 21a560b413720fa37b294fd485b1bf90c876e4d1 Mon Sep 17 00:00:00 2001 From: Beisi Zhou Date: Thu, 4 Jan 2024 18:02:12 +0800 Subject: [PATCH 11/30] add directive --- powershell/cmdlets/class.ts | 21 ++++--- powershell/internal/project.ts | 4 ++ powershell/plugins/create-commands-v2.ts | 4 +- .../BuildTime/Models/PsProxyOutputs.cs | 31 +++++---- .../BuildTime/Models/PsProxyTypes.cs | 63 ++++++++++++------- 5 files changed, 80 insertions(+), 43 deletions(-) diff --git a/powershell/cmdlets/class.ts b/powershell/cmdlets/class.ts index 52f2415b76..feb4ecc64e 100644 --- a/powershell/cmdlets/class.ts +++ b/powershell/cmdlets/class.ts @@ -861,7 +861,8 @@ export class CmdletClass extends Class { break; case CommandType.Atomic: default: - if ($this.operation.details.csharp.verb.toLowerCase() === 'new' + if (!$this.state.project.keepIdentityType && + $this.operation.details.csharp.verb.toLowerCase() === 'new' && $this.operation.details.csharp.virtualParameters?.body.map(p => p.name).includes("IdentityType") && $this.operation.details.csharp.virtualParameters?.body.map(p => p.name).includes("UserAssignedIdentity")) { preProcesses.push($this.ManagedIdentityPreProcessForNewVerbCmdlet); @@ -1162,17 +1163,21 @@ export class CmdletClass extends Class { }); $this.add(updateBodyMethod); } - const preProcessManagedIdentityMethod = new Method(`PreProcessManagedIdentityParameters`, dotnet.Void, { - access: Access.Private - }); - if (!$this.hasMethodWithSameDeclaration(preProcessManagedIdentityMethod)) { - preProcessManagedIdentityMethod.add($this.ManagedIdentityPreProcessForUpdateVerbCmdlet(cmdlet)); - $this.add(preProcessManagedIdentityMethod); + if (!$this.state.project.keepIdentityType) { + const preProcessManagedIdentityMethod = new Method(`PreProcessManagedIdentityParameters`, dotnet.Void, { + access: Access.Private + }); + if (!$this.hasMethodWithSameDeclaration(preProcessManagedIdentityMethod)) { + preProcessManagedIdentityMethod.add($this.ManagedIdentityPreProcessForUpdateVerbCmdlet(cmdlet)); + $this.add(preProcessManagedIdentityMethod); + } } const getPut = function* () { yield `${$this.bodyParameter?.value} = await this.${$this.$('Client').invokeMethod(httpOperationName, ...[...pathParams, ...nonPathParams]).implementation}`; // PreProcess body parameter - yield `this.${preProcessManagedIdentityMethod.name}();`; + if (!$this.state.project.keepIdentityType) { + yield `this.PreProcessManagedIdentityParameters();`; + } yield `this.${updateBodyMethod.name}();`; /** Instance: * _requestBodyParametersBody = await this.Client.GrafanaGetWithResult(SubscriptionId, ResourceGroupName, Name, this, Pipeline); diff --git a/powershell/internal/project.ts b/powershell/internal/project.ts index 37685132ea..efd8061c45 100644 --- a/powershell/internal/project.ts +++ b/powershell/internal/project.ts @@ -156,6 +156,8 @@ export class Project extends codeDomProject { public psm1Internal!: string; public formatPs1xml!: string; public autoSwitchView!: boolean; + public keepIdentityType!: boolean; + public flattenUserAssignedIdentity!: boolean; public apiFolder!: string; public baseFolder!: string; public moduleFolder!: string; @@ -323,6 +325,8 @@ export class Project extends codeDomProject { this.psm1Internal = await this.state.getValue('psm1-internal'); this.formatPs1xml = await this.state.getValue('format-ps1xml'); this.autoSwitchView = await this.state.getValue('auto-switch-view', true); + this.flattenUserAssignedIdentity = await this.state.getValue('flatten-userassignedidentity', true); + this.keepIdentityType = await this.state.getValue('keep-identitytype', false); this.nuspec = await this.state.getValue('nuspec'); this.gitIgnore = `${this.baseFolder}/.gitignore`; this.gitAttributes = `${this.baseFolder}/.gitattributes`; diff --git a/powershell/plugins/create-commands-v2.ts b/powershell/plugins/create-commands-v2.ts index e1790db1b3..2b4d2a0b00 100644 --- a/powershell/plugins/create-commands-v2.ts +++ b/powershell/plugins/create-commands-v2.ts @@ -169,7 +169,7 @@ export /* @internal */ class Inferrer { }; const disableGetPut = await this.state.getValue('disable-getput', false); const keepIdentityType = await this.state.getValue('keep-identitytype', false); - const flattenUserAssignedIdentity = await this.state.getValue('flatten-userassignedidentity', false); + const flattenUserAssignedIdentity = await this.state.getValue('flatten-userassignedidentity', true); this.state.message({ Channel: Channel.Debug, Text: 'detecting high level commands...' }); for (const operationGroup of values(model.operationGroups)) { @@ -199,7 +199,7 @@ export /* @internal */ class Inferrer { */ if (this.isAzure && !disableGetPut - && (!hasPatch || true) + && (!hasPatch || !keepIdentityType) && getOperations && putOperation && putOperation.requests?.length == 1) { diff --git a/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs b/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs index 30f494b173..36611c9fb3 100644 --- a/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs +++ b/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs @@ -188,6 +188,9 @@ internal class BaseOutput public VariantGroup VariantGroup { get; } protected static readonly bool IsAzure = Convert.ToBoolean(@"${$project.azure}"); + protected static readonly bool keepIdentityType = Convert.ToBoolean(@"${$project.keepIdentityType}"); + protected static readonly bool flattenUserAssignedIdentity = Convert.ToBoolean(@"${$project.flattenUserAssignedIdentity}"); + public BaseOutput(VariantGroup variantGroup) { VariantGroup = variantGroup; @@ -315,23 +318,29 @@ private string GetManagedIdentityMappingStatements() private string GetUserAssignedIdentityMappingStatements() { var sb = new StringBuilder(); - sb.AppendLine($"{Indent}{Indent}if ($PSBoundParameters.ContainsKey('UserAssignedIdentity')) {{"); - sb.AppendLine($"{Indent}{Indent}{Indent}$UserAssignedIdentityHashTable = @{{}}"); - sb.AppendLine($"{Indent}{Indent}{Indent}$PSBoundParameters['UserAssignedIdentity'] | foreach {{$UserAssignedIdentityHashTable[$_] = @{{}} }}"); - sb.AppendLine($"{Indent}{Indent}{Indent}$PSBoundParameters['UserAssignedIdentity'] = $UserAssignedIdentityHashTable"); - sb.AppendLine($"{Indent}{Indent}}}"); + if (flattenUserAssignedIdentity) + { + sb.AppendLine($"{Indent}{Indent}if ($PSBoundParameters.ContainsKey('UserAssignedIdentity')) {{"); + sb.AppendLine($"{Indent}{Indent}{Indent}$UserAssignedIdentityHashTable = @{{}}"); + sb.AppendLine($"{Indent}{Indent}{Indent}$PSBoundParameters['UserAssignedIdentity'] | foreach {{$UserAssignedIdentityHashTable[$_] = @{{}} }}"); + sb.AppendLine($"{Indent}{Indent}{Indent}$PSBoundParameters['UserAssignedIdentity'] = $UserAssignedIdentityHashTable"); + sb.AppendLine($"{Indent}{Indent}}}"); + } return sb.ToString(); } private string GetSystemAssignedIdentityMappingStatements() { - if (VariantGroup.CmdletVerb.Equals("New")) - { - return GetSystemAssignedIdentityMappingStatementsForNewVerbCmdlet(); - } - else if (VariantGroup.CmdletVerb.Equals("Update")) + if (!keepIdentityType) { - return GetSystemAssignedIdentityMappingStatementsForUpdateVerbCmdlet(); + if (VariantGroup.CmdletVerb.Equals("New")) + { + return GetSystemAssignedIdentityMappingStatementsForNewVerbCmdlet(); + } + else if (VariantGroup.CmdletVerb.Equals("Update")) + { + return GetSystemAssignedIdentityMappingStatementsForUpdateVerbCmdlet(); + } } return ""; } diff --git a/powershell/resources/psruntime/BuildTime/Models/PsProxyTypes.cs b/powershell/resources/psruntime/BuildTime/Models/PsProxyTypes.cs index 6b53a28c1e..5680dd2355 100644 --- a/powershell/resources/psruntime/BuildTime/Models/PsProxyTypes.cs +++ b/powershell/resources/psruntime/BuildTime/Models/PsProxyTypes.cs @@ -44,6 +44,10 @@ internal class VariantGroup public PSTypeName[] OutputTypes { get; } public bool SupportsShouldProcess { get; } public bool SupportsPaging { get; } + + private static readonly bool keepIdentityType = Convert.ToBoolean(@"${$project.keepIdentityType}"); + private static readonly bool flattenUserAssignedIdentity = Convert.ToBoolean(@"${$project.flattenUserAssignedIdentity}"); + public string DefaultParameterSetName { get; } public bool HasMultipleVariants { get; } public PsHelpInfo HelpInfo { get; } @@ -128,7 +132,7 @@ private string DetermineDefaultParameterSetName() private Variant[] MapManagedIdentityVariant(Variant[] variants) { - if (!CmdletVerb.Equals("New") && !CmdletVerb.Equals("Update")) + if (!CmdletVerb.Equals("New") && !CmdletVerb.Equals("Update") || keepIdentityType && !flattenUserAssignedIdentity) { return variants; } @@ -136,30 +140,14 @@ private Variant[] MapManagedIdentityVariant(Variant[] variants) foreach (var variant in variants) { var variantParametersList = variant.Parameters?.ToList(); - var identityTypeParameter = variantParametersList.Where(p => p.IsIdentityTypeParameter()).FirstOrDefault(); - if (identityTypeParameter != null && identityTypeParameter.PSArgumentCompleterAttribute != null - && (identityTypeParameter.PSArgumentCompleterAttribute.ResourceTypes.Contains("SystemAssigned") - || identityTypeParameter.PSArgumentCompleterAttribute.ResourceTypes.Contains("SystemAssigned,UserAssigned"))) + if (!keepIdentityType) { - variantParametersList?.Remove(identityTypeParameter); - var enableSystemAssignedIdentityParameter = new Parameter(identityTypeParameter.VariantName, "", identityTypeParameter.Metadata, identityTypeParameter.HelpInfo) - { - ParameterType = CmdletVerb.Equals("New") ? typeof(SwitchParameter) : typeof(bool?) - }; - enableSystemAssignedIdentityParameter.ParameterName = "EnableSystemAssignedIdentity"; - enableSystemAssignedIdentityParameter.ParameterType = CmdletVerb.Equals("New") ? typeof(SwitchParameter) : typeof(bool?); - enableSystemAssignedIdentityParameter.Description = "Decides if enable a system assigned identity for the resource."; - enableSystemAssignedIdentityParameter.PSArgumentCompleterAttribute = null; - variantParametersList.Add(enableSystemAssignedIdentityParameter); + MapIdentityTypeToEnableSystemAssignedIdentity(variantParametersList); } - - var userAssignedIdentityParameter = variantParametersList.Where(p => p.IsUserAssignedIdentityParameter()).FirstOrDefault(); - if (userAssignedIdentityParameter != null) + if (flattenUserAssignedIdentity) { - userAssignedIdentityParameter.ParameterName = "UserAssignedIdentity"; - userAssignedIdentityParameter.ParameterType = typeof(string[]); - userAssignedIdentityParameter.Description = "The array of user assigned identities associated with the resource. The elements in array will be ARM resource ids in the form: '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{identityName}."; + FlattenUserAssignedIdentity(variantParametersList); } variant.Parameters = variantParametersList?.ToArray(); @@ -167,9 +155,40 @@ private Variant[] MapManagedIdentityVariant(Variant[] variants) } return mappedManagedIdentityVariant.ToArray(); } - } + private void MapIdentityTypeToEnableSystemAssignedIdentity(List variantParametersList) + { + var identityTypeParameter = variantParametersList.Where(p => p.IsIdentityTypeParameter()).FirstOrDefault(); + if (identityTypeParameter != null && identityTypeParameter.PSArgumentCompleterAttribute != null + && (identityTypeParameter.PSArgumentCompleterAttribute.ResourceTypes.Contains("SystemAssigned") + || identityTypeParameter.PSArgumentCompleterAttribute.ResourceTypes.Contains("SystemAssigned,UserAssigned"))) + { + variantParametersList?.Remove(identityTypeParameter); + var enableSystemAssignedIdentityParameter = new Parameter(identityTypeParameter.VariantName, "", identityTypeParameter.Metadata, identityTypeParameter.HelpInfo) + { + ParameterType = CmdletVerb.Equals("New") ? typeof(SwitchParameter) : typeof(bool?) + }; + enableSystemAssignedIdentityParameter.ParameterName = "EnableSystemAssignedIdentity"; + enableSystemAssignedIdentityParameter.ParameterType = CmdletVerb.Equals("New") ? typeof(SwitchParameter) : typeof(bool?); + enableSystemAssignedIdentityParameter.Description = "Decides if enable a system assigned identity for the resource."; + enableSystemAssignedIdentityParameter.PSArgumentCompleterAttribute = null; + variantParametersList.Add(enableSystemAssignedIdentityParameter); + } + + + } + private void FlattenUserAssignedIdentity(List variantParametersList) + { + var userAssignedIdentityParameter = variantParametersList.Where(p => p.IsUserAssignedIdentityParameter()).FirstOrDefault(); + if (userAssignedIdentityParameter != null) + { + userAssignedIdentityParameter.ParameterName = "UserAssignedIdentity"; + userAssignedIdentityParameter.ParameterType = typeof(string[]); + userAssignedIdentityParameter.Description = "The array of user assigned identities associated with the resource. The elements in array will be ARM resource ids in the form: '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{identityName}."; + } + } + } internal class Variant { From 68d6dbd705467bc8daee9ce889bb93b5f8a51639 Mon Sep 17 00:00:00 2001 From: Beisi Zhou Date: Mon, 8 Jan 2024 13:09:01 +0800 Subject: [PATCH 12/30] remove mapping from new-object cmdlet --- .../BuildTime/Models/PsProxyOutputs.cs | 28 +++++++++------- .../psruntime/BuildTime/PsExtensions.cs | 33 +++++++++++++++---- 2 files changed, 43 insertions(+), 18 deletions(-) diff --git a/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs b/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs index 36611c9fb3..bfbf960197 100644 --- a/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs +++ b/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs @@ -186,6 +186,7 @@ public ParameterNameOutput(string parameterName, bool isLast) internal class BaseOutput { public VariantGroup VariantGroup { get; } + protected Parameter[] Parameters { get; } protected static readonly bool IsAzure = Convert.ToBoolean(@"${$project.azure}"); protected static readonly bool keepIdentityType = Convert.ToBoolean(@"${$project.keepIdentityType}"); @@ -194,6 +195,12 @@ internal class BaseOutput public BaseOutput(VariantGroup variantGroup) { VariantGroup = variantGroup; + var parameterList = new List(); + foreach (var variant in VariantGroup.Variants) + { + parameterList.AddRange(variant.Parameters); + } + Parameters = parameterList.ToArray(); } public string ClearTelemetryContext() { @@ -251,7 +258,8 @@ public override string ToString() => $@"begin {{ {Indent}{Indent}}} {Indent}{Indent}$parameterSet = $PSCmdlet.ParameterSetName {GetTelemetry()} -{GetParameterSetToCmdletMapping()}{GetDefaultValuesStatements()}{GetManagedIdentityMappingStatements()} +{GetParameterSetToCmdletMapping()}{GetDefaultValuesStatements()} +{GetManagedIdentityMappingStatements()} {GetProcessCustomAttributesAtRuntime()} {Indent}{Indent}$wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand(($mapping[$parameterSet]), [System.Management.Automation.CommandTypes]::Cmdlet) {Indent}{Indent}$scriptCmd = {{& $wrappedCmd @PSBoundParameters}} @@ -301,24 +309,22 @@ private string GetDefaultValuesStatements() private string GetManagedIdentityMappingStatements() { + var sb = new StringBuilder(); if (!VariantGroup.IsInternal && IsAzure && (VariantGroup.CmdletVerb.Equals("New") || VariantGroup.CmdletVerb.Equals("Update"))) { - return $@" -{GetUserAssignedIdentityMappingStatements()} -{GetSystemAssignedIdentityMappingStatements()} -"; - } - else - { - return ""; + var statement1 = GetUserAssignedIdentityMappingStatements(); + if (!string.IsNullOrWhiteSpace(statement1)) sb.AppendLine(statement1); + var statement2 = GetSystemAssignedIdentityMappingStatements(); + if (!string.IsNullOrWhiteSpace(statement2)) sb.Append(statement2); } + return sb.ToString(); } private string GetUserAssignedIdentityMappingStatements() { var sb = new StringBuilder(); - if (flattenUserAssignedIdentity) + if (flattenUserAssignedIdentity && this.Parameters.ContainsUserAssignedIdentityParameter()) { sb.AppendLine($"{Indent}{Indent}if ($PSBoundParameters.ContainsKey('UserAssignedIdentity')) {{"); sb.AppendLine($"{Indent}{Indent}{Indent}$UserAssignedIdentityHashTable = @{{}}"); @@ -331,7 +337,7 @@ private string GetUserAssignedIdentityMappingStatements() private string GetSystemAssignedIdentityMappingStatements() { - if (!keepIdentityType) + if (!keepIdentityType && this.Parameters.ContainsEnableSystemAssignedIdentityParameter()) { if (VariantGroup.CmdletVerb.Equals("New")) { diff --git a/powershell/resources/psruntime/BuildTime/PsExtensions.cs b/powershell/resources/psruntime/BuildTime/PsExtensions.cs index 09aa71b465..76219aed5a 100644 --- a/powershell/resources/psruntime/BuildTime/PsExtensions.cs +++ b/powershell/resources/psruntime/BuildTime/PsExtensions.cs @@ -173,20 +173,39 @@ public static bool IsHidden(this Parameter parameter) return parameter.Attributes.Any(attr => attr is DoNotExportAttribute); } - public static bool IsIdentityTypeParameter(this Parameter parameterGroup) + public static bool IsIdentityTypeParameter(this Parameter parameter) { const string IdentityTypeParameterName = "IdentityType"; const string IdentityTypeParameterType = "System.String"; - return parameterGroup.ParameterName.Equals(IdentityTypeParameterName, StringComparison.InvariantCultureIgnoreCase) && - parameterGroup.ParameterType.FullName.Equals(IdentityTypeParameterType, StringComparison.InvariantCultureIgnoreCase); + return parameter.ParameterName.Equals(IdentityTypeParameterName, StringComparison.InvariantCultureIgnoreCase) && + parameter.ParameterType.FullName.Equals(IdentityTypeParameterType, StringComparison.InvariantCultureIgnoreCase); } - public static bool IsUserAssignedIdentityParameter(this Parameter parameterGroup) + public static bool IsUserAssignedIdentityParameter(this Parameter parameter) { const string UserAssignedIdentityParameterName = "UserAssignedIdentity"; - const string UserAssignedIdentityParameterType = "System.Collections.Hashtable"; - return parameterGroup.ParameterName.Equals(UserAssignedIdentityParameterName, StringComparison.InvariantCultureIgnoreCase) && - parameterGroup.ParameterType.FullName.Equals(UserAssignedIdentityParameterType, StringComparison.InvariantCultureIgnoreCase); + const string UserAssignedIdentityParameterType1 = "System.Collections.Hashtable"; + const string UserAssignedIdentityParameterType2 = "System.String[]"; + return parameter.ParameterName.Equals(UserAssignedIdentityParameterName, StringComparison.InvariantCultureIgnoreCase) && + (parameter.ParameterType.FullName.Equals(UserAssignedIdentityParameterType1, StringComparison.InvariantCultureIgnoreCase) || + parameter.ParameterType.FullName.Equals(UserAssignedIdentityParameterType2, StringComparison.InvariantCultureIgnoreCase)); + } + public static bool IsEnableSystemAssignedIdentityParameter(this Parameter parameter) + { + const string EnableSystemAssignedIdentityParameterName = "EnableSystemAssignedIdentity"; + const string EnableSystemAssignedIdentityParameterType = "System.Management.Automation.SwitchParameter"; + return parameter.ParameterName.Equals(EnableSystemAssignedIdentityParameterName, StringComparison.InvariantCultureIgnoreCase) && + parameter.ParameterType.FullName.Equals(EnableSystemAssignedIdentityParameterType, StringComparison.InvariantCultureIgnoreCase); + } + + public static bool ContainsEnableSystemAssignedIdentityParameter(this Parameter[] parameters) + { + return (bool)(parameters?.Where(parameter => parameter.IsEnableSystemAssignedIdentityParameter())?.Any()); + } + + public static bool ContainsUserAssignedIdentityParameter(this Parameter[] parameters) + { + return (bool)(parameters?.Where(parameter => parameter.IsUserAssignedIdentityParameter())?.Any()); } } } From 0158a9a3179d421cb781af78dcbb37aed2f4a51f Mon Sep 17 00:00:00 2001 From: Beisi Zhou Date: Mon, 8 Jan 2024 14:20:59 +0800 Subject: [PATCH 13/30] remove patch for mi-suppported update cmdlet --- powershell/plugins/create-commands-v2.ts | 10 +++++++--- .../psruntime/BuildTime/Models/PsProxyOutputs.cs | 4 ++-- .../resources/psruntime/BuildTime/PsExtensions.cs | 6 ++++-- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/powershell/plugins/create-commands-v2.ts b/powershell/plugins/create-commands-v2.ts index 2b4d2a0b00..44234526f8 100644 --- a/powershell/plugins/create-commands-v2.ts +++ b/powershell/plugins/create-commands-v2.ts @@ -169,16 +169,20 @@ export /* @internal */ class Inferrer { }; const disableGetPut = await this.state.getValue('disable-getput', false); const keepIdentityType = await this.state.getValue('keep-identitytype', false); - const flattenUserAssignedIdentity = await this.state.getValue('flatten-userassignedidentity', true); this.state.message({ Channel: Channel.Debug, Text: 'detecting high level commands...' }); for (const operationGroup of values(model.operationGroups)) { let hasPatch = false; const getOperations: Array = []; let putOperation: Operation | undefined; + let patchOperation: Operation | undefined; for (const operation of values(operationGroup.operations)) { if (operation.requests?.[0]?.protocol?.http?.method.toLowerCase() === 'patch') { hasPatch = true; + // skip adding variants for patch operation to avoid conflicts with getput + if (!keepIdentityType) { + continue; + } } else if (operation.requests?.[0]?.protocol?.http?.method.toLowerCase() === 'get') { getOperations.push(operation); } else if (operation.requests?.[0]?.protocol?.http?.method.toLowerCase() === 'put') { @@ -198,11 +202,11 @@ export /* @internal */ class Inferrer { - get operation response schema type is the same as put operation request schema type */ if (this.isAzure - && !disableGetPut - && (!hasPatch || !keepIdentityType) + && (!disableGetPut && !hasPatch || !keepIdentityType) && getOperations && putOperation && putOperation.requests?.length == 1) { + const getOperation = getOperations.find(getOperation => getOperation.requests?.[0]?.protocol?.http?.path === putOperation?.requests?.[0]?.protocol?.http?.path); const hasQueryParameter = getOperation?.parameters?.find(p => p.protocol.http?.in === 'query' && p.language.default.name !== 'apiVersion'); //parameter.protocal.http.in === 'body' probably only applies to open api 2.0 diff --git a/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs b/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs index bfbf960197..024aa6ace3 100644 --- a/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs +++ b/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs @@ -314,7 +314,7 @@ private string GetManagedIdentityMappingStatements() (VariantGroup.CmdletVerb.Equals("New") || VariantGroup.CmdletVerb.Equals("Update"))) { var statement1 = GetUserAssignedIdentityMappingStatements(); - if (!string.IsNullOrWhiteSpace(statement1)) sb.AppendLine(statement1); + if (!string.IsNullOrWhiteSpace(statement1)) sb.Append(statement1); var statement2 = GetSystemAssignedIdentityMappingStatements(); if (!string.IsNullOrWhiteSpace(statement2)) sb.Append(statement2); } @@ -377,7 +377,7 @@ private string GetSystemAssignedIdentityMappingStatementsForUpdateVerbCmdlet() sb.AppendLine($"{Indent}{Indent}{Indent}}}"); sb.AppendLine($"{Indent}{Indent}{Indent}$PSBoundParameters['IdentityType'] = $IdentityType"); sb.AppendLine($"{Indent}{Indent}{Indent}$null = $PSBoundParameters.Remove('EnableSystemAssignedIdentity')"); - sb.Append($"{Indent}{Indent}}}"); + sb.AppendLine($"{Indent}{Indent}}}"); return sb.ToString(); } } diff --git a/powershell/resources/psruntime/BuildTime/PsExtensions.cs b/powershell/resources/psruntime/BuildTime/PsExtensions.cs index 76219aed5a..4c4915bf1c 100644 --- a/powershell/resources/psruntime/BuildTime/PsExtensions.cs +++ b/powershell/resources/psruntime/BuildTime/PsExtensions.cs @@ -193,9 +193,11 @@ public static bool IsUserAssignedIdentityParameter(this Parameter parameter) public static bool IsEnableSystemAssignedIdentityParameter(this Parameter parameter) { const string EnableSystemAssignedIdentityParameterName = "EnableSystemAssignedIdentity"; - const string EnableSystemAssignedIdentityParameterType = "System.Management.Automation.SwitchParameter"; + const string EnableSystemAssignedIdentityParameterType1 = "System.Management.Automation.SwitchParameter"; + const string EnableSystemAssignedIdentityParameterType2 = "System.Nullable`1[[System.Boolean"; return parameter.ParameterName.Equals(EnableSystemAssignedIdentityParameterName, StringComparison.InvariantCultureIgnoreCase) && - parameter.ParameterType.FullName.Equals(EnableSystemAssignedIdentityParameterType, StringComparison.InvariantCultureIgnoreCase); + (parameter.ParameterType.FullName.Equals(EnableSystemAssignedIdentityParameterType1, StringComparison.InvariantCultureIgnoreCase) || + parameter.ParameterType.FullName.StartsWith(EnableSystemAssignedIdentityParameterType2, StringComparison.InvariantCultureIgnoreCase)); } public static bool ContainsEnableSystemAssignedIdentityParameter(this Parameter[] parameters) From 7869dee3decc75b23ccb0a7a8138c01348993440 Mon Sep 17 00:00:00 2001 From: Beisi Zhou Date: Wed, 10 Jan 2024 15:01:37 +0800 Subject: [PATCH 14/30] skip preprocess mi parameter if identitytype doesn't exist --- powershell/cmdlets/class.ts | 72 +++++++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 27 deletions(-) diff --git a/powershell/cmdlets/class.ts b/powershell/cmdlets/class.ts index feb4ecc64e..122b90b2f5 100644 --- a/powershell/cmdlets/class.ts +++ b/powershell/cmdlets/class.ts @@ -1086,6 +1086,49 @@ export class CmdletClass extends Class { yield `await this.${$this.$('Client').invokeMethod(httpOperationName, ...parameters).implementation}`; } } + + private ContainsIdentityTypeParameter(cmdlet: CmdletClass): boolean { + const $this = cmdlet; + const identityTypeParameter = $this.properties.filter(each => { + for (const attribute of each.attributes) { + for (const parameter of attribute.parameters) { + if ('global::Microsoft.Rest.ParameterCategory.Body' === valueOf(parameter) + && 'IdentityType' === each.name + && 'string' === each.type.declaration) { + return true; + } + } + } + return false; + }); + return identityTypeParameter.length > 0; + } + + private ManagedIdentityPreProcessForNewVerbCmdlet(cmdlet: CmdletClass, pathParams: Array, nonPathParams: Array, viaIdentity: boolean): Statements { + const $this = cmdlet; + + const preProcessManagedIdentityParametersMethod = new Method(`PreProcessManagedIdentityParameters`, dotnet.Void, { + access: Access.Private + }); + + const preProcessManagedIdentityType = function* () { + yield If(`this.UserAssignedIdentity?.Count > 0`, + function* () { + yield If(`"SystemAssigned".Equals(this.IdentityType, StringComparison.InvariantCultureIgnoreCase)`, `this.IdentityType = "SystemAssigned,UserAssigned";`); + yield Else(`this.IdentityType = "UserAssigned";`); + }) + }; + + if (!$this.hasMethodWithSameDeclaration(preProcessManagedIdentityParametersMethod)) { + preProcessManagedIdentityParametersMethod.add(preProcessManagedIdentityType); + $this.add(preProcessManagedIdentityParametersMethod); + } + + return new Statements(function* () { + yield `this.${preProcessManagedIdentityParametersMethod.name}();`; + }); + } + private ManagedIdentityPreProcessForUpdateVerbCmdlet(cmdlet: CmdletClass): Statements { const $this = cmdlet; const doesSupportUserAssignedIdentityMethod = new Method(`DoesSupportUserAssignedIdentity`, dotnet.Bool, { @@ -1163,7 +1206,7 @@ export class CmdletClass extends Class { }); $this.add(updateBodyMethod); } - if (!$this.state.project.keepIdentityType) { + if (!$this.state.project.keepIdentityType && $this.ContainsIdentityTypeParameter(cmdlet)) { const preProcessManagedIdentityMethod = new Method(`PreProcessManagedIdentityParameters`, dotnet.Void, { access: Access.Private }); @@ -1175,7 +1218,7 @@ export class CmdletClass extends Class { const getPut = function* () { yield `${$this.bodyParameter?.value} = await this.${$this.$('Client').invokeMethod(httpOperationName, ...[...pathParams, ...nonPathParams]).implementation}`; // PreProcess body parameter - if (!$this.state.project.keepIdentityType) { + if (!$this.state.project.keepIdentityType && $this.ContainsIdentityTypeParameter(cmdlet)) { yield `this.PreProcessManagedIdentityParameters();`; } yield `this.${updateBodyMethod.name}();`; @@ -1188,31 +1231,6 @@ export class CmdletClass extends Class { return new Statements(getPut); } - private ManagedIdentityPreProcessForNewVerbCmdlet(cmdlet: CmdletClass, pathParams: Array, nonPathParams: Array, viaIdentity: boolean): Statements { - const $this = cmdlet; - - const preProcessManagedIdentityParametersMethod = new Method(`PreProcessManagedIdentityParameters`, dotnet.Void, { - access: Access.Private - }); - - const preProcessManagedIdentityType = function* () { - yield If(`this.UserAssignedIdentity?.Count > 0`, - function* () { - yield If(`"SystemAssigned".Equals(this.IdentityType, StringComparison.InvariantCultureIgnoreCase)`, `this.IdentityType = "SystemAssigned,UserAssigned";`); - yield Else(`this.IdentityType = "UserAssigned";`); - }) - }; - - if (!$this.hasMethodWithSameDeclaration(preProcessManagedIdentityParametersMethod)) { - preProcessManagedIdentityParametersMethod.add(preProcessManagedIdentityType); - $this.add(preProcessManagedIdentityParametersMethod); - } - - return new Statements(function* () { - yield `this.${preProcessManagedIdentityParametersMethod.name}();`; - }); - } - private NewImplementResponseMethod() { const $this = this; const apiCall = $this.apiCall; From ef90e85cb8953d7fe139803df7ccdead9e0c792d Mon Sep 17 00:00:00 2001 From: Beisi Zhou Date: Wed, 10 Jan 2024 19:00:38 +0800 Subject: [PATCH 15/30] bug fix --- .../resources/psruntime/BuildTime/Models/PsProxyOutputs.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs b/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs index 024aa6ace3..5a39320da6 100644 --- a/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs +++ b/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs @@ -365,6 +365,7 @@ private string GetSystemAssignedIdentityMappingStatementsForNewVerbCmdlet() private string GetSystemAssignedIdentityMappingStatementsForUpdateVerbCmdlet() { var sb = new StringBuilder(); + sb.AppendLine($"{Indent}{Indent}$IdentityType = 'None'"); sb.AppendLine($"{Indent}{Indent}if ($PSBoundParameters.ContainsKey('EnableSystemAssignedIdentity')) {{"); sb.AppendLine($"{Indent}{Indent}{Indent}if ($true -eq $PSBoundParameters['EnableSystemAssignedIdentity']) {{"); sb.AppendLine($"{Indent}{Indent}{Indent}{Indent}$IdentityType = 'SystemAssigned'"); @@ -372,12 +373,9 @@ private string GetSystemAssignedIdentityMappingStatementsForUpdateVerbCmdlet() sb.AppendLine($"{Indent}{Indent}{Indent}elseif($false -eq $PSBoundParameters['EnableSystemAssignedIdentity']) {{"); sb.AppendLine($"{Indent}{Indent}{Indent}{Indent}$IdentityType = 'DisableSystemAssigned'"); sb.AppendLine($"{Indent}{Indent}{Indent}}}"); - sb.AppendLine($"{Indent}{Indent}{Indent}else{{"); - sb.AppendLine($"{Indent}{Indent}{Indent}{Indent}$IdentityType = 'None'"); - sb.AppendLine($"{Indent}{Indent}{Indent}}}"); - sb.AppendLine($"{Indent}{Indent}{Indent}$PSBoundParameters['IdentityType'] = $IdentityType"); sb.AppendLine($"{Indent}{Indent}{Indent}$null = $PSBoundParameters.Remove('EnableSystemAssignedIdentity')"); sb.AppendLine($"{Indent}{Indent}}}"); + sb.AppendLine($"{Indent}{Indent}$PSBoundParameters['IdentityType'] = $IdentityType"); return sb.ToString(); } } From 99e40c0beb8355b937ab206080f6bbeb8120f102 Mon Sep 17 00:00:00 2001 From: Beisi Zhou Date: Wed, 10 Jan 2024 19:01:22 +0800 Subject: [PATCH 16/30] skip using getput to replace patch if no identity type exists --- powershell/plugins/create-commands-v2.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/powershell/plugins/create-commands-v2.ts b/powershell/plugins/create-commands-v2.ts index 44234526f8..f87a1c43f4 100644 --- a/powershell/plugins/create-commands-v2.ts +++ b/powershell/plugins/create-commands-v2.ts @@ -179,8 +179,9 @@ export /* @internal */ class Inferrer { for (const operation of values(operationGroup.operations)) { if (operation.requests?.[0]?.protocol?.http?.method.toLowerCase() === 'patch') { hasPatch = true; + patchOperation = operation; // skip adding variants for patch operation to avoid conflicts with getput - if (!keepIdentityType) { + if (!keepIdentityType && this.containsIdentityType(operation)) { continue; } } else if (operation.requests?.[0]?.protocol?.http?.method.toLowerCase() === 'get') { @@ -202,7 +203,8 @@ export /* @internal */ class Inferrer { - get operation response schema type is the same as put operation request schema type */ if (this.isAzure - && (!disableGetPut && !hasPatch || !keepIdentityType) + && (!disableGetPut && !hasPatch || + hasPatch && !keepIdentityType && patchOperation && this.containsIdentityType(patchOperation)) && getOperations && putOperation && putOperation.requests?.length == 1) { @@ -225,6 +227,14 @@ export /* @internal */ class Inferrer { return model; } + containsIdentityType(op: Operation): boolean { + const body = op.requests?.[0].parameters?.find((p) => !p.origin || p.origin.indexOf('modelerfour:synthesized') < 0) || null; + // property identity in the body parameter + const identityProperty = (body && body.schema && isObjectSchema(body.schema)) ? values(getAllProperties(body.schema)).where(property => !property.language.default.readOnly && isObjectSchema(property.schema) && property.language.default.name === 'identity')?.toArray()?.[0] : null; + const identityTypeProperty = (identityProperty && identityProperty.schema && isObjectSchema(identityProperty.schema)) ? values(getAllProperties(identityProperty.schema)).where(property => !property.language.default.readOnly && property.language.default.name === 'type')?.toArray()?.[0] : null; + return identityTypeProperty !== null && identityTypeProperty !== undefined; + } + inferCommand(operation: Array, group: string, suffix: Array = []): Array { operation = operation.filter(each => each !== 'all'); // no instant match From 71b7e803deda4d1c6f5856a94d1cb46728ca47ce Mon Sep 17 00:00:00 2001 From: Beisi Zhou Date: Wed, 10 Jan 2024 19:11:47 +0800 Subject: [PATCH 17/30] bug fix --- powershell/cmdlets/class.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/powershell/cmdlets/class.ts b/powershell/cmdlets/class.ts index 122b90b2f5..fdc828ac2c 100644 --- a/powershell/cmdlets/class.ts +++ b/powershell/cmdlets/class.ts @@ -1172,6 +1172,7 @@ export class CmdletClass extends Class { function* () { yield `this.MyInvocation?.BoundParameters.Add("IdentityType", "SystemAssigned");`; yield `this.MyInvocation?.BoundParameters.Remove("UserAssignedIdentity");`; + yield `this.MyInvocation?.BoundParameters.Add("UserAssignedIdentity", null);`; }); yield Else(function* () { yield `this.MyInvocation?.BoundParameters.Add("IdentityType", "None");`; From 15f2dc2faf8294ffb4d98297941e91159200da7b Mon Sep 17 00:00:00 2001 From: Beisi Zhou Date: Wed, 10 Jan 2024 19:42:26 +0800 Subject: [PATCH 18/30] remove hardcode for UserAssignedIdentityTypeDeclaration --- powershell/cmdlets/class.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/powershell/cmdlets/class.ts b/powershell/cmdlets/class.ts index fdc828ac2c..3a7a02e107 100644 --- a/powershell/cmdlets/class.ts +++ b/powershell/cmdlets/class.ts @@ -1104,6 +1104,22 @@ export class CmdletClass extends Class { return identityTypeParameter.length > 0; } + private GetUserAssignedIdentityTypeDeclaration(cmdlet: CmdletClass): string { + const $this = cmdlet; + const userAssignedIdentityParameter = $this.properties.filter(each => { + for (const attribute of each.attributes) { + for (const parameter of attribute.parameters) { + if ('global::Microsoft.Rest.ParameterCategory.Body' === valueOf(parameter) + && 'UserAssignedIdentity' === each.name) { + return true; + } + } + } + return false; + }); + return userAssignedIdentityParameter?.[0].type.declaration ?? `${$this.state.project.serviceNamespace.fullName}.Models.IUserAssignedIdentities`; + } + private ManagedIdentityPreProcessForNewVerbCmdlet(cmdlet: CmdletClass, pathParams: Array, nonPathParams: Array, viaIdentity: boolean): Statements { const $this = cmdlet; @@ -1154,7 +1170,7 @@ export class CmdletClass extends Class { yield supportsSystemAssignedIdentity; yield If(Or( And(`(bool)(true == this.MyInvocation?.BoundParameters.ContainsKey("UserAssignedIdentity"))`, - `((${$this.state.project.serviceNamespace.fullName}.Models.IUserAssignedIdentities)this.MyInvocation?.BoundParameters["UserAssignedIdentity"])?.Count > 0`), + `((${$this.GetUserAssignedIdentityTypeDeclaration(cmdlet)})this.MyInvocation?.BoundParameters["UserAssignedIdentity"])?.Count > 0`), And(`!(bool)(true == this.MyInvocation?.BoundParameters.ContainsKey("UserAssignedIdentity"))`, `DoesSupportUserAssignedIdentity(${$this.bodyParameter?.value}.IdentityType)`)), `supportsUserAssignedIdentity = true;`) From a1e086c8a9af4e19496c786deb20cb4b1c6ce8e8 Mon Sep 17 00:00:00 2001 From: Beisi Zhou Date: Wed, 10 Jan 2024 20:02:28 +0800 Subject: [PATCH 19/30] rush lint --fix --- powershell/cmdlets/class.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/powershell/cmdlets/class.ts b/powershell/cmdlets/class.ts index 3a7a02e107..880868915e 100644 --- a/powershell/cmdlets/class.ts +++ b/powershell/cmdlets/class.ts @@ -854,7 +854,7 @@ export class CmdletClass extends Class { yield Try(function* () { // make the call. - let preProcesses: PreProcess[] = []; + const preProcesses: Array = []; switch ($this.operation.commandType) { case CommandType.GetPut: preProcesses.push($this.GetPutPreProcess); @@ -863,8 +863,8 @@ export class CmdletClass extends Class { default: if (!$this.state.project.keepIdentityType && $this.operation.details.csharp.verb.toLowerCase() === 'new' - && $this.operation.details.csharp.virtualParameters?.body.map(p => p.name).includes("IdentityType") - && $this.operation.details.csharp.virtualParameters?.body.map(p => p.name).includes("UserAssignedIdentity")) { + && $this.operation.details.csharp.virtualParameters?.body.map(p => p.name).includes('IdentityType') + && $this.operation.details.csharp.virtualParameters?.body.map(p => p.name).includes('UserAssignedIdentity')) { preProcesses.push($this.ManagedIdentityPreProcessForNewVerbCmdlet); } preProcesses.push(undefined); @@ -896,7 +896,7 @@ export class CmdletClass extends Class { }); } - private * ImplementCall(preProcesses: PreProcess[]) { + private * ImplementCall(preProcesses: Array) { const $this = this; const operation = $this.operation; const apiCall = $this.apiCall; @@ -1179,8 +1179,7 @@ export class CmdletClass extends Class { `"SystemAssigned".Equals((string)this.MyInvocation?.BoundParameters["IdentityType"])`), And(And(`(bool)(true == this.MyInvocation?.BoundParameters.ContainsKey("IdentityType"))`, `"None".Equals((string)this.MyInvocation?.BoundParameters["IdentityType"])`), - `DoesSupportSystemAssignedIdentityMethod(${$this.bodyParameter?.value}.IdentityType)`)), `supportsSystemAssignedIdentity = true;`) - + `DoesSupportSystemAssignedIdentityMethod(${$this.bodyParameter?.value}.IdentityType)`)), `supportsSystemAssignedIdentity = true;`); yield `this.MyInvocation?.BoundParameters.Remove("IdentityType");`; yield If(And(`supportsUserAssignedIdentity`, `supportsSystemAssignedIdentity`), `this.MyInvocation?.BoundParameters.Add("IdentityType", "SystemAssigned,UserAssigned");`); yield ElseIf(And(`supportsUserAssignedIdentity`, `!supportsSystemAssignedIdentity`), `this.MyInvocation?.BoundParameters.Add("IdentityType", "UserAssigned");`); From be74d4e699b67b6b83981adc38a99606f58114c8 Mon Sep 17 00:00:00 2001 From: Beisi Zhou Date: Wed, 10 Jan 2024 20:12:23 +0800 Subject: [PATCH 20/30] rush lint --fix --- powershell/cmdlets/class.ts | 71 ++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 36 deletions(-) diff --git a/powershell/cmdlets/class.ts b/powershell/cmdlets/class.ts index 880868915e..38cef5306d 100644 --- a/powershell/cmdlets/class.ts +++ b/powershell/cmdlets/class.ts @@ -1123,16 +1123,16 @@ export class CmdletClass extends Class { private ManagedIdentityPreProcessForNewVerbCmdlet(cmdlet: CmdletClass, pathParams: Array, nonPathParams: Array, viaIdentity: boolean): Statements { const $this = cmdlet; - const preProcessManagedIdentityParametersMethod = new Method(`PreProcessManagedIdentityParameters`, dotnet.Void, { + const preProcessManagedIdentityParametersMethod = new Method('PreProcessManagedIdentityParameters', dotnet.Void, { access: Access.Private }); const preProcessManagedIdentityType = function* () { - yield If(`this.UserAssignedIdentity?.Count > 0`, + yield If('this.UserAssignedIdentity?.Count > 0', function* () { - yield If(`"SystemAssigned".Equals(this.IdentityType, StringComparison.InvariantCultureIgnoreCase)`, `this.IdentityType = "SystemAssigned,UserAssigned";`); - yield Else(`this.IdentityType = "UserAssigned";`); - }) + yield If('"SystemAssigned".Equals(this.IdentityType, StringComparison.InvariantCultureIgnoreCase)', 'this.IdentityType = "SystemAssigned,UserAssigned";'); + yield Else('this.IdentityType = "UserAssigned";'); + }); }; if (!$this.hasMethodWithSameDeclaration(preProcessManagedIdentityParametersMethod)) { @@ -1147,20 +1147,20 @@ export class CmdletClass extends Class { private ManagedIdentityPreProcessForUpdateVerbCmdlet(cmdlet: CmdletClass): Statements { const $this = cmdlet; - const doesSupportUserAssignedIdentityMethod = new Method(`DoesSupportUserAssignedIdentity`, dotnet.Bool, { + const doesSupportUserAssignedIdentityMethod = new Method('DoesSupportUserAssignedIdentity', dotnet.Bool, { access: Access.Private, parameters: [new Parameter('identityType', dotnet.String)] }); if (!$this.hasMethodWithSameDeclaration(doesSupportUserAssignedIdentityMethod)) { - doesSupportUserAssignedIdentityMethod.add(Return(`new System.Collections.Generic.List { "UserAssigned", "SystemAssigned,UserAssigned", "SystemAssigned, UserAssigned"}.Contains(identityType)`)); + doesSupportUserAssignedIdentityMethod.add(Return('new System.Collections.Generic.List { "UserAssigned", "SystemAssigned,UserAssigned", "SystemAssigned, UserAssigned"}.Contains(identityType)')); $this.add(doesSupportUserAssignedIdentityMethod); } - const doesSupportSystemAssignedIdentityMethod = new Method(`DoesSupportSystemAssignedIdentityMethod`, dotnet.Bool, { + const doesSupportSystemAssignedIdentityMethod = new Method('DoesSupportSystemAssignedIdentityMethod', dotnet.Bool, { access: Access.Private, parameters: [new Parameter('identityType', dotnet.String)] }); if (!$this.hasMethodWithSameDeclaration(doesSupportSystemAssignedIdentityMethod)) { - doesSupportSystemAssignedIdentityMethod.add(Return(`new System.Collections.Generic.List { "SystemAssigned", "SystemAssigned,UserAssigned", "SystemAssigned, UserAssigned"}.Contains(identityType)`)); + doesSupportSystemAssignedIdentityMethod.add(Return('new System.Collections.Generic.List { "SystemAssigned", "SystemAssigned,UserAssigned", "SystemAssigned, UserAssigned"}.Contains(identityType)')); $this.add(doesSupportSystemAssignedIdentityMethod); } const preProcessManagedIdentity = function* () { @@ -1169,30 +1169,29 @@ export class CmdletClass extends Class { yield supportsUserAssignedIdentity; yield supportsSystemAssignedIdentity; yield If(Or( - And(`(bool)(true == this.MyInvocation?.BoundParameters.ContainsKey("UserAssignedIdentity"))`, + And('(bool)(true == this.MyInvocation?.BoundParameters.ContainsKey("UserAssignedIdentity"))', `((${$this.GetUserAssignedIdentityTypeDeclaration(cmdlet)})this.MyInvocation?.BoundParameters["UserAssignedIdentity"])?.Count > 0`), - And(`!(bool)(true == this.MyInvocation?.BoundParameters.ContainsKey("UserAssignedIdentity"))`, - `DoesSupportUserAssignedIdentity(${$this.bodyParameter?.value}.IdentityType)`)), `supportsUserAssignedIdentity = true;`) - + And('!(bool)(true == this.MyInvocation?.BoundParameters.ContainsKey("UserAssignedIdentity"))', + `DoesSupportUserAssignedIdentity(${$this.bodyParameter?.value}.IdentityType)`)), 'supportsUserAssignedIdentity = true;'); yield If(Or( - And(`(bool)(true == this.MyInvocation?.BoundParameters.ContainsKey("IdentityType"))`, - `"SystemAssigned".Equals((string)this.MyInvocation?.BoundParameters["IdentityType"])`), - And(And(`(bool)(true == this.MyInvocation?.BoundParameters.ContainsKey("IdentityType"))`, - `"None".Equals((string)this.MyInvocation?.BoundParameters["IdentityType"])`), - `DoesSupportSystemAssignedIdentityMethod(${$this.bodyParameter?.value}.IdentityType)`)), `supportsSystemAssignedIdentity = true;`); - yield `this.MyInvocation?.BoundParameters.Remove("IdentityType");`; - yield If(And(`supportsUserAssignedIdentity`, `supportsSystemAssignedIdentity`), `this.MyInvocation?.BoundParameters.Add("IdentityType", "SystemAssigned,UserAssigned");`); - yield ElseIf(And(`supportsUserAssignedIdentity`, `!supportsSystemAssignedIdentity`), `this.MyInvocation?.BoundParameters.Add("IdentityType", "UserAssigned");`); - yield ElseIf(And(`!supportsUserAssignedIdentity`, `supportsSystemAssignedIdentity`), + And('(bool)(true == this.MyInvocation?.BoundParameters.ContainsKey("IdentityType"))', + '"SystemAssigned".Equals((string)this.MyInvocation?.BoundParameters["IdentityType"])'), + And(And('(bool)(true == this.MyInvocation?.BoundParameters.ContainsKey("IdentityType"))', + '"None".Equals((string)this.MyInvocation?.BoundParameters["IdentityType"])'), + `DoesSupportSystemAssignedIdentityMethod(${$this.bodyParameter?.value}.IdentityType)`)), 'supportsSystemAssignedIdentity = true;'); + yield 'this.MyInvocation?.BoundParameters.Remove("IdentityType");'; + yield If(And('supportsUserAssignedIdentity', 'supportsSystemAssignedIdentity'), 'this.MyInvocation?.BoundParameters.Add("IdentityType", "SystemAssigned,UserAssigned");'); + yield ElseIf(And('supportsUserAssignedIdentity', '!supportsSystemAssignedIdentity'), 'this.MyInvocation?.BoundParameters.Add("IdentityType", "UserAssigned");'); + yield ElseIf(And('!supportsUserAssignedIdentity', 'supportsSystemAssignedIdentity'), function* () { - yield `this.MyInvocation?.BoundParameters.Add("IdentityType", "SystemAssigned");`; - yield `this.MyInvocation?.BoundParameters.Remove("UserAssignedIdentity");`; - yield `this.MyInvocation?.BoundParameters.Add("UserAssignedIdentity", null);`; + yield 'this.MyInvocation?.BoundParameters.Add("IdentityType", "SystemAssigned");'; + yield 'this.MyInvocation?.BoundParameters.Remove("UserAssignedIdentity");'; + yield 'this.MyInvocation?.BoundParameters.Add("UserAssignedIdentity", null);'; }); yield Else(function* () { - yield `this.MyInvocation?.BoundParameters.Add("IdentityType", "None");`; - yield `this.MyInvocation?.BoundParameters.Remove("UserAssignedIdentity");`; - yield `this.MyInvocation?.BoundParameters.Add("UserAssignedIdentity", null);`; + yield 'this.MyInvocation?.BoundParameters.Add("IdentityType", "None");'; + yield 'this.MyInvocation?.BoundParameters.Remove("UserAssignedIdentity"); '; + yield 'this.MyInvocation?.BoundParameters.Add("UserAssignedIdentity", null);'; }); }; return new Statements(preProcessManagedIdentity); @@ -1200,10 +1199,10 @@ export class CmdletClass extends Class { private GetPutPreProcess(cmdlet: CmdletClass, pathParams: Array, nonPathParams: Array, viaIdentity: boolean): Statements { const $this = cmdlet; - const updateBodyMethod = new Method(`Update${$this.bodyParameter?.value}`, dotnet.Void, { + const updateBodyMethod = new Method(`Update${$this.bodyParameter?.value} `, dotnet.Void, { access: Access.Private }); - const httpOperationName = `${$this.operation.callGraph[0].language.csharp?.name}${viaIdentity ? 'ViaIdentity' : ''}WithResult`; + const httpOperationName = `${$this.operation.callGraph[0].language.csharp?.name}${viaIdentity ? 'ViaIdentity' : ''} WithResult`; if (!$this.hasMethodWithSameDeclaration(updateBodyMethod)) { updateBodyMethod.add(function* () { const bodyParameters = $this.properties.filter(each => { @@ -1217,13 +1216,13 @@ export class CmdletClass extends Class { return false; }); for (const param of bodyParameters) { - yield If(`(bool)(true == this.MyInvocation?.BoundParameters.ContainsKey("${param.name}"))`, `this.${param.name} = (${param.type.declaration})(this.MyInvocation?.BoundParameters["${param.name}"]);`); + yield If(`(bool)(true == this.MyInvocation?.BoundParameters.ContainsKey("${param.name}"))`, `this.${param.name} = (${param.type.declaration})(this.MyInvocation?.BoundParameters["${param.name}"]); `); } }); $this.add(updateBodyMethod); } if (!$this.state.project.keepIdentityType && $this.ContainsIdentityTypeParameter(cmdlet)) { - const preProcessManagedIdentityMethod = new Method(`PreProcessManagedIdentityParameters`, dotnet.Void, { + const preProcessManagedIdentityMethod = new Method('PreProcessManagedIdentityParameters', dotnet.Void, { access: Access.Private }); if (!$this.hasMethodWithSameDeclaration(preProcessManagedIdentityMethod)) { @@ -1232,12 +1231,12 @@ export class CmdletClass extends Class { } } const getPut = function* () { - yield `${$this.bodyParameter?.value} = await this.${$this.$('Client').invokeMethod(httpOperationName, ...[...pathParams, ...nonPathParams]).implementation}`; + yield `${$this.bodyParameter?.value} = await this.${$this.$('Client').invokeMethod(httpOperationName, ...[...pathParams, ...nonPathParams]).implementation} `; // PreProcess body parameter if (!$this.state.project.keepIdentityType && $this.ContainsIdentityTypeParameter(cmdlet)) { - yield `this.PreProcessManagedIdentityParameters();`; + yield 'this.PreProcessManagedIdentityParameters();'; } - yield `this.${updateBodyMethod.name}();`; + yield `this.${updateBodyMethod.name} (); `; /** Instance: * _requestBodyParametersBody = await this.Client.GrafanaGetWithResult(SubscriptionId, ResourceGroupName, Name, this, Pipeline); * this.Update_requestBodyParametersBody(); @@ -1262,7 +1261,7 @@ export class CmdletClass extends Class { if (each.language.csharp?.responseType) { parameters.push(new Parameter('response', System.Threading.Tasks.Task({ declaration: each.language.csharp?.responseType }), { - description: `the body result as a ${each.language.csharp?.responseType} from the remote call` + description: `the body result as a ${each.language.csharp?.responseType} from the remote call` })); } if (each.language.csharp?.headerType) { From 9d04c640b2a1d91145a0120325a1861082fecbfa Mon Sep 17 00:00:00 2001 From: Beisi Zhou Date: Thu, 11 Jan 2024 16:38:48 +0800 Subject: [PATCH 21/30] remove extra space --- powershell/cmdlets/class.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/powershell/cmdlets/class.ts b/powershell/cmdlets/class.ts index 38cef5306d..7e958dd9ff 100644 --- a/powershell/cmdlets/class.ts +++ b/powershell/cmdlets/class.ts @@ -1199,10 +1199,10 @@ export class CmdletClass extends Class { private GetPutPreProcess(cmdlet: CmdletClass, pathParams: Array, nonPathParams: Array, viaIdentity: boolean): Statements { const $this = cmdlet; - const updateBodyMethod = new Method(`Update${$this.bodyParameter?.value} `, dotnet.Void, { + const updateBodyMethod = new Method(`Update${$this.bodyParameter?.value}`, dotnet.Void, { access: Access.Private }); - const httpOperationName = `${$this.operation.callGraph[0].language.csharp?.name}${viaIdentity ? 'ViaIdentity' : ''} WithResult`; + const httpOperationName = `${$this.operation.callGraph[0].language.csharp?.name}${viaIdentity ? 'ViaIdentity' : ''}WithResult`; if (!$this.hasMethodWithSameDeclaration(updateBodyMethod)) { updateBodyMethod.add(function* () { const bodyParameters = $this.properties.filter(each => { @@ -1216,7 +1216,7 @@ export class CmdletClass extends Class { return false; }); for (const param of bodyParameters) { - yield If(`(bool)(true == this.MyInvocation?.BoundParameters.ContainsKey("${param.name}"))`, `this.${param.name} = (${param.type.declaration})(this.MyInvocation?.BoundParameters["${param.name}"]); `); + yield If(`(bool)(true == this.MyInvocation?.BoundParameters.ContainsKey("${param.name}"))`, `this.${param.name} = (${param.type.declaration})(this.MyInvocation?.BoundParameters["${param.name}"]);`); } }); $this.add(updateBodyMethod); @@ -1236,7 +1236,7 @@ export class CmdletClass extends Class { if (!$this.state.project.keepIdentityType && $this.ContainsIdentityTypeParameter(cmdlet)) { yield 'this.PreProcessManagedIdentityParameters();'; } - yield `this.${updateBodyMethod.name} (); `; + yield `this.${updateBodyMethod.name}();`; /** Instance: * _requestBodyParametersBody = await this.Client.GrafanaGetWithResult(SubscriptionId, ResourceGroupName, Name, this, Pipeline); * this.Update_requestBodyParametersBody(); @@ -1261,7 +1261,7 @@ export class CmdletClass extends Class { if (each.language.csharp?.responseType) { parameters.push(new Parameter('response', System.Threading.Tasks.Task({ declaration: each.language.csharp?.responseType }), { - description: `the body result as a ${each.language.csharp?.responseType} from the remote call` + description: `the body result as a ${each.language.csharp?.responseType} from the remote call` })); } if (each.language.csharp?.headerType) { From aa18b1997ec774ebe61d45a981ac342a1d63ccd1 Mon Sep 17 00:00:00 2001 From: Beisi Zhou Date: Thu, 11 Jan 2024 23:44:10 +0800 Subject: [PATCH 22/30] support system assigned identity only scenario --- powershell/cmdlets/class.ts | 55 ++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/powershell/cmdlets/class.ts b/powershell/cmdlets/class.ts index 7e958dd9ff..236bd42f8a 100644 --- a/powershell/cmdlets/class.ts +++ b/powershell/cmdlets/class.ts @@ -863,8 +863,8 @@ export class CmdletClass extends Class { default: if (!$this.state.project.keepIdentityType && $this.operation.details.csharp.verb.toLowerCase() === 'new' - && $this.operation.details.csharp.virtualParameters?.body.map(p => p.name).includes('IdentityType') - && $this.operation.details.csharp.virtualParameters?.body.map(p => p.name).includes('UserAssignedIdentity')) { + && $this.ContainsIdentityTypeParameter($this) + && $this.ContainsUserAssignedIdentityParameter($this)) { preProcesses.push($this.ManagedIdentityPreProcessForNewVerbCmdlet); } preProcesses.push(undefined); @@ -1087,21 +1087,28 @@ export class CmdletClass extends Class { } } - private ContainsIdentityTypeParameter(cmdlet: CmdletClass): boolean { + private ContainsSpecifiedParameter(cmdlet: CmdletClass, parameterName: string): boolean { const $this = cmdlet; - const identityTypeParameter = $this.properties.filter(each => { + const parameter = $this.properties.filter(each => { for (const attribute of each.attributes) { for (const parameter of attribute.parameters) { if ('global::Microsoft.Rest.ParameterCategory.Body' === valueOf(parameter) - && 'IdentityType' === each.name - && 'string' === each.type.declaration) { + && parameterName === each.name) { return true; } } } return false; }); - return identityTypeParameter.length > 0; + return parameter.length > 0; + } + + private ContainsIdentityTypeParameter(cmdlet: CmdletClass): boolean { + return cmdlet.ContainsSpecifiedParameter(cmdlet, 'IdentityType'); + } + + private ContainsUserAssignedIdentityParameter(cmdlet: CmdletClass): boolean { + return cmdlet.ContainsSpecifiedParameter(cmdlet, 'UserAssignedIdentity'); } private GetUserAssignedIdentityTypeDeclaration(cmdlet: CmdletClass): string { @@ -1117,12 +1124,11 @@ export class CmdletClass extends Class { } return false; }); - return userAssignedIdentityParameter?.[0].type.declaration ?? `${$this.state.project.serviceNamespace.fullName}.Models.IUserAssignedIdentities`; + return userAssignedIdentityParameter?.[0]?.type?.declaration ?? undefined; } private ManagedIdentityPreProcessForNewVerbCmdlet(cmdlet: CmdletClass, pathParams: Array, nonPathParams: Array, viaIdentity: boolean): Statements { const $this = cmdlet; - const preProcessManagedIdentityParametersMethod = new Method('PreProcessManagedIdentityParameters', dotnet.Void, { access: Access.Private }); @@ -1147,14 +1153,7 @@ export class CmdletClass extends Class { private ManagedIdentityPreProcessForUpdateVerbCmdlet(cmdlet: CmdletClass): Statements { const $this = cmdlet; - const doesSupportUserAssignedIdentityMethod = new Method('DoesSupportUserAssignedIdentity', dotnet.Bool, { - access: Access.Private, - parameters: [new Parameter('identityType', dotnet.String)] - }); - if (!$this.hasMethodWithSameDeclaration(doesSupportUserAssignedIdentityMethod)) { - doesSupportUserAssignedIdentityMethod.add(Return('new System.Collections.Generic.List { "UserAssigned", "SystemAssigned,UserAssigned", "SystemAssigned, UserAssigned"}.Contains(identityType)')); - $this.add(doesSupportUserAssignedIdentityMethod); - } + const containsUserAssignedIdentity = $this.ContainsUserAssignedIdentityParameter($this); const doesSupportSystemAssignedIdentityMethod = new Method('DoesSupportSystemAssignedIdentityMethod', dotnet.Bool, { access: Access.Private, parameters: [new Parameter('identityType', dotnet.String)] @@ -1163,16 +1162,28 @@ export class CmdletClass extends Class { doesSupportSystemAssignedIdentityMethod.add(Return('new System.Collections.Generic.List { "SystemAssigned", "SystemAssigned,UserAssigned", "SystemAssigned, UserAssigned"}.Contains(identityType)')); $this.add(doesSupportSystemAssignedIdentityMethod); } + if (containsUserAssignedIdentity) { + const doesSupportUserAssignedIdentityMethod = new Method('DoesSupportUserAssignedIdentity', dotnet.Bool, { + access: Access.Private, + parameters: [new Parameter('identityType', dotnet.String)] + }); + if (!$this.hasMethodWithSameDeclaration(doesSupportUserAssignedIdentityMethod)) { + doesSupportUserAssignedIdentityMethod.add(Return('new System.Collections.Generic.List { "UserAssigned", "SystemAssigned,UserAssigned", "SystemAssigned, UserAssigned"}.Contains(identityType)')); + $this.add(doesSupportUserAssignedIdentityMethod); + } + } const preProcessManagedIdentity = function* () { const supportsUserAssignedIdentity = new LocalVariable('supportsUserAssignedIdentity', dotnet.Var, { initializer: `${dotnet.False}` }); const supportsSystemAssignedIdentity = new LocalVariable('supportsSystemAssignedIdentity', dotnet.Var, { initializer: `${dotnet.False}` }); yield supportsUserAssignedIdentity; yield supportsSystemAssignedIdentity; - yield If(Or( - And('(bool)(true == this.MyInvocation?.BoundParameters.ContainsKey("UserAssignedIdentity"))', - `((${$this.GetUserAssignedIdentityTypeDeclaration(cmdlet)})this.MyInvocation?.BoundParameters["UserAssignedIdentity"])?.Count > 0`), - And('!(bool)(true == this.MyInvocation?.BoundParameters.ContainsKey("UserAssignedIdentity"))', - `DoesSupportUserAssignedIdentity(${$this.bodyParameter?.value}.IdentityType)`)), 'supportsUserAssignedIdentity = true;'); + if (containsUserAssignedIdentity) { + yield If(Or( + And('(bool)(true == this.MyInvocation?.BoundParameters.ContainsKey("UserAssignedIdentity"))', + `((${$this.GetUserAssignedIdentityTypeDeclaration(cmdlet)})this.MyInvocation?.BoundParameters["UserAssignedIdentity"])?.Count > 0`), + And('!(bool)(true == this.MyInvocation?.BoundParameters.ContainsKey("UserAssignedIdentity"))', + `DoesSupportUserAssignedIdentity(${$this.bodyParameter?.value}.IdentityType)`)), 'supportsUserAssignedIdentity = true;'); + } yield If(Or( And('(bool)(true == this.MyInvocation?.BoundParameters.ContainsKey("IdentityType"))', '"SystemAssigned".Equals((string)this.MyInvocation?.BoundParameters["IdentityType"])'), From 111bfb946a962d4062331d0160537a7e9c70d4f1 Mon Sep 17 00:00:00 2001 From: Beisi Zhou Date: Fri, 12 Jan 2024 00:03:43 +0800 Subject: [PATCH 23/30] polish --- powershell/cmdlets/class.ts | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/powershell/cmdlets/class.ts b/powershell/cmdlets/class.ts index 236bd42f8a..60c57e989b 100644 --- a/powershell/cmdlets/class.ts +++ b/powershell/cmdlets/class.ts @@ -1088,19 +1088,7 @@ export class CmdletClass extends Class { } private ContainsSpecifiedParameter(cmdlet: CmdletClass, parameterName: string): boolean { - const $this = cmdlet; - const parameter = $this.properties.filter(each => { - for (const attribute of each.attributes) { - for (const parameter of attribute.parameters) { - if ('global::Microsoft.Rest.ParameterCategory.Body' === valueOf(parameter) - && parameterName === each.name) { - return true; - } - } - } - return false; - }); - return parameter.length > 0; + return cmdlet.operation.details.csharp.virtualParameters?.body?.map(p => p.name)?.includes(parameterName) ?? false; } private ContainsIdentityTypeParameter(cmdlet: CmdletClass): boolean { From c7814bb2291682d9fd2bb21321e6d80a753f4363 Mon Sep 17 00:00:00 2001 From: Beisi Zhou Date: Thu, 18 Jan 2024 13:14:49 +0800 Subject: [PATCH 24/30] fix internal cmdlets --- powershell/cmdlets/class.ts | 49 +++++++++++-------- powershell/plugins/create-commands-v2.ts | 21 ++++++-- .../BuildTime/Models/PsProxyOutputs.cs | 2 +- 3 files changed, 47 insertions(+), 25 deletions(-) diff --git a/powershell/cmdlets/class.ts b/powershell/cmdlets/class.ts index 60c57e989b..536c180128 100644 --- a/powershell/cmdlets/class.ts +++ b/powershell/cmdlets/class.ts @@ -861,11 +861,13 @@ export class CmdletClass extends Class { break; case CommandType.Atomic: default: - if (!$this.state.project.keepIdentityType && - $this.operation.details.csharp.verb.toLowerCase() === 'new' - && $this.ContainsIdentityTypeParameter($this) - && $this.ContainsUserAssignedIdentityParameter($this)) { - preProcesses.push($this.ManagedIdentityPreProcessForNewVerbCmdlet); + if (!$this.state.project.keepIdentityType + && $this.ContainsIdentityTypeParameter($this)) { + if ($this.operation.details.csharp.verb.toLowerCase() === 'new' && $this.ContainsUserAssignedIdentityParameter($this)) { + preProcesses.push($this.ManagedIdentityPreProcessForNewVerbCmdlet); + } else if ($this.operation.details.csharp.verb.toLowerCase() === 'update') { + preProcesses.push($this.ManagedIdentityPreProcessForUpdateVerbCmdlet); + } } preProcesses.push(undefined); break; @@ -1184,16 +1186,31 @@ export class CmdletClass extends Class { yield ElseIf(And('!supportsUserAssignedIdentity', 'supportsSystemAssignedIdentity'), function* () { yield 'this.MyInvocation?.BoundParameters.Add("IdentityType", "SystemAssigned");'; - yield 'this.MyInvocation?.BoundParameters.Remove("UserAssignedIdentity");'; - yield 'this.MyInvocation?.BoundParameters.Add("UserAssignedIdentity", null);'; + if (containsUserAssignedIdentity) { + yield 'this.MyInvocation?.BoundParameters.Remove("UserAssignedIdentity");'; + yield 'this.MyInvocation?.BoundParameters.Add("UserAssignedIdentity", null);'; + } }); yield Else(function* () { yield 'this.MyInvocation?.BoundParameters.Add("IdentityType", "None");'; - yield 'this.MyInvocation?.BoundParameters.Remove("UserAssignedIdentity"); '; - yield 'this.MyInvocation?.BoundParameters.Add("UserAssignedIdentity", null);'; + if (containsUserAssignedIdentity) { + yield 'this.MyInvocation?.BoundParameters.Remove("UserAssignedIdentity"); '; + yield 'this.MyInvocation?.BoundParameters.Add("UserAssignedIdentity", null);'; + } }); }; - return new Statements(preProcessManagedIdentity); + + const preProcessManagedIdentityMethod = new Method('PreProcessManagedIdentityParameters', dotnet.Void, { + access: Access.Private + }); + + if (!$this.hasMethodWithSameDeclaration(preProcessManagedIdentityMethod)) { + preProcessManagedIdentityMethod.add(preProcessManagedIdentity); + $this.add(preProcessManagedIdentityMethod); + } + return new Statements(function* () { + yield `this.${preProcessManagedIdentityMethod.name}();`; + }); } private GetPutPreProcess(cmdlet: CmdletClass, pathParams: Array, nonPathParams: Array, viaIdentity: boolean): Statements { @@ -1220,20 +1237,12 @@ export class CmdletClass extends Class { }); $this.add(updateBodyMethod); } - if (!$this.state.project.keepIdentityType && $this.ContainsIdentityTypeParameter(cmdlet)) { - const preProcessManagedIdentityMethod = new Method('PreProcessManagedIdentityParameters', dotnet.Void, { - access: Access.Private - }); - if (!$this.hasMethodWithSameDeclaration(preProcessManagedIdentityMethod)) { - preProcessManagedIdentityMethod.add($this.ManagedIdentityPreProcessForUpdateVerbCmdlet(cmdlet)); - $this.add(preProcessManagedIdentityMethod); - } - } + const getPut = function* () { yield `${$this.bodyParameter?.value} = await this.${$this.$('Client').invokeMethod(httpOperationName, ...[...pathParams, ...nonPathParams]).implementation} `; // PreProcess body parameter if (!$this.state.project.keepIdentityType && $this.ContainsIdentityTypeParameter(cmdlet)) { - yield 'this.PreProcessManagedIdentityParameters();'; + yield $this.ManagedIdentityPreProcessForUpdateVerbCmdlet(cmdlet); } yield `this.${updateBodyMethod.name}();`; /** Instance: diff --git a/powershell/plugins/create-commands-v2.ts b/powershell/plugins/create-commands-v2.ts index 4669efea5c..0393005d4b 100644 --- a/powershell/plugins/create-commands-v2.ts +++ b/powershell/plugins/create-commands-v2.ts @@ -116,6 +116,7 @@ export /* @internal */ class Inferrer { subjectPrefix!: string; isAzure!: boolean; supportJsonInput!: boolean; + keepIdentityType!: boolean; constructor(private state: State) { } @@ -143,6 +144,7 @@ export /* @internal */ class Inferrer { this.state.setValue('prefix', this.prefix); const model = this.state.model; + this.keepIdentityType = await this.state.getValue('keep-identitytype', false); this.state.message({ Channel: Channel.Debug, Text: `[CMDLET-PREFIX] => '${model.language.default.prefix}'` @@ -168,7 +170,6 @@ export /* @internal */ class Inferrer { parameters: new Dictionary(), }; const disableGetPut = await this.state.getValue('disable-getput', false); - const keepIdentityType = await this.state.getValue('keep-identitytype', false); this.state.message({ Channel: Channel.Debug, Text: 'detecting high level commands...' }); for (const operationGroup of values(model.operationGroups)) { @@ -181,7 +182,7 @@ export /* @internal */ class Inferrer { hasPatch = true; patchOperation = operation; // skip adding variants for patch operation to avoid conflicts with getput - if (!keepIdentityType && this.containsIdentityType(operation)) { + if (this.shouldReplacePatchByGetPut(operation) && putOperation) { continue; } } else if (operation.requests?.[0]?.protocol?.http?.method.toLowerCase() === 'get') { @@ -204,7 +205,7 @@ export /* @internal */ class Inferrer { */ if (this.isAzure && (!disableGetPut && !hasPatch || - hasPatch && !keepIdentityType && patchOperation && this.containsIdentityType(patchOperation)) + hasPatch && patchOperation && this.shouldReplacePatchByGetPut(patchOperation)) && getOperations && putOperation && putOperation.requests?.length == 1) { @@ -227,7 +228,15 @@ export /* @internal */ class Inferrer { return model; } - containsIdentityType(op: Operation): boolean { + private containsUserAssignedIdentity(op: Operation): boolean { + const body = op.requests?.[0].parameters?.find((p) => !p.origin || p.origin.indexOf('modelerfour:synthesized') < 0) || null; + // property identity in the body parameter + const identityProperty = (body && body.schema && isObjectSchema(body.schema)) ? values(getAllProperties(body.schema)).where(property => !property.language.default.readOnly && isObjectSchema(property.schema) && property.language.default.name === 'identity')?.toArray()?.[0] : null; + const userAssignedIdentityProperty = (identityProperty && identityProperty.schema && isObjectSchema(identityProperty.schema)) ? values(getAllProperties(identityProperty.schema)).where(property => !property.language.default.readOnly && property.language.default.name === 'userAssignedIdentities')?.toArray()?.[0] : null; + return userAssignedIdentityProperty !== null && userAssignedIdentityProperty !== undefined; + } + + private containsIdentityType(op: Operation): boolean { const body = op.requests?.[0].parameters?.find((p) => !p.origin || p.origin.indexOf('modelerfour:synthesized') < 0) || null; // property identity in the body parameter const identityProperty = (body && body.schema && isObjectSchema(body.schema)) ? values(getAllProperties(body.schema)).where(property => !property.language.default.readOnly && isObjectSchema(property.schema) && property.language.default.name === 'identity')?.toArray()?.[0] : null; @@ -235,6 +244,10 @@ export /* @internal */ class Inferrer { return identityTypeProperty !== null && identityTypeProperty !== undefined; } + private shouldReplacePatchByGetPut(op: Operation): boolean { + return !this.keepIdentityType && op && this.containsIdentityType(op) && this.containsUserAssignedIdentity(op); + } + inferCommand(operation: Array, group: string, suffix: Array = []): Array { operation = operation.filter(each => each !== 'all'); // no instant match diff --git a/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs b/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs index 5a39320da6..d3d934ee88 100644 --- a/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs +++ b/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs @@ -310,7 +310,7 @@ private string GetDefaultValuesStatements() private string GetManagedIdentityMappingStatements() { var sb = new StringBuilder(); - if (!VariantGroup.IsInternal && IsAzure && + if (IsAzure && (VariantGroup.CmdletVerb.Equals("New") || VariantGroup.CmdletVerb.Equals("Update"))) { var statement1 = GetUserAssignedIdentityMappingStatements(); From 4bc7fa5febbc33919762f015d2845a3ebb2910c0 Mon Sep 17 00:00:00 2001 From: Beisi Zhou Date: Thu, 18 Jan 2024 14:01:44 +0800 Subject: [PATCH 25/30] get+put update identity type only --- powershell/cmdlets/class.ts | 18 ++++++++---------- powershell/plugins/create-commands-v2.ts | 15 ++++----------- 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/powershell/cmdlets/class.ts b/powershell/cmdlets/class.ts index 536c180128..59bcf2bafa 100644 --- a/powershell/cmdlets/class.ts +++ b/powershell/cmdlets/class.ts @@ -861,13 +861,11 @@ export class CmdletClass extends Class { break; case CommandType.Atomic: default: - if (!$this.state.project.keepIdentityType - && $this.ContainsIdentityTypeParameter($this)) { - if ($this.operation.details.csharp.verb.toLowerCase() === 'new' && $this.ContainsUserAssignedIdentityParameter($this)) { - preProcesses.push($this.ManagedIdentityPreProcessForNewVerbCmdlet); - } else if ($this.operation.details.csharp.verb.toLowerCase() === 'update') { - preProcesses.push($this.ManagedIdentityPreProcessForUpdateVerbCmdlet); - } + if (!$this.state.project.keepIdentityType && + $this.operation.details.csharp.verb.toLowerCase() === 'new' && + $this.ContainsIdentityTypeParameter($this) && + $this.ContainsUserAssignedIdentityParameter($this)) { + preProcesses.push($this.ManagedIdentityPreProcessForNewVerbCmdlet); } preProcesses.push(undefined); break; @@ -1141,7 +1139,7 @@ export class CmdletClass extends Class { }); } - private ManagedIdentityPreProcessForUpdateVerbCmdlet(cmdlet: CmdletClass): Statements { + private ManagedIdentityPreProcessForUpdateVerbCmdletWithGetResult(cmdlet: CmdletClass): Statements { const $this = cmdlet; const containsUserAssignedIdentity = $this.ContainsUserAssignedIdentityParameter($this); const doesSupportSystemAssignedIdentityMethod = new Method('DoesSupportSystemAssignedIdentityMethod', dotnet.Bool, { @@ -1200,7 +1198,7 @@ export class CmdletClass extends Class { }); }; - const preProcessManagedIdentityMethod = new Method('PreProcessManagedIdentityParameters', dotnet.Void, { + const preProcessManagedIdentityMethod = new Method('PreProcessManagedIdentityParametersWithGetResult', dotnet.Void, { access: Access.Private }); @@ -1242,7 +1240,7 @@ export class CmdletClass extends Class { yield `${$this.bodyParameter?.value} = await this.${$this.$('Client').invokeMethod(httpOperationName, ...[...pathParams, ...nonPathParams]).implementation} `; // PreProcess body parameter if (!$this.state.project.keepIdentityType && $this.ContainsIdentityTypeParameter(cmdlet)) { - yield $this.ManagedIdentityPreProcessForUpdateVerbCmdlet(cmdlet); + yield $this.ManagedIdentityPreProcessForUpdateVerbCmdletWithGetResult(cmdlet); } yield `this.${updateBodyMethod.name}();`; /** Instance: diff --git a/powershell/plugins/create-commands-v2.ts b/powershell/plugins/create-commands-v2.ts index 0393005d4b..fdb782d0d7 100644 --- a/powershell/plugins/create-commands-v2.ts +++ b/powershell/plugins/create-commands-v2.ts @@ -181,8 +181,8 @@ export /* @internal */ class Inferrer { if (operation.requests?.[0]?.protocol?.http?.method.toLowerCase() === 'patch') { hasPatch = true; patchOperation = operation; - // skip adding variants for patch operation to avoid conflicts with getput - if (this.shouldReplacePatchByGetPut(operation) && putOperation) { + // bez: remove/hide patch operation to avoid conflicts with replacements + if (this.shouldReplacePatchByGetPut(operation)) { continue; } } else if (operation.requests?.[0]?.protocol?.http?.method.toLowerCase() === 'get') { @@ -228,14 +228,6 @@ export /* @internal */ class Inferrer { return model; } - private containsUserAssignedIdentity(op: Operation): boolean { - const body = op.requests?.[0].parameters?.find((p) => !p.origin || p.origin.indexOf('modelerfour:synthesized') < 0) || null; - // property identity in the body parameter - const identityProperty = (body && body.schema && isObjectSchema(body.schema)) ? values(getAllProperties(body.schema)).where(property => !property.language.default.readOnly && isObjectSchema(property.schema) && property.language.default.name === 'identity')?.toArray()?.[0] : null; - const userAssignedIdentityProperty = (identityProperty && identityProperty.schema && isObjectSchema(identityProperty.schema)) ? values(getAllProperties(identityProperty.schema)).where(property => !property.language.default.readOnly && property.language.default.name === 'userAssignedIdentities')?.toArray()?.[0] : null; - return userAssignedIdentityProperty !== null && userAssignedIdentityProperty !== undefined; - } - private containsIdentityType(op: Operation): boolean { const body = op.requests?.[0].parameters?.find((p) => !p.origin || p.origin.indexOf('modelerfour:synthesized') < 0) || null; // property identity in the body parameter @@ -245,7 +237,8 @@ export /* @internal */ class Inferrer { } private shouldReplacePatchByGetPut(op: Operation): boolean { - return !this.keepIdentityType && op && this.containsIdentityType(op) && this.containsUserAssignedIdentity(op); + // bez: we are assuming patch and put both exist at the same time + return !this.keepIdentityType && op && this.containsIdentityType(op); } inferCommand(operation: Array, group: string, suffix: Array = []): Array { From 0db0d8a3b00e1a1a8a2f275d62643d01d96fae67 Mon Sep 17 00:00:00 2001 From: Beisi Zhou Date: Thu, 18 Jan 2024 15:14:41 +0800 Subject: [PATCH 26/30] add fallback logic if replacement is failed --- powershell/plugins/create-commands-v2.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/powershell/plugins/create-commands-v2.ts b/powershell/plugins/create-commands-v2.ts index fdb782d0d7..8ff3e91249 100644 --- a/powershell/plugins/create-commands-v2.ts +++ b/powershell/plugins/create-commands-v2.ts @@ -216,6 +216,12 @@ export /* @internal */ class Inferrer { const schema = putOperation?.requests?.[0]?.parameters?.find(p => p.protocol.http?.in === 'body')?.schema; if (getOperation && !hasQueryParameter && schema && [...values(getOperation?.responses)].filter(each => (each).schema !== schema).length === 0) { await this.addVariants(putOperation.parameters, putOperation, this.createCommandVariant('create', [operationGroup.$key], [], this.state.model), '', this.state, [getOperation], CommandType.GetPut); + } else if (hasPatch && patchOperation && this.shouldReplacePatchByGetPut(patchOperation)) { + // bez: patch operation can't be replaced by Get+PUT, add patch operation back and keep identity type + for (const variant of await this.inferCommandNames(patchOperation, operationGroup.$key, this.state)) { + await this.addVariants(patchOperation.parameters, patchOperation, variant, '', this.state); + } + this.state.setValue('keep-identitytype', true); } } } From b63554d9104b6e6f992fab40c7f0342b16033438 Mon Sep 17 00:00:00 2001 From: Beisi Zhou Date: Thu, 18 Jan 2024 17:54:20 +0800 Subject: [PATCH 27/30] add tranform in last layer --- .../psruntime/BuildTime/Cmdlets/ExportProxyCmdlet.cs | 4 ++-- .../resources/psruntime/BuildTime/Models/PsProxyOutputs.cs | 1 + .../resources/psruntime/BuildTime/Models/PsProxyTypes.cs | 5 +++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/powershell/resources/psruntime/BuildTime/Cmdlets/ExportProxyCmdlet.cs b/powershell/resources/psruntime/BuildTime/Cmdlets/ExportProxyCmdlet.cs index 8cc63b9e80..c4baf6615d 100644 --- a/powershell/resources/psruntime/BuildTime/Cmdlets/ExportProxyCmdlet.cs +++ b/powershell/resources/psruntime/BuildTime/Cmdlets/ExportProxyCmdlet.cs @@ -70,9 +70,9 @@ protected override void ProcessRecord() var variantGroups = profileGroups.SelectMany(pg => pg.Variants .GroupBy(v => new { v.CmdletName, v.IsInternal }) .Select(vg => new VariantGroup(ModuleName, vg.Key.CmdletName, vg.Select(v => v).ToArray(), - Path.Combine(vg.Key.IsInternal ? InternalFolder : ExportsFolder, pg.ProfileFolder), pg.ProfileName, isInternal: vg.Key.IsInternal))) + Path.Combine(vg.Key.IsInternal ? InternalFolder : ExportsFolder, pg.ProfileFolder), pg.ProfileName, isInternal: vg.Key.IsInternal, + containsInternalCmdlet: vg.Key.IsInternal ? false : pg.Variants.GroupBy(v => new { v.CmdletName, v.IsInternal }).Where(vr => vr.Key.CmdletName.Equals(vg.Key.CmdletName)).Count() > 1))) .ToArray(); - var license = new StringBuilder(); license.Append(@" # ---------------------------------------------------------------------------------- diff --git a/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs b/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs index d3d934ee88..72a0fabd24 100644 --- a/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs +++ b/powershell/resources/psruntime/BuildTime/Models/PsProxyOutputs.cs @@ -311,6 +311,7 @@ private string GetManagedIdentityMappingStatements() { var sb = new StringBuilder(); if (IsAzure && + !VariantGroup.ContainsInternalCmdlet && (VariantGroup.CmdletVerb.Equals("New") || VariantGroup.CmdletVerb.Equals("Update"))) { var statement1 = GetUserAssignedIdentityMappingStatements(); diff --git a/powershell/resources/psruntime/BuildTime/Models/PsProxyTypes.cs b/powershell/resources/psruntime/BuildTime/Models/PsProxyTypes.cs index 5680dd2355..440912567f 100644 --- a/powershell/resources/psruntime/BuildTime/Models/PsProxyTypes.cs +++ b/powershell/resources/psruntime/BuildTime/Models/PsProxyTypes.cs @@ -53,6 +53,7 @@ internal class VariantGroup public PsHelpInfo HelpInfo { get; } public bool IsGenerated { get; } public bool IsInternal { get; } + public bool ContainsInternalCmdlet { get; } public string OutputFolder { get; } public string FileName { get; } @@ -60,7 +61,7 @@ internal class VariantGroup public CommentInfo CommentInfo { get; } - public VariantGroup(string moduleName, string cmdletName, Variant[] variants, string outputFolder, string profileName = NoProfiles, bool isTest = false, bool isInternal = false) + public VariantGroup(string moduleName, string cmdletName, Variant[] variants, string outputFolder, string profileName = NoProfiles, bool isTest = false, bool isInternal = false, bool containsInternalCmdlet = false) { ModuleName = moduleName; CmdletName = cmdletName; @@ -88,7 +89,7 @@ public VariantGroup(string moduleName, string cmdletName, Variant[] variants, st HelpInfo = Variants.Select(v => v.HelpInfo).FirstOrDefault() ?? new PsHelpInfo(); IsGenerated = Variants.All(v => v.Attributes.OfType().Any()); IsInternal = isInternal; - + ContainsInternalCmdlet = containsInternalCmdlet; OutputFolder = outputFolder; FileName = $"{CmdletName}{(isTest ? ".Tests" : String.Empty)}.ps1"; FilePath = Path.Combine(OutputFolder, FileName); From 3d11734f5ab33f3981ec760474a9bc5873105d0d Mon Sep 17 00:00:00 2001 From: Beisi Zhou Date: Fri, 19 Jan 2024 18:30:34 +0800 Subject: [PATCH 28/30] allow skip renaming identityuserassignedidentity to userassignedidentity --- .../plugins/plugin-create-inline-properties.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/powershell/plugins/plugin-create-inline-properties.ts b/powershell/plugins/plugin-create-inline-properties.ts index ca11ec0f75..19610d4090 100644 --- a/powershell/plugins/plugin-create-inline-properties.ts +++ b/powershell/plugins/plugin-create-inline-properties.ts @@ -53,14 +53,14 @@ function getNameOptions(typeName: string, components: Array) { return [...result.values()]; } -function getProposedNameForObjectInlinedProperty(propertyName: string, inlinedPropertyName: string): string { +function getProposedNameForObjectInlinedProperty(propertyName: string, inlinedPropertyName: string, flattenUserAssignedIdentity: boolean): string { const proposedName = getPascalIdentifier(`${propertyName === 'properties' || propertyName === 'error' - || (propertyName === 'identity' && inlinedPropertyName === 'UserAssignedIdentities') ? '' : pascalCase(fixLeadingNumber(deconstruct(propertyName)).map(each => singularize(each)))} ${inlinedPropertyName}`); + || (flattenUserAssignedIdentity && propertyName === 'identity' && inlinedPropertyName === 'UserAssignedIdentities') ? '' : pascalCase(fixLeadingNumber(deconstruct(propertyName)).map(each => singularize(each)))} ${inlinedPropertyName}`); return proposedName; } -function createVirtualProperties(schema: ObjectSchema, stack: Array, threshold: number, conflicts: Array) { +function createVirtualProperties(schema: ObjectSchema, stack: Array, threshold: number, conflicts: Array, flattenUserAssignedIdentity: boolean) { // Some properties should be removed are wrongly kept as null and need to clean them if (schema.properties) { schema.properties = schema.properties.filter(each => each); @@ -122,7 +122,7 @@ function createVirtualProperties(schema: ObjectSchema, stack: Array, thr if (!isObjectSchema(parentSchema)) continue; - createVirtualProperties(parentSchema, [...stack, `${schema.language.default.name}`], threshold, conflicts); + createVirtualProperties(parentSchema, [...stack, `${schema.language.default.name}`], threshold, conflicts, flattenUserAssignedIdentity); const parentProperties = parentSchema.language.default.virtualProperties || { owned: [], @@ -178,7 +178,7 @@ function createVirtualProperties(schema: ObjectSchema, stack: Array, thr const propertyName = property.language.default.name; // for each object member, make sure that it's inlined it's children that it can. - createVirtualProperties(property.schema, [...stack, `${schema.language.default.name}`], threshold, conflicts); + createVirtualProperties(property.schema, [...stack, `${schema.language.default.name}`], threshold, conflicts, flattenUserAssignedIdentity); // this happens if there is a circular reference. // this means that this class should not attempt any inlining of that property at all . @@ -220,7 +220,6 @@ function createVirtualProperties(schema: ObjectSchema, stack: Array, thr required: property.required || property.language.default.required, }; virtualProperties.owned.push(privateProperty); - for (const inlinedProperty of [...virtualChildProperties.inherited, ...virtualChildProperties.owned]) { // child properties are be inlined without prefixing the name with the property name // unless there is a collision, in which case, we have to resolve @@ -229,7 +228,7 @@ function createVirtualProperties(schema: ObjectSchema, stack: Array, thr // deeper child properties should be inlined with their parent's name // ie, this.[properties].owner.name should be this.ownerName - const proposedName = getProposedNameForObjectInlinedProperty(propertyName, inlinedProperty.name); + const proposedName = getProposedNameForObjectInlinedProperty(propertyName, inlinedProperty.name, flattenUserAssignedIdentity); const components = [...removeSequentialDuplicates([propertyName, ...inlinedProperty.nameComponents])]; let readonly = inlinedProperty.readOnly || property.readOnly; @@ -428,7 +427,6 @@ async function createVirtuals(state: State): Promise { */ const threshold = await state.getValue('inlining-threshold', 24); const conflicts = new Array(); - for (const schema of values(state.model.schemas.objects)) { // did we already inline this objecct if (schema.language.default.inlined) { @@ -436,7 +434,7 @@ async function createVirtuals(state: State): Promise { } // we have an object, let's process it. - createVirtualProperties(schema, new Array(), threshold, conflicts); + createVirtualProperties(schema, new Array(), threshold, conflicts, await state.getValue('flatten-userassignedidentity', true)); } if (length(conflicts) > 0) { From 2dfa3a54fc3f2ffc42ffcb5d307291befb2cb964 Mon Sep 17 00:00:00 2001 From: Beisi Zhou Date: Thu, 25 Jan 2024 09:32:00 +0800 Subject: [PATCH 29/30] wip --- powershell/cmdlets/class.ts | 77 ++++++++++--------- powershell/plugins/create-commands-v2.ts | 67 ++++++++-------- .../plugin-create-inline-properties.ts | 20 ++--- powershell/utils/command-operation.ts | 3 +- 4 files changed, 84 insertions(+), 83 deletions(-) diff --git a/powershell/cmdlets/class.ts b/powershell/cmdlets/class.ts index 59bcf2bafa..fa87e7e500 100644 --- a/powershell/cmdlets/class.ts +++ b/powershell/cmdlets/class.ts @@ -358,7 +358,7 @@ type operationParameter = { }; type PreProcess = ((cmdlet: CmdletClass, pathParameters: Array, nonPathParameters: Array, viaIdentity: boolean) => Statements) | undefined; - +type ProcessGetResponse = ((cmdlet: CmdletClass) => Statements) | undefined; export class CmdletClass extends Class { private cancellationToken!: Expression; public state: State; @@ -854,25 +854,27 @@ export class CmdletClass extends Class { yield Try(function* () { // make the call. - const preProcesses: Array = []; + let preProcess: PreProcess; switch ($this.operation.commandType) { + case CommandType.ManagedIdentityUpdate: + preProcess = $this.ManagedIdentityUpdateCmdletPreProcess; case CommandType.GetPut: - preProcesses.push($this.GetPutPreProcess); + preProcess = $this.GetPutPreProcess; break; case CommandType.Atomic: default: if (!$this.state.project.keepIdentityType && $this.operation.details.csharp.verb.toLowerCase() === 'new' && - $this.ContainsIdentityTypeParameter($this) && - $this.ContainsUserAssignedIdentityParameter($this)) { - preProcesses.push($this.ManagedIdentityPreProcessForNewVerbCmdlet); + $this.ContainsIdentityTypeParameter() && + $this.ContainsUserAssignedIdentityParameter()) { + preProcess = $this.ManagedIdentityPreProcessForNewVerbCmdlet; } - preProcesses.push(undefined); + preProcess = undefined; break; } const actualCall = function* () { yield $this.eventListener.signal(Events.CmdletBeforeAPICall); - yield $this.ImplementCall(preProcesses); + yield $this.ImplementCall(preProcess); yield $this.eventListener.signal(Events.CmdletAfterAPICall); }; @@ -896,7 +898,7 @@ export class CmdletClass extends Class { }); } - private * ImplementCall(preProcesses: Array) { + private * ImplementCall(preProcess: PreProcess) { const $this = this; const operation = $this.operation; const apiCall = $this.apiCall; @@ -999,12 +1001,8 @@ export class CmdletClass extends Class { parameters.push(serializationMode); } - if (preProcesses) { - for (const preProcess of preProcesses) { - if (preProcess) { - yield preProcess($this, pathParameters, [...otherParams.map(each => toExpression(each.value)), dotnet.This, pipeline], false); - } - } + if (preProcess) { + yield preProcess($this, pathParameters, [...otherParams.map(each => toExpression(each.value)), dotnet.This, pipeline], false); } yield `await this.${$this.$('Client').invokeMethod(httpOperationName, ...parameters).implementation}`; } @@ -1048,12 +1046,9 @@ export class CmdletClass extends Class { if (serializationMode) { parameters.push(serializationMode); } - if (preProcesses) { - for (const preProcess of preProcesses) { - if (preProcess) { - yield preProcess($this, pathParameters, [...otherParams.map(each => toExpression(each.value)), dotnet.This, pipeline], true); - } - } + + if (preProcess) { + yield preProcess($this, pathParameters, [...otherParams.map(each => toExpression(each.value)), dotnet.This, pipeline], true); } yield `await this.${$this.$('Client').invokeMethod(`${httpOperationName}ViaIdentity`, ...parameters).implementation}`; }; @@ -1076,27 +1071,23 @@ export class CmdletClass extends Class { const jsonParameter = new Field('_jsonString', System.String); parameters = [...pathParameters, ...nonPathParameters, jsonParameter, ...callbackMethods, dotnet.This, pipeline]; } - if (preProcesses) { - for (const preProcess of preProcesses) { - if (preProcess) { - yield preProcess($this, pathParameters, [...otherParams.map(each => toExpression(each.value)), dotnet.This, pipeline], false); - } - } + if (preProcess) { + yield preProcess($this, pathParameters, [...otherParams.map(each => toExpression(each.value)), dotnet.This, pipeline], false); } yield `await this.${$this.$('Client').invokeMethod(httpOperationName, ...parameters).implementation}`; } } - private ContainsSpecifiedParameter(cmdlet: CmdletClass, parameterName: string): boolean { - return cmdlet.operation.details.csharp.virtualParameters?.body?.map(p => p.name)?.includes(parameterName) ?? false; + private GetSpecifiedParameter(parameterName: string): boolean { + return this.operation.details.csharp.virtualParameters?.body?.map(p => p.name)?.includes(parameterName) ?? false; } - private ContainsIdentityTypeParameter(cmdlet: CmdletClass): boolean { - return cmdlet.ContainsSpecifiedParameter(cmdlet, 'IdentityType'); + private ContainsIdentityTypeParameter(): boolean { + return this.GetSpecifiedParameter('IdentityType'); } - private ContainsUserAssignedIdentityParameter(cmdlet: CmdletClass): boolean { - return cmdlet.ContainsSpecifiedParameter(cmdlet, 'UserAssignedIdentity'); + private ContainsUserAssignedIdentityParameter(): boolean { + return this.GetSpecifiedParameter('UserAssignedIdentity'); } private GetUserAssignedIdentityTypeDeclaration(cmdlet: CmdletClass): string { @@ -1139,9 +1130,9 @@ export class CmdletClass extends Class { }); } - private ManagedIdentityPreProcessForUpdateVerbCmdletWithGetResult(cmdlet: CmdletClass): Statements { + private ProcessGetResponseForManagedIdentityUpdateCmdlet(cmdlet: CmdletClass): Statements { const $this = cmdlet; - const containsUserAssignedIdentity = $this.ContainsUserAssignedIdentityParameter($this); + const containsUserAssignedIdentity = $this.ContainsUserAssignedIdentityParameter(); const doesSupportSystemAssignedIdentityMethod = new Method('DoesSupportSystemAssignedIdentityMethod', dotnet.Bool, { access: Access.Private, parameters: [new Parameter('identityType', dotnet.String)] @@ -1211,7 +1202,11 @@ export class CmdletClass extends Class { }); } - private GetPutPreProcess(cmdlet: CmdletClass, pathParams: Array, nonPathParams: Array, viaIdentity: boolean): Statements { + private ManagedIdentityUpdateCmdletPreProcess(cmdlet: CmdletClass, pathParams: Array, nonPathParams: Array, viaIdentity: boolean): Statements { + return cmdlet.GetPutPreProcess(cmdlet, pathParams, nonPathParams, viaIdentity, cmdlet.ProcessGetResponseForManagedIdentityUpdateCmdlet); + } + + private GetPutPreProcess(cmdlet: CmdletClass, pathParams: Array, nonPathParams: Array, viaIdentity: boolean, processGetResponse: ProcessGetResponse = undefined): Statements { const $this = cmdlet; const updateBodyMethod = new Method(`Update${$this.bodyParameter?.value}`, dotnet.Void, { access: Access.Private @@ -1239,8 +1234,8 @@ export class CmdletClass extends Class { const getPut = function* () { yield `${$this.bodyParameter?.value} = await this.${$this.$('Client').invokeMethod(httpOperationName, ...[...pathParams, ...nonPathParams]).implementation} `; // PreProcess body parameter - if (!$this.state.project.keepIdentityType && $this.ContainsIdentityTypeParameter(cmdlet)) { - yield $this.ManagedIdentityPreProcessForUpdateVerbCmdletWithGetResult(cmdlet); + if (processGetResponse) { + yield processGetResponse($this); } yield `this.${updateBodyMethod.name}();`; /** Instance: @@ -1802,6 +1797,12 @@ export class CmdletClass extends Class { const nullable = this.state.project.schemaDefinitionResolver.resolveTypeDeclaration(vSchema, !!(vParam.origin).required, this.state).isNullable; let cmdletParameter: Property; if (propertyType.schema.type !== SchemaType.Array) { + // if (vParam.name === 'IdentityType') { + // cmdletParameter = new Property('EnableSystemAssignedIdentity', SwitchParameter, { + // set: toExpression(`${expandedBodyParameter.value}.${getVirtualPropertyName((vParam.origin)) || vParam.origin.name} = value`), + // new: PropertiesRequiringNew.has(vParam.name) ? Modifier.New : Modifier.None + // }); + // } cmdletParameter = new Property(vParam.name, propertyType, { get: toExpression(`${expandedBodyParameter.value}.${getVirtualPropertyName((vParam.origin)) || vParam.origin.name}${!nullable ? '' : ` ?? ${propertyType.defaultOfType}`}`), // /* ${inspect(vParam.origin)} */ // get: toExpression(`null == ${expandedBodyParameter.value}.${vParam.origin.name} ? ${propertyType.defaultOfType} : (${propertyType.declaration}) ${expandedBodyParameter.value}.${vParam.origin.name}`), diff --git a/powershell/plugins/create-commands-v2.ts b/powershell/plugins/create-commands-v2.ts index 8ff3e91249..8c6043b797 100644 --- a/powershell/plugins/create-commands-v2.ts +++ b/powershell/plugins/create-commands-v2.ts @@ -116,7 +116,6 @@ export /* @internal */ class Inferrer { subjectPrefix!: string; isAzure!: boolean; supportJsonInput!: boolean; - keepIdentityType!: boolean; constructor(private state: State) { } @@ -144,7 +143,6 @@ export /* @internal */ class Inferrer { this.state.setValue('prefix', this.prefix); const model = this.state.model; - this.keepIdentityType = await this.state.getValue('keep-identitytype', false); this.state.message({ Channel: Channel.Debug, Text: `[CMDLET-PREFIX] => '${model.language.default.prefix}'` @@ -170,7 +168,7 @@ export /* @internal */ class Inferrer { parameters: new Dictionary(), }; const disableGetPut = await this.state.getValue('disable-getput', false); - + const disableTransformIdentityType = await this.state.getValue('disable-transform-identity-type', false); this.state.message({ Channel: Channel.Debug, Text: 'detecting high level commands...' }); for (const operationGroup of values(model.operationGroups)) { let hasPatch = false; @@ -181,8 +179,8 @@ export /* @internal */ class Inferrer { if (operation.requests?.[0]?.protocol?.http?.method.toLowerCase() === 'patch') { hasPatch = true; patchOperation = operation; - // bez: remove/hide patch operation to avoid conflicts with replacements - if (this.shouldReplacePatchByGetPut(operation)) { + // bez: remove patch operation to avoid conflicts with replacements + if (!disableTransformIdentityType && this.IsManagedIdentityOperation(operation)) { continue; } } else if (operation.requests?.[0]?.protocol?.http?.method.toLowerCase() === 'get') { @@ -194,36 +192,32 @@ export /* @internal */ class Inferrer { await this.addVariants(operation.parameters, operation, variant, '', this.state); } } - /* - generate variants for Update(Get+Put) for subjects only if: - - there is no patch operation - - there is a get operation - - there is a put operation - - get operation path is the same as put operation path - - there is only one put request schema - - get operation response schema type is the same as put operation request schema type - */ - if (this.isAzure - && (!disableGetPut && !hasPatch || - hasPatch && patchOperation && this.shouldReplacePatchByGetPut(patchOperation)) - && getOperations - && putOperation - && putOperation.requests?.length == 1) { + if (this.isAzure && getOperations && putOperation && putOperation.requests?.length == 1) { const getOperation = getOperations.find(getOperation => getOperation.requests?.[0]?.protocol?.http?.path === putOperation?.requests?.[0]?.protocol?.http?.path); - const hasQueryParameter = getOperation?.parameters?.find(p => p.protocol.http?.in === 'query' && p.language.default.name !== 'apiVersion'); - //parameter.protocal.http.in === 'body' probably only applies to open api 2.0 - const schema = putOperation?.requests?.[0]?.parameters?.find(p => p.protocol.http?.in === 'body')?.schema; - if (getOperation && !hasQueryParameter && schema && [...values(getOperation?.responses)].filter(each => (each).schema !== schema).length === 0) { - await this.addVariants(putOperation.parameters, putOperation, this.createCommandVariant('create', [operationGroup.$key], [], this.state.model), '', this.state, [getOperation], CommandType.GetPut); - } else if (hasPatch && patchOperation && this.shouldReplacePatchByGetPut(patchOperation)) { - // bez: patch operation can't be replaced by Get+PUT, add patch operation back and keep identity type + const supportsCombineGetPutOperation = getOperation && this.supportsGetPut(getOperation, putOperation); + if (!disableTransformIdentityType && supportsCombineGetPutOperation && + (hasPatch && patchOperation && this.IsManagedIdentityOperation(patchOperation) + || !hasPatch && putOperation && this.IsManagedIdentityOperation(putOperation))) { + await this.addVariants(putOperation.parameters, putOperation, this.createCommandVariant('create', [operationGroup.$key], [], this.state.model), '', this.state, [getOperation], CommandType.ManagedIdentityUpdate); + } else if (!disableTransformIdentityType && !supportsCombineGetPutOperation && hasPatch && patchOperation && this.IsManagedIdentityOperation(patchOperation)) { + // bez: add patch operation back and disable transforming identity type for (const variant of await this.inferCommandNames(patchOperation, operationGroup.$key, this.state)) { await this.addVariants(patchOperation.parameters, patchOperation, variant, '', this.state); } - this.state.setValue('keep-identitytype', true); + await this.state.setValue('disable-transform-identity-type', true); + } else if (!disableGetPut && !hasPatch && supportsCombineGetPutOperation) { + /* generate variants for Update(Get+Put) for subjects only if: + - there is a get operation + - there is a put operation + - get operation path is the same as put operation path + - there is only one put request schema + - get operation response schema type is the same as put operation request schema type + */ + await this.addVariants(putOperation.parameters, putOperation, this.createCommandVariant('create', [operationGroup.$key], [], this.state.model), '', this.state, [getOperation], CommandType.GetPut); } } + } // for (const operation of values(model.http.operations)) { // for (const variant of await this.inferCommandNames(operation, this.state)) { @@ -234,6 +228,18 @@ export /* @internal */ class Inferrer { return model; } + /** + * Judge if the response of get operation can be piped as the input of put operation + * 1. there is only one put request schema + * 2. get operation response schema type is the same as put operation request schema type + */ + private supportsGetPut(getOperation: Operation, putOperation: Operation): boolean { + const hasQueryParameter = getOperation?.parameters?.find(p => p.protocol.http?.in === 'query' && p.language.default.name !== 'apiVersion'); + //parameter.protocal.http.in === 'body' probably only applies to open api 2.0 + const schema = putOperation?.requests?.[0]?.parameters?.find(p => p.protocol.http?.in === 'body')?.schema; + return (getOperation && !hasQueryParameter && schema && [...values(getOperation?.responses)].filter(each => (each).schema !== schema).length === 0) ?? false; + } + private containsIdentityType(op: Operation): boolean { const body = op.requests?.[0].parameters?.find((p) => !p.origin || p.origin.indexOf('modelerfour:synthesized') < 0) || null; // property identity in the body parameter @@ -242,9 +248,8 @@ export /* @internal */ class Inferrer { return identityTypeProperty !== null && identityTypeProperty !== undefined; } - private shouldReplacePatchByGetPut(op: Operation): boolean { - // bez: we are assuming patch and put both exist at the same time - return !this.keepIdentityType && op && this.containsIdentityType(op); + private IsManagedIdentityOperation(op: Operation): boolean { + return this.containsIdentityType(op); } inferCommand(operation: Array, group: string, suffix: Array = []): Array { diff --git a/powershell/plugins/plugin-create-inline-properties.ts b/powershell/plugins/plugin-create-inline-properties.ts index 19610d4090..0840e4be5c 100644 --- a/powershell/plugins/plugin-create-inline-properties.ts +++ b/powershell/plugins/plugin-create-inline-properties.ts @@ -53,14 +53,7 @@ function getNameOptions(typeName: string, components: Array) { return [...result.values()]; } -function getProposedNameForObjectInlinedProperty(propertyName: string, inlinedPropertyName: string, flattenUserAssignedIdentity: boolean): string { - const proposedName = getPascalIdentifier(`${propertyName === 'properties' - || propertyName === 'error' - || (flattenUserAssignedIdentity && propertyName === 'identity' && inlinedPropertyName === 'UserAssignedIdentities') ? '' : pascalCase(fixLeadingNumber(deconstruct(propertyName)).map(each => singularize(each)))} ${inlinedPropertyName}`); - return proposedName; -} - -function createVirtualProperties(schema: ObjectSchema, stack: Array, threshold: number, conflicts: Array, flattenUserAssignedIdentity: boolean) { +function createVirtualProperties(schema: ObjectSchema, stack: Array, threshold: number, conflicts: Array) { // Some properties should be removed are wrongly kept as null and need to clean them if (schema.properties) { schema.properties = schema.properties.filter(each => each); @@ -122,7 +115,7 @@ function createVirtualProperties(schema: ObjectSchema, stack: Array, thr if (!isObjectSchema(parentSchema)) continue; - createVirtualProperties(parentSchema, [...stack, `${schema.language.default.name}`], threshold, conflicts, flattenUserAssignedIdentity); + createVirtualProperties(parentSchema, [...stack, `${schema.language.default.name}`], threshold, conflicts); const parentProperties = parentSchema.language.default.virtualProperties || { owned: [], @@ -178,7 +171,7 @@ function createVirtualProperties(schema: ObjectSchema, stack: Array, thr const propertyName = property.language.default.name; // for each object member, make sure that it's inlined it's children that it can. - createVirtualProperties(property.schema, [...stack, `${schema.language.default.name}`], threshold, conflicts, flattenUserAssignedIdentity); + createVirtualProperties(property.schema, [...stack, `${schema.language.default.name}`], threshold, conflicts); // this happens if there is a circular reference. // this means that this class should not attempt any inlining of that property at all . @@ -228,7 +221,9 @@ function createVirtualProperties(schema: ObjectSchema, stack: Array, thr // deeper child properties should be inlined with their parent's name // ie, this.[properties].owner.name should be this.ownerName - const proposedName = getProposedNameForObjectInlinedProperty(propertyName, inlinedProperty.name, flattenUserAssignedIdentity); + const proposedName = getPascalIdentifier(`${propertyName === 'properties' + || propertyName === 'error' + || propertyName === 'identity' && inlinedProperty.Name === 'UserAssignedIdentities' ? '' : pascalCase(fixLeadingNumber(deconstruct(propertyName)).map(each => singularize(each)))} ${inlinedProperty.name}`); const components = [...removeSequentialDuplicates([propertyName, ...inlinedProperty.nameComponents])]; let readonly = inlinedProperty.readOnly || property.readOnly; @@ -416,7 +411,6 @@ function createVirtualParameters(operation: CommandOperation) { operation.details.default.virtualParameters = virtualParameters; } - async function createVirtuals(state: State): Promise { /* A model class should provide inlined properties for anything in a property called properties @@ -434,7 +428,7 @@ async function createVirtuals(state: State): Promise { } // we have an object, let's process it. - createVirtualProperties(schema, new Array(), threshold, conflicts, await state.getValue('flatten-userassignedidentity', true)); + createVirtualProperties(schema, new Array(), threshold, conflicts); } if (length(conflicts) > 0) { diff --git a/powershell/utils/command-operation.ts b/powershell/utils/command-operation.ts index 62c3208d92..b351be5ece 100644 --- a/powershell/utils/command-operation.ts +++ b/powershell/utils/command-operation.ts @@ -62,7 +62,8 @@ export interface VirtualParameter { export enum CommandType { Atomic, - GetPut + GetPut, + ManagedIdentityUpdate, } export class CommandOperation extends Extensions implements CommandOperation { From 9a1a493e7c114df1f34fe34fa7dd306874600f7b Mon Sep 17 00:00:00 2001 From: Beisi Zhou Date: Thu, 25 Jan 2024 12:44:24 +0800 Subject: [PATCH 30/30] Update powershell/cmdlets/class.ts --- powershell/cmdlets/class.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/powershell/cmdlets/class.ts b/powershell/cmdlets/class.ts index fa87e7e500..0a7391d375 100644 --- a/powershell/cmdlets/class.ts +++ b/powershell/cmdlets/class.ts @@ -858,6 +858,7 @@ export class CmdletClass extends Class { switch ($this.operation.commandType) { case CommandType.ManagedIdentityUpdate: preProcess = $this.ManagedIdentityUpdateCmdletPreProcess; + break; case CommandType.GetPut: preProcess = $this.GetPutPreProcess; break;