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 |