powershell/resources/assets/generate-portal-ux.ps1 (340 lines of code) (raw):
# ----------------------------------------------------------------------------------
${$project.pwshCommentHeader}
#
# This Script will create a folder dedicated to Azure-specific content and includes metadata files essential for enhancing the user experience (UX) within the Azure portal.
# These files are utilized by the Azure portal to effectively present the usage of cmdlets related to specific resources on portal pages.
# ----------------------------------------------------------------------------------
param([switch]$NotIsolated)
$ErrorActionPreference = 'Stop'
$pwsh = [System.Diagnostics.Process]::GetCurrentProcess().Path
if(-not $NotIsolated) {
Write-Host -ForegroundColor Green 'Creating isolated process...'
& "$pwsh" -NonInteractive -NoLogo -NoProfile -File $MyInvocation.MyCommand.Path @PSBoundParameters -NotIsolated
return
}
$moduleName = '${$project.moduleName}'
$rootModuleName = '${$project.rootModuleName}'
if ($rootModuleName -eq "")
{
$rootModuleName = $moduleName
}
$modulePsd1 = Get-Item -Path (Join-Path $PSScriptRoot "./$moduleName.psd1")
$modulePath = $modulePsd1.FullName
# Load DLL to use build-time cmdlets
Import-Module -Name $modulePath
Import-Module -Name (Join-Path $PSScriptRoot "./bin/$moduleName.private.dll")
$instance = [${$project.serviceNamespace.moduleClass.declaration}]::Instance
# Module info is shared per profile
$moduleInfo = Get-Module -Name $moduleName
$parameterSetsInfo = Get-Module -Name "$moduleName.private"
function Test-FunctionSupported()
{
[CmdletBinding()]
Param (
[Parameter()]
[string]
$FunctionName
)
If (-not $FunctionName.Contains("_")) {
return $false
}
$cmdletName, $parameterSetName = $FunctionName.Split("_")
If ($parameterSetName.Contains("List") -or $parameterSetName.Contains("ViaIdentity") -or $parameterSetName.Contains("ViaJson")) {
return $false
}
If ($cmdletName.StartsWith("New") -or $cmdletName.StartsWith("Set") -or $cmdletName.StartsWith("Update")) {
return $false
}
$parameterSetInfo = $parameterSetsInfo.ExportedCmdlets[$FunctionName]
foreach ($parameterInfo in $parameterSetInfo.Parameters.Values)
{
$category = (Get-ParameterAttribute -ParameterInfo $parameterInfo -AttributeName "CategoryAttribute").Categories
$invalideCategory = @('Query', 'Body')
if ($invalideCategory -contains $category)
{
return $false
}
}
$customFiles = Get-ChildItem -Path custom -Filter "$cmdletName.*"
if ($customFiles.Length -ne 0)
{
Write-Host -ForegroundColor Yellow "There are come custom files for $cmdletName, skip generate UX data for it."
return $false
}
return $true
}
function Get-MappedCmdletFromFunctionName()
{
[CmdletBinding()]
Param (
[Parameter()]
[string]
$FunctionName
)
$cmdletName, $parameterSetName = $FunctionName.Split("_")
return $cmdletName
}
function Get-ParameterAttribute()
{
[CmdletBinding()]
Param (
[Parameter()]
[System.Management.Automation.ParameterMetadata]
$ParameterInfo,
[Parameter()]
[String]
$AttributeName
)
return $ParameterInfo.Attributes | Where-Object { $_.TypeId.Name -eq $AttributeName }
}
function Get-CmdletAttribute()
{
[CmdletBinding()]
Param (
[Parameter()]
[System.Management.Automation.CommandInfo]
$CmdletInfo,
[Parameter()]
[String]
$AttributeName
)
return $CmdletInfo.ImplementingType.GetTypeInfo().GetCustomAttributes([System.object], $true) | Where-Object { $_.TypeId.Name -eq $AttributeName }
}
function Get-CmdletDescription()
{
[CmdletBinding()]
Param (
[Parameter()]
[String]
$CmdletName
)
$helpInfo = Get-Help $CmdletName -Full
$description = $helpInfo.Description.Text
if ($null -eq $description)
{
return ""
}
return $description
}
# Test whether the parameter is from swagger http path
function Test-ParameterFromSwagger()
{
[CmdletBinding()]
Param (
[Parameter()]
[System.Management.Automation.ParameterMetadata]
$ParameterInfo
)
$category = (Get-ParameterAttribute -ParameterInfo $ParameterInfo -AttributeName "CategoryAttribute").Categories
$doNotExport = Get-ParameterAttribute -ParameterInfo $ParameterInfo -AttributeName "DoNotExportAttribute"
if ($null -ne $doNotExport)
{
return $false
}
$valideCategory = @('Path')
if ($valideCategory -contains $category)
{
return $true
}
return $false
}
function New-ExampleForParameterSet()
{
[CmdletBinding()]
Param (
[Parameter()]
[System.Management.Automation.CommandInfo]
$ParameterSetInfo
)
$parameters = $ParameterSetInfo.Parameters.Values | Where-Object { Test-ParameterFromSwagger $_ }
$result = @()
foreach ($parameter in $parameters)
{
$category = (Get-ParameterAttribute -parameterInfo $parameter -AttributeName "CategoryAttribute").Categories
$sourceName = (Get-ParameterAttribute -parameterInfo $parameter -AttributeName "InfoAttribute").SerializedName
$name = $parameter.Name
$result += [ordered]@{
name = "-$Name"
value = "[$category.$sourceName]"
}
}
return $result
}
function New-ParameterArrayInParameterSet()
{
[CmdletBinding()]
Param (
[Parameter()]
[System.Management.Automation.CommandInfo]
$ParameterSetInfo
)
$parameters = $ParameterSetInfo.Parameters.Values | Where-Object { Test-ParameterFromSwagger $_ }
$result = @()
foreach ($parameter in $parameters)
{
$isMandatory = (Get-ParameterAttribute -parameterInfo $parameter -AttributeName "ParameterAttribute").Mandatory
$parameterName = $parameter.Name
$parameterType = $parameter.ParameterType.ToString().Split('.')[1]
if ($parameter.SwitchParameter)
{
$parameterSignature = "-$parameterName"
}
else
{
$parameterSignature = "-$parameterName <$parameterType>"
}
if ($parameterName -eq "SubscriptionId")
{
$isMandatory = $false
}
if (-not $isMandatory)
{
$parameterSignature = "[$parameterSignature]"
}
$result += $parameterSignature
}
return $result
}
function New-MetadataForParameterSet()
{
[CmdletBinding()]
Param (
[Parameter()]
[System.Management.Automation.CommandInfo]
$ParameterSetInfo
)
$httpAttribute = Get-CmdletAttribute -CmdletInfo $ParameterSetInfo -AttributeName "HttpPathAttribute"
$httpPath = $httpAttribute.Path
$apiVersion = $httpAttribute.ApiVersion
$provider = [System.Text.RegularExpressions.Regex]::New("/providers/([\w+\.]+)/").Match($httpPath).Groups[1].Value
$resourcePath = "/" + $httpPath.Split("$provider/")[1]
$resourceType = [System.Text.RegularExpressions.Regex]::New("/([\w]+)/\{\w+\}").Matches($resourcePath) | ForEach-Object {$_.groups[1].Value} | Join-String -Separator "/"
$cmdletName = Get-MappedCmdletFromFunctionName $ParameterSetInfo.Name
$description = (Get-CmdletAttribute -CmdletInfo $ParameterSetInfo -AttributeName "DescriptionAttribute").Description
[object[]]$example = New-ExampleForParameterSet $ParameterSetInfo
if ($Null -eq $example)
{
$example = @()
}
[string[]]$signature = New-ParameterArrayInParameterSet $ParameterSetInfo
if ($Null -eq $signature)
{
$signature = @()
}
return @{
Path = $httpPath
Provider = $provider
ResourceType = $resourceType
ApiVersion = $apiVersion
CmdletName = $cmdletName
Description = $description
Example = $example
Signature = @{
parameters = $signature
}
}
}
function Merge-WithExistCmdletMetadata()
{
[CmdletBinding()]
Param (
[Parameter()]
[System.Collections.Specialized.OrderedDictionary]
$ExistedCmdletInfo,
[Parameter()]
[Hashtable]
$ParameterSetMetadata
)
$ExistedCmdletInfo.help.parameterSets += $ParameterSetMetadata.Signature
$ExistedCmdletInfo.examples += [ordered]@{
description = $ParameterSetMetadata.Description
parameters = $ParameterSetMetadata.Example
}
return $ExistedCmdletInfo
}
function New-MetadataForCmdlet()
{
[CmdletBinding()]
Param (
[Parameter()]
[Hashtable]
$ParameterSetMetadata
)
$cmdletName = $ParameterSetMetadata.CmdletName
$description = Get-CmdletDescription $cmdletName
$result = [ordered]@{
name = $cmdletName
description = $description
path = $ParameterSetMetadata.Path
help = [ordered]@{
learnMore = [ordered]@{
url = "https://learn.microsoft.com/powershell/module/$rootModuleName/$cmdletName".ToLower()
}
parameterSets = @()
}
examples = @()
}
$result = Merge-WithExistCmdletMetadata -ExistedCmdletInfo $result -ParameterSetMetadata $ParameterSetMetadata
return $result
}
$parameterSets = $parameterSetsInfo.ExportedCmdlets.Keys | Where-Object { Test-FunctionSupported($_) }
$resourceTypes = @{}
foreach ($parameterSetName in $parameterSets)
{
$cmdletInfo = $parameterSetsInfo.ExportedCommands[$parameterSetName]
$parameterSetMetadata = New-MetadataForParameterSet -ParameterSetInfo $cmdletInfo
$cmdletName = $parameterSetMetadata.CmdletName
if (-not ($moduleInfo.ExportedCommands.ContainsKey($cmdletName)))
{
continue
}
if ($resourceTypes.ContainsKey($parameterSetMetadata.ResourceType))
{
$ExistedCmdletInfo = $resourceTypes[$parameterSetMetadata.ResourceType].commands | Where-Object { $_.name -eq $cmdletName }
if ($ExistedCmdletInfo)
{
$ExistedCmdletInfo = Merge-WithExistCmdletMetadata -ExistedCmdletInfo $ExistedCmdletInfo -ParameterSetMetadata $parameterSetMetadata
}
else
{
$cmdletInfo = New-MetadataForCmdlet -ParameterSetMetadata $parameterSetMetadata
$resourceTypes[$parameterSetMetadata.ResourceType].commands += $cmdletInfo
}
}
else
{
$cmdletInfo = New-MetadataForCmdlet -ParameterSetMetadata $parameterSetMetadata
$resourceTypes[$parameterSetMetadata.ResourceType] = [ordered]@{
resourceType = $parameterSetMetadata.ResourceType
apiVersion = $parameterSetMetadata.ApiVersion
learnMore = @{
url = "https://learn.microsoft.com/powershell/module/$rootModuleName".ToLower()
}
commands = @($cmdletInfo)
provider = $parameterSetMetadata.Provider
}
}
}
$UXFolder = '${$lib.path.relative($project.baseFolder, $project.uxFolder)}'
if (Test-Path $UXFolder)
{
Remove-Item -Path $UXFolder -Recurse
}
$null = New-Item -ItemType Directory -Path $UXFolder
foreach ($resourceType in $resourceTypes.Keys)
{
$resourceTypeFileName = $resourceType -replace "/", "-"
if ($resourceTypeFileName -eq "")
{
continue
}
$resourceTypeInfo = $resourceTypes[$resourceType]
$provider = $resourceTypeInfo.provider
$providerFolder = "$UXFolder/$provider"
if (-not (Test-Path $providerFolder))
{
$null = New-Item -ItemType Directory -Path $providerFolder
}
$resourceTypeInfo.Remove("provider")
$resourceTypeInfo | ConvertTo-Json -Depth 10 | Out-File "$providerFolder/$resourceTypeFileName.json"
}