SwaggerUtils.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)
Import-Module -Name 'PSSwaggerUtility'
. "$PSScriptRoot\PSSwagger.Constants.ps1" -Force
. "$PSScriptRoot\Trie.ps1" -Force
. "$PSScriptRoot\PSCommandVerbMap.ps1" -Force
Microsoft.PowerShell.Utility\Import-LocalizedData  LocalizedData -filename PSSwagger.Resources.psd1
$script:CmdVerbTrie = $null
$script:CSharpCodeNamer = $null
$script:CSharpCodeNamerLoadAttempted = $false

$script:PluralizationService = $null
# System.Data.Entity.Design.PluralizationServices.PluralizationService is not yet supported on coreclr.
if(-not (Get-OperatingSystemInfo).IsCore)
{
    if(-not ('System.Data.Entity.Design.PluralizationServices.PluralizationService' -as [Type]))
    {
        Add-Type -AssemblyName System.Data.Entity.Design
    }

    $script:PluralizationService = [System.Data.Entity.Design.PluralizationServices.PluralizationService]::CreateService([System.Globalization.CultureInfo]::CurrentCulture)

    $PluralToSingularMapPath = Join-Path -Path $PSScriptRoot -ChildPath 'PluralToSingularMap.json'
    if(Test-Path -Path $PluralToSingularMapPath -PathType Leaf)
    {
        $PluralToSingularMapJsonObject = ConvertFrom-Json -InputObject ((Get-Content -Path $PluralToSingularMapPath) -join [Environment]::NewLine) -ErrorAction Stop
        $PluralToSingularMapJsonObject.CustomPluralToSingularMapping | ForEach-Object {
            $script:PluralizationService.AddWord($_.PSObject.Properties.Value, $_.PSObject.Properties.Name)
        }
    }
}

$script:IgnoredAutoRestParameters = @(@('Modeler', 'm'), @('AddCredentials'), @('CodeGenerator', 'g'))
$script:PSSwaggerDefaultNamespace = "Microsoft.PowerShell"

function ConvertTo-SwaggerDictionary {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [string]
        $SwaggerSpecPath,

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

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

        [Parameter(Mandatory=$false)]
        [string]
        $ModuleName,

        [Parameter(Mandatory=$false)]
        [Version]
        $ModuleVersion = '0.0.1',

        [Parameter(Mandatory = $false)]
        [string]
        $DefaultCommandPrefix,

        [Parameter(Mandatory = $false)]
        [switch]
        $AzureSpec,

        [Parameter(Mandatory = $false)]
        [switch]
        $DisableVersionSuffix,

        [Parameter(Mandatory = $false)]
        [hashtable]
        $PowerShellCodeGen,

        [Parameter(Mandatory = $false)]
        [PSCustomObject]
        $PSMetaJsonObject
    )
    
    Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState

    $swaggerDocObject = ConvertFrom-Json ((Get-Content $SwaggerSpecPath) -join [Environment]::NewLine) -ErrorAction Stop
    $swaggerDict = @{}

    if(-not (Get-Member -InputObject $swaggerDocObject -Name 'info')) {
        Throw $LocalizedData.InvalidSwaggerSpecification
    }

    if ($PowerShellCodeGen -and (Get-Member -InputObject $swaggerDocObject -Name 'securityDefinitions')) {
        $swaggerDict['SecurityDefinitions'] = $swaggerDocObject.securityDefinitions
        if ((Get-Member -InputObject $swaggerDocObject.securityDefinitions -Name 'azure_auth')) {
            $PowerShellCodeGen['ServiceType'] = 'azure'
        }
    }

    if ((Get-Member -InputObject $swaggerDocObject -Name 'security')) {
        $swaggerDict['Security'] = $swaggerDocObject.security
    }

    $GetSwaggerInfo_params = @{
        Info = $swaggerDocObject.info
        ModuleVersion  = $ModuleVersion
    }
    if($ModuleName)
    {
        $GetSwaggerInfo_params['ModuleName'] = $ModuleName
    }
    $swaggerDict['Info'] = Get-SwaggerInfo @GetSwaggerInfo_params
    $swaggerDict['Info']['DefaultCommandPrefix'] = $DefaultCommandPrefix

    $SwaggerParameters = @{}
    $SwaggerDefinitions = @{}
    $SwaggerPaths = @{}

    $PSMetaParametersJsonObject = $null
    if($PSMetaJsonObject) {
        if(Get-Member -InputObject $PSMetaJsonObject -Name 'parameters'){
            $PSMetaParametersJsonObject = $PSMetaJsonObject.parameters
        }
    }
    foreach($FilePath in $SwaggerSpecFilePaths) {
        $swaggerObject = ConvertFrom-Json ((Get-Content $FilePath) -join [Environment]::NewLine) -ErrorAction Stop
        if(Get-Member -InputObject $swaggerObject -Name 'parameters') {
            
            $GetSwaggerParameters_Params = @{
                Parameters = $swaggerObject.parameters
                Info = $swaggerDict['Info']
                SwaggerParameters = $swaggerParameters
                DefinitionFunctionsDetails = $DefinitionFunctionsDetails
                AzureSpec = $AzureSpec
                PSMetaParametersJsonObject = $PSMetaParametersJsonObject
            }

            Get-SwaggerParameters @GetSwaggerParameters_Params
        }

        if(Get-Member -InputObject $swaggerObject -Name 'definitions') {
            Get-SwaggerDefinitionMultiItemObject -Object $swaggerObject.definitions -SwaggerDictionary $SwaggerDefinitions
        }

        if(-not (Get-Member -InputObject $swaggerObject -Name 'paths') -or 
           -not (Get-HashtableKeyCount -Hashtable $swaggerObject.Paths.PSObject.Properties)) {
            Write-Warning -Message ($LocalizedData.SwaggerPathsMissing -f $FilePath)
        }

        Get-SwaggerPathMultiItemObject -Object $swaggerObject.paths -SwaggerDictionary $SwaggerPaths
    }

    $swaggerDict['Parameters'] = $swaggerParameters
    $swaggerDict['Definitions'] = $swaggerDefinitions
    $swaggerDict['Paths'] = $swaggerPaths

    return $swaggerDict
}

function Get-SwaggerInfo {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [PSCustomObject]
        $Info,

        [Parameter(Mandatory=$false)]
        [string]
        $ModuleName,

        [Parameter(Mandatory=$false)]
        [Version]
        $ModuleVersion = '0.0.1'
    )

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

    $infoVersion = '1-0-0'
    if((Get-Member -InputObject $Info -Name 'Version') -and $Info.Version) { 
        $infoVersion = $Info.Version
    }

    $infoTitle = $Info.title
    $CodeOutputDirectory = ''
    $infoName = ''
    $NameSpace = ''
    $codeGenFileRequired = $false
    $modelsName = 'Models'
    if(Get-Member -InputObject $Info -Name 'x-ms-code-generation-settings') {
        $prop = Test-PropertyWithAliases -InputObject $Info.'x-ms-code-generation-settings' -Aliases @('ClientName', 'Name')
        if ($prop) {
            $infoName = $Info.'x-ms-code-generation-settings'.$prop
        }

        $prop = Test-PropertyWithAliases -InputObject $Info.'x-ms-code-generation-settings' -Aliases @('OutputDirectory', 'o', 'output')
        if ($prop) {
            # When OutputDirectory is specified, we'll have to copy the code from here to the module directory later on
            $CodeOutputDirectory = $Info.'x-ms-code-generation-settings'.$prop
            if ((Test-Path -Path $CodeOutputDirectory) -and (Get-ChildItem -Path (Join-Path -Path $CodeOutputDirectory -ChildPath "*.cs") -Recurse)) {
                throw $LocalizedData.OutputDirectoryMustBeEmpty -f ($CodeOutputDirectory)
            } else {
                Write-Warning -Message ($LocalizedData.CodeDirectoryWillBeCreated -f $CodeOutputDirectory)
            }
        }

        $prop = Test-PropertyWithAliases -InputObject $Info.'x-ms-code-generation-settings' -Aliases @('ModelsName', 'mname')
        if ($prop) {
            # When ModelsName is specified, this changes the subnamespace of the models from 'Models' to whatever is specified
            $modelsName = $Info.'x-ms-code-generation-settings'.$prop
        }

        $prop = Test-PropertyWithAliases -InputObject $Info.'x-ms-code-generation-settings' -Aliases @('Namespace', 'n')
        if ($prop) {
            # When NameSpace is specified, this overrides our namespace
            $NameSpace = $Info.'x-ms-code-generation-settings'.$prop
            # Warn the user that custom namespaces are not recommended
            Write-Warning -Message $LocalizedData.CustomNamespaceNotRecommended
        }

        # When the following values are specified, the property will be overwritten by PSSwagger using a CodeGenSettings file
        foreach ($ignoredParameterAliases in $script:IgnoredAutoRestParameters) {
            $prop = Test-PropertyWithAliases -InputObject $Info.'x-ms-code-generation-settings' -Aliases $ignoredParameterAliases
            if ($prop) {
                Write-Warning -Message ($LocalizedData.AutoRestParameterIgnored -f ($prop, $Info.'x-ms-code-generation-settings'.$prop))
                $codeGenFileRequired = $true
            }
        }
    }

    if (-not $infoName) {
        # Remove special characters as info name is used as client variable name in the generated commands.
        $infoName = ($infoTitle -replace '[^a-zA-Z0-9_]','')
    }

    $Description = ''
    if((Get-Member -InputObject $Info -Name 'Description') -and $Info.Description) { 
        $Description = $Info.Description
    }

    $ProjectUri = ''
    $ContactEmail = ''
    $ContactName = ''
    if(Get-Member -InputObject $Info -Name 'Contact')
    {
        # The identifying name of the contact person/organization.
        if((Get-Member -InputObject $Info.Contact -Name 'Name') -and
            $Info.Contact.Name)
        { 
            $ContactName = $Info.Contact.Name
        }

        # The URL pointing to the contact information. MUST be in the format of a URL.
        if((Get-Member -InputObject $Info.Contact -Name 'Url') -and
            $Info.Contact.Url)
        { 
            $ProjectUri = $Info.Contact.Url
        }

        # The email address of the contact person/organization. MUST be in the format of an email address.
        if((Get-Member -InputObject $Info.Contact -Name 'Email') -and
            $Info.Contact.Email)
        { 
            $ContactEmail = $Info.Contact.Email
        }        
    }

    $LicenseUri = ''
    $LicenseName = ''
    if(Get-Member -InputObject $Info -Name 'License')
    {
        # A URL to the license used for the API. MUST be in the format of a URL.
        if((Get-Member -InputObject $Info.License -Name 'Url') -and
          $Info.License.Url)
        { 
            $LicenseUri = $Info.License.Url
        }

        # License name.
        if((Get-Member -InputObject $Info.License -Name 'Name') -and
          $Info.License.Name)
        { 
            $LicenseName = $Info.License.Name
        }
    }

    # Using the info name as module name when $ModuleName is not specified.
    # This is required for PSMeta generaration.
    if(-not $ModuleName)
    {
        $ModuleName = $infoName
    }

    if (-not $NameSpace) {
        # Default namespace supports sxs
        $NamespaceVersionSuffix = "v$("$ModuleVersion" -replace '\.','')"
        $NameSpace = "$script:PSSwaggerDefaultNamespace.$ModuleName.$NamespaceVersionSuffix"
    }

    # AutoRest generates client name with 'Client' appended to info title when a NameSpace part is same as the info name.
    if($NameSpace.Split('.', [System.StringSplitOptions]::RemoveEmptyEntries) -contains $infoName)
    {
        $infoName = $infoName + 'Client'
    }

    return @{
        InfoVersion = $infoVersion
        InfoTitle = $infoTitle
        InfoName = $infoName
        Version = $ModuleVersion
        NameSpace = $NameSpace
        ModuleName = $ModuleName
        Description = $Description
        ContactName = $ContactName
        ContactEmail = $ContactEmail
        ProjectUri = $ProjectUri
        LicenseUri = $LicenseUri
        LicenseName = $LicenseName
        CodeOutputDirectory = $CodeOutputDirectory
        CodeGenFileRequired = $codeGenFileRequired
        Models = $modelsName
    }
}

function Test-PropertyWithAliases {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [PSCustomObject]
        $InputObject,

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

    foreach ($alias in $Aliases) {
        if ((Get-Member -InputObject $InputObject -Name $alias) -and $InputObject.$alias) {
            return $alias
        }
    }

    return $null
}

function Get-SwaggerParameters {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [PSCustomObject]
        $Parameters,

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

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

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

        [Parameter(Mandatory = $false)]
        [switch]
        $AzureSpec,

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

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

    foreach($Parameter in $Parameters.PSObject.Properties.GetEnumerator()) {
        $GlobalParameterName = $Parameter.Name
        $GPJsonValueObject = $Parameter.Value

        if ($SwaggerParameters.ContainsKey($GlobalParameterName))
        {
            Write-Verbose -Message ($LocalizedData.SkippingExistingParameter -f $GlobalParameterName)
            continue
        }

        $IsParamMandatory = '$false'
        $ParameterDescription = ''
        $x_ms_parameter_location = 'client'
        $x_ms_parameter_grouping = ''
        $ConstantValue = ''
        $ReadOnlyGlobalParameter = $false
        if ((Get-Member -InputObject $GPJsonValueObject -Name 'x-ms-client-name') -and $GPJsonValueObject.'x-ms-client-name') {
            $parameterName = Get-PascalCasedString -Name $GPJsonValueObject.'x-ms-client-name'
        } elseif ((Get-Member -InputObject $GPJsonValueObject -Name 'Name') -and $GPJsonValueObject.Name)
        {
            $parameterName = Get-PascalCasedString -Name $GPJsonValueObject.Name
        }

        if(Get-Member -InputObject $GPJsonValueObject -Name 'x-ms-parameter-location')
        {
            $x_ms_parameter_location = $GPJsonValueObject.'x-ms-parameter-location'
        }

        if ($AzureSpec) {
            # Some global parameters have constant values not expressed in the Swagger spec when dealing with Azure
            if ('subscriptionId' -eq $parameterName) {
                # See PSSwagger.Constants.ps1 $functionBodyStr for this variable name
                $ConstantValue = '`$subscriptionId'
            } elseif ('apiversion' -eq $parameterName) {
                $ReadOnlyGlobalParameter = $true
            }       
        }

        if((Get-Member -InputObject $GPJsonValueObject -Name 'Required') -and
            $GPJsonValueObject.Required)
        {
            $IsParamMandatory = '$true'
        }

        if ((Get-Member -InputObject $GPJsonValueObject -Name 'Description') -and
            $GPJsonValueObject.Description)
        {
            $ParameterDescription = $GPJsonValueObject.Description
        }

        $GetParamTypeParams = @{
            ParameterJsonObject = $GPJsonValueObject
            ModelsNameSpace = "$($Info.NameSpace).$($Info.Models)"
            ParameterName = $parameterName
            DefinitionFunctionsDetails = $DefinitionFunctionsDetails
        }

        $paramTypeObject = Get-ParamType @GetParamTypeParams

        if (Get-Member -InputObject $GPJsonValueObject -Name 'x-ms-parameter-grouping') {
            $groupObject = $GPJsonValueObject.'x-ms-parameter-grouping'
            if (Get-Member -InputObject $groupObject -Name 'name') {
                $parsedName = Get-ParameterGroupName -RawName $groupObject.name
            } elseif (Get-Member -InputObject $groupObject -Name 'postfix') {
                $parsedName = Get-ParameterGroupName -OperationId $OperationId -Postfix $groupObject.postfix
            } else {
                $parsedName = Get-ParameterGroupName -OperationId $OperationId
            }

            $x_ms_parameter_grouping = $parsedName
        }

        $FlattenOnPSCmdlet = $false
        if($PSMetaParametersJsonObject -and 
          (Get-Member -InputObject $PSMetaParametersJsonObject -Name $GlobalParameterName) -and
          (Get-Member -InputObject $PSMetaParametersJsonObject.$GlobalParameterName -Name 'x-ps-parameter-info') -and
          (Get-Member -InputObject $PSMetaParametersJsonObject.$GlobalParameterName.'x-ps-parameter-info' -Name 'flatten')) {
            $FlattenOnPSCmdlet = $PSMetaParametersJsonObject.$GlobalParameterName.'x-ps-parameter-info'.'flatten'
        }

        $SwaggerParameters[$GlobalParameterName] = @{
            Name = $parameterName
            Type = $paramTypeObject.ParamType
            ValidateSet = $paramTypeObject.ValidateSetString
            Mandatory = $IsParamMandatory
            Description = $ParameterDescription
            IsParameter = $paramTypeObject.IsParameter
            x_ms_parameter_location = $x_ms_parameter_location
            x_ms_parameter_grouping = $x_ms_parameter_grouping
            ConstantValue = $ConstantValue
            ReadOnlyGlobalParameter = $ReadOnlyGlobalParameter
            FlattenOnPSCmdlet = $FlattenOnPSCmdlet
        }
    }
}

function Get-SwaggerPathMultiItemObject {
    param(
        [Parameter(Mandatory=$true)]
        [PSCustomObject]
        $Object,

        [Parameter(Mandatory=$true)]
        [PSCustomObject]
        $SwaggerDictionary
    )

    $Object.PSObject.Properties | ForEach-Object {
        if($SwaggerDictionary.ContainsKey($_.name)) {
            Write-Verbose -Message ($LocalizedData.SkippingExistingKeyFromSwaggerMultiItemObject -f $_)
        } else {
            $SwaggerDictionary[$_.name] = $_
        }
    }
}

function Get-SwaggerDefinitionMultiItemObject {
    param(
        [Parameter(Mandatory=$true)]
        [PSCustomObject]
        $Object,

        [Parameter(Mandatory=$true)]
        [PSCustomObject]
        $SwaggerDictionary
    )

    $Object.PSObject.Properties | ForEach-Object {
        $ModelName = Get-CSharpModelName -Name $_.Name
        if($SwaggerDictionary.ContainsKey($ModelName)) {
            Write-Verbose -Message ($LocalizedData.SkippingExistingKeyFromSwaggerMultiItemObject -f $ModelName)
        } else {
            $SwaggerDictionary[$ModelName] = $_
        }
    }
}

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

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

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

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

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

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

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

    $index = (Get-HashtableKeyCount -Hashtable $ParametersTable)
    $operationId = $null
    if(Get-Member -InputObject $JsonPathItemObject -Name 'OperationId'){
        $operationId = $JsonPathItemObject.operationId
    }
    
    $JsonPathItemObject.parameters | ForEach-Object {
        $AllParameterDetails = Get-ParameterDetails -ParameterJsonObject $_ `
                                                    -SwaggerDict $SwaggerDict `
                                                    -DefinitionFunctionsDetails $DefinitionFunctionsDetails `
                                                    -OperationId $operationId `
                                                    -ParameterGroupCache $ParameterGroupCache `
                                                    -PSMetaParametersJsonObject $PSMetaParametersJsonObject
        foreach ($ParameterDetails in $AllParameterDetails) {
            if($ParameterDetails -and ($ParameterDetails.ContainsKey('x_ms_parameter_grouping_group') -or $ParameterDetails.Type))
            {
                $ParametersTable[$index] = $ParameterDetails
                $index = $index + 1            
            }
        }
    }
}

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

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

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

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

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

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

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

    $NameSpace = $SwaggerDict['Info'].NameSpace
    $Models = $SwaggerDict['Info'].Models
    $DefinitionTypeNamePrefix = "$Namespace.$Models."
    $parameterName = ''
    if ((Get-Member -InputObject $ParameterJsonObject -Name 'x-ms-client-name') -and $ParameterJsonObject.'x-ms-client-name') {
        $parameterName = Get-PascalCasedString -Name $ParameterJsonObject.'x-ms-client-name'
    } elseif ((Get-Member -InputObject $ParameterJsonObject -Name 'Name') -and $ParameterJsonObject.Name)
    {
        $parameterName = Get-PascalCasedString -Name $ParameterJsonObject.Name
    }
   
    $GetParamTypeParameters = @{
        ParameterJsonObject = $ParameterJsonObject
        ModelsNamespace = "$NameSpace.$Models"
        ParameterName = $parameterName
        DefinitionFunctionsDetails = $DefinitionFunctionsDetails
        SwaggerDict = $SwaggerDict
    }

    $paramTypeObject = Get-ParamType @GetParamTypeParameters

    # Swagger Path Operations can be defined with reference to the global method based parameters.
    # Add the method based global parameters as a function parameter.
    $AllParameterDetailsArrayTemp = @()
    $x_ms_parameter_grouping = ''
    if($paramTypeObject.GlobalParameterDetails)
    {
        $ParameterDetails = $paramTypeObject.GlobalParameterDetails
        $x_ms_parameter_grouping = $ParameterDetails.'x_ms_parameter_grouping'
    }
    else
    {
        $IsParamMandatory = '$false'
        $ParameterDescription = ''
        $x_ms_parameter_location = 'method'

        if ((Get-Member -InputObject $ParameterJsonObject -Name 'Required') -and 
            $ParameterJsonObject.Required)
        {
            $IsParamMandatory = '$true'
        }

        if ((Get-Member -InputObject $ParameterJsonObject -Name 'Description') -and 
            $ParameterJsonObject.Description)
        {
            $ParameterDescription = $ParameterJsonObject.Description
        }

        if ($OperationId -and (Get-Member -InputObject $ParameterJsonObject -Name 'x-ms-parameter-grouping')) {
            $groupObject = $ParameterJsonObject.'x-ms-parameter-grouping'
            if (Get-Member -InputObject $groupObject -Name 'name') {
                $parsedName = Get-ParameterGroupName -RawName $groupObject.name
            } elseif (Get-Member -InputObject $groupObject -Name 'postfix') {
                $parsedName = Get-ParameterGroupName -OperationId $OperationId -Postfix $groupObject.postfix
            } else {
                $parsedName = Get-ParameterGroupName -OperationId $OperationId
            }

            $x_ms_parameter_grouping = $parsedName
        }

        $FlattenOnPSCmdlet = $false
        if($PSMetaParametersJsonObject -and 
          (Get-Member -InputObject $PSMetaParametersJsonObject -Name $parameterName) -and
          (Get-Member -InputObject $PSMetaParametersJsonObject.$parameterName -Name 'x-ps-parameter-info') -and
          (Get-Member -InputObject $PSMetaParametersJsonObject.$parameterName.'x-ps-parameter-info' -Name 'flatten')) {
            $FlattenOnPSCmdlet = $PSMetaParametersJsonObject.$parameterName.'x-ps-parameter-info'.'flatten'
        }

        $ParameterDetails = @{
            Name = $parameterName
            Type = $paramTypeObject.ParamType
            ValidateSet = $paramTypeObject.ValidateSetString
            Mandatory = $IsParamMandatory
            Description = $ParameterDescription
            IsParameter = $paramTypeObject.IsParameter
            x_ms_parameter_location = $x_ms_parameter_location
            x_ms_parameter_grouping = $x_ms_parameter_grouping
            OriginalParameterName = $ParameterJsonObject.Name
            FlattenOnPSCmdlet = $FlattenOnPSCmdlet
        }
    }

    if ((Get-Member -InputObject $ParameterJsonObject -Name 'x-ms-client-flatten') -and $ParameterJsonObject.'x-ms-client-flatten') {
        $referenceTypeName = $ParameterDetails.Type.Replace($DefinitionTypeNamePrefix, '')
        # If the parameter should be flattened, return an array of parameter detail objects for each parameter of the referenced definition
        Write-Verbose -Message ($LocalizedData.FlatteningParameterType -f ($parameterName, $referenceTypeName))
        $AllParameterDetails = @{}
        Expand-Parameters -ReferenceTypeName $referenceTypeName -DefinitionFunctionsDetails $DefinitionFunctionsDetails -AllParameterDetails $AllParameterDetails
        foreach ($expandedParameterDetail in $AllParameterDetails.GetEnumerator()) {
            Write-Verbose -Message ($LocalizedData.ParameterExpandedTo -f ($parameterName, $expandedParameterDetail.Key))
            $AllParameterDetailsArrayTemp += $expandedParameterDetail.Value
        }
    } else {
        # If the parameter shouldn't be flattened, just return the original parameter detail object
        $AllParameterDetailsArrayTemp += $ParameterDetails
    }

    # Loop through the parameters in case they belong to different groups after being expanded
    $AllParameterDetailsArray = @()
    foreach ($expandedParameterDetail in $AllParameterDetailsArrayTemp) {
        # The parent parameter object, wherever it is, set a grouping name
        if ($x_ms_parameter_grouping) {
            $expandedParameterDetail.'x_ms_parameter_grouping' = $x_ms_parameter_grouping
            # An empty parameter details object is created that contains all known parameters in this group
            if ($ParameterGroupCache.ContainsKey($x_ms_parameter_grouping)) {
                $ParameterDetails = $ParameterGroupCache[$x_ms_parameter_grouping]
            } else {
                $ParameterDetails = @{
                    Name = $x_ms_parameter_grouping
                    x_ms_parameter_grouping_group = @{}
                    IsParameter = $true
                }
            }

            if (-not $ParameterDetails.'x_ms_parameter_grouping_group'.ContainsKey($expandedParameterDetail.Name)) {
                $ParameterDetails.'x_ms_parameter_grouping_group'[$expandedParameterDetail.Name] = $expandedParameterDetail
            }

            $AllParameterDetailsArray += $ParameterDetails
            $ParameterGroupCache[$x_ms_parameter_grouping] = $ParameterDetails
        } else {
            $AllParameterDetailsArray += $expandedParameterDetail
        }
    }

    # Properties of ParameterDetails object
    # .x_ms_parameter_grouping - string - non-empty if this is part of a group, contains the group's parsed name (should be the C# Type name)
    # .x_ms_parameter_grouping_group - hashtable - table of parameter names to ParameterDetails, indicates this ParameterDetails object is a grouping
    return $AllParameterDetailsArray
}

function Expand-Parameters {
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory=$true)]
        [string]
        $ReferenceTypeName,

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

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

    # Expand unexpanded x-ms-client-flatten
    # Leave it unexpanded afterwards
    if ($DefinitionFunctionsDetails[$ReferenceTypeName].ContainsKey('Unexpanded_x_ms_client_flatten_DefinitionNames') -and
        ($DefinitionFunctionsDetails[$ReferenceTypeName].'Unexpanded_x_ms_client_flatten_DefinitionNames'.Count -gt 0)) {
        foreach ($unexpandedDefinitionName in $DefinitionFunctionsDetails[$ReferenceTypeName].'Unexpanded_x_ms_client_flatten_DefinitionNames') {
            if ($DefinitionFunctionsDetails[$unexpandedDefinitionName].ContainsKey('ExpandedParameters') -and -not $DefinitionFunctionsDetails[$unexpandedDefinitionName].ExpandedParameters) {
                Expand-Parameters -ReferenceTypeName $unexpandedDefinitionName -DefinitionFunctionsDetails $DefinitionFunctionsDetails -AllParameterDetails $AllParameterDetails
            }

            Flatten-ParameterTable -ReferenceTypeName $unexpandedDefinitionName -DefinitionFunctionsDetails $DefinitionFunctionsDetails -AllParameterDetails $AllParameterDetails
        }
    }

    Flatten-ParameterTable -ReferenceTypeName $ReferenceTypeName -DefinitionFunctionsDetails $DefinitionFunctionsDetails -AllParameterDetails $AllParameterDetails
}

<#
.DESCRIPTION
   Flattens the given type's parameter table into cmdlet parameters.
#>

function Flatten-ParameterTable {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [string]
        $ReferenceTypeName,

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

        [Parameter(Mandatory=$true)]
        [hashtable]
        $AllParameterDetails
    )
    foreach ($parameterEntry in $DefinitionFunctionsDetails[$ReferenceTypeName]['ParametersTable'].GetEnumerator()) {
        if ($AllParameterDetails.ContainsKey($parameterEntry.Key)) {
            throw $LocalizedData.DuplicateExpandedProperty -f ($parameterEntry.Key)
        }

        $AllParameterDetails[$parameterEntry.Key] = Clone-ParameterDetail -ParameterDetail $parameterEntry.Value -OtherEntries @{'IsParameter'=$true}
    }
}

<#
.DESCRIPTION
   Clones a given parameter detail object by shallow copying all properties. Optionally adds additional entries.
#>

function Clone-ParameterDetail {
    param(
        [Parameter(Mandatory=$true)]
        [hashtable]
        $ParameterDetail,

        [Parameter(Mandatory=$false)]
        [hashtable]
        $OtherEntries
    )

    $clonedParameterDetail = @{}
    foreach ($kvp in $ParameterDetail.GetEnumerator()) {
        $clonedParameterDetail[$kvp.Key] = $kvp.Value
    }

    foreach ($kvp in $OtherEntries.GetEnumerator()) {
        $clonedParameterDetail[$kvp.Key] = $kvp.Value
    }

    return $clonedParameterDetail
}

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

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

        [Parameter(Mandatory=$true)]
        [string]
        [AllowEmptyString()]
        $ParameterName,

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

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

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

    $DefinitionTypeNamePrefix = "$ModelsNamespace."
    $paramType = ""
    $ValidateSetString = $null
    $isParameter = $true
    $GlobalParameterDetails = $null
    $ReferenceTypeName = $null
    if((Get-Member -InputObject $ParameterJsonObject -Name 'Type') -and $ParameterJsonObject.Type)
    {
        $paramType = $ParameterJsonObject.Type

        # Use the format as parameter type if that is available as a type in PowerShell
        if ((Get-Member -InputObject $ParameterJsonObject -Name 'Format') -and 
            $ParameterJsonObject.Format -and 
            ($null -ne ($ParameterJsonObject.Format -as [Type]))) 
        {
            $paramType = $ParameterJsonObject.Format
        }
        elseif (($ParameterJsonObject.Type -eq 'array') -and
                (Get-Member -InputObject $ParameterJsonObject -Name 'Items') -and 
                $ParameterJsonObject.Items)
        {
            if((Get-Member -InputObject $ParameterJsonObject.Items -Name '$ref') -and 
                $ParameterJsonObject.Items.'$ref')
            {
                $ReferenceTypeValue = $ParameterJsonObject.Items.'$ref'
                $ReferenceTypeName = Get-CSharpModelName -Name $ReferenceTypeValue.Substring( $( $ReferenceTypeValue.LastIndexOf('/') ) + 1 )
                $paramType = $DefinitionTypeNamePrefix + "$ReferenceTypeName[]"
            }
            elseif((Get-Member -InputObject $ParameterJsonObject.Items -Name 'Type') -and $ParameterJsonObject.Items.Type)
            {
                $ReferenceTypeName = Get-PSTypeFromSwaggerObject -JsonObject $ParameterJsonObject.Items
                $paramType = "$($ReferenceTypeName)[]"
            }
        }
        elseif ((Get-Member -InputObject $ParameterJsonObject -Name 'AdditionalProperties') -and 
                $ParameterJsonObject.AdditionalProperties)
        {
            # Dictionary
            if($ParameterJsonObject.Type -eq 'object') {
                if((Get-Member -InputObject $ParameterJsonObject.AdditionalProperties -Name 'Type') -and
                $ParameterJsonObject.AdditionalProperties.Type) {
                    $AdditionalPropertiesType = Get-PSTypeFromSwaggerObject -JsonObject $ParameterJsonObject.AdditionalProperties
                    $paramType = "System.Collections.Generic.Dictionary[[$AdditionalPropertiesType],[$AdditionalPropertiesType]]"
                }
                elseif((Get-Member -InputObject $ParameterJsonObject.AdditionalProperties -Name '$ref') -and
                    $ParameterJsonObject.AdditionalProperties.'$ref')
                {
                    $ReferenceTypeValue = $ParameterJsonObject.AdditionalProperties.'$ref'
                    $ReferenceTypeName = Get-CSharpModelName -Name $ReferenceTypeValue.Substring( $( $ReferenceTypeValue.LastIndexOf('/') ) + 1 )
                    $AdditionalPropertiesType = $DefinitionTypeNamePrefix + "$ReferenceTypeName"
                    $paramType = "System.Collections.Generic.Dictionary[[string],[$AdditionalPropertiesType]]"
                }
                else {
                    $Message = $LocalizedData.UnsupportedSwaggerProperties -f ('ParameterJsonObject', $($ParameterJsonObject | Out-String))
                    Write-Warning -Message $Message
                }
            }
            else {
                $Message = $LocalizedData.UnsupportedSwaggerProperties -f ('ParameterJsonObject', $($ParameterJsonObject | Out-String))
                Write-Warning -Message $Message
            }
        }
    }
    elseif($parameterName -eq 'Properties' -and
           (Get-Member -InputObject $ParameterJsonObject -Name 'x-ms-client-flatten') -and 
           ($ParameterJsonObject.'x-ms-client-flatten') )
    {
        # 'x-ms-client-flatten' extension allows to flatten deeply nested properties into the current definition.
        # Users often provide feedback that they don't want to create multiple levels of properties to be able to use an operation.
        # By applying the x-ms-client-flatten extension, you move the inner properties to the top level of your definition.

        $ReferenceParameterValue = $ParameterJsonObject.'$ref'
        $x_ms_client_flatten_ReferenceTypeName = Get-CSharpModelName -Name $ReferenceParameterValue.Substring( $( $ReferenceParameterValue.LastIndexOf('/') ) + 1 )
        Set-TypeUsedAsClientFlatten -ReferenceTypeName $x_ms_client_flatten_ReferenceTypeName -DefinitionFunctionsDetails $DefinitionFunctionsDetails
    }
    elseif ( (Get-Member -InputObject $ParameterJsonObject -Name '$ref') -and ($ParameterJsonObject.'$ref') )
    {
        <#
            Currently supported parameter references:
                #/parameters/<PARAMETERNAME> or #../../<Parameters>.Json/parameters/<PARAMETERNAME>
                #/definitions/<DEFINITIONNAME> or #../../<Definitions>.Json/definitions/<DEFINITIONNAME>
        #>

        $ReferenceParameterValue = $ParameterJsonObject.'$ref'
        $ReferenceParts = $ReferenceParameterValue -split '/' | ForEach-Object { if($_.Trim()){ $_.Trim() } }
        if($ReferenceParts.Count -ge 3)
        {
            if($ReferenceParts[-2] -eq 'Parameters')
            {
                # #<...>/parameters/<PARAMETERNAME>
                $GlobalParameters = $SwaggerDict['Parameters']
                $GlobalParamDetails = $GlobalParameters[$ReferenceParts[-1]]

                # Get the definition name of the global parameter so that 'New-<DefinitionName>Object' can be generated.
                if($GlobalParamDetails.Type -and $GlobalParamDetails.Type -match '[.]') {
                    $ReferenceTypeName = ($GlobalParamDetails.Type -split '[.]')[-1]
                }

                # Valid values for this extension are: "client", "method".
                $GlobalParameterDetails = $GlobalParamDetails
                if(-not ($GlobalParamDetails -and 
                   $GlobalParamDetails.ContainsKey('x_ms_parameter_location') -and 
                   ($GlobalParamDetails.x_ms_parameter_location -eq 'method')))
                {
                    $isParameter = $false
                }
            }
            elseif($ReferenceParts[-2] -eq 'Definitions')
            {
                # #<...>/definitions/<DEFINITIONNAME>
                $ReferenceTypeName = Get-CSharpModelName -Name $ReferenceParts[-1]
                $paramType = $DefinitionTypeNamePrefix + $ReferenceTypeName
            }
        }
    }
    elseif ((Get-Member -InputObject $ParameterJsonObject -Name 'Schema') -and ($ParameterJsonObject.Schema) -and
            (Get-Member -InputObject $ParameterJsonObject.Schema -Name '$ref') -and ($ParameterJsonObject.Schema.'$ref') )
    {
        $ReferenceParameterValue = $ParameterJsonObject.Schema.'$ref'
        $ReferenceTypeName = Get-CSharpModelName -Name $ReferenceParameterValue.Substring( $( $ReferenceParameterValue.LastIndexOf('/') ) + 1 )
        $paramType = $DefinitionTypeNamePrefix + $ReferenceTypeName

        if((Get-Member -InputObject $ParameterJsonObject -Name 'x-ms-client-flatten') -and
           ($ParameterJsonObject.'x-ms-client-flatten'))
        {
            Set-TypeUsedAsClientFlatten -ReferenceTypeName $ReferenceTypeName -DefinitionFunctionsDetails $DefinitionFunctionsDetails
            
            # Assigning $null to $ReferenceTypeName so that this referenced definition is not set as UsedAsPathOperationInputType
            $ReferenceTypeName = $null
        }
    }
    else 
    {
        $paramType = 'object'
    }

    $paramType = Get-PSTypeFromSwaggerObject -ParameterType $paramType
    if ((Get-Member -InputObject $ParameterJsonObject -Name 'Enum') -and $ParameterJsonObject.Enum)
    {
        # AutoRest doesn't generate a parameter on Async method for the path operation
        # when a parameter is required and has singly enum value.
        # Also, no enum type gets generated by AutoRest.
        if(($ParameterJsonObject.Enum.Count -eq 1) -and
           (Get-Member -InputObject $ParameterJsonObject -Name 'Required') -and 
           $ParameterJsonObject.Required -eq 'true')
        {
            $paramType = ""
        }
        elseif((Get-Member -InputObject $ParameterJsonObject -Name 'x-ms-enum') -and 
            $ParameterJsonObject.'x-ms-enum' -and 
            ($ParameterJsonObject.'x-ms-enum'.modelAsString -eq $false))
        {
            $paramType = $DefinitionTypeNamePrefix + (Get-CSharpModelName -Name $ParameterJsonObject.'x-ms-enum'.Name)
        }
        else
        {
            $ValidateSet = $ParameterJsonObject.Enum | ForEach-Object {$_ -replace "'","''"}
            $ValidateSetString = "'$($ValidateSet -join "', '")'"
        }
    }
    
    if ($ReferenceTypeName) {
        $ReferencedDefinitionDetails = @{}
        if($DefinitionFunctionsDetails.ContainsKey($ReferenceTypeName)) {
            $ReferencedDefinitionDetails = $DefinitionFunctionsDetails[$ReferenceTypeName]
        }
        $ReferencedDefinitionDetails['UsedAsPathOperationInputType'] = $true
    }

    if($paramType -and 
       (-not $paramType.Contains($DefinitionTypeNamePrefix)) -and
       ($null -eq ($paramType -as [Type])))
    {
        Write-Warning -Message ($LocalizedData.InvalidPathParameterType -f $paramType, $ParameterName)
    }

    return @{
        ParamType = $paramType
        ValidateSetString = $ValidateSetString
        IsParameter = $isParameter
        GlobalParameterDetails = $GlobalParameterDetails
    }
}

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

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

    $ReferencedFunctionDetails = @{}
    if($DefinitionFunctionsDetails.ContainsKey($ReferenceTypeName))
    {
        $ReferencedFunctionDetails = $DefinitionFunctionsDetails[$ReferenceTypeName]
    }
    else
    {
        $ReferencedFunctionDetails['Name'] = $ReferenceTypeName 
    }

    $ReferencedFunctionDetails['IsUsedAs_x_ms_client_flatten'] = $true

    $DefinitionFunctionsDetails[$ReferenceTypeName] = $ReferencedFunctionDetails
}

function Get-PSTypeFromSwaggerObject
{
    param(
        [Parameter(Mandatory=$false)]
        [AllowEmptyString()]
        [string]
        $ParameterType,

        [Parameter(Mandatory=$false)]
        [PSObject]
        $JsonObject
    )

    $ParameterFormat = $null

    if($JsonObject) {
        if((Get-Member -InputObject $JsonObject -Name 'Type') -and $JsonObject.Type)
        {
            $ParameterType = $JsonObject.Type
        }

        if((Get-Member -InputObject $JsonObject -Name 'Format') -and
        $JsonObject.Format -and ($null -ne ($JsonObject.Format -as [Type])))
        {
            $ParameterFormat = $JsonObject.Format
        }
    }
    
    switch ($ParameterType) {
        'Boolean' {
            $ParameterType = 'switch'
            break
        }

        'Integer' {
            if($ParameterFormat) {
                $ParameterType = $ParameterFormat
            }
            else {
                $ParameterType = 'int64'
            }
            break
        }

        'Number' {
            if($ParameterFormat) {
                $ParameterType = $ParameterFormat
            }
            else {
                $ParameterType = 'double'
            }
            break
        }
    }

    return $ParameterType
}

function Get-SingularizedValue
{
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [string]
        $Name
    )
    
    if($script:PluralizationService)
    {
        $Name = $script:PluralizationService.Singularize($Name)
    }

    return Get-PascalCasedString -Name $Name
}

function Get-PathCommandName
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory=$true)]
        [string]
        $OperationId
    )

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

    $opId = $OperationId

    if ($script:CmdVerbTrie -eq $null) {
        $script:CmdVerbTrie = New-Trie
        $script:PSCommandVerbMap.GetEnumerator() | ForEach-Object {
            $script:CmdVerbTrie = Add-WordToTrie -Word $_.Name -Trie $script:CmdVerbTrie
        }

        # Add approved verbs to Command Verb Trie
        Get-Verb | ForEach-Object {
            $script:CmdVerbTrie = Add-WordToTrie -Word $_.Verb -Trie $script:CmdVerbTrie
        }        
    }

    $currentTriePtr = $script:CmdVerbTrie
    
    $opIdValues = $opId  -split "_",2
    
    if($opIdValues -and ($opIdValues.Count -eq 2)) {
        $cmdNoun = (Get-SingularizedValue -Name $opIdValues[0])
        $cmdVerb = $opIdValues[1]
    }
    else {
        # OperationId can be specified without '_' (Underscore), Verb will retrieved by the below logic for non-approved verbs.
        $cmdNoun = ''
        $cmdVerb = Get-SingularizedValue -Name $opId
    }

    if (-not (Get-Verb -Verb $cmdVerb))
    {
        $UnapprovedVerb = $cmdVerb
        $message = $LocalizedData.UnapprovedVerb -f ($UnapprovedVerb)
        Write-Verbose $message
        
        if ($script:PSCommandVerbMap.ContainsKey($cmdVerb))
        {
            # This condition happens when there aren't any suffixes
            $cmdVerb = $script:PSCommandVerbMap[$cmdVerb] -Split ',' | ForEach-Object { if($_.Trim()){ $_.Trim() } }
            $cmdVerb | ForEach-Object {
                $message = $LocalizedData.ReplacedVerb -f ($_, $UnapprovedVerb)
                Write-Verbose -Message $message
            }
        }
        else
        {
            # This condition happens in cases like: CreateSuffix, CreateOrUpdateSuffix
            $longestVerbMatch = $null
            $currentVerbCandidate = ''
            $firstWord = ''
            $firstWordStarted = $false
            $buildFirstWord = $false
            $firstWordEnd = -1
            $verbMatchEnd = -1
            for($i = 0; $i -lt $UnapprovedVerb.Length; $i++) {
                # Add the start condition of the first word so that the end condition is easier
                if (-not $firstWordStarted) {
                    $firstWordStarted = $true
                    $buildFirstWord = $true
                } elseif ($buildFirstWord -and ([int]$UnapprovedVerb[$i] -ge 65) -and ([int]$UnapprovedVerb[$i] -le 90)) {
                    # Stop building the first word when we encounter another capital letter
                    $buildFirstWord = $false
                    $firstWordEnd = $i
                }

                if ($buildFirstWord) {
                    $firstWord += $UnapprovedVerb[$i]
                }

                if ($currentTriePtr) {
                    # If we're still running along the trie just fine, keep checking the next letter
                    $currentVerbCandidate += $UnapprovedVerb[$i]
                    $currentTriePtr = Test-Trie -Trie $currentTriePtr -Letter $UnapprovedVerb[$i]
                    if ($currentTriePtr -and (Test-TrieLeaf -Trie $currentTriePtr)) {
                        # The latest verb match is also the longest verb match
                        $longestVerbMatch = $currentVerbCandidate
                        $verbMatchEnd = $i+1
                    }
                }
            }

            if ($longestVerbMatch) {
                $beginningOfSuffix = $verbMatchEnd
                $cmdVerb = $longestVerbMatch
            } else {
                $beginningOfSuffix = $firstWordEnd
                $cmdVerb = $firstWord
            }

            if ($script:PSCommandVerbMap.ContainsKey($cmdVerb)) { 
                $cmdVerb = $script:PSCommandVerbMap[$cmdVerb] -Split ',' | ForEach-Object { if($_.Trim()){ $_.Trim() } }
            }

            if (-1 -ne $beginningOfSuffix) {
                # This is still empty when a verb match is found that is the entire string, but it might not be worth checking for that case and skipping the below operation
                $cmdNounSuffix = $UnapprovedVerb.Substring($beginningOfSuffix)
                # Add command noun suffix only when the current noun doesn't contain it or vice-versa.
                if(-not $cmdNoun -or (($cmdNoun -notmatch $cmdNounSuffix) -and ($cmdNounSuffix -notmatch $cmdNoun))) {
                    $cmdNoun = $cmdNoun + (Get-PascalCasedString -Name $UnapprovedVerb.Substring($beginningOfSuffix))
                } elseif($cmdNounSuffix -match $cmdNoun) {
                    $cmdNoun = $cmdNounSuffix
                }
            }
        }
    }

    # Singularize command noun
    if($cmdNoun) {
        $cmdNoun = Get-SingularizedValue -Name $cmdNoun
    }

    $cmdletInfos = $cmdVerb | ForEach-Object {
        $Verb = Get-PascalCasedString -Name $_
        if($cmdNoun){
            $CommandName = "$Verb-$cmdNoun"
        } else {
            $CommandName = Get-SingularizedValue -Name $Verb
        }
        $cmdletInfo = @{}
        $cmdletInfo['name'] = $CommandName
        $cmdletInfo
        Write-Verbose -Message ($LocalizedData.UsingCmdletNameForSwaggerPathOperation -f ($CommandName, $OperationId))
    }

    return $cmdletInfos
}

function Get-PathFunctionBody
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory=$true)]
        [PSCustomObject[]]
        $ParameterSetDetails,

        [Parameter(Mandatory=$true)]
        [string]
        [AllowEmptyString()]
        $ODataExpressionBlock,

        [Parameter(Mandatory=$true)]
        [string]
        [AllowEmptyString()]
        $ParameterGroupsExpressionBlock,

        [Parameter(Mandatory=$true)]
        [string]
        [AllowEmptyString()]
        $GlobalParameterBlock,

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

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

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

        [Parameter(Mandatory=$true)]
        [string]
        [AllowEmptyString()]
        $OverrideBaseUriBlock,
        
        [Parameter(Mandatory=$true)]
        [PSCustomObject]
        $FlattenedParametersOnPSCmdlet,

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

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

    $outputTypeBlock = $null
    $Info = $swaggerDict['Info']
    $DefinitionList = $swaggerDict['Definitions']
    $UseAzureCsharpGenerator = $SwaggerMetaDict['UseAzureCsharpGenerator']
    $infoVersion = $Info['infoVersion']
    $modulePostfix = $Info['infoName']
    $clientName = '$' + $modulePostfix
    $NameSpace = $info.namespace
    $fullModuleName = $Namespace + '.' + $modulePostfix
    $apiVersion = $null
    $SubscriptionId = $null
    $BaseUri = $null
    $GetServiceCredentialStr = ''
    $AdvancedFunctionEndCodeBlock = ''
    $GetServiceCredentialStr = 'Get-AzServiceCredential'

    # Expanding again expands $clientName
    $GlobalParameterBlock = $executionContext.InvokeCommand.ExpandString($GlobalParameterBlock)
    
    $parameterSetBasedMethodStr = ''
    foreach ($parameterSetDetail in $ParameterSetDetails) {
        # Responses isn't actually used right now, but keeping this when we need to handle responses per parameter set
        $Responses = $parameterSetDetail.Responses
        $operationId = $parameterSetDetail.OperationId
        $methodName = $parameterSetDetail.MethodName
        $operations = $parameterSetDetail.Operations
        $ParamList = $parameterSetDetail.ExpandedParamList
        $Cmdlet = ''
        if ($parameterSetDetail.ContainsKey('Cmdlet') -and $parameterSetDetail.Cmdlet) {
            $Cmdlet = $parameterSetDetail.Cmdlet
        }

        $CmdletArgs = ''
        if ($parameterSetDetail.ContainsKey('CmdletArgs') -and $parameterSetDetail.CmdletArgs) {
            $CmdletArgs = $parameterSetDetail.CmdletArgs
        }

        $CmdletParameter = ''
        if ($parameterSetDetail.ContainsKey('CmdletParameter') -and $parameterSetDetail.CmdletParameter) {
            $CmdletParameter = $parameterSetDetail.CmdletParameter
        }

        if ($Responses) {
            $responseBodyParams = @{
                                    responses = $Responses.PSObject.Properties
                                    namespace = $Namespace
                                    definitionList = $DefinitionList
                                    Models = $Info.Models
                                }

            $responseBody, $currentOutputTypeBlock = Get-Response @responseBodyParams

            # For now, use the first non-empty output type
            if ((-not $outputTypeBlock) -and $currentOutputTypeBlock) {
                $outputTypeBlock = $currentOutputTypeBlock
            }
        }

        if ($methodName) {
            $methodBlock = $executionContext.InvokeCommand.ExpandString($methodBlockFunctionCall)
        } else {
            $methodBlock = $executionContext.InvokeCommand.ExpandString($methodBlockCmdletCall)
        }
        $additionalConditionStart = ''
        $additionalConditionEnd = ''
        if ($parameterSetDetail.ContainsKey('AdditionalConditions') -and $parameterSetDetail.AdditionalConditions) {
            if ($parameterSetDetail.AdditionalConditions.Count -eq 1) {
                $additionalConditionStart = " if ($($parameterSetDetail.AdditionalConditions[0])) {$([Environment]::NewLine)"
                $additionalConditionEnd = "$([Environment]::NewLine) } else { `$taskResult = `$null }"
            } elseif ($parameterSetDetail.AdditionalConditions.Count -gt 1) {
                $additionalConditionStart = " if ("
                foreach ($condition in $parameterSetDetail.AdditionalConditions) {
                    $additionalConditionStart += "($condition) -and"
                }
                $additionalConditionStart = $additionalConditionStart.Substring(0, $additionalConditionStart.Length-5)
                $additionalConditionStart = ") {$([Environment]::NewLine)"
                $additionalConditionEnd = "$([Environment]::NewLine) } else { `$taskResult = `$null }"
            }
        }

        if ($parameterSetBasedMethodStr) {
            # Add the elseif condition
            $parameterSetBasedMethodStr += $executionContext.InvokeCommand.ExpandString($parameterSetBasedMethodStrElseIfCase)
        } else {
            # Add the beginning if condition
            $parameterSetBasedMethodStr += $executionContext.InvokeCommand.ExpandString($parameterSetBasedMethodStrIfCase)
        }
    }

    # Prepare the code block for constructing the actual operation parameters which got flattened on the generated cmdlet.
    $FlattenedParametersBlock = ''
    $FlattenedParametersOnPSCmdlet.GetEnumerator() | ForEach-Object {
        $SwaggerOperationParameterName = $_.Name        
        $DefinitionDetails = $_.Value
        $FlattenedParamType = $DefinitionDetails.Name

        $FlattenedParametersList = $DefinitionDetails.ParametersTable.GetEnumerator() | ForEach-Object { $_.Name }
        $FlattenedParametersListStr = ''
        if($FlattenedParametersList) {
            $FlattenedParametersListStr = "@('$($flattenedParametersList -join "', '")')"
        }

        $FlattenedParametersBlock += $executionContext.InvokeCommand.ExpandString($constructFlattenedParameter)
    }

    $body = $executionContext.InvokeCommand.ExpandString($functionBodyStr)

    $bodyObject = @{ OutputTypeBlock = $outputTypeBlock;
                     Body = $body;
                    }

    $result = @{
        BodyObject = $bodyObject
        ParameterSetDetails = $ParameterSetDetails
    }

    return $result
}

function Test-OperationNameInDefinitionList
{
    param(
        [Parameter(Mandatory=$true)]
        [string]
        $Name,

        [Parameter(Mandatory=$true)]
        [PSCustomObject]
        $SwaggerDict
    )

    return $SwaggerDict['Definitions'].ContainsKey($Name)
}

function Get-OutputType
{
    param
    (
        [Parameter(Mandatory=$true)]
        [PSCustomObject]
        $Schema,

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

        [Parameter(Mandatory=$true)]
        [PSCustomObject]
        $DefinitionList
    )

    $outputType = ""
    if(Get-member -inputobject $schema -name '$ref')
    {
        $ref = $schema.'$ref'
        $RefParts = $ref -split '/' | ForEach-Object { if($_.Trim()){ $_.Trim() } }
        if(($RefParts.Count -ge 3) -and ($RefParts[-2] -eq 'definitions'))
        {
            $key = Get-CSharpModelName -Name $RefParts[-1]
            if ($definitionList.ContainsKey($key))
            {
                $definition = ($definitionList[$key]).Value
                if(Get-Member -InputObject $definition -name 'properties')
                {
                    $fullPathDataType = ""
                    if(Get-HashtableKeyCount -Hashtable $definition.properties.PSObject.Properties)
                    {
                        $defProperties = $definition.properties

                        # If this data type is actually a collection of another $ref
                        if(Get-member -InputObject $defProperties -Name 'value')
                        {
                            $defValue = $defProperties.value
                            $outputValueType = ""
                            
                            # Iff the value has items with $ref nested properties,
                            # this is a collection and hence we need to find the type of collection

                            if((Get-Member -InputObject $defValue -Name 'items') -and 
                                (Get-Member -InputObject $defValue.items -Name '$ref'))
                            {
                                $defRef = $defValue.items.'$ref'
                                $DefRefParts = $defRef -split '/' | ForEach-Object { if($_.Trim()){ $_.Trim() } }
                                if(($DefRefParts.Count -ge 3) -and ($DefRefParts[-2] -eq 'definitions'))
                                {
                                    $defKey = Get-CSharpModelName -Name $DefRefParts[-1]
                                    $fullPathDataType = "$ModelsNamespace.$defKey"
                                }
                                if(Get-member -InputObject $defValue -Name 'type') 
                                {
                                    $defType = $defValue.type
                                    switch ($defType) 
                                    {
                                        "array" { $outputValueType = '[]' }
                                        Default {
                                            $exceptionMessage = $LocalizedData.DataTypeNotImplemented -f ($defType, $ref)
                                            throw ([System.NotImplementedException] $exceptionMessage)
                                        }
                                    }
                                }

                                if($outputValueType -and $fullPathDataType) {$fullPathDataType = $fullPathDataType + " " + $outputValueType}
                            }
                            else
                            { # if this datatype has value, but no $ref and items
                                $fullPathDataType = "$ModelsNamespace.$key"
                            }
                        }
                        else
                        { # if this datatype is not a collection of another $ref
                            $fullPathDataType = "$ModelsNamespace.$key"
                        }
                    }

                    if($fullPathDataType)
                    {
                        $fullPathDataType = $fullPathDataType.Replace('[','').Replace(']','').Trim()
                        $outputType += $executionContext.InvokeCommand.ExpandString($outputTypeStr)
                    }
                }
            }
        }
    }

    return $outputType
}

function Get-Response
{
    param
    (
        [Parameter(Mandatory=$true)]
        [PSCustomObject]
        $Responses,
        
        [Parameter(Mandatory=$true)]
        [string]
        $NameSpace, 

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

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

    $outputTypeFlag = $false
    $responseBody = ""
    $outputType = ""
    $failWithDesc = ""

    $failWithDesc = ""
    $responses | ForEach-Object {
        $responseStatusValue = "'" + $_.Name + "'"
        $value = $_.Value

        switch($_.Name) {
            # Handle Success
            {200..299 -contains $_} {
                if(-not $outputTypeFlag -and (Get-member -inputobject $value -name "schema"))
                {
                    # Add the [OutputType] for the function
                    $OutputTypeParams = @{
                        "schema"  = $value.schema
                        "ModelsNamespace" = "$NameSpace.$Models"
                        "definitionList" = $definitionList
                    }

                    $outputType = Get-OutputType @OutputTypeParams
                    $outputTypeFlag = $true
                }
            }
            # Handle Client Error
            {400..499 -contains $_} {
                if($Value.description)
                {
                    $failureDescription = "Write-Error 'CLIENT ERROR: " + $value.description + "'"
                    $failWithDesc += $executionContext.InvokeCommand.ExpandString($failCase)
                }
            }
            # Handle Server Error
            {500..599 -contains $_} {
                if($Value.description)
                {
                    $failureDescription = "Write-Error 'SERVER ERROR: " + $value.description + "'"
                    $failWithDesc += $executionContext.InvokeCommand.ExpandString($failCase)
                }
            }
        }
    }

    $responseBody += $executionContext.InvokeCommand.ExpandString($responseBodySwitchCase)
    
    return $responseBody, $outputType
}

<#
 
    Get-ToolsPath
 
#>

function Get-ToolsPath {
        [string]$AutoRestToolsPath = $null

        # Note: DLLs are automatically downloaded and extracted into the folder
        # "$env:USERPROFILE/.autorest/plugins/autorest/$VERSION" if they do not
        # exist for newer versions of autorest.
        [string]$basePath = Join-Path -Path $env:USERPROFILE -ChildPath ".autorest" | Join-Path -ChildPath "plugins" | Join-Path -ChildPath "autorest"

        
        if(Test-Path $basePath) { # Try to load newer version of autorest
            $versions = @(Get-ChildItem -Directory $basePath | ForEach-Object {[System.Version]$_.Name} | Sort-Object -Descending)
            
            if($versions.Length -ne 0) {
                [string]$version = $versions[0] # Get newest
                $AutoRestToolsPath = Join-Path -Path $basePath -ChildPath $version
            }
        } else { # Fallback to old version of autorest
            $AutoRestToolsPath = Get-Command -Name 'AutoRest.exe' | 
                Select-Object -First 1 -ErrorAction Ignore | 
                    ForEach-Object {Split-Path -LiteralPath $_.Source}
        }

        if (-not ($AutoRestToolsPath))
        {
            throw $LocalizedData.AutoRestNotInPath
        }

        return $AutoRestToolsPath
}

function Get-CSharpModelName
{
    param(
        [Parameter(Mandatory=$true)]
        [string]
        $Name
    )

    if (-not $script:CSharpCodeNamerLoadAttempted) {
        $script:CSharpCodeNamerLoadAttempted = $true
        $script:CSharpCodeNamer = New-ObjectFromDependency -TypeNames @('AutoRest.CSharp.CodeNamerCs', 'AutoRest.CSharp.CSharpCodeNamer') -AssemblyName 'AutoRest.CSharp.dll' -AssemblyResolver {
            param(
                [string]
                $AssemblyName
            )
   
            [string]$AutoRestToolsPath = Get-ToolsPath

            $AssemblyFilePath = Join-Path -Path $AutoRestToolsPath -ChildPath $AssemblyName
            if(-not $AssemblyFilePath -or -not (Test-Path -LiteralPath $AssemblyFilePath -PathType Leaf))
            {
                throw ($LocalizedData.PathNotFound -f $AssemblyFilePath)
            }

            Add-Type -LiteralPath $AssemblyFilePath
        }
    }

    if($script:CSharpCodeNamer)
    {
        return $script:CSharpCodeNamer.GetTypeName($Name)
    }
    else
    {
        return $Name.Replace('[','').Replace(']','')
    }
}

<# Create an object from an external dependency with possible type name changes. Optionally resolve the external dependency using a delegate.
Input to $AssemblyResolver is $AssemblyName.
Doesn't support constructors with args currently. #>

function New-ObjectFromDependency {
    param(
        [Parameter(Mandatory=$true)]
        [string[]]
        $TypeNames,

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

        [Parameter(Mandatory=$false)]
        [System.Action[string]]
        $AssemblyResolver
    )

    if ($TypeNames.Count -gt 0) {
        $assemblyLoadAttempted = $false
        foreach ($typeName in $TypeNames) {
            if (-not ($typeName -as [Type])) {
                if (-not $assemblyLoadAttempted) {
                    if ($AssemblyResolver -ne $null) {
                        $AssemblyResolver.Invoke($AssemblyName)
                    } else {
                        $null = Add-Type -Path $AssemblyName
                    }

                    $assemblyLoadAttempted = $true
                }
            } else {
                return New-Object -TypeName $typeName
            }
        }
    }

    return $null
}

function Get-PowerShellCodeGenSettings {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [string]
        $Path,

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

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

    if($PSMetaJsonObject) {
        $swaggerObject = $PSMetaJsonObject
    }
    else {
        $swaggerObject = ConvertFrom-Json ((Get-Content $Path) -join [Environment]::NewLine) -ErrorAction Stop
    }
    if ((Get-Member -InputObject $swaggerObject -Name 'info') -and (Get-Member -InputObject $swaggerObject.'info' -Name 'x-ps-code-generation-settings')) {
        $props = Get-Member -InputObject $swaggerObject.'info'.'x-ps-code-generation-settings' -MemberType NoteProperty
        foreach ($prop in $props) {
            if (-not $CodeGenSettings.ContainsKey($prop.Name)) {                
                Write-Warning -Message ($LocalizedData.UnknownPSMetadataProperty -f ('x-ps-code-generation-settings', $prop.Name))
            } else {
                $CodeGenSettings[$prop.Name] = $swaggerObject.'info'.'x-ps-code-generation-settings'.$($prop.Name)
            }
        }
    }
}
# SIG # Begin signature block
# MIIasAYJKoZIhvcNAQcCoIIaoTCCGp0CAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUhI4H5sNjtsvBG9aGF+SO9Xhv
# uMugghWDMIIEwzCCA6ugAwIBAgITMwAAALfuAa/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
# MBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMCMGCSqGSIb3DQEJBDEWBBTC
# tUSUu72+8oP/8/qVuDfJZFUM+zBQBgorBgEEAYI3AgEMMUIwQKAWgBQAUABvAHcA
# ZQByAFMAaABlAGwAbKEmgCRodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vUG93ZXJT
# aGVsbCAwDQYJKoZIhvcNAQEBBQAEggEALPASQxd0p52LKnkCO7sgy2nGMXx1iTLq
# Lnq2JexI6jMqHGb0hDWaUIX+Ueeg2EoVFrroB1otBa+SztTrrc+Yq1700Bj+Hknu
# 4AFjXCIIYFjvnu45ybCx1yJjqiIvg/1+vLJ2gdk+g0+mDNTbRYOENTuYxagjKWHl
# BQjT1MyHe9za148CybRdQmJrzO3YonrTsR7bhyhiWCy1EFAM4uUtUxyZET3+k+NI
# u+gxwj+c12Hb6pygDoo8gh3oJkkufgE+VkWXUG7tnoYraxTzcYiA9nKFd4QyC5xt
# 57OtGvMU6QwOxdFsw8mbS9105oQ8jOn+KNHYhQaz0BUJ7hhptDJ2QqGCAigwggIk
# BgkqhkiG9w0BCQYxggIVMIICEQIBATCBjjB3MQswCQYDVQQGEwJVUzETMBEGA1UE
# CBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9z
# b2Z0IENvcnBvcmF0aW9uMSEwHwYDVQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQ
# Q0ECEzMAAAC37gGv+vDHqLsAAAAAALcwCQYFKw4DAhoFAKBdMBgGCSqGSIb3DQEJ
# AzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTE3MDgwODE3MzUyMVowIwYJ
# KoZIhvcNAQkEMRYEFH2RNVdMCpJgTtSMqc5VsF2Cni2gMA0GCSqGSIb3DQEBBQUA
# BIIBAA/MKEPgbhOU7RhcpkBRghiNas6DQqKp3S1y10W53JThziOnIHXKTSe6OwlY
# BHXi2WiwyMnwCXz6MyxDeQIeVHpqwzBWZVICFTXUqzBnKGmtsvN+QMPucKfNGnvw
# lRFUr063SrvPhwlI6JMYybzfkH6D8Qz3x2aDxXp0VXop85odqZA4N7MXh4yGFNWv
# gXi2oN2qUGJ+bnHUPVkCT7bZLzcHpdmkjkmSwBR8BeszYSn3TwEuIUQ/pA4zJzJg
# r/Udv+g+mtka7rS4+8HLuj5DUSCxwCXVFDliqemm+k+QpiITGgsZF7SqKaXOZXrN
# CMGf7BAQBGZvQIIzwOHSsrn65kk=
# SIG # End signature block