create-model-cmdlets.ps1

# ----------------------------------------------------------------------------------
# Code generated by Microsoft (R) AutoRest Code Generator (autorest: 3.10.2, generator: @autorest/powershell@4.0.708)
# Changes may cause incorrect behavior and will be lost if the code is regenerated.
# ----------------------------------------------------------------------------------

function CreateModelCmdlet {

    param([Hashtable[]]$Models)

    if ($Models.Count -eq 0)
    {
        return
    }

    $ModelCsPath = Join-Path (Join-Path $PSScriptRoot 'generated\api') 'Models'
    $OutputDir = Join-Path $PSScriptRoot 'custom\autogen-model-cmdlets'
    $null = New-Item -ItemType Directory -Force -Path $OutputDir
    if (''.length -gt 0) {
        $ModuleName = ''
    } else {
        $ModuleName = 'CommvaultPowerShell'
    }
    $CsFiles = Get-ChildItem -Path $ModelCsPath -Recurse -Filter *.cs
    $Content = ''
    $null = $CsFiles | ForEach-Object -Process { if ($_.Name.Split('.').count -eq 2 )
        { $Content += get-content $_.fullname -raw 
        } }

    $Tree = [Microsoft.CodeAnalysis.CSharp.SyntaxFactory]::ParseCompilationUnit($Content)
    $Nodes = $Tree.ChildNodes().ChildNodes()
    $classConstantMember = @{}
    foreach ($Model in $Models)
    {
        $ModelName = $Model.modelName
        $InterfaceNode = $Nodes | Where-Object { ($_.Keyword.value -eq 'interface') -and ($_.Identifier.value -eq "I$ModelName") }
        $ClassNode = $Nodes | Where-Object { ($_.Keyword.value -eq 'class') -and ($_.Identifier.value -eq "$ModelName") }
        $classConstantMember = @()
        foreach ($class in $ClassNode) {
            foreach ($member in $class.Members) {
                $isConstant = $false
                foreach ($attr in $member.AttributeLists) {
                    $memberName = $attr.Attributes.Name.ToString()
                    if ($memberName.EndsWith('.Constant')) {
                        $isConstant = $true
                        break
                    }
                }
                if (($member.Modifiers.ToString() -eq 'public') -and $isConstant) {
                    $classConstantMember += $member.Identifier.Value
                }
            }
        }
        if ($InterfaceNode.count -eq 0) {
            continue
        }
        # through a queue, we iterate all the parent models.
        $Queue = @($InterfaceNode)
        $visited = @("I$ModelName")
        $AllInterfaceNodes = @()
        while ($Queue.count -ne 0)
        {
            $AllInterfaceNodes += $Queue[0]
            # Baselist contains the direct parent models.
            foreach ($parent in $Queue[0].BaseList.Types)
            {
                if (($parent.Type.Right.Identifier.Value -ne 'IJsonSerializable') -and (-not $visited.Contains($parent.Type.Right.Identifier.Value)))
                {
                    $Queue = [Array]$Queue + ($Nodes | Where-Object { ($_.Keyword.value -eq 'interface') -and ($_.Identifier.value -eq $parent.Type.Right.Identifier.Value) })
                    $visited = [Array]$visited + $parent.Type.Right.Identifier.Value
                }
            }
            $first, $Queue = $Queue
        }

        $Namespace =  $InterfaceNode.Parent.Name
        $ObjectType = $ModelName
        $ObjectTypeWithNamespace = "${Namespace}.${ObjectType}"
        # remove duplicated module name
        if ($ObjectType.StartsWith('CV')) {
            $ModulePrefix = ''
        } else {
            $ModulePrefix = 'CV'
        }
        $OutputPath = Join-Path -ChildPath "New-Az${ModulePrefix}${ObjectType}Object.ps1" -Path $OutputDir

        $ParameterDefineScriptList = New-Object System.Collections.Generic.List[string]
        $ParameterAssignScriptList = New-Object System.Collections.Generic.List[string]
        foreach ($Node in $AllInterfaceNodes)
        {
            foreach ($Member in $Node.Members)
            {
                if ($classConstantMember.Contains($Member.Identifier.Value)) {
                    # skip constant member
                    continue
                }
                $Arguments = $Member.AttributeLists.Attributes.ArgumentList.Arguments
                $Required = $false
                $Description = ""
                $Readonly = $False
                $mutability = @{Read = $true; Create = $true; Update = $true}
                foreach ($Argument in $Arguments)
                {
                    if ($Argument.NameEquals.Name.Identifier.Value -eq "Required")
                    {
                        $Required = $Argument.Expression.Token.Value
                    }
                    if ($Argument.NameEquals.Name.Identifier.Value -eq "Description")
                    {
                        $Description = $Argument.Expression.Token.Value.Trim('.').replace('"', '`"')
                    }
                    if ($Argument.NameEquals.Name.Identifier.Value -eq "Readonly")
                    {
                        $Readonly = $Argument.Expression.Token.Value
                    }
                    if ($Argument.NameEquals.Name.Identifier.Value -eq "Read")
                    {
                        $mutability.Read = $Argument.Expression.Token.Value
                    }
                    if ($Argument.NameEquals.Name.Identifier.Value -eq "Create")
                    {
                        $mutability.Create = $Argument.Expression.Token.Value
                    }
                    if ($Argument.NameEquals.Name.Identifier.Value -eq "Update")
                    {
                        $mutability.Update = $Argument.Expression.Token.Value
                    }
                }
                if ($Readonly)
                {
                    continue
                }
                $Identifier = $Member.Identifier.Value
                $Type = $Member.Type.ToString().replace('?', '').Split("::")[-1]
                $Type = $Member.Type.ToString().replace('?', '').Split("::")[-1]
                if ($Type.StartsWith("System.Collections.Generic.List"))
                {
                    # if the type is a list, we need to convert it to array
                    $matched = $Type -match '\<(?<Name>.+)\>$'
                    if ($matched)
                    {
                        $Type = $matches.Name + '[]';
                    }
                }
                $ParameterDefinePropertyList = New-Object System.Collections.Generic.List[string]
                if ($Required -and $mutability.Create -and $mutability.Update)
                {
                    $ParameterDefinePropertyList.Add("Mandatory")
                }
                if ($Description -ne "")
                {
                    $ParameterDefinePropertyList.Add("HelpMessage=`"${Description}.`"")
                }
                $ParameterDefineProperty = [System.String]::Join(", ", $ParameterDefinePropertyList)
                # check whether completer is needed
                $completer = '';              
                if(IsEnumType($Member)){                    
                    $completer += GetCompleter($Member)
                }
                $ParameterDefineScript = "
        [Parameter($ParameterDefineProperty)]${completer}
        [${Type}]
        `$${Identifier}"

                $ParameterDefineScriptList.Add($ParameterDefineScript)
                $ParameterAssignScriptList.Add("
        if (`$PSBoundParameters.ContainsKey('${Identifier}')) {
            `$Object.${Identifier} = `$${Identifier}
        }"
)
            }
        }
        $ParameterDefineScript = $ParameterDefineScriptList | Join-String -Separator ","
        $ParameterAssignScript = $ParameterAssignScriptList | Join-String -Separator ""

        $cmdletName = "New-Az${ModulePrefix}${ObjectType}Object"
        if ('' -ne $Model.cmdletName) {
            $cmdletName = $Model.cmdletName
        }
        $OutputPath = Join-Path -ChildPath "${cmdletName}.ps1" -Path $OutputDir
        $cmdletNameInLowerCase = $cmdletName.ToLower()
        $Script = "
# ----------------------------------------------------------------------------------
# Code generated by Microsoft (R) AutoRest Code Generator (autorest: 3.10.2, generator: @autorest/powershell@4.0.708)
# Changes may cause incorrect behavior and will be lost if the code is regenerated.
# ----------------------------------------------------------------------------------

<#
.Synopsis
Create an in-memory object for ${ObjectType}.
.Description
Create an in-memory object for ${ObjectType}.

.Outputs
${ObjectTypeWithNamespace}
.Link
https://learn.microsoft.com/powershell/module/${ModuleName}/${cmdletNameInLowerCase}
#>
function ${cmdletName} {
    [OutputType('${ObjectTypeWithNamespace}')]
    [CmdletBinding(PositionalBinding=`$false)]
    Param(
${ParameterDefineScript}
    )

    process {
        `$Object = [${ObjectTypeWithNamespace}]::New()
${ParameterAssignScript}
        return `$Object
    }
}
"

        Set-Content -Path $OutputPath -Value $Script
    }
}

function IsEnumType {
    param (
        [Microsoft.CodeAnalysis.CSharp.Syntax.PropertyDeclarationSyntax]$property
    )
    $isEnum = $false
    foreach ($attr in $property.AttributeLists) {
        $attributeName = $attr.Attributes.Name.ToString()
        if ($attributeName.Contains('ArgumentCompleter')) {
            $isEnum = $true
            break
        }
    }
    return $isEnum;
}

function GetCompleter {
    param (
        [Microsoft.CodeAnalysis.CSharp.Syntax.PropertyDeclarationSyntax]$property
    )
    foreach ($attr in $property.AttributeLists) {
        $attributeName = $attr.Attributes.Name.ToString()
        if ($attributeName.Contains('ArgumentCompleter')) {
            $attributeName = $attributeName.Split("::")[-1]
            $possibleValues = [System.String]::Join(", ", $attr.Attributes.ArgumentList.Arguments)
            $completer += "`n [${attributeName}(${possibleValues})]"
            return $completer
        }
    }
}