private/specs/Get-SpecsPropertiesAsParameterList.ps1

<#
.SYNOPSIS
Extract all parameters from the given API spec parameter root
 
.DESCRIPTION
Extract all parameters from the given API spec parameter root (e.g., PUT parameters)
 
.PARAMETER JSONFilePath
Mandatory. The service specification file to process.
 
.PARAMETER SpecificationData
Mandatory. The source content to crawl for data.
 
.PARAMETER RelevantParamRoot
Mandatory. The array of root parameters to process (e.g., PUT parameters).
 
.PARAMETER UrlPath
Mandatory. The API Path in the JSON specification file to process
 
.PARAMETER ResourceType
Mandatory. The Resource Type to investigate
 
.EXAMPLE
Get-SpecsPropertiesAsParameterList -JSONFilePath '(...)/resource-manager/Microsoft.KeyVault/stable/2022-07-01/keyvault.json' -RelevantParamRoot @(@{ $ref: "../(...)"}) '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.KeyVault/vaults/{vaultName}' -ResourceType 'vaults'
 
Fetch all parameters (e.g., PUT) from the KeyVault REST path.
#>

function Get-SpecsPropertiesAsParameterList {

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string] $JSONFilePath,

        [Parameter(Mandatory = $true)]
        [array] $RelevantParamRoot,

        [Parameter(Mandatory = $true)]
        [string] $UrlPath,

        [Parameter(Mandatory = $true)]
        [string] $ResourceType
    )

    $specificationData = Get-Content -Path $JSONFilePath -Raw | ConvertFrom-Json -AsHashtable
    $definitions = $specificationData.definitions
    $specParameters = $specificationData.parameters

    $templateData = @()

    $matchingPathObjectParametersRef = ($relevantParamRoot | Where-Object { $_.in -eq 'body' }).schema.'$ref'

    if (-not $matchingPathObjectParametersRef) {
        # If 'parameters' does not exist (as the API isn't consistent), we try the resource type instead
        $matchingPathObjectParametersRef = ($relevantParamRoot | Where-Object { $_.name -eq $ResourceType }).schema.'$ref'
    }
    if (-not $matchingPathObjectParametersRef) {
        # If even that doesn't exist (as the API is even more inconsistent), let's try a 'singular' resource type
        $matchingPathObjectParametersRef = ($relevantParamRoot | Where-Object { $_.name -eq ($ResourceType.Substring(0, $ResourceType.Length - 1)) }).schema.'$ref'
    }

    if ($matchingPathObjectParametersRef -like '*.*') {
        # if the reference directly points to another file
        $resolvedParameterRef = Resolve-SpecPropertyReference -JSONFilePath $JSONFilePath -SpecificationData $specificationData -Parameter @{ '$ref' = $matchingPathObjectParametersRef }

        # Overwrite data to process
        $specificationData = $resolvedParameterRef.specificationData
        $definitions = $specificationData.definitions
        $specParameters = $specificationData.parameters
    }

    if([String]::IsNullOrEmpty($matchingPathObjectParametersRef)) {
        # If we still cannot find a path with a body - there likely isn't any. Hence we skip
        return $templateData
    }

    # Get top-most parameters
    $outerParameters = $definitions[(Split-Path $matchingPathObjectParametersRef -Leaf)]

    # Handle resource name
    # --------------------
    # Note: The name can be specified in different locations like the PUT statement, but also in the spec's 'parameters' object as a reference
    # Case: The name in the url is also a parameter of the PUT statement
    $pathServiceName = (Split-Path $UrlPath -Leaf) -replace '{|}', ''
    if ($relevantParamRoot.name -contains $pathServiceName) {
        $param = $relevantParamRoot | Where-Object { $_.name -eq $pathServiceName }

        $parameterObject = @{
            level       = 0
            name        = 'name'
            type        = 'string'
            description = $param.description
            required    = $true
        }

        $parameterObject = Set-OptionalParameter -SourceParameterObject $param -TargetObject $parameterObject
    } else {
        # Case: The name is a ref in the spec's 'parameters' object. E.g., { "$ref": "#/parameters/BlobServicesName" }
        # For this, we need to find the correct ref, as there can be multiple
        $nonDefaultParameter = $relevantParamRoot.'$ref' | Where-Object { $_ -like '#/parameters/*' } | Where-Object { $specParameters[(Split-Path $_ -Leaf)].name -eq $pathServiceName }
        if ($nonDefaultParameter) {
            $param = $specParameters[(Split-Path $nonDefaultParameter -Leaf)]

            $parameterObject = @{
                level       = 0
                name        = 'name'
                type        = 'string'
                description = $param.description
                required    = $true
            }

            $parameterObject = Set-OptionalParameter -SourceParameterObject $param -TargetObject $parameterObject
        }
    }

    $templateData += $parameterObject

    # Process outer properties
    # ------------------------0
    foreach ($outerParameter in $outerParameters.properties.Keys | Where-Object { $_ -notin @('location') -and -not $outerParameters.properties[$_].readOnly } | Sort-Object) {
        $innerParamInputObject = @{
            JSONFilePath              = $JSONFilePath
            Parameter                 = $outerParameters.properties[$outerParameter]
            SpecificationData         = $SpecificationData
            Level                     = 0
            Name                      = $outerParameter
            Parent                    = ''
            RequiredParametersOnLevel = $outerParameters.required
        }
        $templateData += Get-SpecsPropertyAsParameter @innerParamInputObject
    }

    # Special case: Location
    # The location parameter is not explicitely documented at this place (even though it should). It is however referenced as 'required' and must be included
    if ($outerParameters.required -contains 'location' -or $outerParameters.properties.Keys -contains 'location') {
        $parameterObject = @{
            level       = 0
            name        = 'location'
            type        = 'string'
            description = 'Location for all Resources.'
            required    = $false
            default     = ($UrlPath -like '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/*') ? 'resourceGroup().location' : 'deployment().location'
        }
        $templateData += $parameterObject
    }

    return $templateData
}