Paths.psm1

#########################################################################################
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# Licensed under the MIT license.
#
# PSSwagger Module
#
#########################################################################################

Microsoft.PowerShell.Core\Set-StrictMode -Version Latest
Import-Module (Join-Path -Path $PSScriptRoot -ChildPath Utilities.psm1) -DisableNameChecking
Import-Module (Join-Path -Path $PSScriptRoot -ChildPath SwaggerUtils.psm1) -DisableNameChecking
. "$PSScriptRoot\PSSwagger.Constants.ps1" -Force
Microsoft.PowerShell.Utility\Import-LocalizedData  LocalizedData -filename PSSwagger.Resources.psd1

function Get-SwaggerSpecPathInfo
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$true)]
        [PSObject]
        $JsonPathItemObject,

        [Parameter(Mandatory=$true)]
        [PSCustomObject] 
        $PathFunctionDetails,

        [Parameter(Mandatory = $true)]
        [hashTable]
        $swaggerDict,

        [Parameter(Mandatory = $true)]
        [hashtable]
        $SwaggerMetaDict,

        [Parameter(Mandatory = $true)]
        [hashtable]
        $DefinitionFunctionsDetails,

        [Parameter(Mandatory=$true)]
        [hashtable]
        $ParameterGroupCache,

        [Parameter(Mandatory=$false)]
        [PSCustomObject]
        $PSMetaJsonObject
    )

    Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
    $UseAzureCsharpGenerator = $SwaggerMetaDict['UseAzureCsharpGenerator']
    $EndpointRelativePath = $JsonPathItemObject.Name

    $PSMetaPathJsonObject = $null
    if($PSMetaJsonObject) {
        if(Get-Member -InputObject $PSMetaJsonObject.paths -Name $EndpointRelativePath) {
            $PSMetaPathJsonObject = $PSMetaJsonObject.paths.$EndpointRelativePath
        }
        elseif(Get-Member -InputObject $PSMetaJsonObject.'x-ms-paths' -Name $EndpointRelativePath) {
            $PSMetaPathJsonObject = $PSMetaJsonObject.'x-ms-paths'.$EndpointRelativePath
        }
    }

    # First get path level common parameters, if any, which will be common to all operations in this swagger path.
    $PathCommonParameters = @{}
    if(Get-Member -InputObject $JsonPathItemObject.value -Name 'Parameters')
    {
        $PSMetaParametersJsonObject = $null
        if($PSMetaPathJsonObject -and (Get-Member -InputObject $PSMetaPathJsonObject -Name 'parameters')) {
            $PSMetaParametersJsonObject = $PSMetaPathJsonObject.'parameters'
        }

        $GetPathParamInfo_params = @{
            JsonPathItemObject = $JsonPathItemObject.Value
            SwaggerDict = $swaggerDict
            DefinitionFunctionsDetails = $DefinitionFunctionsDetails
            ParameterGroupCache = $ParameterGroupCache
            ParametersTable = $PathCommonParameters
            PSMetaParametersJsonObject = $PSMetaParametersJsonObject
        }
        Get-PathParamInfo @GetPathParamInfo_params
    }

    $JsonPathItemObject.value.PSObject.Properties | ForEach-Object {
        $longRunningOperation = $false
        $operationType = $_.Name
        if (((Get-Member -InputObject $_.Value -Name 'x-ms-long-running-operation') -and $_.Value.'x-ms-long-running-operation')) {
            $longRunningOperation = $true
        }

        $x_ms_pageableObject = $null
        if (((Get-Member -InputObject $_.Value -Name 'x-ms-pageable') -and $_.Value.'x-ms-pageable')) {
            $x_ms_pageableObject = @{}
            if (((Get-Member -InputObject $_.Value.'x-ms-pageable' -Name 'operationName') -and $_.Value.'x-ms-pageable'.'operationName')) {
                $x_ms_pageableObject['operationName'] = $_.Value.'x-ms-pageable'.'operationName'
            }

            if (((Get-Member -InputObject $_.Value.'x-ms-pageable' -Name 'itemName') -and $_.Value.'x-ms-pageable'.'itemName')) {
                $x_ms_pageableObject['itemName'] = $_.Value.'x-ms-pageable'.'itemName'
            }

            if ((Get-Member -InputObject $_.Value.'x-ms-pageable' -Name 'nextLinkName')) {
                if ($_.Value.'x-ms-pageable'.'nextLinkName') {
                    $x_ms_pageableObject['nextLinkName'] = $_.Value.'x-ms-pageable'.'nextLinkName'
                } else {
                    $x_ms_pageableObject = $null
                }
            }
        }

        if(Get-Member -InputObject $_.Value -Name 'security')
        {
            $operationSecurityObject = $_.Value.'security'
        } elseif ($swaggerDict.ContainsKey('Security')) {
            $operationSecurityObject = $swaggerDict['Security']
        } else {
            $operationSecurityObject = $null
        }

        $cmdletInfoOverrides = @()
        $PSMetaOperationJsonObject = $null
        if($PSMetaPathJsonObject -and
           (Get-Member -InputObject $PSMetaPathJsonObject -Name $operationType)) {
            $PSMetaOperationJsonObject = $PSMetaPathJsonObject.$operationType
        }
        
        if($PSMetaOperationJsonObject -and
           (Get-Member -InputObject $PSMetaOperationJsonObject -Name 'x-ps-cmdlet-infos')) {
            $PSMetaOperationJsonObject.'x-ps-cmdlet-infos' | ForEach-Object {
                $cmdletInfoOverrides += @{
                    Name = $_.name
                }
            }
        }
        elseif ((Get-Member -InputObject $_.Value -Name 'x-ps-cmdlet-infos') -and $_.Value.'x-ps-cmdlet-infos') {
            foreach ($cmdletMetadata in $_.Value.'x-ps-cmdlet-infos') {
                $cmdletInfoOverride = @{}
                if ((Get-Member -InputObject $cmdletMetadata -Name 'name') -and $cmdletMetadata.name) {
                    $cmdletInfoOverride['name'] = $cmdletMetadata.name
                }

                $cmdletInfoOverrides += $cmdletInfoOverride
            }
        }

        if(Get-Member -InputObject $_.Value -Name 'OperationId')
        {
            $operationId = $_.Value.operationId
            Write-Verbose -Message ($LocalizedData.GettingSwaggerSpecPathInfo -f $operationId)

            $FunctionDescription = ""
            if((Get-Member -InputObject $_.value -Name 'description') -and $_.value.description) {
                $FunctionDescription = $_.value.description 
            }
            
            $ParametersTable = @{}
            # Add Path common parameters to the operation's parameters list.
            $PathCommonParameters.GetEnumerator() | ForEach-Object {
                # Cloning the common parameters object so that some values can be updated.
                $PathCommonParamDetails = $_.Value.Clone()
                if($PathCommonParamDetails.ContainsKey('OriginalParameterName') -and $PathCommonParamDetails.OriginalParameterName)
                {
                    $PathCommonParamDetails['OriginalParameterName'] = ''
                }
                $ParametersTable[$_.Key] = $PathCommonParamDetails
            }

            $PSMetaParametersJsonObject = $null
            if($PSMetaOperationJsonObject -and (Get-Member -InputObject $PSMetaOperationJsonObject -Name 'parameters')) {
                $PSMetaParametersJsonObject = $PSMetaOperationJsonObject.'parameters'
            }

            $GetPathParamInfo_params2 = @{
                JsonPathItemObject = $_.value
                SwaggerDict = $swaggerDict
                DefinitionFunctionsDetails = $DefinitionFunctionsDetails
                ParameterGroupCache = $ParameterGroupCache
                ParametersTable = $ParametersTable
                PSMetaParametersJsonObject = $PSMetaParametersJsonObject
            }
            Get-PathParamInfo @GetPathParamInfo_params2

            $responses = ""
            if((Get-Member -InputObject $_.value -Name 'responses') -and $_.value.responses) {
                $responses = $_.value.responses 
            }

            if($cmdletInfoOverrides)
            {
                $commandNames = $cmdletInfoOverrides
            } else {
                $commandNames = Get-PathCommandName -OperationId $operationId
            }

            $ParameterSetDetail = @{
                Description = $FunctionDescription
                ParameterDetails = $ParametersTable
                Responses = $responses
                OperationId = $operationId
                OperationType = $operationType
                EndpointRelativePath = $EndpointRelativePath
                PathCommonParameters = $PathCommonParameters
                Priority = 100 # Default
                'x-ms-pageable' = $x_ms_pageableObject
            }

            if ((Get-Member -InputObject $_.Value -Name 'x-ms-odata') -and $_.Value.'x-ms-odata') {
                # Currently only the existence of this property is really important, but might as well save the value
                $ParameterSetDetail.'x-ms-odata' = $_.Value.'x-ms-odata'
            }

            # There's probably a better way to do this...
            $opIdValues = $operationId -split "_",2
            if(-not $opIdValues -or ($opIdValues.Count -ne 2)) {
                $approximateVerb = $operationId
            } else {
                $approximateVerb = $opIdValues[1]
                if ((-not $UseAzureCsharpGenerator) -and 
                    (Test-OperationNameInDefinitionList -Name $opIdValues[0] -SwaggerDict $swaggerDict))
                { 
                    $ParameterSetDetail['UseOperationsSuffix'] = $true
                }
            }

            if ($approximateVerb.StartsWith("List")) {
                $ParameterSetDetail.Priority = 0
            }

            $commandNames | ForEach-Object {
                $FunctionDetails = @{}
                if ($PathFunctionDetails.ContainsKey($_.name)) {
                    $FunctionDetails = $PathFunctionDetails[$_.name]
                } else {
                    $FunctionDetails['CommandName'] = $_.name
                    $FunctionDetails['x-ms-long-running-operation'] = $longRunningOperation
                }

                if ($operationSecurityObject) {
                    $FunctionDetails['Security'] = $operationSecurityObject
                }

                $ParameterSetDetails = @()
                if ($FunctionDetails.ContainsKey('ParameterSetDetails')) {
                    $ParameterSetDetails = $FunctionDetails['ParameterSetDetails']
                } 

                $ParameterSetDetails += $ParameterSetDetail
                $FunctionDetails['ParameterSetDetails'] = $ParameterSetDetails
                $PathFunctionDetails[$_.name] = $FunctionDetails
            }
        }
        elseif(-not ((Get-Member -InputObject $_ -Name 'Name') -and ($_.Name -eq 'Parameters')))
        {
            $Message = $LocalizedData.UnsupportedSwaggerProperties -f ('JsonPathItemObject', $($_.Value | Out-String))
            Write-Warning -Message $Message
        }
    }
}

function New-SwaggerSpecPathCommand
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory=$true)]
        [hashtable]
        $PathFunctionDetails,

        [Parameter(Mandatory = $true)]
        [hashtable]
        $SwaggerMetaDict,

        [Parameter(Mandatory = $true)]
        [hashtable]
        $SwaggerDict,

        [Parameter(Mandatory=$true)]
        [hashtable]
        $DefinitionFunctionsDetails
    )
    
    Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

    $FunctionsToExport = @()
    Preprocess-PagingOperations -PathFunctionDetails $PathFunctionDetails
    $PathFunctionDetails.GetEnumerator() | ForEach-Object {
        $FunctionsToExport += New-SwaggerPath -FunctionDetails $_.Value `
                                              -SwaggerMetaDict $SwaggerMetaDict `
                                              -SwaggerDict $SwaggerDict `
                                              -PathFunctionDetails $PathFunctionDetails `
                                              -DefinitionFunctionsDetails $DefinitionFunctionsDetails
    }

    return $FunctionsToExport
}

<# Mark any operations as paging operations if they're the target of any operation's x-ms-pageable.operationName property.
These operations will not generate -Page and -Paging, even though they're marked as pageable.
These are single page operations and should never be unrolled (in the case of -not -Paging) or accept IPage parameters (in the case of -Page) #>

function Preprocess-PagingOperations {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [hashtable]
        $PathFunctionDetails
    )

    $PathFunctionDetails.GetEnumerator() | ForEach-Object {
        $_.Value.ParameterSetDetails | ForEach-Object {
            $parameterSetDetail = $_
            if ($parameterSetDetail.ContainsKey('x-ms-pageable') -and $parameterSetDetail.'x-ms-pageable') {
                if ($parameterSetDetail.'x-ms-pageable'.ContainsKey('operationName')) {
                    $matchingPath = $PathFunctionDetails.GetEnumerator() | Where-Object { $_.Value.ParameterSetDetails | Where-Object { $_.OperationId -eq $parameterSetDetail.'x-ms-pageable'.'operationName'} } | Select-Object -First 1
                    $matchingPath.Value['IsNextPageOperation'] = $true
                }
            }
        }
    }
}

function Add-UniqueParameter {
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory=$true)]
        [hashtable]
        $ParameterDetails,
        
        [Parameter(Mandatory=$true)]
        [hashtable]
        $CandidateParameterDetails,

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

        [Parameter(Mandatory=$true)]
        [hashtable]
        $ParametersToAdd,

        [Parameter(Mandatory=$true)]
        [hashtable]
        $ParameterHitCount
    )

    Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

    $parameterName = $CandidateParameterDetails.Name
    if($parameterDetails.IsParameter) {
        if (-not $parameterHitCount.ContainsKey($parameterName)) {
            $parameterHitCount[$parameterName] = 0
        }

        $parameterHitCount[$parameterName]++
        if (-not ($parametersToAdd.ContainsKey($parameterName))) {
            $parametersToAdd[$parameterName] = @{
                # We can grab details like Type, Name, ValidateSet from any of the parameter definitions
                Details = $CandidateParameterDetails
                ParameterSetInfo = @{$OperationId = @{
                    Name = $OperationId
                    Mandatory = $CandidateParameterDetails.Mandatory
                }}
            }
        } else {
            $parametersToAdd[$parameterName].ParameterSetInfo[$OperationId] = @{
                                                                        Name = $OperationId
                                                                        Mandatory = $CandidateParameterDetails.Mandatory
                                                                    }
        }
    }
}

function New-SwaggerPath
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory=$true)]
        [hashtable]
        $FunctionDetails,

        [Parameter(Mandatory = $true)]
        [hashtable]
        $SwaggerMetaDict,

        [Parameter(Mandatory = $true)]
        [hashtable]
        $SwaggerDict,

        [Parameter(Mandatory = $true)]
        [hashtable]
        $PathFunctionDetails,

        [Parameter(Mandatory=$true)]
        [hashtable]
        $DefinitionFunctionsDetails
    )

    Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

    $commandName = $FunctionDetails.CommandName
    $parameterSetDetails = $FunctionDetails['ParameterSetDetails']
    $isLongRunningOperation = $FunctionDetails.ContainsKey('x-ms-long-running-operation') -and $FunctionDetails.'x-ms-long-running-operation'
    $isNextPageOperation = $FunctionDetails.ContainsKey('IsNextPageOperation') -and $FunctionDetails.'IsNextPageOperation'
    $info = $SwaggerDict['Info']
    $namespace = $info['NameSpace']
    $models = $info['Models']
    $modulePostfix = $info['infoName']
    $clientName = '$' + $modulePostfix
    $UseAzureCsharpGenerator = $SwaggerMetaDict['UseAzureCsharpGenerator']
    
    $description = ''
    $paramBlock = ''
    $paramHelp = ''
    $parametersToAdd = @{}
    $flattenedParametersOnPSCmdlet = @{}
    $parameterHitCount = @{}
    $globalParameterBlock = ''
    $x_ms_pageableObject = $null
    foreach ($parameterSetDetail in $parameterSetDetails) {
        if ($parameterSetDetail.ContainsKey('x-ms-pageable') -and $parameterSetDetail.'x-ms-pageable' -and (-not $isNextPageOperation)) {
            if ($x_ms_pageableObject -and 
                $x_ms_pageableObject.ContainsKey('ReturnType') -and 
                ($x_ms_pageableObject.ReturnType -ne 'NONE') -and
                ($x_ms_pageableObject.ReturnType -ne $parameterSetDetail.ReturnType)) {
                Write-Warning -Message ($LocalizedData.MultiplePageReturnTypes -f ($commandName))
                $x_ms_pageableObject.ReturnType = 'NONE'
            } elseif (-not $x_ms_pageableObject) {
                $x_ms_pageableObject = $parameterSetDetail.'x-ms-pageable'
                $x_ms_pageableObject['ReturnType'] = $parameterSetDetail.ReturnType
                if ($x_ms_pageableObject.Containskey('operationName')) {
                    # Search for the cmdlet with a parameter set with the given operationName
                    $pagingFunctionDetails = $PathFunctionDetails.GetEnumerator() | Where-Object { $_.Value.ParameterSetDetails | Where-Object { $_.OperationId -eq $x_ms_pageableObject.operationName }} | Select-Object -First 1
                    if (-not $pagingFunctionDetails) {
                        throw ($LocalizedData.FailedToFindPagingOperation -f ($($x_ms_pageableObject.OperationName), $commandName))
                    }

                    $pagingParameterSet = $pagingFunctionDetails.Value.ParameterSetDetails | Where-Object { $_.OperationId -eq $x_ms_pageableObject.operationName }
                    $unmatchedParameters = @()
                    # This list of parameters works for when -Page is called...
                    $cmdletArgsPageParameterSet = ''
                    # ...and this list of parameters works for when unrolling paged results (when -Paging is not used)
                    $cmdletArgsNoPaging = ''
                    foreach ($pagingParameterEntry in $pagingParameterSet.ParameterDetails.GetEnumerator()) {
                        $pagingParameter = $pagingParameterEntry.Value
                        # Ignore parameters that are readonly or have a constant value
                        if ($pagingParameter.ContainsKey('ReadOnlyGlobalParameter') -and $pagingParameter.ReadOnlyGlobalParameter) {
                            continue
                        }

                        if ($pagingParameter.ContainsKey('ConstantValue') -and $pagingParameter.ConstantValue) {
                            continue
                        }

                        $matchingCurrentParameter = $parameterSetDetail.ParameterDetails.GetEnumerator() | Where-Object { $_.Value.Name -eq $pagingParameter.Name } | Select-Object -First 1
                        if ($matchingCurrentParameter) {
                            $cmdletArgsPageParameterSet += "-$($pagingParameter.Name) `$$($pagingParameter.Name) "
                            $cmdletArgsNoPaging += "-$($pagingParameter.Name) `$$($pagingParameter.Name) "
                        } else {
                            $unmatchedParameters += $pagingParameter
                            $cmdletArgsPageParameterSet += "-$($pagingParameter.Name) `$Page.NextPageLink "
                            $cmdletArgsNoPaging += "-$($pagingParameter.Name) `$result.NextPageLink "
                        }
                    }

                    if ($unmatchedParameters.Count -ne 1) {
                        throw ($LocalizedData.InvalidPagingOperationSchema -f ($commandName, $pagingFunctionDetails.Value.CommandName))
                    }

                    $x_ms_pageableObject['Cmdlet'] = $pagingFunctionDetails.Value.CommandName
                    $x_ms_pageableObject['CmdletArgsPage'] = $cmdletArgsPageParameterSet.Trim()
                    $x_ms_pageableObject['CmdletArgsPaging'] = $cmdletArgsNoPaging.Trim()
                } else {
                    $x_ms_pageableObject['Operations'] = $parameterSetDetail.Operations
                    $x_ms_pageableObject['MethodName'] = "$($parameterSetDetail.MethodName.Substring(0, $parameterSetDetail.MethodName.IndexOf('WithHttpMessagesAsync')))NextWithHttpMessagesAsync"
                }
            }
        }

        $parameterSetDetail.ParameterDetails.GetEnumerator() | ForEach-Object {
            $parameterDetails = $_.Value
            $parameterRequiresAdding = $true
            if ($parameterDetails.ContainsKey('x_ms_parameter_location') -and ('client' -eq $parameterDetails.'x_ms_parameter_location')) {
                # Check if a global has been added already
                if ($parametersToAdd.ContainsKey("$($parameterDetails.Name)Global")) {
                    $parameterRequiresAdding = $false
                } elseif ($parameterDetails.ContainsKey('ReadOnlyGlobalParameter') -and $parameterDetails.ReadOnlyGlobalParameter) {
                    $parameterRequiresAdding = $false
                } else {
                    $globalParameterName = $parameterDetails.Name
                    $globalParameterValue = "```$$($parameterDetails.Name)"
                    if ($parameterDetails.ContainsKey('ConstantValue') -and $parameterDetails.ConstantValue) {
                        # A parameter with a constant value doesn't need to be in the parameter block
                        $parameterRequiresAdding = $false
                        $globalParameterValue = $parameterDetails.ConstantValue
                    }
                    
                    $globalParameterBlock += [Environment]::NewLine + $executionContext.InvokeCommand.ExpandString($GlobalParameterBlockStr)
                }
            }

            if ($parameterRequiresAdding) {
                $AddUniqueParameter_params = @{
                    ParameterDetails = $parameterDetails
                    OperationId = $parameterSetDetail.OperationId
                    ParametersToAdd = $parametersToAdd
                    ParameterHitCount = $parameterHitCount
                }

                if ($parameterDetails.ContainsKey('x_ms_parameter_grouping_group')) {
                    foreach ($parameterDetailEntry in $parameterDetails.'x_ms_parameter_grouping_group'.GetEnumerator()) {
                        $AddUniqueParameter_params['CandidateParameterDetails'] = $parameterDetailEntry.Value
                        Add-UniqueParameter @AddUniqueParameter_params
                    }
                } elseif($parameterDetails.ContainsKey('FlattenOnPSCmdlet') -and $parameterDetails.FlattenOnPSCmdlet) {
                    $DefinitionName = ($parameterDetails.Type -split '[.]')[-1]
                    if($DefinitionFunctionsDetails.ContainsKey($DefinitionName)) {
                        $DefinitionDetails = $DefinitionFunctionsDetails[$DefinitionName]
                        $flattenedParametersOnPSCmdlet[$parameterDetails.Name] = $DefinitionDetails
                        $DefinitionDetails.ParametersTable.GetEnumerator() | ForEach-Object {
                            $AddUniqueParameter_params['CandidateParameterDetails'] = $_.value
                            Add-UniqueParameter @AddUniqueParameter_params
                        }
                    }
                    else{
                        Throw ($LocalizedData.InvalidPSMetaFlattenParameter -f ($parameterDetails.Name,$parameterDetails.Type))
                    }
                } else {
                    $AddUniqueParameter_params['CandidateParameterDetails'] = $parameterDetails
                    Add-UniqueParameter @AddUniqueParameter_params
                }
            } else {
                # This magic string is here to distinguish local vs global parameters with the same name, e.g. in the Azure Resources API
                $parametersToAdd["$($parameterDetails.Name)Global"] = $null
            }
        }
    }
    $topParameterToAdd = $null
    $skipParameterToAdd = $null
    $pagingBlock = ''
    $pagingOperationName = ''
    $pagingOperations = ''
    $Cmdlet = ''
    $CmdletParameter = ''
    $CmdletArgs = ''
    $pageType = 'Array'
    $resultBlockStr = $resultBlockNoPaging
    if ($x_ms_pageableObject) {
        if ($x_ms_pageableObject.ReturnType -ne 'NONE') {
            $pageType = $x_ms_pageableObject.ReturnType
        }

        if ($x_ms_pageableObject.ContainsKey('Operations')) {
            $pagingOperations = $x_ms_pageableObject.Operations
            $pagingOperationName = $x_ms_pageableObject.MethodName
        } else {
            $Cmdlet = $x_ms_pageableObject.Cmdlet
            $CmdletArgs = $x_ms_pageableObject.CmdletArgsPaging
        }

        $topParameterToAdd = @{
            Details = @{
                Name = 'Top'
                Type = 'int'
                Mandatory = '$false'
                Description = 'Return the top N items as specified by the parameter value. Applies after the -Skip parameter.'
                IsParameter = $true
                ValidateSet = $null
                ExtendedData = @{
                    Type = 'int'
                    HasDefaultValue = $true
                    DefaultValue = -1
                }
            }
            ParameterSetInfo = @{}
        }

        $skipParameterToAdd = @{
            Details = @{
                Name = 'Skip'
                Type = 'int'
                Mandatory = '$false'
                Description = 'Skip the first N items as specified by the parameter value.'
                IsParameter = $true
                ValidateSet = $null
                ExtendedData = @{
                    Type = 'int'
                    HasDefaultValue = $true
                    DefaultValue = -1
                }
            }
            ParameterSetInfo = @{}
        }
    }

    # Process security section
    $azSubscriptionIdBlock = ""
    $authFunctionCall = ""
    $overrideBaseUriBlock = ""
    $httpClientHandlerCall = ""
    $securityParametersToAdd = @()
    $PowerShellCodeGen = $SwaggerMetaDict['PowerShellCodeGen']
    if (($PowerShellCodeGen['ServiceType'] -eq 'azure') -or ($PowerShellCodeGen['ServiceType'] -eq 'azure_stack')) {
        $azSubscriptionIdBlock = "`$subscriptionId = Get-AzSubscriptionId"
    }
    if ($PowerShellCodeGen['CustomAuthCommand']) {
        $authFunctionCall = $PowerShellCodeGen['CustomAuthCommand']
    }
    if ($PowerShellCodeGen['HostOverrideCommand']) {
        $hostOverrideCommand = $PowerShellCodeGen['HostOverrideCommand']
        $overrideBaseUriBlock = $executionContext.InvokeCommand.ExpandString($HostOverrideBlock)
    }
    # If the auth function hasn't been set by metadata, try to discover it from the security and securityDefinition objects in the spec
    if (-not $authFunctionCall) {
        if ($FunctionDetails.ContainsKey('Security')) {
            # For now, just take the first security object
            if ($FunctionDetails.Security.Count -gt 1) {
                Write-Warning ($LocalizedData.MultipleSecurityTypesNotSupported -f $commandName)
            }
            $firstSecurityObject = Get-Member -InputObject $FunctionDetails.Security[0] -MemberType NoteProperty
            # If there's no security object, we don't care about the security definition object
            if ($firstSecurityObject) {
                # If there is one, we need to know the definition
                if (-not $swaggerDict.ContainsKey("SecurityDefinitions")) {
                    throw $LocalizedData.SecurityDefinitionsObjectMissing
                }

                $securityDefinitions = $swaggerDict.SecurityDefinitions
                $securityDefinition = $securityDefinitions.$($firstSecurityObject.Name)
                if (-not $securityDefinition) {
                    throw ($LocalizedData.SpecificSecurityDefinitionMissing -f ($firstSecurityObject.Name))
                }

                if (-not (Get-Member -InputObject $securityDefinition -Name 'type')) {
                    throw ($LocalizedData.SecurityDefinitionMissingProperty -f ($firstSecurityObject.Name, 'type'))
                }

                $type = $securityDefinition.type
                if ($type -eq 'basic') {
                    # For Basic Authentication, allow the user to pass in a PSCredential object.
                    $credentialParameter = @{
                        Details = @{
                            Name = 'Credential'
                            Type = 'PSCredential'
                            Mandatory = '$true'
                            Description = 'User credentials.'
                            IsParameter = $true
                            ValidateSet = $null
                            ExtendedData = @{
                                Type = 'PSCredential'
                                HasDefaultValue = $false
                            }
                        }
                        ParameterSetInfo = @{}
                    }
                    $securityParametersToAdd += @{
                        Parameter = $credentialParameter
                        IsConflictingWithOperationParameter = $false
                    }
                    # If the service is specified to not issue authentication challenges, we can't rely on HttpClientHandler
                    if ($PowerShellCodeGen['NoAuthChallenge'] -and ($PowerShellCodeGen['NoAuthChallenge'] -eq $true)) {
                        $authFunctionCall = 'Get-AutoRestCredential -Credential $Credential'
                    } else {
                        # Use an empty service client credentials object because we're using HttpClientHandler instead
                        $authFunctionCall = 'Get-AutoRestCredential'
                        $httpClientHandlerCall = '$httpClientHandler = New-HttpClientHandler -Credential $Credential'
                    }
                } elseif ($type -eq 'apiKey') {
                    if (-not (Get-Member -InputObject $securityDefinition -Name 'name')) {
                        throw ($LocalizedData.SecurityDefinitionMissingProperty -f ($firstSecurityObject.Name, 'name'))
                    }

                    if (-not (Get-Member -InputObject $securityDefinition -Name 'in')) {
                        throw ($LocalizedData.SecurityDefinitionMissingProperty -f ($firstSecurityObject.Name, 'in'))
                    }

                    $name = $securityDefinition.name
                    $in = $securityDefinition.in
                    # For API key authentication, the user should supply the API key, but the in location and the name are generated from the spec
                    # In addition, we'd be unable to authenticate without the API key, so make it mandatory
                    $credentialParameter = @{
                        Details = @{
                            Name = 'APIKey'
                            Type = 'string'
                            Mandatory = '$true'
                            Description = 'API key given by service owner.'
                            IsParameter = $true
                            ValidateSet = $null
                            ExtendedData = @{
                                Type = 'string'
                                HasDefaultValue = $false
                            }
                        }
                        ParameterSetInfo = @{}
                    }
                    $securityParametersToAdd += @{
                        Parameter = $credentialParameter
                        IsConflictingWithOperationParameter = $false
                    }
                    $authFunctionCall = "Get-AutoRestCredential -APIKey `$APIKey -Location '$in' -Name '$name'"
                } else {
                    Write-Warning -Message ($LocalizedData.UnsupportedAuthenticationType -f ($type))
                }
            }
        }
    }

    if (-not $authFunctionCall) {
        # At this point, there was no supported security object or overridden auth function, so assume no auth
        $authFunctionCall = 'Get-AutoRestCredential'
    }

    $clientArgumentList = $clientArgumentListNoHandler
    if ($httpClientHandlerCall) {
        $clientArgumentList = $clientArgumentListHttpClientHandler
    }

    $nonUniqueParameterSets = @()
    foreach ($parameterSetDetail in $parameterSetDetails) {
        # Add parameter sets to paging parameter sets
        if ($topParameterToAdd -and $parameterSetDetail.ContainsKey('x-ms-pageable') -and $parameterSetDetail.'x-ms-pageable' -and (-not $isNextPageOperation)) {
            $topParameterToAdd.ParameterSetInfo[$parameterSetDetail.OperationId] = @{
                Name = $parameterSetDetail.OperationId
                Mandatory = '$false'
            }
        }

        if ($skipParameterToAdd -and $parameterSetDetail.ContainsKey('x-ms-pageable') -and $parameterSetDetail.'x-ms-pageable' -and (-not $isNextPageOperation)) {
            $skipParameterToAdd.ParameterSetInfo[$parameterSetDetail.OperationId] = @{
                Name = $parameterSetDetail.OperationId
                Mandatory = '$false'
            }
        }

        # Test for uniqueness of parameters
        $parameterSetDetail.ParameterDetails.GetEnumerator() | ForEach-Object {
            $parameterDetails = $_.Value
            # Check if the paging parameters are conflicting
            # Note that this has to be moved elsewhere to be more generic, but this is temporarily located here to solve this scenario for paging at least
            if ($topParameterToAdd -and $parameterDetails.Name -eq 'Top') {
                $topParameterToAdd = $null
                # If the parameter is not OData, full paging support isn't possible.
                if (-not $parameterDetails.ExtendedData.ContainsKey('IsODataParameter') -or -not $parameterDetails.ExtendedData.IsODataParameter) {
                    Write-Warning -Message ($LocalizedData.ParameterConflictAndResult -f ('Top', $commandName, $parameterSetDetail.OperationId, $LocalizedData.PagingNotFullySupported))
                }
            }

            if ($skipParameterToAdd -and $parameterDetails.Name -eq 'Skip') {
                $skipParameterToAdd = $null
                # If the parameter is not OData, full paging support isn't possible.
                if (-not $parameterDetails.ExtendedData.ContainsKey('IsODataParameter') -or -not $parameterDetails.ExtendedData.IsODataParameter) {
                    Write-Warning -Message ($LocalizedData.ParameterConflictAndResult -f ('Skip', $commandName, $parameterSetDetail.OperationId, $LocalizedData.PagingNotFullySupported))
                }
            }

            foreach ($additionalParameter in $securityParametersToAdd) {
                if ($parameterDetails.Name -eq $additionalParameter.Parameter.Details.Name) {
                    $additionalParameter.IsConflictingWithOperationParameter = $true
                    Write-Warning -Message ($LocalizedData.ParameterConflictAndResult -f ($additionalParameter.Parameter.Details.Name, $commandName, $parameterSetDetail.OperationId, $LocalizedData.CredentialParameterNotSupported))
                }
            }
            
            if ($parameterHitCount[$parameterDetails.Name] -eq 1) {
                # continue here brings us back to the top of the $parameterSetDetail.ParameterDetails.GetEnumerator() | ForEach-Object loop
                continue
            }
        }

        # At this point none of the parameters in this set are unique
        $nonUniqueParameterSets += $parameterSetDetail
    }

    if ($topParameterToAdd) {
        $parametersToAdd[$topParameterToAdd.Details.Name] = $topParameterToAdd
    }

    if ($skipParameterToAdd) {
        $parametersToAdd[$skipParameterToAdd.Details.Name] = $skipParameterToAdd
    }

    foreach ($additionalParameter in $securityParametersToAdd) {
        if (-not $additionalParameter.IsConflictingWithOperationParameter) {
            $parametersToAdd[$additionalParameter.Parameter.Details.Name] = $additionalParameter.Parameter
        }
    }
    
    if ($topParameterToAdd -and $skipParameterToAdd) {
        $resultBlockStr = $executionContext.InvokeCommand.ExpandString($resultBlockWithSkipAndTop)
    } elseif ($topParameterToAdd -and -not $skipParameterToAdd) {
        $resultBlockStr = $executionContext.InvokeCommand.ExpandString($resultBlockWithTop)
    } elseif (-not $topParameterToAdd -and $skipParameterToAdd) {
        $resultBlockStr = $executionContext.InvokeCommand.ExpandString($resultBlockWithSkip)
    }

    $getTaskResult = $executionContext.InvokeCommand.ExpandString($getTaskResultBlock)

    if ($pagingOperations) {
        if ($topParameterToAdd) {
            $pagingBlock = $executionContext.InvokeCommand.ExpandString($PagingBlockStrFunctionCallWithTop)
        } else {
            $pagingBlock = $executionContext.InvokeCommand.ExpandString($PagingBlockStrFunctionCall)
        }
    } elseif ($Cmdlet) {
        if ($topParameterToAdd) {
            $pagingBlock = $executionContext.InvokeCommand.ExpandString($PagingBlockStrCmdletCallWithTop)
        } else {
            $pagingBlock = $executionContext.InvokeCommand.ExpandString($PagingBlockStrCmdletCall)
        }
    }

    # For description, we're currently using the default parameter set's description, since concatenating multiple descriptions doesn't ever really work out well.
    if ($nonUniqueParameterSets.Length -gt 1) {
        # Pick the highest priority set among $nonUniqueParameterSets, but really it doesn't matter, cause...
        # Print warning that this generated cmdlet has ambiguous parameter sets
        $defaultParameterSet = $nonUniqueParameterSets | Sort-Object -Property Priority | Select-Object -First 1
        $DefaultParameterSetName = $defaultParameterSet.OperationId
        $description = $defaultParameterSet.Description
        Write-Warning -Message ($LocalizedData.CmdletHasAmbiguousParameterSets -f ($commandName))
    } elseif ($nonUniqueParameterSets.Length -eq 1) {
        # If there's only one non-unique, we can prevent errors by making this the default
        $DefaultParameterSetName = $nonUniqueParameterSets[0].OperationId
        $description = $nonUniqueParameterSets[0].Description
    } else {
        # Pick the highest priority set among all sets
        $defaultParameterSet = $parameterSetDetails | Sort-Object @{e = {$_.Priority -as [int] }} | Select-Object -First 1
        $DefaultParameterSetName = $defaultParameterSet.OperationId
        $description = $defaultParameterSet.Description
    }

    $oDataExpression = ""
    $oDataExpressionBlock = ""
    # Variable used to replace in function body
    $parameterGroupsExpressionBlock = ""
    # Variable used to store all group expressions, concatenate, then store in $parameterGroupsExpressionBlock
    $parameterGroupsExpressions = @{}
    $parametersToAdd.GetEnumerator() | ForEach-Object {
        $parameterToAdd = $_.Value
        if ($parameterToAdd) {
            $parameterName = $parameterToAdd.Details.Name
            $AllParameterSetsString = ''
            foreach ($parameterSetInfoEntry in $parameterToAdd.ParameterSetInfo.GetEnumerator()) {
                $parameterSetInfo = $parameterSetInfoEntry.Value
                $isParamMandatory = $parameterSetInfo.Mandatory
                $ParameterSetPropertyString = ", ParameterSetName = '$($parameterSetInfo.Name)'"
                if ($AllParameterSetsString) {
                    # Two tabs
                    $AllParameterSetsString += [Environment]::NewLine + " " + $executionContext.InvokeCommand.ExpandString($parameterAttributeString)
                } else {
                    $AllParameterSetsString = $executionContext.InvokeCommand.ExpandString($parameterAttributeString)
                }
            }

            if (-not $AllParameterSetsString) {
                $isParamMandatory = $parameterToAdd.Details.Mandatory
                $ParameterSetPropertyString = ""
                $AllParameterSetsString = $executionContext.InvokeCommand.ExpandString($parameterAttributeString)
            }

            $paramName = "`$$parameterName" 
            $ValidateSetDefinition = $null
            if ($parameterToAdd.Details.ValidateSet)
            {
                $ValidateSetString = $parameterToAdd.Details.ValidateSet
                $ValidateSetDefinition = $executionContext.InvokeCommand.ExpandString($ValidateSetDefinitionString)
            }

            $parameterDefaultValueOption = ""
            $paramType = "$([Environment]::NewLine) "
            if ($parameterToAdd.Details.ContainsKey('ExtendedData')) {
                if ($parameterToAdd.Details.ExtendedData.ContainsKey('IsODataParameter') -and $parameterToAdd.Details.ExtendedData.IsODataParameter) {
                    $paramType = "[$($parameterToAdd.Details.Type)]$paramType"
                    $oDataExpression += " if (`$$parameterName) { `$oDataQuery += `"&```$$parameterName=`$$parameterName`" }" + [Environment]::NewLine
                } else {
                    # Assuming you can't group ODataQuery parameters
                    if ($parameterToAdd.Details.ContainsKey('x_ms_parameter_grouping') -and $parameterToAdd.Details.'x_ms_parameter_grouping') {
                        $parameterGroupPropertyName = $parameterToAdd.Details.Name
                        $groupName = $parameterToAdd.Details.'x_ms_parameter_grouping'
                        $fullGroupName = $parameterToAdd.Details.ExtendedData.GroupType
                        if ($parameterGroupsExpressions.ContainsKey($groupName)) {
                            $parameterGroupsExpression = $parameterGroupsExpressions[$groupName]
                        } else {
                            $parameterGroupsExpression = $executionContext.InvokeCommand.ExpandString($parameterGroupCreateExpression)
                        }

                        $parameterGroupsExpression += [Environment]::NewLine + $executionContext.InvokeCommand.ExpandString($parameterGroupPropertyExpression)
                        $parameterGroupsExpressions[$groupName] = $parameterGroupsExpression
                    }
                    
                    if ($parameterToAdd.Details.ExtendedData.Type) {
                        $paramType = "[$($parameterToAdd.Details.ExtendedData.Type)]$paramType"
                        if ($parameterToAdd.Details.ExtendedData.HasDefaultValue) {
                            if ($parameterToAdd.Details.ExtendedData.DefaultValue) {
                                if ([NullString]::Value -eq $parameterToAdd.Details.ExtendedData.DefaultValue) {
                                    $parameterDefaultValue = "[NullString]::Value"
                                } elseif ("System.String" -eq $parameterToAdd.Details.ExtendedData.Type) {
                                    $parameterDefaultValue = "`"$($parameterToAdd.Details.ExtendedData.DefaultValue)`""
                                } else {
                                    $parameterDefaultValue = "$($parameterToAdd.Details.ExtendedData.DefaultValue)"
                                }
                            } else {
                                $parameterDefaultValue = "`$null"
                            }

                            $parameterDefaultValueOption = $executionContext.InvokeCommand.ExpandString($parameterDefaultValueString)
                        }
                    }
                }

                $paramBlock += $executionContext.InvokeCommand.ExpandString($parameterDefString)
                $pDescription = $parameterToAdd.Details.Description
                $paramHelp += $executionContext.InvokeCommand.ExpandString($helpParamStr)
            } elseif($parameterToAdd.Details.Containskey('Type')) {
                $paramType = "[$($parameterToAdd.Details.Type)]$paramType"

                $paramblock += $executionContext.InvokeCommand.ExpandString($parameterDefString)
                $pDescription = $parameterToAdd.Details.Description
                $paramHelp += $executionContext.InvokeCommand.ExpandString($helpParamStr)
            } else {
                Write-Warning ($LocalizedData.ParameterMissingFromAutoRestCode -f ($parameterName, $commandName))
            }
        }
    }

    foreach ($parameterGroupsExpressionEntry in $parameterGroupsExpressions.GetEnumerator()) {
        $parameterGroupsExpressionBlock += $parameterGroupsExpressionEntry.Value + [Environment]::NewLine
    }

    if ($oDataExpression) {
        $oDataExpression = $oDataExpression.Trim()
        $oDataExpressionBlock = $executionContext.InvokeCommand.ExpandString($oDataExpressionBlockStr)
    }

    $paramBlock = $paramBlock.TrimEnd().TrimEnd(",")
    $commandHelp = $executionContext.InvokeCommand.ExpandString($helpDescStr)
    if ($isLongRunningOperation) {
        if ($paramBlock) {
            $ParamBlockReplaceStr = $paramBlock + ",`r`n" + $AsJobParameterString
        } else {
            $ParamBlockReplaceStr = $AsJobParameterString
        }

        $PathFunctionBody = $executionContext.InvokeCommand.ExpandString($PathFunctionBodyAsJob)
    } else {
        $ParamBlockReplaceStr = $paramBlock
        $PathFunctionBody = $executionContext.InvokeCommand.ExpandString($PathFunctionBodySynch)
    }

    $functionBodyParams = @{
                                ParameterSetDetails = $parameterSetDetails
                                ODataExpressionBlock = $oDataExpressionBlock
                                ParameterGroupsExpressionBlock = $parameterGroupsExpressionBlock
                                GlobalParameterBlock = $GlobalParameterBlock
                                SwaggerDict = $SwaggerDict
                                SwaggerMetaDict = $SwaggerMetaDict
                                SecurityBlock = $executionContext.InvokeCommand.ExpandString($securityBlockStr)
                                OverrideBaseUriBlock = $overrideBaseUriBlock
                                ClientArgumentList = $clientArgumentList
                                FlattenedParametersOnPSCmdlet = $flattenedParametersOnPSCmdlet
                           }

    $pathGenerationPhaseResult = Get-PathFunctionBody @functionBodyParams
    $bodyObject = $pathGenerationPhaseResult.BodyObject

    $body = $bodyObject.Body
    $outputTypeBlock = $bodyObject.OutputTypeBlock

    if ($UseAzureCsharpGenerator) {
        $dependencyInitFunction = "Initialize-PSSwaggerDependencies -Azure"
    } else {
        $dependencyInitFunction = "Initialize-PSSwaggerDependencies"
    }
    
    $CommandString = $executionContext.InvokeCommand.ExpandString($advFnSignatureForPath)
    $GeneratedCommandsPath = Join-Path -Path (Join-Path -Path $SwaggerMetaDict['outputDirectory'] -ChildPath $GeneratedCommandsName) `
                                       -ChildPath 'SwaggerPathCommands'

    if(-not (Test-Path -Path $GeneratedCommandsPath -PathType Container)) {
        $null = New-Item -Path $GeneratedCommandsPath -ItemType Directory
    }

    $CommandFilePath = Join-Path -Path $GeneratedCommandsPath -ChildPath "$commandName.ps1"
    Out-File -InputObject $CommandString -FilePath $CommandFilePath -Encoding ascii -Force -Confirm:$false -WhatIf:$false

    Write-Verbose -Message ($LocalizedData.GeneratedPathCommand -f $commandName)

    return $commandName
}

function Set-ExtendedCodeMetadata {
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory=$true)]
        [string]
        $MainClientTypeName,

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

    $resultRecord = @{
        VerboseMessages = @()
        ErrorMessages = @()
        WarningMessages = @()
    }
    
    $resultRecord.VerboseMessages += $LocalizedData.ExtractingMetadata

    $PathFunctionDetails = Import-CliXml -Path $CliXmlTmpPath
    $errorOccurred = $false
    $PathFunctionDetails.GetEnumerator() | ForEach-Object {
        $FunctionDetails = $_.Value
        $ParameterSetDetails = $FunctionDetails['ParameterSetDetails']
        foreach ($parameterSetDetail in $ParameterSetDetails) {
            if ($errorOccurred) {
                return
            }
            
            $operationId = $parameterSetDetail.OperationId
            $methodName = ''
            $operations = ''
            $operationsWithSuffix = ''
            $opIdValues = $operationId -split '_',2 
            if(-not $opIdValues -or ($opIdValues.count -ne 2)) {
                $methodName = $operationId + 'WithHttpMessagesAsync'
            } else {            
                $operationName = $opIdValues[0]
                $operationType = $opIdValues[1]
                $operations = ".$operationName"
                if ($parameterSetDetail['UseOperationsSuffix'] -and $parameterSetDetail['UseOperationsSuffix'])
                { 
                    $operationsWithSuffix = $operations + 'Operations'
                }

                $methodName = $operationType + 'WithHttpMessagesAsync'
            }

            $parameterSetDetail['MethodName'] = $methodName
            $parameterSetDetail['Operations'] = $operations

            # For some reason, moving this out of this loop causes issues
            $clientType = $MainClientTypeName -as [Type]
            if (-not $clientType) {
                $resultRecord.ErrorMessages += $LocalizedData.ExpectedServiceClientTypeNotFound -f ($MainClientTypeName)
                Export-CliXml -InputObject $resultRecord -Path $CliXmlTmpPath
                $errorOccurred = $true
                return
            }

            # Process global parameters
            $paramObject = $parameterSetDetail.ParameterDetails
            $clientType.GetProperties() | ForEach-Object {
                $propertyName = $_.Name
                $matchingParamDetail = $paramObject.GetEnumerator() | Where-Object { $_.Value.Name -eq $propertyName } | Select-Object -First 1 -ErrorAction Ignore
                if ($matchingParamDetail) {
                    $setSingleParameterMetadataParms = @{
                                                            CommandName = $FunctionDetails['CommandName']
                                                            Name = $matchingParamDetail.Value.Name
                                                            HasDefaultValue = $false
                                                            IsGrouped = $false
                                                            Type = $_.PropertyType
                                                            MatchingParamDetail = $matchingParamDetail.Value
                                                            ResultRecord = $resultRecord
                                                        }
                    if (-not (Set-SingleParameterMetadata @setSingleParameterMetadataParms))
                    {
                        Export-CliXml -InputObject $ResultRecord -Path $CliXmlTmpPath
                        $errorOccurred = $true
                        return
                    }
                }
            }

            if ($operationsWithSuffix) {
                $operationName = $operationsWithSuffix.Substring(1)
                $propertyObject = $clientType.GetProperties() | Where-Object { $_.Name -eq $operationName } | Select-Object -First 1 -ErrorAction Ignore
                if (-not $propertyObject) {
                    # The Operations suffix logic isn't rock solid, so this is safety check.
                    $operationName = $operations.Substring(1)
                    $propertyObject = $clientType.GetProperties() | Where-Object { $_.Name -eq $operationName } | Select-Object -First 1 -ErrorAction Ignore
                    if (-not $propertyObject) {
                        $resultRecord.ErrorMessages += $LocalizedData.ExpectedOperationsClientTypeNotFound -f ($operationName, $clientType)
                        Export-CliXml -InputObject $resultRecord -Path $CliXmlTmpPath
                        $errorOccurred = $true
                        return
                    }
                } else {
                    $parameterSetDetail['Operations'] = $operationsWithSuffix
                }

                $clientType = $propertyObject.PropertyType
            } elseif ($operations) {
                $operationName = $operations.Substring(1)
                $propertyObject = $clientType.GetProperties() | Where-Object { $_.Name -eq $operationName } | Select-Object -First 1 -ErrorAction Ignore
                if (-not $propertyObject) {
                    $resultRecord.ErrorMessages += $LocalizedData.ExpectedOperationsClientTypeNotFound -f ($operationName, $clientType)
                    Export-CliXml -InputObject $resultRecord -Path $CliXmlTmpPath
                    $errorOccurred = $true
                    return
                }

                $clientType = $propertyObject.PropertyType
            }

            $methodInfo = $clientType.GetMethods() | Where-Object { $_.Name -eq $MethodName } | Select-Object -First 1
            if (-not $methodInfo) {
                $resultRecord.ErrorMessages += $LocalizedData.ExpectedMethodOnTypeNotFound -f ($MethodName, $clientType)
                Export-CliXml -InputObject $resultRecord -Path $CliXmlTmpPath
                $errorOccurred = $true
                return
            }

            # Process output type
            $returnType = $methodInfo.ReturnType
            if ($returnType.Name -eq 'Task`1') {
                $returnType = $returnType.GenericTypeArguments[0]
            }

            if ($returnType.Name -eq 'AzureOperationResponse`1') {
                $returnType = $returnType.GenericTypeArguments[0]
            }

            $returnTypeString = Convert-GenericTypeToString -Type $returnType
            $parameterSetDetail['ReturnType'] = $returnTypeString

            $ParamList = @()
            $oDataQueryFound = $false
            $methodInfo.GetParameters() | Sort-Object -Property Position | ForEach-Object {
                $hasDefaultValue = $_.HasDefaultValue
                # All Types should be converted to their string names, otherwise the CLI XML gets too large
                $type = $_.ParameterType.ToString()
                $metadata = @{
                    Name = Get-PascalCasedString -Name $_.Name
                    HasDefaultValue = $hasDefaultValue
                    Type = $type
                }

                $matchingParamDetail = $paramObject.GetEnumerator() | Where-Object { $_.Value.Name -eq $metadata.Name } | Select-Object -First 1 -ErrorAction Ignore
                if ($matchingParamDetail) {
                    # Not all parameters in the code is present in the Swagger spec (autogenerated parameters like CustomHeaders or ODataQuery parameters)
                    $matchingParamDetail = $matchingParamDetail[0].Value
                    if ($matchingParamDetail.ContainsKey('x_ms_parameter_grouping_group')) {
                        # Look through this parameter group's parameters and extract the individual metadata
                        $paramToAdd = "`$$($matchingParamDetail.Name)"
                        $parameterGroupType = $_.ParameterType
                        $parameterGroupType.GetProperties() | ForEach-Object {
                            $parameterGroupProperty = $_
                            $matchingGroupedParameterDetailEntry = $matchingParamDetail.'x_ms_parameter_grouping_group'.GetEnumerator() | Where-Object { $_.Value.Name -eq $parameterGroupProperty.Name } | Select-Object -First 1 -ErrorAction Ignore
                            if ($matchingGroupedParameterDetailEntry) {
                                $setSingleParameterMetadataParms = @{
                                    CommandName = $FunctionDetails['CommandName']
                                    Name = $matchingParamDetail.Name
                                    HasDefaultValue = $false
                                    IsGrouped = $true
                                    Type = $_.PropertyType
                                    MatchingParamDetail = $matchingGroupedParameterDetailEntry.Value
                                    ResultRecord = $resultRecord
                                }

                                if (-not (Set-SingleParameterMetadata @setSingleParameterMetadataParms))
                                {
                                    Export-CliXml -InputObject $ResultRecord -Path $CliXmlTmpPath
                                    $errorOccurred = $true
                                    return
                                }

                                $matchingGroupedParameterDetailEntry.Value.ExtendedData.GroupType = $parameterGroupType.ToString()
                            }
                        }
                    } else {
                        # Single parameter
                        $setSingleParameterMetadataParms = @{
                            CommandName = $FunctionDetails['CommandName']
                            Name = $_.Name
                            HasDefaultValue = $hasDefaultValue
                            IsGrouped = $false
                            Type = $_.ParameterType
                            MatchingParamDetail = $matchingParamDetail
                            ResultRecord = $resultRecord
                        }

                        if ($hasDefaultValue) {
                            $setSingleParameterMetadataParms['DefaultValue'] = $_.DefaultValue
                        }

                        if (-not (Set-SingleParameterMetadata @setSingleParameterMetadataParms))
                        {
                            Export-CliXml -InputObject $ResultRecord -Path $CliXmlTmpPath
                            $errorOccurred = $true
                            return
                        }

                        $paramToAdd = $matchingParamDetail.ExtendedData.ParamToAdd
                    }
                    
                    $ParamList += $paramToAdd
                } else {
                    if ($metadata.Type.StartsWith("Microsoft.Rest.Azure.OData.ODataQuery``1")) {
                        if ($oDataQueryFound) {
                            $resultRecord.ErrorMessages += ($LocalizedData.MultipleODataQueriesOneFunction -f ($operationId))
                            Export-CliXml -InputObject $resultRecord -Path $CliXmlTmpPath
                            return
                        } else {
                            # Escape backticks
                            $oDataQueryType = $metadata.Type.Replace("``", "````")
                            $ParamList += "`$(if (`$oDataQuery) { New-Object -TypeName `"$oDataQueryType`" -ArgumentList `$oDataQuery } else { `$null })"
                            $oDataQueryFound = $true
                        }
                    }
                }
            }
            
            if ($parameterSetDetail.ContainsKey('x-ms-odata') -and $parameterSetDetail.'x-ms-odata') {
                $paramObject.GetEnumerator() | ForEach-Object {
                    $paramDetail = $_.Value
                    if (-not $paramDetail.ContainsKey('ExtendedData')) {
                        $metadata = @{
                            IsODataParameter = $true
                        }

                        $paramDetail.ExtendedData = $metadata
                    }
                }
            }

            $parameterSetDetail['ExpandedParamList'] = $ParamList -Join ", "
        }

        if ($errorOccurred) {
            return
        }
    }

    $resultRecord.Result = $PathFunctionDetails
    Export-CliXml -InputObject $resultRecord -Path $CliXmlTmpPath
}

function Convert-GenericTypeToString {
    param(
        [Parameter(Mandatory=$true)]
        [Type]$Type
    )

    if (-not $Type.IsGenericType) {
        return $Type.FullName
    }

    $genericTypeStr = ''
    foreach ($genericTypeArg in $Type.GenericTypeArguments) {
        $genericTypeStr += "$(Convert-GenericTypeToString -Type $genericTypeArg),"
    }

    $genericTypeStr = $genericTypeStr.Substring(0, $genericTypeStr.Length - 1)
    return "$($Type.FullName.Substring(0, $Type.FullName.IndexOf('`')))[$genericTypeStr]"
}

function Set-SingleParameterMetadata {
    param(
        [Parameter(Mandatory=$true)]
        [string]
        $CommandName,

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

        [Parameter(Mandatory=$true)]
        [bool]
        $HasDefaultValue,

        [Parameter(Mandatory=$true)]
        [bool]
        $IsGrouped,

        [Parameter(Mandatory=$true)]
        [System.Type]
        $Type,

        [Parameter(Mandatory=$true)]
        [hashtable]
        $MatchingParamDetail,

        [Parameter(Mandatory=$true)]
        [hashtable]
        $ResultRecord,

        [Parameter(Mandatory=$false)]
        [object]
        $DefaultValue
    )
    
    $name = Get-PascalCasedString -Name $_.Name
    $metadata = @{
                    Name = $name
                    HasDefaultValue = $HasDefaultValue
                    Type = $Type.ToString()
                    ParamToAdd = "`$$name"
                }

    if ($HasDefaultValue) {
        # Setting this default value actually matter, but we might as well
        if ("System.String" -eq $metadata.Type) {
            if ($DefaultValue -eq $null) {
                $metadata.HasDefaultValue = $false
                # This is the part that works around PS automatic string coercion
                $metadata.ParamToAdd = "`$(if (`$PSBoundParameters.ContainsKey('$($metadata.Name)')) { $($metadata.ParamToAdd) } else { [NullString]::Value })"
            }
        } elseif ("System.Nullable``1[System.Boolean]" -eq $metadata.Type) {
            if($DefaultValue -ne $null) {
                $DefaultValue = "`$$DefaultValue"
            }

            $metadata.Type = "switch"
        } else {
            $DefaultValue = $_.DefaultValue
            if (-not ($_.ParameterType.IsValueType) -and $DefaultValue) {
                $ResultRecord.ErrorMessages += $LocalizedData.ReferenceTypeDefaultValueNotSupported -f ($metadata.Name, $metadata.Type, $CommandName)
                return $false
            }
        }

        $metadata['DefaultValue'] = $DefaultValue
    } else {
        if ('$false' -eq $matchingParamDetail.Mandatory -and (-not $IsGrouped)) {
            # This happens in the case of optional path parameters, even if the path parameter is at the end
            $ResultRecord.WarningMessages += ($LocalizedData.OptionalParameterNowRequired -f ($metadata.Name, $CommandName))
        }
    }

    $MatchingParamDetail['ExtendedData'] = $metadata
    return $true
}

function Get-TemporaryCliXmlFilePath {
    param(
        [Parameter(Mandatory=$true)]
        [string]
        $FullModuleName
    )

    $random = [Guid]::NewGuid().Guid
    $filePath = Join-Path -Path (Get-XDGDirectory -DirectoryType Cache) -ChildPath "$FullModuleName.$random.xml"
    return $filePath
}
# SIG # Begin signature block
# MIIasAYJKoZIhvcNAQcCoIIaoTCCGp0CAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUglSfETl+53rANJitrSvOuUaq
# JiWgghWDMIIEwzCCA6ugAwIBAgITMwAAALfuAa/68MeouwAAAAAAtzANBgkqhkiG
# 9w0BAQUFADB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G
# A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSEw
# HwYDVQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EwHhcNMTYwOTA3MTc1ODQ1
# WhcNMTgwOTA3MTc1ODQ1WjCBszELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp
# bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw
# b3JhdGlvbjENMAsGA1UECxMETU9QUjEnMCUGA1UECxMebkNpcGhlciBEU0UgRVNO
# OkJCRUMtMzBDQS0yREJFMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBT
# ZXJ2aWNlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuCMjQSw3ep1m
# SndFRK0xgVRgm9wSl3i2llRtDdxzAWN9gQtYAE3hJP0/pV/7HHkshYPfMIRf7Pm/
# dxSsAN+7ATnNUk+wpe46rfe0FDNxoE6CYaiMSNjKcMXH55bGXNnwrrcsMaZrVXzS
# IQcmAhUQw1jdLntbdTyCAwJ2UqF/XmVtWV/U466G8JP8VGLddeaucY0YKhgYwMnt
# Sp9ElCkVDcUP01L9pgn9JmKUfD3yFt2p1iZ9VKCrlla10JQwe7aNW7xjzXxvcvlV
# IXeA4QSabo4dq8HUh7JoYMqh3ufr2yNgTs/rSxG6D5ITcI0PZkH4PYjO2GbGIcOF
# RVOf5RxVrwIDAQABo4IBCTCCAQUwHQYDVR0OBBYEFJZnqouaH5kw+n1zGHTDXjCT
# 5OMAMB8GA1UdIwQYMBaAFCM0+NlSRnAK7UD7dvuzK7DDNbMPMFQGA1UdHwRNMEsw
# SaBHoEWGQ2h0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3Rz
# L01pY3Jvc29mdFRpbWVTdGFtcFBDQS5jcmwwWAYIKwYBBQUHAQEETDBKMEgGCCsG
# AQUFBzAChjxodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY3Jv
# c29mdFRpbWVTdGFtcFBDQS5jcnQwEwYDVR0lBAwwCgYIKwYBBQUHAwgwDQYJKoZI
# hvcNAQEFBQADggEBAG7J+Fdd7DgxG6awnA8opmQfW5DHnNDC/JPLof1sA8Nqczym
# cnWIHmlWhqA7TUy4q02lKenO+R/vbmHna1BrC/KkczAyhOzkI2WFU3PeYubv8EjK
# fYPmrNvS8fCsHJXj3N6fuFwXkHmCVBjTchK93auG09ckBYx5Mt4zW0TUbbw4/QAZ
# X64rbut6Aw/C1bpxqBb8vvMssBB9Hw2m8ApFTApaEVOE/sKemVlq0VIo0fCXqRST
# Lb6/QOav3S8S+N34RBNx/aKKOFzBDy6Ni45QvtRfBoNX3f4/mm4TFdNs+SeLQA+0
# oBs7UgdoxGSpX6vsWaH8dtlBw3NZK7SFi9bBMI4wggTtMIID1aADAgECAhMzAAAB
# QJap7nBW/swHAAEAAAFAMA0GCSqGSIb3DQEBBQUAMHkxCzAJBgNVBAYTAlVTMRMw
# EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN
# aWNyb3NvZnQgQ29ycG9yYXRpb24xIzAhBgNVBAMTGk1pY3Jvc29mdCBDb2RlIFNp
# Z25pbmcgUENBMB4XDTE2MDgxODIwMTcxN1oXDTE3MTEwMjIwMTcxN1owgYMxCzAJ
# BgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25k
# MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xDTALBgNVBAsTBE1PUFIx
# HjAcBgNVBAMTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjCCASIwDQYJKoZIhvcNAQEB
# BQADggEPADCCAQoCggEBANtLi+kDal/IG10KBTnk1Q6S0MThi+ikDQUZWMA81ynd
# ibdobkuffryavVSGOanxODUW5h2s+65r3Akw77ge32z4SppVl0jII4mzWSc0vZUx
# R5wPzkA1Mjf+6fNPpBqks3m8gJs/JJjE0W/Vf+dDjeTc8tLmrmbtBDohlKZX3APb
# LMYb/ys5qF2/Vf7dSd9UBZSrM9+kfTGmTb1WzxYxaD+Eaxxt8+7VMIruZRuetwgc
# KX6TvfJ9QnY4ItR7fPS4uXGew5T0goY1gqZ0vQIz+lSGhaMlvqqJXuI5XyZBmBre
# ueZGhXi7UTICR+zk+R+9BFF15hKbduuFlxQiCqET92ECAwEAAaOCAWEwggFdMBMG
# A1UdJQQMMAoGCCsGAQUFBwMDMB0GA1UdDgQWBBSc5ehtgleuNyTe6l6pxF+QHc7Z
# ezBSBgNVHREESzBJpEcwRTENMAsGA1UECxMETU9QUjE0MDIGA1UEBRMrMjI5ODAz
# K2Y3ODViMWMwLTVkOWYtNDMxNi04ZDZhLTc0YWU2NDJkZGUxYzAfBgNVHSMEGDAW
# gBTLEejK0rQWWAHJNy4zFha5TJoKHzBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8v
# Y3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNDb2RTaWdQQ0Ff
# MDgtMzEtMjAxMC5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRw
# Oi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY0NvZFNpZ1BDQV8wOC0z
# MS0yMDEwLmNydDANBgkqhkiG9w0BAQUFAAOCAQEAa+RW49cTHSBA+W3p3k7bXR7G
# bCaj9+UJgAz/V+G01Nn5XEjhBn/CpFS4lnr1jcmDEwxxv/j8uy7MFXPzAGtOJar0
# xApylFKfd00pkygIMRbZ3250q8ToThWxmQVEThpJSSysee6/hU+EbkfvvtjSi0lp
# DimD9aW9oxshraKlPpAgnPWfEj16WXVk79qjhYQyEgICamR3AaY5mLPuoihJbKwk
# Mig+qItmLPsC2IMvI5KR91dl/6TV6VEIlPbW/cDVwCBF/UNJT3nuZBl/YE7ixMpT
# Th/7WpENW80kg3xz6MlCdxJfMSbJsM5TimFU98KNcpnxxbYdfqqQhAQ6l3mtYDCC
# BbwwggOkoAMCAQICCmEzJhoAAAAAADEwDQYJKoZIhvcNAQEFBQAwXzETMBEGCgmS
# JomT8ixkARkWA2NvbTEZMBcGCgmSJomT8ixkARkWCW1pY3Jvc29mdDEtMCsGA1UE
# AxMkTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTEwMDgz
# MTIyMTkzMloXDTIwMDgzMTIyMjkzMloweTELMAkGA1UEBhMCVVMxEzARBgNVBAgT
# Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m
# dCBDb3Jwb3JhdGlvbjEjMCEGA1UEAxMaTWljcm9zb2Z0IENvZGUgU2lnbmluZyBQ
# Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCycllcGTBkvx2aYCAg
# Qpl2U2w+G9ZvzMvx6mv+lxYQ4N86dIMaty+gMuz/3sJCTiPVcgDbNVcKicquIEn0
# 8GisTUuNpb15S3GbRwfa/SXfnXWIz6pzRH/XgdvzvfI2pMlcRdyvrT3gKGiXGqel
# cnNW8ReU5P01lHKg1nZfHndFg4U4FtBzWwW6Z1KNpbJpL9oZC/6SdCnidi9U3RQw
# WfjSjWL9y8lfRjFQuScT5EAwz3IpECgixzdOPaAyPZDNoTgGhVxOVoIoKgUyt0vX
# T2Pn0i1i8UU956wIAPZGoZ7RW4wmU+h6qkryRs83PDietHdcpReejcsRj1Y8wawJ
# XwPTAgMBAAGjggFeMIIBWjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTLEejK
# 0rQWWAHJNy4zFha5TJoKHzALBgNVHQ8EBAMCAYYwEgYJKwYBBAGCNxUBBAUCAwEA
# ATAjBgkrBgEEAYI3FQIEFgQU/dExTtMmipXhmGA7qDFvpjy82C0wGQYJKwYBBAGC
# NxQCBAweCgBTAHUAYgBDAEEwHwYDVR0jBBgwFoAUDqyCYEBWJ5flJRP8KuEKU5VZ
# 5KQwUAYDVR0fBEkwRzBFoEOgQYY/aHR0cDovL2NybC5taWNyb3NvZnQuY29tL3Br
# aS9jcmwvcHJvZHVjdHMvbWljcm9zb2Z0cm9vdGNlcnQuY3JsMFQGCCsGAQUFBwEB
# BEgwRjBEBggrBgEFBQcwAoY4aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9j
# ZXJ0cy9NaWNyb3NvZnRSb290Q2VydC5jcnQwDQYJKoZIhvcNAQEFBQADggIBAFk5
# Pn8mRq/rb0CxMrVq6w4vbqhJ9+tfde1MOy3XQ60L/svpLTGjI8x8UJiAIV2sPS9M
# uqKoVpzjcLu4tPh5tUly9z7qQX/K4QwXaculnCAt+gtQxFbNLeNK0rxw56gNogOl
# VuC4iktX8pVCnPHz7+7jhh80PLhWmvBTI4UqpIIck+KUBx3y4k74jKHK6BOlkU7I
# G9KPcpUqcW2bGvgc8FPWZ8wi/1wdzaKMvSeyeWNWRKJRzfnpo1hW3ZsCRUQvX/Ta
# rtSCMm78pJUT5Otp56miLL7IKxAOZY6Z2/Wi+hImCWU4lPF6H0q70eFW6NB4lhhc
# yTUWX92THUmOLb6tNEQc7hAVGgBd3TVbIc6YxwnuhQ6MT20OE049fClInHLR82zK
# wexwo1eSV32UjaAbSANa98+jZwp0pTbtLS8XyOZyNxL0b7E8Z4L5UrKNMxZlHg6K
# 3RDeZPRvzkbU0xfpecQEtNP7LN8fip6sCvsTJ0Ct5PnhqX9GuwdgR2VgQE6wQuxO
# 7bN2edgKNAltHIAxH+IOVN3lofvlRxCtZJj/UBYufL8FIXrilUEnacOTj5XJjdib
# Ia4NXJzwoq6GaIMMai27dmsAHZat8hZ79haDJLmIz2qoRzEvmtzjcT3XAH5iR9HO
# iMm4GPoOco3Boz2vAkBq/2mbluIQqBC0N1AI1sM9MIIGBzCCA++gAwIBAgIKYRZo
# NAAAAAAAHDANBgkqhkiG9w0BAQUFADBfMRMwEQYKCZImiZPyLGQBGRYDY29tMRkw
# FwYKCZImiZPyLGQBGRYJbWljcm9zb2Z0MS0wKwYDVQQDEyRNaWNyb3NvZnQgUm9v
# dCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDcwNDAzMTI1MzA5WhcNMjEwNDAz
# MTMwMzA5WjB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G
# A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSEw
# HwYDVQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EwggEiMA0GCSqGSIb3DQEB
# AQUAA4IBDwAwggEKAoIBAQCfoWyx39tIkip8ay4Z4b3i48WZUSNQrc7dGE4kD+7R
# p9FMrXQwIBHrB9VUlRVJlBtCkq6YXDAm2gBr6Hu97IkHD/cOBJjwicwfyzMkh53y
# 9GccLPx754gd6udOo6HBI1PKjfpFzwnQXq/QsEIEovmmbJNn1yjcRlOwhtDlKEYu
# J6yGT1VSDOQDLPtqkJAwbofzWTCd+n7Wl7PoIZd++NIT8wi3U21StEWQn0gASkdm
# EScpZqiX5NMGgUqi+YSnEUcUCYKfhO1VeP4Bmh1QCIUAEDBG7bfeI0a7xC1Un68e
# eEExd8yb3zuDk6FhArUdDbH895uyAc4iS1T/+QXDwiALAgMBAAGjggGrMIIBpzAP
# BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQjNPjZUkZwCu1A+3b7syuwwzWzDzAL
# BgNVHQ8EBAMCAYYwEAYJKwYBBAGCNxUBBAMCAQAwgZgGA1UdIwSBkDCBjYAUDqyC
# YEBWJ5flJRP8KuEKU5VZ5KShY6RhMF8xEzARBgoJkiaJk/IsZAEZFgNjb20xGTAX
# BgoJkiaJk/IsZAEZFgltaWNyb3NvZnQxLTArBgNVBAMTJE1pY3Jvc29mdCBSb290
# IENlcnRpZmljYXRlIEF1dGhvcml0eYIQea0WoUqgpa1Mc1j0BxMuZTBQBgNVHR8E
# STBHMEWgQ6BBhj9odHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9k
# dWN0cy9taWNyb3NvZnRyb290Y2VydC5jcmwwVAYIKwYBBQUHAQEESDBGMEQGCCsG
# AQUFBzAChjhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY3Jv
# c29mdFJvb3RDZXJ0LmNydDATBgNVHSUEDDAKBggrBgEFBQcDCDANBgkqhkiG9w0B
# AQUFAAOCAgEAEJeKw1wDRDbd6bStd9vOeVFNAbEudHFbbQwTq86+e4+4LtQSooxt
# YrhXAstOIBNQmd16QOJXu69YmhzhHQGGrLt48ovQ7DsB7uK+jwoFyI1I4vBTFd1P
# q5Lk541q1YDB5pTyBi+FA+mRKiQicPv2/OR4mS4N9wficLwYTp2OawpylbihOZxn
# LcVRDupiXD8WmIsgP+IHGjL5zDFKdjE9K3ILyOpwPf+FChPfwgphjvDXuBfrTot/
# xTUrXqO/67x9C0J71FNyIe4wyrt4ZVxbARcKFA7S2hSY9Ty5ZlizLS/n+YWGzFFW
# 6J1wlGysOUzU9nm/qhh6YinvopspNAZ3GmLJPR5tH4LwC8csu89Ds+X57H2146So
# dDW4TsVxIxImdgs8UoxxWkZDFLyzs7BNZ8ifQv+AeSGAnhUwZuhCEl4ayJ4iIdBD
# 6Svpu/RIzCzU2DKATCYqSCRfWupW76bemZ3KOm+9gSd0BhHudiG/m4LBJ1S2sWo9
# iaF2YbRuoROmv6pH8BJv/YoybLL+31HIjCPJZr2dHYcSZAI9La9Zj7jkIeW1sMpj
# tHhUBdRBLlCslLCleKuzoJZ1GtmShxN1Ii8yqAhuoFuMJb+g74TKIdbrHk/Jmu5J
# 4PcBZW+JC33Iacjmbuqnl84xKf8OxVtc2E0bodj6L54/LlUWa8kTo/0xggSXMIIE
# kwIBATCBkDB5MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G
# A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSMw
# IQYDVQQDExpNaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQQITMwAAAUCWqe5wVv7M
# BwABAAABQDAJBgUrDgMCGgUAoIGwMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEE
# MBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMCMGCSqGSIb3DQEJBDEWBBQZ
# ik8/I/8DKLiv4FgFSr0Rz75LizBQBgorBgEEAYI3AgEMMUIwQKAWgBQAUABvAHcA
# ZQByAFMAaABlAGwAbKEmgCRodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vUG93ZXJT
# aGVsbCAwDQYJKoZIhvcNAQEBBQAEggEAN/KEw65Hl3OYvH1s04yHYh97PrrHyVMb
# 07bO1GzV/aFSO7Vqp+olcJCPSAocUdr2LOkYJ61E7SaZ48TEyS0FZrRgex+Pad89
# rosB8E2DKyc5SE9HQlCDVANjFPZloY4KWWs7gART2PcRb1n0SBt6omFpDNUdi346
# TeFMxdQj/BQd1KDDRaPqrpqHXLHuCB5v6X9IEUjk40hikEXWYi2FGg52nP0c85NS
# Ng/td603+rHJxylakngUi2ARcKIK5X/FsqMpGgqbsPOAeYoGw7mGgL7RApi6MHbB
# jwi1+YA2QvChnHTOy0w4mcoXdpbtWmdC9QueMSkg3Wh6ZKxkJq4xZqGCAigwggIk
# BgkqhkiG9w0BCQYxggIVMIICEQIBATCBjjB3MQswCQYDVQQGEwJVUzETMBEGA1UE
# CBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9z
# b2Z0IENvcnBvcmF0aW9uMSEwHwYDVQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQ
# Q0ECEzMAAAC37gGv+vDHqLsAAAAAALcwCQYFKw4DAhoFAKBdMBgGCSqGSIb3DQEJ
# AzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTE3MDgwODE3MzUyNFowIwYJ
# KoZIhvcNAQkEMRYEFOVS5yI2UfRYxi4gUvuCd7odA4SeMA0GCSqGSIb3DQEBBQUA
# BIIBABR45sOu3J8hpL8xqKbtD6U1/MI0kn7LrIDPnAvm3MKgkh0qMAMoFE1Nz4yV
# TpCIEzml9VQ4jOx9OyjMESgD1nIvKZ5+SrKTl2aviay+kjcHFI8pbRZallSUWeUH
# lfU/5zuPOLmM+bt/gP7/WnRPPIw185ySoXIN4SJN6Es3AZgCQc2Iwj6qquC25wMZ
# oBenRL23BIrrnOCo686qumdGmKawG/q4uS4D8VsR2ixbyNVJJArgvH1rr+sN9bby
# kORX2PfVSHVhd9KRw9YenUQ9eHp8WlCkKD46nohleF7oPNbaP2GoRJHtU3pCjvJD
# dR35x6Sdb4+0RmdHDS3H6bzWqqQ=
# SIG # End signature block