PSSwagger.psm1

#########################################################################################
#
# Copyright (c) Microsoft Corporation. All rights reserved.
#
# Licensed under the MIT license.
#
# PSSwagger Module
#
#########################################################################################
Microsoft.PowerShell.Core\Set-StrictMode -Version Latest

$SubScripts = @(
    'PSSwagger.Constants.ps1'
)
$SubScripts | ForEach-Object {. (Join-Path -Path $PSScriptRoot -ChildPath $_) -Force}

$SubModules = @(
    'SwaggerUtils.psm1',
    'Utilities.psm1',
    'Paths.psm1',
    'Definitions.psm1'
)

$SubModules | ForEach-Object {Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath $_) -Force -Scope Local -DisableNameChecking}

Microsoft.PowerShell.Utility\Import-LocalizedData  LocalizedData -filename PSSwagger.Resources.psd1

<#
.SYNOPSIS
    PowerShell command to generate the PowerShell commands for a given RESTful Web Services using Swagger/OpenAPI documents.
 
.DESCRIPTION
    PowerShell command to generate the PowerShell commands for a given RESTful Web Services using Swagger/OpenAPI documents.
 
.EXAMPLE
    PS> New-PSSwaggerModule -SpecificationUri 'https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/arm-batch/2015-12-01/swagger/BatchManagement.json' -Path 'C:\GeneratedModules\' -Name 'AzBatchManagement' -UseAzureCsharpGenerator
    Generates a PS Module for the specified SpecificationUri.
 
.EXAMPLE
    PS> New-PSSwaggerModule -SpecificationPath 'C:\SwaggerSpecs\BatchManagement.json' -Path 'C:\GeneratedModules\' -Name 'AzBatchManagement' -UseAzureCsharpGenerator
    Generates a PS Module for the specified SpecificationPath.
 
.PARAMETER SpecificationPath
    Full Path to a Swagger based JSON spec.
 
.PARAMETER SpecificationUri
    Uri to a Swagger based JSON spec.
 
.PARAMETER AssemblyFileName
    File name of the pre-compiled SDK assembly.
    This assembly along with its dependencies should be available in '.\ref\fullclr\' folder under the target module version base path ($Path\$Name\$Version\).
    If your generated module needs to work on PowerShell Core, place the coreclr assembly along with its depdencies under '.\ref\coreclr\' folder under the target module version base path ($Path\$Name\$Version\).
    For FullClr, the specified assembly should be available at "$Path\$Name\$Version\ref\fullclr\$AssemblyFileName".
    For CoreClr, the specified assembly should be available at "$Path\$Name\$Version\ref\coreclr\$AssemblyFileName".
 
.PARAMETER ClientTypeName
    Client type name in the pre-compiled SDK assembly.
    Specify if client type name is different from the value of 'Title' field from the input specification, or
    if client type namespace is different from the specified namespace in the specification.
    It is recommended to specify the fully qualified client type name.
 
.PARAMETER ModelsName
    Models name if it is different from default value 'Models'.
    It is recommended to specify the custom models name in using x-ms-code-generation-settings extension in specification.
 
.PARAMETER Path
    Full Path to a file where the commands are exported to.
 
.PARAMETER Name
    Name of the module to be generated. A folder with this name will be created in the location specified by Path parameter.
 
.PARAMETER Version
    Version of the generated PowerShell module.
 
.PARAMETER NoVersionFolder
    Switch to not create the version folder under the generated module folder.
 
.PARAMETER DefaultCommandPrefix
    Prefix value to be prepended to cmdlet noun or to cmdlet name without verb.
 
.PARAMETER Header
    Text to include as a header comment in the PSSwagger generated files.
    It also can be a path to a .txt file with the content to be added as header in the PSSwagger generated files.
 
    Supported predefined license header values:
    - NONE: Suppresses the default header.
    - MICROSOFT_MIT: Adds predefined Microsoft MIT license text with default PSSwagger code generation header content.
    - MICROSOFT_MIT_NO_VERSION: Adds predefined Microsoft MIT license text with default PSSwagger code generation header content without version.
    - MICROSOFT_MIT_NO_CODEGEN: Adds predefined Microsoft MIT license text without default PSSwagger code generation header content.
    - MICROSOFT_APACHE: Adds predefined Microsoft Apache license text with default PSSwagger code generation header content.
    - MICROSOFT_APACHE_NO_VERSION: Adds predefined Microsoft Apache license text with default PSSwagger code generation header content without version.
    - MICROSOFT_APACHE_NO_CODEGEN: Adds predefined Microsoft Apache license text without default PSSwagger code generation header content.
 
.PARAMETER NoAssembly
    Switch to disable saving the precompiled module assembly and instead enable dynamic compilation.
 
.PARAMETER PowerShellCorePath
    Path to PowerShell.exe for PowerShell Core.
    Only required if PowerShell Core not installed via MSI in the default path.
 
.PARAMETER IncludeCoreFxAssembly
    Switch to additionally compile the module's binary component for Core CLR.
 
.PARAMETER InstallToolsForAllUsers
    User wants to install local tools for all users.
   
.PARAMETER TestBuild
    Switch to disable optimizations during build of full CLR binary component.
   
.PARAMETER SymbolPath
    Path to save the generated C# code and PDB file. Defaults to $Path\symbols.
 
.PARAMETER ConfirmBootstrap
    Automatically consent to downloading nuget.exe or NuGet packages as required.
 
.PARAMETER UseAzureCsharpGenerator
    Switch to specify whether AzureCsharp code generator is required. By default, this command uses CSharp code generator.
 
.INPUTS
 
.OUTPUTS
 
.NOTES
 
.LINK
 
#>

function New-PSSwaggerModule
{
    [CmdletBinding(DefaultParameterSetName='SpecificationPath')]
    param(
        [Parameter(Mandatory = $true, ParameterSetName = 'SpecificationPath')]
        [Parameter(Mandatory = $true, ParameterSetName = 'SdkAssemblyWithSpecificationPath')]
        [string] 
        $SpecificationPath,

        [Parameter(Mandatory = $true, ParameterSetName = 'SpecificationUri')]
        [Parameter(Mandatory = $true, ParameterSetName = 'SdkAssemblyWithSpecificationUri')]
        [Uri]
        $SpecificationUri,

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

        [Parameter(Mandatory = $true, ParameterSetName = 'SdkAssemblyWithSpecificationPath')]
        [Parameter(Mandatory = $true, ParameterSetName = 'SdkAssemblyWithSpecificationUri')]
        [string]
        $AssemblyFileName,
        
        [Parameter(Mandatory = $false, ParameterSetName = 'SdkAssemblyWithSpecificationPath')]
        [Parameter(Mandatory = $false, ParameterSetName = 'SdkAssemblyWithSpecificationUri')]
        [string]
        $ClientTypeName,

        [Parameter(Mandatory = $false, ParameterSetName = 'SdkAssemblyWithSpecificationPath')]
        [Parameter(Mandatory = $false, ParameterSetName = 'SdkAssemblyWithSpecificationUri')]
        [string]
        $ModelsName,

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

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

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

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

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

        [Parameter()]
        [switch]
        $UseAzureCsharpGenerator,

        [Parameter(Mandatory = $false, ParameterSetName = 'SpecificationPath')]
        [Parameter(Mandatory = $false, ParameterSetName = 'SpecificationUri')]
        [switch]
        $NoAssembly,

        [Parameter(Mandatory = $false, ParameterSetName = 'SpecificationPath')]
        [Parameter(Mandatory = $false, ParameterSetName = 'SpecificationUri')]
        [string]
        $PowerShellCorePath,

        [Parameter(Mandatory = $false, ParameterSetName = 'SpecificationPath')]
        [Parameter(Mandatory = $false, ParameterSetName = 'SpecificationUri')]
        [switch]
        $IncludeCoreFxAssembly,

        [Parameter(Mandatory = $false, ParameterSetName = 'SpecificationPath')]
        [Parameter(Mandatory = $false, ParameterSetName = 'SpecificationUri')]
        [switch]
        $InstallToolsForAllUsers,

        [Parameter(Mandatory = $false, ParameterSetName = 'SpecificationPath')]
        [Parameter(Mandatory = $false, ParameterSetName = 'SpecificationUri')]
        [switch]
        $TestBuild,

        [Parameter(Mandatory = $false, ParameterSetName = 'SpecificationPath')]
        [Parameter(Mandatory = $false, ParameterSetName = 'SpecificationUri')]
        [string]
        $SymbolPath,

        [Parameter(Mandatory = $false, ParameterSetName = 'SpecificationPath')]
        [Parameter(Mandatory = $false, ParameterSetName = 'SpecificationUri')]
        [switch]
        $ConfirmBootstrap
    )

    if ($NoAssembly -and $IncludeCoreFxAssembly) {
        $message = $LocalizedData.ParameterSetNotAllowed -f ('IncludeCoreFxAssembly', 'NoAssembly')
        throw $message
        return
    }

    if ($NoAssembly -and $TestBuild) {
        $message = $LocalizedData.ParameterSetNotAllowed -f ('TestBuild', 'NoAssembly')
        throw $message
        return
    }

    if ($NoAssembly -and $PowerShellCorePath) {
        $message = $LocalizedData.ParameterSetNotAllowed -f ('PowerShellCorePath', 'NoAssembly')
        throw $message
        return
    }

    if ($NoAssembly -and $SymbolPath) {
        $message = $LocalizedData.ParameterSetNotAllowed -f ('SymbolPath', 'NoAssembly')
        throw $message
        return
    }
    
    $SwaggerSpecFilePaths = @()
    $AutoRestModeler = 'Swagger'
    
    if (($PSCmdlet.ParameterSetName -eq 'SpecificationUri') -or 
        ($PSCmdlet.ParameterSetName -eq 'SdkAssemblyWithSpecificationUri'))
    {
        # Ensure that if the URI is coming from github, it is getting the raw content
        if($SpecificationUri.Host -eq 'github.com'){
            $SpecificationUri = "https://raw.githubusercontent.com$($SpecificationUri.AbsolutePath.Replace('/blob/','/'))"
            $message = $LocalizedData.ConvertingSwaggerSpecToGithubContent -f ($SpecificationUri)
            Write-Verbose -Message $message -Verbose
        }

        $TempPath = Join-Path -Path (Get-XDGDirectory -DirectoryType Cache) -ChildPath (Get-Random)
        $null = New-Item -Path $TempPath -ItemType Directory -Force -Confirm:$false -WhatIf:$false

        $SwaggerFileName = Split-Path -Path $SpecificationUri -Leaf
        $SpecificationPath = Join-Path -Path $TempPath -ChildPath $SwaggerFileName

        $message = $LocalizedData.SwaggerSpecDownloadedTo -f ($SpecificationUri, $SpecificationPath)
        Write-Verbose -Message $message
        
        $ev = $null
        Invoke-WebRequest -Uri $SpecificationUri -OutFile $SpecificationPath -ErrorVariable ev
        if($ev) {
            return 
        }

        $jsonObject = ConvertFrom-Json -InputObject ((Get-Content -Path $SpecificationPath) -join [Environment]::NewLine) -ErrorAction Stop
        if((Get-Member -InputObject $jsonObject -Name 'Documents') -and ($jsonObject.Documents.Count))
        {
            $AutoRestModeler = 'CompositeSwagger'
            $BaseSwaggerUri = "$SpecificationUri".Substring(0, "$SpecificationUri".LastIndexOf('/'))
            foreach($document in $jsonObject.Documents)
            {
                $FileName = Split-Path -Path $document -Leaf
                $DocumentFolderPrefix = (Split-Path -Path $document -Parent).Replace('/', [System.IO.Path]::DirectorySeparatorChar).TrimStart('.')
                
                $DocumentFolderPath = Join-Path -Path $TempPath -ChildPath $DocumentFolderPrefix

                if(-not (Test-Path -LiteralPath $DocumentFolderPath -PathType Container))
                {
                    $null = New-Item -Path $DocumentFolderPath -ItemType Container -Force -Confirm:$false -WhatIf:$false
                }
                $SwaggerDocumentPath = Join-Path -Path $DocumentFolderPath -ChildPath $FileName

                $ev = $null
                Invoke-WebRequest -Uri $($BaseSwaggerUri + $($document.replace('\','/').TrimStart('.'))) -OutFile $SwaggerDocumentPath -ErrorVariable ev
                if($ev) {
                    return 
                }
                $SwaggerSpecFilePaths += $SwaggerDocumentPath
            }
        }
        else
        {
            $SwaggerSpecFilePaths += $SpecificationPath
        }
    }    

    $outputDirectory = Microsoft.PowerShell.Management\Resolve-Path -Path $Path | Select-Object -First 1 -ErrorAction Ignore
    $outputDirectory = "$outputDirectory".TrimEnd('\').TrimEnd('/')
    if (-not $SymbolPath) {
        $SymbolPath = Join-Path -Path $Path -ChildPath "symbols"
    }

    if (-not $outputDirectory -or (-not (Test-path -Path $outputDirectory -PathType Container)))
    {
        throw $LocalizedData.PathNotFound -f ($Path)
        return
    }
  
    # Validate swagger path and composite swagger paths
    if (-not (Test-path -Path $SpecificationPath))
    {
        throw $LocalizedData.SwaggerSpecPathNotExist -f ($SpecificationPath)
        return
    }

    # Get the PowerShell Metadata if .psmeta.json file is available.
    $PSMetaJsonObject = $null
    $PSMetaFilePath = [regex]::replace($SpecificationPath, ".json$", ".psmeta.json")
    if (Test-Path -Path $PSMetaFilePath -PathType Leaf) {
        $PSMetaJsonObject = ConvertFrom-Json -InputObject ((Get-Content -Path $PSMetaFilePath) -join [Environment]::NewLine) -ErrorAction Stop
    }

    if (($PSCmdlet.ParameterSetName -eq 'SpecificationPath') -or 
        ($PSCmdlet.ParameterSetName -eq 'SdkAssemblyWithSpecificationPath'))
    {
        $jsonObject = ConvertFrom-Json -InputObject ((Get-Content -Path $SpecificationPath) -join [Environment]::NewLine) -ErrorAction Stop
        if((Get-Member -InputObject $jsonObject -Name 'Documents') -and ($jsonObject.Documents.Count))
        {
            $AutoRestModeler = 'CompositeSwagger'
            $SwaggerBaseDir = Split-Path -Path $SpecificationPath -Parent
            foreach($document in $jsonObject.Documents)
            {
                $FileName = Split-Path -Path $document -Leaf
                if(Test-Path -Path $document -PathType Leaf)
                {
                    $SwaggerSpecFilePaths += $document
                }
                elseif(Test-Path -Path (Join-Path -Path $SwaggerBaseDir -ChildPath $document) -PathType Leaf)
                {
                    $SwaggerSpecFilePaths += Join-Path -Path $SwaggerBaseDir -ChildPath $document
                }
                else {
                    throw $LocalizedData.PathNotFound -f ($document)
                    return
                }
            }
        }
        else
        {
            $SwaggerSpecFilePaths += $SpecificationPath
        }
    }

    if (($PSCmdlet.ParameterSetName -eq 'SpecificationPath') -or 
        ($PSCmdlet.ParameterSetName -eq 'SpecificationUri'))
    {
        $frameworksToCheckDependencies = @('net4')
        if ($IncludeCoreFxAssembly) {
            if ((-not (Get-OperatingSystemInfo).IsCore) -and (-not $PowerShellCorePath)) {
                $psCore = Get-PSSwaggerMsi -Name "PowerShell*" -MaximumVersion "6.0.0.11" | Sort-Object -Property Version -Descending
                if ($null -ne $psCore) {
                    # PSCore exists via MSI, but the MSI provider doesn't seem to provide an install path
                    # First check the default path (for now, just Windows)
                    $psCore | ForEach-Object {
                        if (-not $PowerShellCorePath) {
                            $message = $LocalizedData.FoundPowerShellCoreMsi -f ($($_.Version))
                            Write-Verbose -Message $message
                            $possiblePsPath = (Join-Path -Path "$env:ProgramFiles" -ChildPath "PowerShell" | Join-Path -ChildPath "$($_.Version)" | Join-Path -ChildPath "PowerShell.exe")
                            if (Test-Path -Path $possiblePsPath) {
                                $PowerShellCorePath = $possiblePsPath
                            }
                        }
                    }
                }
            }

            if (-not $PowerShellCorePath) {
                throw $LocalizedData.MustSpecifyPsCorePath
            }

            if ((Get-Item $PowerShellCorePath).PSIsContainer) {
                $PowerShellCorePath = Join-Path -Path $PowerShellCorePath -ChildPath "PowerShell.exe"
            }

            if (-not (Test-Path -Path $PowerShellCorePath)) {
                $message = $LocalizedData.PsCorePathNotFound -f ($PowerShellCorePath)
                throw $message
            }

            $frameworksToCheckDependencies += 'netstandard1'
        }

        $userConsent = Initialize-PSSwaggerLocalTool -AllUsers:$InstallToolsForAllUsers -Azure:$UseAzureCsharpGenerator -Framework $frameworksToCheckDependencies -AcceptBootstrap:$ConfirmBootstrap
    }

    $DefinitionFunctionsDetails = @{}
    $PowerShellCodeGen = @{
        CodeGenerator = ""
        Path = ""
        NoAssembly = ""
        PowerShellCorePath = ""
        IncludeCoreFxAssembly = ""
        TestBuild = ""
        SymbolPath = ""
        ConfirmBootstrap = ""
        AdditionalFilesPath = ""
        ServiceType = ""
        CustomAuthCommand = ""
        HostOverrideCommand = ""
        NoAuthChallenge = $false
        NameSpacePrefix = ''
        Header = ''
    }

    # Parse the JSON and populate the dictionary
    $ConvertToSwaggerDictionary_params = @{
        SwaggerSpecPath            = $SpecificationPath
        ModuleName                 = $Name
        ModuleVersion              = $Version
        DefaultCommandPrefix       = $DefaultCommandPrefix
        Header                     = $($Header -join "`r`n")
        SwaggerSpecFilePaths       = $SwaggerSpecFilePaths
        DefinitionFunctionsDetails = $DefinitionFunctionsDetails
        AzureSpec                  = $UseAzureCsharpGenerator
        PowerShellCodeGen          = $PowerShellCodeGen
        PSMetaJsonObject           = $PSMetaJsonObject
        ClientTypeName             = $ClientTypeName
        ModelsName                 = $ModelsName
    }
    $swaggerDict = ConvertTo-SwaggerDictionary @ConvertToSwaggerDictionary_params

    Get-PowerShellCodeGenSettings -Path $SpecificationPath -CodeGenSettings $PowerShellCodeGen -PSMetaJsonObject $PSMetaJsonObject
    if(-not $PSMetaJsonObject) {
        foreach ($additionalSwaggerSpecPath in $SwaggerSpecFilePaths) {
            Get-PowerShellCodeGenSettings -Path $additionalSwaggerSpecPath -CodeGenSettings $PowerShellCodeGen
        }
    }

    # Expand partner metadata
    if ($PowerShellCodeGen['ServiceType']) {
        $partnerFilePath = Join-Path -Path $PSScriptRoot -ChildPath "ServiceTypes" | Join-Path -ChildPath "$($PowerShellCodeGen['ServiceType'].ToLowerInvariant()).PSMeta.json"
        if (-not (Test-Path -Path $partnerFilePath -PathType Leaf)) {
            Write-Warning -Message ($LocalizedData.ServiceTypeMetadataFileNotFound -f $partnerFilePath)
        } else {
            Get-PowerShellCodeGenSettings -Path $partnerFilePath -CodeGenSettings $PowerShellCodeGen
        }
    }
    
    $nameSpace = $swaggerDict['info'].NameSpace
    $models = $swaggerDict['info'].Models
    if($NoVersionFolder -or $PSVersionTable.PSVersion -lt '5.0.0') {
        if (-not $outputDirectory.EndsWith($Name, [System.StringComparison]::OrdinalIgnoreCase)) {
            $outputDirectory = Join-Path -Path $outputDirectory -ChildPath $Name
            $SymbolPath = Join-Path -Path $SymbolPath -ChildPath $Name
        }
    } else {
        $ModuleNameandVersionFolder = Join-Path -Path $Name -ChildPath $Version

        if ($outputDirectory.EndsWith($Name, [System.StringComparison]::OrdinalIgnoreCase)) {
            $outputDirectory = Join-Path -Path $outputDirectory -ChildPath $ModuleVersion
            $SymbolPath = Join-Path -Path $SymbolPath -ChildPath $ModuleVersion
        } elseif (-not $outputDirectory.EndsWith($ModuleNameandVersionFolder, [System.StringComparison]::OrdinalIgnoreCase)) {
            $outputDirectory = Join-Path -Path $outputDirectory -ChildPath $ModuleNameandVersionFolder
            $SymbolPath = Join-Path -Path $SymbolPath -ChildPath $ModuleNameandVersionFolder
        }
    }

    $null = New-Item -ItemType Directory $outputDirectory -Force -ErrorAction Stop -Confirm:$false -WhatIf:$false
    $null = New-Item -ItemType Directory $SymbolPath -Force -ErrorAction Stop -Confirm:$false -WhatIf:$false

    $swaggerMetaDict = @{
        OutputDirectory = $outputDirectory
        UseAzureCsharpGenerator = $UseAzureCsharpGenerator
        SwaggerSpecPath = $SpecificationPath
        SwaggerSpecFilePaths = $SwaggerSpecFilePaths
        AutoRestModeler = $AutoRestModeler
        PowerShellCodeGen = $PowerShellCodeGen
    }

    $ParameterGroupCache = @{}
    $PathFunctionDetails = @{}

    foreach($FilePath in $SwaggerSpecFilePaths) {
        $jsonObject = ConvertFrom-Json -InputObject ((Get-Content -Path $FilePath) -join [Environment]::NewLine) -ErrorAction Stop

        if(Get-Member -InputObject $jsonObject -Name 'Definitions') {
            # Handle the Definitions
            $jsonObject.Definitions.PSObject.Properties | ForEach-Object {
                Get-SwaggerSpecDefinitionInfo -JsonDefinitionItemObject $_ `
                                            -Namespace $Namespace `
                                            -DefinitionFunctionsDetails $DefinitionFunctionsDetails `
                                            -Models $models
            }
        }

        if(Get-Member -InputObject $jsonObject -Name 'Paths') {
            # Handle the Paths
            $jsonObject.Paths.PSObject.Properties | ForEach-Object {
                Get-SwaggerSpecPathInfo -JsonPathItemObject $_ `
                                        -PathFunctionDetails $PathFunctionDetails `
                                        -SwaggerDict $swaggerDict `
                                        -SwaggerMetaDict $swaggerMetaDict `
                                        -DefinitionFunctionsDetails $DefinitionFunctionsDetails `
                                        -ParameterGroupCache $ParameterGroupCache `
                                        -PSMetaJsonObject $PSMetaJsonObject
            }
        }

        if(Get-Member -InputObject $jsonObject -Name 'x-ms-paths') {
            # Handle extended paths
            $jsonObject.'x-ms-paths'.PSObject.Properties | ForEach-Object {
                Get-SwaggerSpecPathInfo -JsonPathItemObject $_ `
                                        -PathFunctionDetails $PathFunctionDetails `
                                        -SwaggerDict $swaggerDict `
                                        -SwaggerMetaDict $swaggerMetaDict `
                                        -DefinitionFunctionsDetails $DefinitionFunctionsDetails `
                                        -ParameterGroupCache $ParameterGroupCache `
                                        -PSMetaJsonObject $PSMetaJsonObject

            }
        }
    }

    $FullClrAssemblyFilePath = $null
    if($AssemblyFileName) {
        $FullClrAssemblyFilePath = Join-Path -Path $outputDirectory -ChildPath 'ref' | Join-Path -ChildPath 'fullclr' | Join-Path -ChildPath $AssemblyFileName
        if(-not (Test-Path -Path $FullClrAssemblyFilePath -PathType Leaf)) {
            $message = $LocalizedData.PathNotFound -f $FullClrAssemblyFilePath
            Write-Error -Message $message -ErrorId AssemblyNotFound
            return
        }
    }
    else {
        $ConvertToCsharpCode_params = @{
            SwaggerDict             = $swaggerDict
            SwaggerMetaDict         = $swaggerMetaDict
            PowerShellCorePath      = $PowerShellCorePath
            InstallToolsForAllUsers = $InstallToolsForAllUsers
            UserConsent             = $userConsent
            TestBuild               = $TestBuild
            NoAssembly              = $NoAssembly
            SymbolPath              = $SymbolPath
        }
        $AssemblyGenerationResult = ConvertTo-CsharpCode @ConvertToCsharpCode_params
        if(-not $AssemblyGenerationResult) {
            return
        }
        $FullClrAssemblyFilePath = $AssemblyGenerationResult['FullClrAssemblyFilePath']
    }

    $NameSpace = $SwaggerDict['info'].NameSpace
    $FullClientTypeName = $Namespace + '.' + $SwaggerDict['Info'].ClientTypeName

    $PathFunctionDetails = Update-PathFunctionDetails -PathFunctionDetails $PathFunctionDetails -FullClientTypeName $FullClientTypeName
    if(-not $PathFunctionDetails) {
        return
    }

    # Need to expand the definitions early as parameter flattening feature requires the parameters list of the definition/model types.
    Expand-SwaggerDefinition -DefinitionFunctionsDetails $DefinitionFunctionsDetails -NameSpace $NameSpace -Models $Models

    $HeaderContent = Get-HeaderContent -SwaggerDict $SwaggerDict -ErrorVariable ev
    if($ev) {
        return
    }
    $PSHeaderComment = $null
    if($HeaderContent) {
        $PSHeaderComment = ($PSCommentFormatString -f $HeaderContent)
    }

    $FunctionsToExport = @()
    $FunctionsToExport += New-SwaggerSpecPathCommand -PathFunctionDetails $PathFunctionDetails `
                                                     -SwaggerMetaDict $swaggerMetaDict `
                                                     -SwaggerDict $swaggerDict `
                                                     -DefinitionFunctionsDetails $DefinitionFunctionsDetails `
                                                     -PSHeaderComment $PSHeaderComment

    $FunctionsToExport += New-SwaggerDefinitionCommand -DefinitionFunctionsDetails $DefinitionFunctionsDetails `
                                                       -SwaggerMetaDict $swaggerMetaDict `
                                                       -NameSpace $nameSpace `
                                                       -Models $models `
                                                       -HeaderContent $HeaderContent

    $RootModuleFilePath = Join-Path $outputDirectory "$Name.psm1"
    $testCoreModuleRequirements = ''
    $testFullModuleRequirements = ''
    if ($UseAzureCSharpGenerator) {
        $testCoreModuleRequirements = '. (Join-Path -Path $PSScriptRoot "Test-CoreRequirements.ps1")' + [Environment]::NewLine + " "
        $testFullModuleRequirements = '. (Join-Path -Path $PSScriptRoot "Test-FullRequirements.ps1")' + [Environment]::NewLine + " "
    }

    $DynamicAssemblyGenerationCode = $null
    if ($AssemblyFileName) {
        $DllFileName = $AssemblyFileName
    }
    else {
        $DllFileName = "$Namespace.dll"
        $DynamicAssemblyGenerationCode = $ExecutionContext.InvokeCommand.ExpandString($DynamicAssemblyGenerationBlock)
    }

    Out-File -FilePath $RootModuleFilePath `
             -InputObject @($PSHeaderComment, $ExecutionContext.InvokeCommand.ExpandString($RootModuleContents))`
             -Encoding ascii `
             -Force `
             -Confirm:$false `
             -WhatIf:$false

    New-ModuleManifestUtility -Path $outputDirectory `
        -FunctionsToExport $FunctionsToExport `
        -Info $swaggerDict['info'] `
        -PSHeaderComment $PSHeaderComment

    $CopyFilesMap = [ordered]@{}
    if($UseAzureCsharpGenerator) {
        $CopyFilesMap['New-ArmServiceClient.ps1'] = 'New-ServiceClient.ps1'
        $CopyFilesMap['Test-FullRequirements.ps1'] = 'Test-FullRequirements.ps1'
        $CopyFilesMap['Test-CoreRequirements.ps1'] = 'Test-CoreRequirements.ps1'
    }
    else {
        $CopyFilesMap['New-ServiceClient.ps1'] = 'New-ServiceClient.ps1'        
    }

    if (-not $AssemblyFileName) {
        $CopyFilesMap['AssemblyGenerationHelpers.ps1'] = 'AssemblyGenerationHelpers.ps1'
        $CopyFilesMap['AssemblyGenerationHelpers.Resources.psd1'] = 'AssemblyGenerationHelpers.Resources.psd1'
    }

    $CopyFilesMap.GetEnumerator() | ForEach-Object {
        Copy-PSFileWithHeader -SourceFilePath (Join-Path -Path "$PSScriptRoot" -ChildPath $_.Name) `
            -DestinationFilePath (Join-Path -Path "$outputDirectory" -ChildPath $_.Value) `
            -PSHeaderComment $PSHeaderComment
    }

    Write-Verbose -Message ($LocalizedData.SuccessfullyGeneratedModule -f $Name,$outputDirectory)
}

#region Module Generation Helpers

function Update-PathFunctionDetails {
    param(
        [Parameter(Mandatory=$true)]
        [PSCustomObject]
        $PathFunctionDetails,

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

    $cliXmlTmpPath = Get-TemporaryCliXmlFilePath -FullClientTypeName $FullClientTypeName

    try {
        Export-CliXml -InputObject $PathFunctionDetails -Path $cliXmlTmpPath
        $PathsPsm1FilePath = Join-Path -Path $PSScriptRoot -ChildPath Paths.psm1
        $command = @"
            Add-Type -Path '$FullClrAssemblyFilePath'
            Import-Module -Name '$PathsPsm1FilePath' -DisableNameChecking
            Set-ExtendedCodeMetadata -MainClientTypeName '$FullClientTypeName' -CliXmlTmpPath '$cliXmlTmpPath'
"@

        $null = & PowerShell.exe -command "& {$command}"

        $codeReflectionResult = Import-CliXml -Path $cliXmlTmpPath
        if ($codeReflectionResult.ContainsKey('VerboseMessages') -and
            $codeReflectionResult.VerboseMessages -and
            ($codeReflectionResult.VerboseMessages.Count -gt 0)) {
            $verboseMessages = $codeReflectionResult.VerboseMessages -Join [Environment]::NewLine
            Write-Verbose -Message $verboseMessages
        }

        if ($codeReflectionResult.ContainsKey('WarningMessages') -and
            $codeReflectionResult.WarningMessages -and
            ($codeReflectionResult.WarningMessages.Count -gt 0)) {
            $warningMessages = $codeReflectionResult.WarningMessages -Join [Environment]::NewLine
            Write-Warning -Message $warningMessages
        }

        if (-not $codeReflectionResult.Result -or 
            $codeReflectionResult.ErrorMessages.Count -gt 0) {
            $errorMessage = (, ($LocalizedData.MetadataExtractFailed) + 
                $codeReflectionResult.ErrorMessages) -Join [Environment]::NewLine
            Write-Error -Message $errorMessage -ErrorId 'UnableToExtractDetailsFromSdkAssembly'
            return
        }

        return $codeReflectionResult.Result
    }
    finally {
        if (Test-Path -Path $cliXmlTmpPath -PathType Leaf) {
            $null = Remove-Item -Path $cliXmlTmpPath -Force -WhatIf:$false -Confirm:$false
        }
    }
}

function ConvertTo-CsharpCode
{
    param
    (
        [Parameter(Mandatory=$true)]
        [hashtable]
        $SwaggerDict,
        
        [Parameter(Mandatory = $true)]
        [hashtable]
        $SwaggerMetaDict,

        [Parameter()]
        [string]
        $PowerShellCorePath,

        [Parameter()]
        [switch]
        $InstallToolsForAllUsers,

        [Parameter()]
        [switch]
        $UserConsent,

        [Parameter()]
        [switch]
        $TestBuild,

        [Parameter()]
        [switch]
        $NoAssembly,

        [Parameter()]
        [string]
        $SymbolPath
    )

    Write-Verbose -Message $LocalizedData.GenerateCodeUsingAutoRest
    $info = $SwaggerDict['Info']    

    $AutoRestCommand = Get-Command -Name AutoRest -ErrorAction Ignore | Select-Object -First 1 -ErrorAction Ignore
    if (-not $AutoRestCommand) {
        throw $LocalizedData.AutoRestNotInPath
    }

    if (-not (Get-OperatingSystemInfo).IsCore -and 
        (-not (Get-Command -Name 'Csc.Exe' -ErrorAction Ignore))) {
        throw $LocalizedData.CscExeNotInPath
    }

    $outputDirectory = $SwaggerMetaDict['outputDirectory']
    $nameSpace = $info.NameSpace
    $generatedCSharpPath = Join-Path -Path $outputDirectory -ChildPath "Generated.Csharp"
    $codeGenerator = "CSharp"

    if ($SwaggerMetaDict['UseAzureCsharpGenerator'])
    { 
        $codeGenerator = "Azure.CSharp"
    }

    $clrPath = Join-Path -Path $outputDirectory -ChildPath 'ref' | Join-Path -ChildPath 'fullclr'
    if (-not (Test-Path -Path $clrPath)) {
        $null = New-Item -Path $clrPath -ItemType Directory -Force -Confirm:$false -WhatIf:$false
    }

    $outAssembly = "$NameSpace.dll"
    # Delete the previously generated assembly, even if -NoAssembly is specified
    if (Test-Path -Path (Join-Path -Path $clrPath -ChildPath $outAssembly)) {
        $null = Remove-Item -Path (Join-Path -Path $clrPath -ChildPath $outAssembly) -Force
    }

    # Clean old generated code for when operations change or the command is re-run (Copy-Item can't clobber)
    # Note that we don't need to require this as empty as this folder is generated by PSSwagger, not a user folder
    if (Test-Path -Path $generatedCSharpPath) {
        $null = Remove-Item -Path $generatedCSharpPath -Recurse -Force
    }

    if ($NoAssembly) {
        $outAssembly = ''
    }

    $result = @{
        GeneratedCSharpPath = $generatedCSharpPath
        FullClrAssemblyFilePath = ''
        CoreClrAssemblyFilePath = ''
    }

    $tempCodeGenSettingsPath = ''
    # Latest AutoRest inconsistently appends 'Client' to the specified infoName to generated the client name.
    # We need to override the client name to ensure that generated PowerShell cmdlets work fine.
    # Note: -ClientName doesn't seem to work for legacy invocation
    $ClientName = $info['ClientTypeName']
    try {
        if ($info.ContainsKey('CodeGenFileRequired') -and $info.CodeGenFileRequired) {
            # Some settings need to be overwritten
            # Write the following parameters: AddCredentials, CodeGenerator, Modeler
            $tempCodeGenSettings = @{
                AddCredentials = $true
                CodeGenerator  = $codeGenerator
                Modeler        = $swaggerMetaDict['AutoRestModeler']
                ClientName     = $ClientName
            }

            $tempCodeGenSettingsPath = "$(Join-Path -Path (Get-XDGDirectory -DirectoryType Cache) -ChildPath (Get-Random)).json"
            $tempCodeGenSettings | ConvertTo-Json | Out-File -FilePath $tempCodeGenSettingsPath

            $autoRestParams = @('-Input', $swaggerMetaDict['SwaggerSpecPath'], '-OutputDirectory', $generatedCSharpPath, '-Namespace', $NameSpace, '-CodeGenSettings', $tempCodeGenSettingsPath)
        }
        elseif ( ($AutoRestCommand.Name -eq 'AutoRest.exe') -or 
            ($swaggerMetaDict['AutoRestModeler'] -eq 'CompositeSwagger')) {
            # None of the PSSwagger-required params are being overwritten, just call the CLI directly to avoid the extra disk op
            $autoRestParams = @('-Input', $swaggerMetaDict['SwaggerSpecPath'], 
                '-OutputDirectory', $generatedCSharpPath, 
                '-Namespace', $NameSpace, 
                '-AddCredentials', $true, 
                '-CodeGenerator', $codeGenerator, 
                '-ClientName', $ClientName
                '-Modeler', $swaggerMetaDict['AutoRestModeler'] 
            )
        }
        else {
            # See https://aka.ms/autorest/cli for AutoRest.cmd options.
            $autoRestParams = @(
                "--input-file=$($swaggerMetaDict['SwaggerSpecPath'])",
                "--output-folder=$generatedCSharpPath",
                "--namespace=$NameSpace",
                '--add-credentials',
                '--clear-output-folder=true',
                "--override-client-name=$ClientName"
                '--verbose',
                '--csharp'
            )

            if ($codeGenerator -eq 'Azure.CSharp') {
                $autoRestParams += '--azure-arm'
            }

            if (('continue' -eq $DebugPreference) -or 
                ('inquire' -eq $DebugPreference)) {
                $autoRestParams += '--debug'
            }            
        }

        Write-Verbose -Message $LocalizedData.InvokingAutoRestWithParams
        Write-Verbose -Message $($autoRestParams | Out-String)
        $autorestMessages = & AutoRest $autoRestParams
        if ($autorestMessages) {
            Write-Verbose -Message $($autorestMessages | Out-String)
        }
        if ($LastExitCode)
        {
            Write-Error -Message $LocalizedData.AutoRestError -ErrorId 'SourceCodeGenerationError'
            return
        }
    }
    finally {
        if ($tempCodeGenSettingsPath -and (Test-Path -Path $tempCodeGenSettingsPath)) {
            $null = Remove-Item -Path $tempCodeGenSettingsPath -Force -ErrorAction Ignore
        }
    }

    Write-Verbose -Message $LocalizedData.GenerateAssemblyFromCode
    if ($info.ContainsKey('CodeOutputDirectory') -and $info.CodeOutputDirectory) {
        $null = Copy-Item -Path $info.CodeOutputDirectory -Destination $generatedCSharpPath -Filter "*.cs" -Recurse -ErrorAction Ignore
    }

    $allCSharpFiles= Get-ChildItem -Path "$generatedCSharpPath\*.cs" `
                                   -Recurse `
                                   -File `
                                   -Exclude Program.cs,TemporaryGeneratedFile* |
                                        Where-Object DirectoryName -notlike '*Azure.Csharp.Generated*'
    $allCodeFiles = @()
    foreach ($file in $allCSharpFiles) {
        $newFileName = Join-Path -Path $file.Directory -ChildPath "$($file.BaseName).Code.ps1"
        $null = Move-Item -Path $file.FullName -Destination $newFileName -Force
        $allCodeFiles += $newFileName
    }
    
    $allCSharpFilesArrayString = "@('"+ $($allCodeFiles -join "','") + "')"
    # Compile full CLR (PSSwagger requires to be invoked from full PowerShell)
    $codeCreatedByAzureGenerator = [bool]$SwaggerMetaDict['UseAzureCsharpGenerator']

    $dependencies = Get-PSSwaggerExternalDependencies -Azure:$codeCreatedByAzureGenerator -Framework 'net4'
    $microsoftRestClientRuntimeAzureRequiredVersion = if ($dependencies.ContainsKey('Microsoft.Rest.ClientRuntime.Azure')) { $dependencies['Microsoft.Rest.ClientRuntime.Azure'].RequiredVersion } else { '' }
    
    if(-not $OutAssembly) {
        $TempGuid = [Guid]::NewGuid().Guid
        if (-not $OutAssembly) {
            $OutAssembly = "$TempGuid.dll"
        }
        $ClrPath = Join-Path -Path (Get-XDGDirectory -DirectoryType Cache) -ChildPath ([Guid]::NewGuid().Guid)
        $null = New-Item -Path $ClrPath -ItemType Directory -Force -WhatIf:$false -Confirm:$false
    }

    $AddPSSwaggerClientType_params = @{
        OutputAssemblyName = $outAssembly
        ClrPath = $clrPath
        CSharpFiles = $allCodeFiles
        CodeCreatedByAzureGenerator = $codeCreatedByAzureGenerator
        MicrosoftRestClientRuntimeAzureRequiredVersion = $microsoftRestClientRuntimeAzureRequiredVersion
        MicrosoftRestClientRuntimeRequiredVersion = $dependencies['Microsoft.Rest.ClientRuntime'].RequiredVersion
        NewtonsoftJsonRequiredVersion = $dependencies['Newtonsoft.Json'].RequiredVersion
        AllUsers = $InstallToolsForAllUsers
        BootstrapConsent = $UserConsent
        TestBuild = $TestBuild
        SymbolPath = $SymbolPath
    }

    if(-not (PSSwaggerUtility\Add-PSSwaggerClientType @AddPSSwaggerClientType_params)) {
        $message = $LocalizedData.UnableToGenerateAssembly -f ($outAssembly)
        Write-Error -ErrorId 'UnableToGenerateAssembly' -Message $message
        return
    }

    $message = $LocalizedData.GeneratedAssembly -f ($outAssembly)
    Write-Verbose -Message $message
    $result['FullClrAssemblyFilePath'] = Join-Path -Path $ClrPath -ChildPath $OutAssembly

    # If we're not going to save the assembly, no need to generate the core CLR one now
    if ($PowerShellCorePath -and (-not $NoAssembly)) {
        if (-not $outAssembly) {
            $outAssembly = "$NameSpace.dll"
        }

        # Compile core CLR
        $clrPath = Join-Path -Path $outputDirectory -ChildPath 'ref' | Join-Path -ChildPath 'coreclr'
        if (Test-Path (Join-Path -Path $outputDirectory -ChildPath $outAssembly)) {
            $null = Remove-Item -Path $outAssembly -Force
        }

        if (-not (Test-Path -Path $clrPath)) {
            $null = New-Item $clrPath -ItemType Directory -Force -Confirm:$false -WhatIf:$false
        }
        $dependencies = Get-PSSwaggerExternalDependencies -Azure:$codeCreatedByAzureGenerator -Framework 'netstandard1'
        $microsoftRestClientRuntimeAzureRequiredVersion = if ($dependencies.ContainsKey('Microsoft.Rest.ClientRuntime.Azure')) { $dependencies['Microsoft.Rest.ClientRuntime.Azure'].RequiredVersion } else { '' }

        # In some cases, PSCore doesn't inherit this process's PSModulePath
        $command = @"
            `$env:PSModulePath += ';$env:PSModulePath'
            `$AddPSSwaggerClientType_params = @{
                OutputAssemblyName = '$outAssembly'
                ClrPath = '$clrPath'
                CSharpFiles = $allCSharpFilesArrayString
                MicrosoftRestClientRuntimeAzureRequiredVersion = '$microsoftRestClientRuntimeAzureRequiredVersion'
                MicrosoftRestClientRuntimeRequiredVersion = '$($dependencies['Microsoft.Rest.ClientRuntime'].RequiredVersion)'
                NewtonsoftJsonRequiredVersion = '$($dependencies['Newtonsoft.Json'].RequiredVersion)'
                CodeCreatedByAzureGenerator = `$$codeCreatedByAzureGenerator
                BootstrapConsent = `$$UserConsent
            }
            PSSwaggerUtility\Add-PSSwaggerClientType @AddPSSwaggerClientType_params
"@

        $success = & "$PowerShellCorePath" -command "& {$command}"
        if ((Test-AssemblyCompilationSuccess -Output ($success | Out-String))) {
            $message = $LocalizedData.GeneratedAssembly -f ($outAssembly)
            Write-Verbose -Message $message
        } else {
            $message = $LocalizedData.UnableToGenerateAssembly -f ($outAssembly)
            Write-Error -ErrorId 'UnableToGenerateCoreClrAssembly' -Message $message
            return
        }
        $result['CoreClrAssemblyFilePath'] = Join-Path -Path $ClrPath -ChildPath $OutAssembly
    }

    return $result
}

function Test-AssemblyCompilationSuccess {
    param(
        [Parameter(Mandatory = $true)]
        [string]
        $Output
    )

    Write-Verbose -Message ($LocalizedData.AssemblyCompilationResult -f ($Output))
    $tokens = $Output.Split(' ')
    return ($tokens[$tokens.Count-1].Trim().EndsWith('True'))
}

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

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

        [Parameter(Mandatory=$true)]
        [hashtable]
        $Info,
        
        [Parameter(Mandatory=$false)]
        [AllowEmptyString()]
        [string]
        $PSHeaderComment
    )

    $FormatsToProcess = Get-ChildItem -Path "$Path\$GeneratedCommandsName\FormatFiles\*.ps1xml" `
                                      -File `
                                      -ErrorAction Ignore | Foreach-Object { $_.FullName.Replace($Path, '.') }
                                      
    $ModuleManifestFilePath = "$(Join-Path -Path $Path -ChildPath $Info.ModuleName).psd1"
    $NewModuleManifest_params = @{
        Path = $ModuleManifestFilePath
        ModuleVersion = $Info.Version
        Description = $Info.Description
        CopyRight = $info.LicenseName
        Author = $info.ContactEmail
        NestedModules = @('PSSwaggerUtility')
        RootModule = "$($Info.ModuleName).psm1"
        FormatsToProcess = $FormatsToProcess
        FunctionsToExport = $FunctionsToExport
        CmdletsToExport = @()
        AliasesToExport = @()
        VariablesToExport = @()
        PassThru = $true
    }

    if($Info.DefaultCommandPrefix)
    {
        $NewModuleManifest_params['DefaultCommandPrefix'] = $Info.DefaultCommandPrefix
    }

    if($PSVersionTable.PSVersion -ge '5.0.0')
    {
        # Below parameters are not available on PS 3 and 4 versions.
        if($Info.ProjectUri)
        {
            $NewModuleManifest_params['ProjectUri'] = $Info.ProjectUri
        }

        if($Info.LicenseUri)
        {
            $NewModuleManifest_params['LicenseUri'] = $Info.LicenseUri
        }
    }

    $PassThruContent = New-ModuleManifest @NewModuleManifest_params
    
    # Add header comment
    if ($PSHeaderComment) {
        Out-File -FilePath $ModuleManifestFilePath `
            -InputObject @($PSHeaderComment, $PassThruContent)`
            -Encoding ascii `
            -Force `
            -Confirm:$false `
            -WhatIf:$false
    }
}

function Get-HeaderContent {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [PSCustomObject]
        $SwaggerDict
    )

    $Header = $swaggerDict['Info'].Header
    $HeaderContent = ($DefaultGeneratedFileHeader -f $MyInvocation.MyCommand.Module.Version)
    if ($Header) {
        switch ($Header) {
            'MICROSOFT_MIT' {
                return $MicrosoftMitLicenseHeader + [Environment]::NewLine + [Environment]::NewLine + $HeaderContent
            }
            'MICROSOFT_MIT_NO_VERSION' {
                return $MicrosoftMitLicenseHeader + [Environment]::NewLine + [Environment]::NewLine + $DefaultGeneratedFileHeaderWithoutVersion
            }
            'MICROSOFT_MIT_NO_CODEGEN' {
                return $MicrosoftMitLicenseHeader
            }
            'MICROSOFT_APACHE' {
                return $MicrosoftApacheLicenseHeader + [Environment]::NewLine + [Environment]::NewLine + $HeaderContent
            }
            'MICROSOFT_APACHE_NO_VERSION' {
                return $MicrosoftApacheLicenseHeader + [Environment]::NewLine + [Environment]::NewLine + $DefaultGeneratedFileHeaderWithoutVersion
            }
            'MICROSOFT_APACHE_NO_CODEGEN' {
                return $MicrosoftApacheLicenseHeader
            }
            'NONE' {
                return ''
            }
        }
        
        $HeaderFilePath = Resolve-Path -Path $Header -ErrorAction Ignore
        if ($HeaderFilePath) {
            # Selecting the first path when multiple paths are returned by Resolve-Path cmdlet.
            if ($HeaderFilePath.PSTypeNames -contains 'System.Array') {
                $HeaderFilePath = $HeaderFilePath[0]
            }

            if (-not $HeaderFilePath.Path.EndsWith('.txt', [System.StringComparison]::OrdinalIgnoreCase)) {
                $message = ($LocalizedData.InvalidHeaderFileExtension -f $Header)
                Write-Error -Message $message -ErrorId 'InvalidHeaderFileExtension' -Category InvalidArgument
                return
            }

            if (-not (Test-Path -LiteralPath $HeaderFilePath -PathType Leaf)) {
                $message = ($LocalizedData.InvalidHeaderFilePath -f $Header)
                Write-Error -Message $message -ErrorId 'InvalidHeaderFilePath' -Category InvalidArgument
                return    
            }
            
            $HeaderContent = Get-Content -LiteralPath $HeaderFilePath -Raw
        }
        elseif ($Header.EndsWith('.txt', [System.StringComparison]::OrdinalIgnoreCase)) {
            # If this is an existing '.txt' file above Resolve-Path returns a valid header file path
            $message = ($LocalizedData.PathNotFound -f $Header)
            Write-Error -Message $message -ErrorId 'HeaderFilePathNotFound' -Category InvalidArgument
            return
        }
        else {
            $HeaderContent = $Header
        }
    }

    # Escape block comment character sequence, if any, using the PowerShell escape character, grave-accent(`).
    $HeaderContent = $HeaderContent.Replace('<#', '<`#').Replace('#>', '#`>')

    if ($HeaderContent -match '--') {
        Write-Warning -Message $LocalizedData.HeaderContentTwoHyphenWarning
        $HeaderContent = $HeaderContent.Replace('--', '==')
    }

    return $HeaderContent
}

function Copy-PSFileWithHeader {
    param(
        [Parameter(Mandatory = $true)]
        [string]
        $SourceFilePath,

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

        [Parameter(Mandatory = $false)]
        [AllowEmptyString()]
        [string]
        $PSHeaderComment
    )

    if (-not (Test-Path -Path $SourceFilePath -PathType Leaf)) {
        Throw ($LocalizedData.PathNotFound -f $SourceFilePath)
    }

    $FileContent = Get-Content -Path $SourceFilePath -Raw
    Out-File -FilePath $DestinationFilePath `
        -InputObject @($PSHeaderComment, $FileContent)`
        -Encoding ascii `
        -Force `
        -Confirm:$false `
        -WhatIf:$false
}

#endregion

Export-ModuleMember -Function New-PSSwaggerModule, New-PSSwaggerMetadataFile
# SIG # Begin signature block
# MIIarQYJKoZIhvcNAQcCoIIanjCCGpoCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUIp7tH2ofSMYFzzkCPOa4YPWi
# QT+gghWAMIIEwjCCA6qgAwIBAgITMwAAAL6kD/XJpQ7hMAAAAAAAvjANBgkqhkiG
# 9w0BAQUFADB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G
# A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSEw
# HwYDVQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EwHhcNMTYwOTA3MTc1ODQ5
# WhcNMTgwOTA3MTc1ODQ5WjCBsjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp
# bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw
# b3JhdGlvbjEMMAoGA1UECxMDQU9DMScwJQYDVQQLEx5uQ2lwaGVyIERTRSBFU046
# ODQzRC0zN0Y2LUYxMDQxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNl
# cnZpY2UwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCROfFjRVxKmgTC
# tN14U6jxq1vAK7TBi39qS2BIU56Xw1IeOFNjg7bw6O8DMLr04Ghia8ath6lj3yab
# PSyXiYULrfk/7PkLUAqDbr6CFA/kuvoLYmePEgYKgI2vtruq05MABGYyw4WpUfLt
# chCNiBYWawyrdeHaw80xvfUrb7cDAU8st94bIkgyboaDN7f3oIzQHqyxok8XSSaZ
# JKTyqNtEtDo7p6ZJ3ygCa98lCk/SjpVnLkGlX0lJ3y/H2FM28gNnfQZQO8Pe0ICv
# 3KCpi4CPqx9LEuPgQoJrYK573I1LJlbjTV+l73UHPbo2w40W9L1SGu5UWrwNb6tZ
# qk4RwEvJAgMBAAGjggEJMIIBBTAdBgNVHQ4EFgQUHG4NXaJsQp0+3x29Li7nwpc0
# kH8wHwYDVR0jBBgwFoAUIzT42VJGcArtQPt2+7MrsMM1sw8wVAYDVR0fBE0wSzBJ
# oEegRYZDaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMv
# TWljcm9zb2Z0VGltZVN0YW1wUENBLmNybDBYBggrBgEFBQcBAQRMMEowSAYIKwYB
# BQUHMAKGPGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljcm9z
# b2Z0VGltZVN0YW1wUENBLmNydDATBgNVHSUEDDAKBggrBgEFBQcDCDANBgkqhkiG
# 9w0BAQUFAAOCAQEAbmBxbLeCqxsZFPMYFz/20DMP8Q12dH/1cNQursRMH0Yg0cTw
# Ln1IF3DGypfHZJwbyl9HWNVf+2Jq05zMajfjxiEu+khzmMnA9/BJ1utPwR0nPyyL
# bN+0IGBMfYLeIAdC81e0CW9TpWpc6lH/jgWbhviUt4Mvt2DQMWIQ7WwJAdBeGjCn
# tLINPxC9RmHysFGexMsXS+hYNR2z/h/PmvsNwhq7CtM6bM71ZvYFaBSCmtdQ8/KQ
# CPiN6acb2V/28VuZEwjq3GFAJfcKMvhssewRgCYsKxhvWZHUkBrUxWnsvxNCOWPp
# enBiVSYl5nT9jBoVoTDChMITR35gr//DmhzXszCCBOswggPToAMCAQICEzMAAAF4
# JVq1zSPGX5UAAQAAAXgwDQYJKoZIhvcNAQEFBQAweTELMAkGA1UEBhMCVVMxEzAR
# BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p
# Y3Jvc29mdCBDb3Jwb3JhdGlvbjEjMCEGA1UEAxMaTWljcm9zb2Z0IENvZGUgU2ln
# bmluZyBQQ0EwHhcNMTcwODExMjAxMTE1WhcNMTgwODExMjAxMTE1WjCBgjELMAkG
# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEMMAoGA1UECxMDQU9DMR4w
# HAYDVQQDExVNaWNyb3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUA
# A4IBDwAwggEKAoIBAQCZbh1TVaudsrIbXUPPB9c8S+E+dKSbskHKYlG6SGTH8jhT
# hpuvGiAO87F2b9GHVN+DvszaMkGy/xQgHaGEJLfpnb3kpakic7E0bjDHdG4KnHRb
# no/wfUkGLfS79o+cw//RY8Ck6yE+0czDBcxp0Gbw5JyGP+KFqvzRR/3Tv3nt/5x0
# 5ZnEOHYP+eDVikDvn/DH+oxxtiCfX3tkvtm/yX4eOb47YdmYKQjCgz2+Nil/lupY
# vU0QFIjvke3jshqQINDng/vO9ys2qA0ex/q5hlLKQTST99dGoM86Ge6F723ReToq
# KnGLN8kiCG7uNapOAIQrpCHZq96CVumiaA5ZvxU9AgMBAAGjggFgMIIBXDATBgNV
# HSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUjuhtD3FD7tk/RKloJFX05cpgLjcw
# UQYDVR0RBEowSKRGMEQxDDAKBgNVBAsTA0FPQzE0MDIGA1UEBRMrMjI5ODAzKzFh
# YmY5ZTVmLWNlZDAtNDJlNi1hNjVkLWQ5MzUwOTU5ZmUwZTAfBgNVHSMEGDAWgBTL
# EejK0rQWWAHJNy4zFha5TJoKHzBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3Js
# Lm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNDb2RTaWdQQ0FfMDgt
# MzEtMjAxMC5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8v
# d3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY0NvZFNpZ1BDQV8wOC0zMS0y
# MDEwLmNydDANBgkqhkiG9w0BAQUFAAOCAQEAYnG/oHG/xgZYR8NAMHZ/vE9GM0e4
# 7YdhuTea2uY7pSGwM707wp8Wan0Fa6evK1PWfcd/XNOh2BpEv5o8RmKDoEsG0ECP
# 13Jug7cklfKreBVHQ+Djg43VVFLZpuo7aOAVK6wjlcnpPUtn+SfH9K0aM2FjDKVJ
# FW6XFKXBat5R+Zp6uOxWTxpSeMTeDC5zF6IY6ogR1uzU+9EQoRlAvkwX6po+exEL
# nMLr4++P+fqOxIU+PODIoB8ijClAqwwKvLlMPa3qlrNHt+LweTMu7lvGC/RA18wU
# zzXAeomuZ03blUw+bkOiVgWOk4S0RN7EnW7zjJV8gd/+G2dbToUi1cB/fTCCBbww
# ggOkoAMCAQICCmEzJhoAAAAAADEwDQYJKoZIhvcNAQEFBQAwXzETMBEGCgmSJomT
# 8ixkARkWA2NvbTEZMBcGCgmSJomT8ixkARkWCW1pY3Jvc29mdDEtMCsGA1UEAxMk
# TWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTEwMDgzMTIy
# MTkzMloXDTIwMDgzMTIyMjkzMloweTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldh
# c2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBD
# b3Jwb3JhdGlvbjEjMCEGA1UEAxMaTWljcm9zb2Z0IENvZGUgU2lnbmluZyBQQ0Ew
# ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCycllcGTBkvx2aYCAgQpl2
# U2w+G9ZvzMvx6mv+lxYQ4N86dIMaty+gMuz/3sJCTiPVcgDbNVcKicquIEn08Gis
# TUuNpb15S3GbRwfa/SXfnXWIz6pzRH/XgdvzvfI2pMlcRdyvrT3gKGiXGqelcnNW
# 8ReU5P01lHKg1nZfHndFg4U4FtBzWwW6Z1KNpbJpL9oZC/6SdCnidi9U3RQwWfjS
# jWL9y8lfRjFQuScT5EAwz3IpECgixzdOPaAyPZDNoTgGhVxOVoIoKgUyt0vXT2Pn
# 0i1i8UU956wIAPZGoZ7RW4wmU+h6qkryRs83PDietHdcpReejcsRj1Y8wawJXwPT
# AgMBAAGjggFeMIIBWjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTLEejK0rQW
# WAHJNy4zFha5TJoKHzALBgNVHQ8EBAMCAYYwEgYJKwYBBAGCNxUBBAUCAwEAATAj
# BgkrBgEEAYI3FQIEFgQU/dExTtMmipXhmGA7qDFvpjy82C0wGQYJKwYBBAGCNxQC
# BAweCgBTAHUAYgBDAEEwHwYDVR0jBBgwFoAUDqyCYEBWJ5flJRP8KuEKU5VZ5KQw
# UAYDVR0fBEkwRzBFoEOgQYY/aHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9j
# cmwvcHJvZHVjdHMvbWljcm9zb2Z0cm9vdGNlcnQuY3JsMFQGCCsGAQUFBwEBBEgw
# RjBEBggrBgEFBQcwAoY4aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0
# cy9NaWNyb3NvZnRSb290Q2VydC5jcnQwDQYJKoZIhvcNAQEFBQADggIBAFk5Pn8m
# Rq/rb0CxMrVq6w4vbqhJ9+tfde1MOy3XQ60L/svpLTGjI8x8UJiAIV2sPS9MuqKo
# VpzjcLu4tPh5tUly9z7qQX/K4QwXaculnCAt+gtQxFbNLeNK0rxw56gNogOlVuC4
# iktX8pVCnPHz7+7jhh80PLhWmvBTI4UqpIIck+KUBx3y4k74jKHK6BOlkU7IG9KP
# cpUqcW2bGvgc8FPWZ8wi/1wdzaKMvSeyeWNWRKJRzfnpo1hW3ZsCRUQvX/TartSC
# Mm78pJUT5Otp56miLL7IKxAOZY6Z2/Wi+hImCWU4lPF6H0q70eFW6NB4lhhcyTUW
# X92THUmOLb6tNEQc7hAVGgBd3TVbIc6YxwnuhQ6MT20OE049fClInHLR82zKwexw
# o1eSV32UjaAbSANa98+jZwp0pTbtLS8XyOZyNxL0b7E8Z4L5UrKNMxZlHg6K3RDe
# ZPRvzkbU0xfpecQEtNP7LN8fip6sCvsTJ0Ct5PnhqX9GuwdgR2VgQE6wQuxO7bN2
# edgKNAltHIAxH+IOVN3lofvlRxCtZJj/UBYufL8FIXrilUEnacOTj5XJjdibIa4N
# XJzwoq6GaIMMai27dmsAHZat8hZ79haDJLmIz2qoRzEvmtzjcT3XAH5iR9HOiMm4
# GPoOco3Boz2vAkBq/2mbluIQqBC0N1AI1sM9MIIGBzCCA++gAwIBAgIKYRZoNAAA
# AAAAHDANBgkqhkiG9w0BAQUFADBfMRMwEQYKCZImiZPyLGQBGRYDY29tMRkwFwYK
# CZImiZPyLGQBGRYJbWljcm9zb2Z0MS0wKwYDVQQDEyRNaWNyb3NvZnQgUm9vdCBD
# ZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDcwNDAzMTI1MzA5WhcNMjEwNDAzMTMw
# MzA5WjB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSEwHwYD
# VQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EwggEiMA0GCSqGSIb3DQEBAQUA
# A4IBDwAwggEKAoIBAQCfoWyx39tIkip8ay4Z4b3i48WZUSNQrc7dGE4kD+7Rp9FM
# rXQwIBHrB9VUlRVJlBtCkq6YXDAm2gBr6Hu97IkHD/cOBJjwicwfyzMkh53y9Gcc
# LPx754gd6udOo6HBI1PKjfpFzwnQXq/QsEIEovmmbJNn1yjcRlOwhtDlKEYuJ6yG
# T1VSDOQDLPtqkJAwbofzWTCd+n7Wl7PoIZd++NIT8wi3U21StEWQn0gASkdmEScp
# ZqiX5NMGgUqi+YSnEUcUCYKfhO1VeP4Bmh1QCIUAEDBG7bfeI0a7xC1Un68eeEEx
# d8yb3zuDk6FhArUdDbH895uyAc4iS1T/+QXDwiALAgMBAAGjggGrMIIBpzAPBgNV
# HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQjNPjZUkZwCu1A+3b7syuwwzWzDzALBgNV
# HQ8EBAMCAYYwEAYJKwYBBAGCNxUBBAMCAQAwgZgGA1UdIwSBkDCBjYAUDqyCYEBW
# J5flJRP8KuEKU5VZ5KShY6RhMF8xEzARBgoJkiaJk/IsZAEZFgNjb20xGTAXBgoJ
# kiaJk/IsZAEZFgltaWNyb3NvZnQxLTArBgNVBAMTJE1pY3Jvc29mdCBSb290IENl
# cnRpZmljYXRlIEF1dGhvcml0eYIQea0WoUqgpa1Mc1j0BxMuZTBQBgNVHR8ESTBH
# MEWgQ6BBhj9odHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0
# cy9taWNyb3NvZnRyb290Y2VydC5jcmwwVAYIKwYBBQUHAQEESDBGMEQGCCsGAQUF
# BzAChjhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY3Jvc29m
# dFJvb3RDZXJ0LmNydDATBgNVHSUEDDAKBggrBgEFBQcDCDANBgkqhkiG9w0BAQUF
# AAOCAgEAEJeKw1wDRDbd6bStd9vOeVFNAbEudHFbbQwTq86+e4+4LtQSooxtYrhX
# AstOIBNQmd16QOJXu69YmhzhHQGGrLt48ovQ7DsB7uK+jwoFyI1I4vBTFd1Pq5Lk
# 541q1YDB5pTyBi+FA+mRKiQicPv2/OR4mS4N9wficLwYTp2OawpylbihOZxnLcVR
# DupiXD8WmIsgP+IHGjL5zDFKdjE9K3ILyOpwPf+FChPfwgphjvDXuBfrTot/xTUr
# XqO/67x9C0J71FNyIe4wyrt4ZVxbARcKFA7S2hSY9Ty5ZlizLS/n+YWGzFFW6J1w
# lGysOUzU9nm/qhh6YinvopspNAZ3GmLJPR5tH4LwC8csu89Ds+X57H2146SodDW4
# TsVxIxImdgs8UoxxWkZDFLyzs7BNZ8ifQv+AeSGAnhUwZuhCEl4ayJ4iIdBD6Svp
# u/RIzCzU2DKATCYqSCRfWupW76bemZ3KOm+9gSd0BhHudiG/m4LBJ1S2sWo9iaF2
# YbRuoROmv6pH8BJv/YoybLL+31HIjCPJZr2dHYcSZAI9La9Zj7jkIeW1sMpjtHhU
# BdRBLlCslLCleKuzoJZ1GtmShxN1Ii8yqAhuoFuMJb+g74TKIdbrHk/Jmu5J4PcB
# ZW+JC33Iacjmbuqnl84xKf8OxVtc2E0bodj6L54/LlUWa8kTo/0xggSXMIIEkwIB
# ATCBkDB5MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSMwIQYD
# VQQDExpNaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQQITMwAAAXglWrXNI8ZflQAB
# AAABeDAJBgUrDgMCGgUAoIGwMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwG
# CisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMCMGCSqGSIb3DQEJBDEWBBQ7sfSp
# jLQE41gJ5Ve2CBJ7eWxaXjBQBgorBgEEAYI3AgEMMUIwQKAWgBQAUABvAHcAZQBy
# AFMAaABlAGwAbKEmgCRodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vUG93ZXJTaGVs
# bCAwDQYJKoZIhvcNAQEBBQAEggEAfJSRs84ik4mwa16rTKVMHT8W5LlF2APYMgCg
# GUWLn3+pvY03jXGe3qWsf4CZ6HG37v7prEV1/aFwOgkuc8gXDOtlx8qpmgJVplps
# 5bSK3G7dzlLxqCypm3p50uKRh1m/5lZw9zGo2HcDkhEaTNB6RC6whMALNCSP5Loa
# ck1iEAW8Ujzkq8CrL9GsGLsx3RydkEc6kHebXgTNzdFNOObB4JhoVaxuxOUY3xcd
# EVeUHbDUaq3TGWXJ6EM0W10Vv8oDh22KN7UW0TMpKInuWVWwRfP+MeyCIXUdr+sW
# o0cZq8XLDatXLtOqK+sV3uoxlQ+1s3BCGfX3i/iueW7ZScm3RKGCAigwggIkBgkq
# hkiG9w0BCQYxggIVMIICEQIBATCBjjB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMK
# V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0
# IENvcnBvcmF0aW9uMSEwHwYDVQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EC
# EzMAAAC+pA/1yaUO4TAAAAAAAL4wCQYFKw4DAhoFAKBdMBgGCSqGSIb3DQEJAzEL
# BgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTE3MTAxMDIwNDc1MlowIwYJKoZI
# hvcNAQkEMRYEFDpfn62rM+gcbNEILns0uj4hbwarMA0GCSqGSIb3DQEBBQUABIIB
# AE1wO8jiLGcvBEhIxy9KpFaWy1ic2MwxwQAy4RUI3YrlB/o4Ta4G3EXJqGUjqAza
# Qf44DPdmaPtMDzELIvVOE3mTKRwo0kSCyUdwLZtMS7owvvYo7ZkOGH+ulBwHZ4XY
# rM8bFQ6kujVE2JI15Q/ceNADOA13ACWKY8K1tlqt1TYdq7UN0x7gKYVm3zULc/9O
# K2vGnIK05nNZVPT8/JDkVcjmWIErgZ0gnxFFjmVze9jYge1dUKPEwEJo49eElqm1
# z7Hc/h1bf2Y6LW11Hv8ruSOslRknL0cmnpHid7LUWnIVAR0aykU7Hg1Ru879Ekjb
# EwHxiOE4WvcH4ZjLjYoNOd0=
# SIG # End signature block