├── .gitattributes ├── .github ├── FUNDING.yml └── workflows │ ├── build.yml │ └── validate.yml ├── .gitignore ├── AutoRest ├── AutoRest.psd1 ├── AutoRest.psm1 ├── bin │ └── readme.md ├── changelog.md ├── en-us │ ├── about_AutoRest.help.txt │ └── strings.psd1 ├── functions │ ├── ConvertFrom-ARSwagger.ps1 │ ├── Export-ARCommand.ps1 │ └── readme.md ├── internal │ ├── classes │ │ ├── 00-ParameterType.ps1 │ │ ├── 01-CommandParameter.ps1 │ │ └── 02-Command.ps1 │ ├── configurations │ │ ├── configuration.ps1 │ │ └── readme.md │ ├── functions │ │ └── readme.md │ ├── scriptblocks │ │ └── scriptblocks.ps1 │ ├── scripts │ │ ├── license.ps1 │ │ ├── postimport.ps1 │ │ ├── preimport.ps1 │ │ └── strings.ps1 │ ├── tepp │ │ ├── assignment.ps1 │ │ ├── example.tepp.ps1 │ │ └── readme.md │ └── tools │ │ └── ConvertTo-Hashtable.ps1 ├── readme.md ├── tests │ ├── functions │ │ └── readme.md │ ├── general │ │ ├── FileIntegrity.Exceptions.ps1 │ │ ├── FileIntegrity.Tests.ps1 │ │ ├── Help.Exceptions.ps1 │ │ ├── Help.Tests.ps1 │ │ ├── Manifest.Tests.ps1 │ │ ├── PSScriptAnalyzer.Tests.ps1 │ │ ├── strings.Exceptions.ps1 │ │ └── strings.Tests.ps1 │ ├── pester.ps1 │ └── readme.md ├── transformExamples │ ├── Demo-ParameterAdd.psd1 │ ├── Demo.psd1 │ ├── Gitea-swagger.v1.json │ └── readme.md └── xml │ ├── AutoRest.Format.ps1xml │ ├── AutoRest.Types.ps1xml │ └── readme.md ├── LICENSE ├── README.md ├── azFunctionResources ├── clientModule │ ├── function.ps1 │ ├── functions │ │ └── Connect-AutoRest.ps1 │ ├── internal │ │ ├── configurations │ │ │ └── connection.ps1 │ │ └── functions │ │ │ └── Get-InternalConnectionData.ps1 │ └── moduleroot.psm1 ├── functionOverride │ ├── Get-Example.json │ ├── Get-Example.ps1 │ └── Get-Example.psd1 ├── host-az.json ├── host.json ├── local.settings.json ├── profile.ps1 ├── profileFunctions │ ├── Convert-AzureFunctionParameter.ps1 │ └── Write-AzureFunctionOutput.ps1 ├── readme.md ├── requirements.psd1 └── run.ps1 ├── azure-pipelines.yml ├── backlogged ├── ConvertFrom-ARExcel.ps1 ├── ConvertTo-ARCommand.ps1 ├── New-Command.ps1 ├── New-CommandBody.ps1 ├── New-CommandHelpBlock.ps1 └── New-CommandParameterBlock.ps1 ├── build ├── AzureFunction.readme.md ├── vsts-build.ps1 ├── vsts-createFunctionClientModule.ps1 ├── vsts-packageFunction.ps1 ├── vsts-prerequisites.ps1 └── vsts-validate.ps1 ├── install.ps1 └── library └── AutoRest ├── AutoRest.sln └── AutoRest ├── AutoRest.csproj └── Class1.cs /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | FriedrichWeinmann 5 | patreon: # Replace with a single Patreon username 6 | open_collective: # Replace with a single Open Collective username 7 | ko_fi: # Replace with a single Ko-fi username 8 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 9 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 10 | liberapay: # Replace with a single Liberapay username 11 | issuehunt: # Replace with a single IssueHunt username 12 | otechie: # Replace with a single Otechie username 13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | 6 | jobs: 7 | build: 8 | 9 | runs-on: windows-2019 10 | 11 | steps: 12 | - uses: actions/checkout@v1 13 | - name: Install Prerequisites 14 | run: .\build\vsts-prerequisites.ps1 15 | shell: powershell 16 | - name: Validate 17 | run: .\build\vsts-validate.ps1 18 | shell: powershell 19 | - name: Build 20 | run: .\build\vsts-build.ps1 -ApiKey $env:APIKEY 21 | shell: powershell 22 | env: 23 | APIKEY: ${{ secrets.ApiKey }} 24 | -------------------------------------------------------------------------------- /.github/workflows/validate.yml: -------------------------------------------------------------------------------- 1 | on: [pull_request] 2 | 3 | jobs: 4 | validate: 5 | 6 | runs-on: windows-2019 7 | 8 | steps: 9 | - uses: actions/checkout@v1 10 | - name: Install Prerequisites 11 | run: .\build\vsts-prerequisites.ps1 12 | shell: powershell 13 | - name: Validate 14 | run: .\build\vsts-validate.ps1 15 | shell: powershell 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 |  2 | # ignore the settings folder and files for VSCode and PSS 3 | .vscode/* 4 | *.psproj 5 | *TempPoint* 6 | 7 | # Ignore staging info from Visual Studio 8 | library/AutoRest/.vs/* 9 | library/AutoRest/AutoRest/bin/* 10 | library/AutoRest/AutoRest/obj/* 11 | 12 | # ignore PowerShell Studio MetaData 13 | AutoRest/AutoRest.psproj 14 | AutoRest/AutoRest.psproj.bak 15 | AutoRest/AutoRest.psprojs 16 | AutoRest/AutoRest.psproj 17 | 18 | # ignore the TestResults 19 | TestResults/* 20 | 21 | # ignore the publishing Directory 22 | publish/* -------------------------------------------------------------------------------- /AutoRest/AutoRest.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | # Script module or binary module file associated with this manifest 3 | RootModule = 'AutoRest.psm1' 4 | 5 | # Version number of this module. 6 | ModuleVersion = '1.0.4' 7 | 8 | # ID used to uniquely identify this module 9 | GUID = '18c33632-995c-4b5e-82fb-c52c2f6a176f' 10 | 11 | # Author of this module 12 | Author = 'Friedrich Weinmann' 13 | 14 | # Company or vendor of this module 15 | CompanyName = 'Microsoft' 16 | 17 | # Copyright statement for this module 18 | Copyright = 'Copyright (c) 2021 Friedrich Weinmann' 19 | 20 | # Description of the functionality provided by this module 21 | Description = 'Automate command creation wrapping a REST api' 22 | 23 | # Minimum version of the Windows PowerShell engine required by this module 24 | PowerShellVersion = '5.1' 25 | 26 | # Modules that must be imported into the global environment prior to importing 27 | # this module 28 | RequiredModules = @( 29 | @{ ModuleName = 'PSFramework'; ModuleVersion = '1.6.214' } 30 | # @{ ModuleName = 'ImportExcel'; ModuleVersion = '7.1.0' } 31 | @{ ModuleName = 'String'; ModuleVersion = '1.0.0' } 32 | ) 33 | 34 | # Assemblies that must be loaded prior to importing this module 35 | # RequiredAssemblies = @('bin\AutoRest.dll') 36 | 37 | # Type files (.ps1xml) to be loaded when importing this module 38 | # TypesToProcess = @('xml\AutoRest.Types.ps1xml') 39 | 40 | # Format files (.ps1xml) to be loaded when importing this module 41 | # FormatsToProcess = @('xml\AutoRest.Format.ps1xml') 42 | 43 | # Functions to export from this module 44 | FunctionsToExport = @( 45 | 'ConvertFrom-ARSwagger' 46 | 'Export-ARCommand' 47 | ) 48 | 49 | # Private data to pass to the module specified in ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. 50 | PrivateData = @{ 51 | 52 | #Support for PowerShellGet galleries. 53 | PSData = @{ 54 | 55 | # Tags applied to this module. These help with module discovery in online galleries. 56 | Tags = @('rest', 'codegen') 57 | 58 | # A URL to the license for this module. 59 | LicenseUri = 'https://github.com/FriedrichWeinmann/AutoRest/blob/master/LICENSE' 60 | 61 | # A URL to the main website for this project. 62 | ProjectUri = 'https://github.com/FriedrichWeinmann/AutoRest' 63 | 64 | # A URL to an icon representing this module. 65 | # IconUri = '' 66 | 67 | # ReleaseNotes of this module 68 | # ReleaseNotes = '' 69 | 70 | } # End of PSData hashtable 71 | 72 | } # End of PrivateData hashtable 73 | } -------------------------------------------------------------------------------- /AutoRest/AutoRest.psm1: -------------------------------------------------------------------------------- 1 | $script:ModuleRoot = $PSScriptRoot 2 | $script:ModuleVersion = (Import-PowerShellDataFile -Path "$($script:ModuleRoot)\AutoRest.psd1").ModuleVersion 3 | 4 | # Detect whether at some level dotsourcing was enforced 5 | $script:doDotSource = Get-PSFConfigValue -FullName AutoRest.Import.DoDotSource -Fallback $false 6 | if ($AutoRest_dotsourcemodule) { $script:doDotSource = $true } 7 | 8 | <# 9 | Note on Resolve-Path: 10 | All paths are sent through Resolve-Path/Resolve-PSFPath in order to convert them to the correct path separator. 11 | This allows ignoring path separators throughout the import sequence, which could otherwise cause trouble depending on OS. 12 | Resolve-Path can only be used for paths that already exist, Resolve-PSFPath can accept that the last leaf my not exist. 13 | This is important when testing for paths. 14 | #> 15 | 16 | # Detect whether at some level loading individual module files, rather than the compiled module was enforced 17 | $importIndividualFiles = Get-PSFConfigValue -FullName AutoRest.Import.IndividualFiles -Fallback $false 18 | if ($AutoRest_importIndividualFiles) { $importIndividualFiles = $true } 19 | if (Test-Path (Resolve-PSFPath -Path "$($script:ModuleRoot)\..\.git" -SingleItem -NewChild)) { $importIndividualFiles = $true } 20 | if ("" -eq '') { $importIndividualFiles = $true } 21 | 22 | function Import-ModuleFile 23 | { 24 | <# 25 | .SYNOPSIS 26 | Loads files into the module on module import. 27 | 28 | .DESCRIPTION 29 | This helper function is used during module initialization. 30 | It should always be dotsourced itself, in order to proper function. 31 | 32 | This provides a central location to react to files being imported, if later desired 33 | 34 | .PARAMETER Path 35 | The path to the file to load 36 | 37 | .EXAMPLE 38 | PS C:\> . Import-ModuleFile -File $function.FullName 39 | 40 | Imports the file stored in $function according to import policy 41 | #> 42 | [CmdletBinding()] 43 | Param ( 44 | [string] 45 | $Path 46 | ) 47 | 48 | $resolvedPath = $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($Path).ProviderPath 49 | if ($doDotSource) { . $resolvedPath } 50 | else { $ExecutionContext.InvokeCommand.InvokeScript($false, ([scriptblock]::Create([io.file]::ReadAllText($resolvedPath))), $null, $null) } 51 | } 52 | 53 | #region Load individual files 54 | if ($importIndividualFiles) 55 | { 56 | # Execute Preimport actions 57 | foreach ($path in (& "$ModuleRoot\internal\scripts\preimport.ps1")) { 58 | . Import-ModuleFile -Path $path 59 | } 60 | 61 | # Import all internal functions 62 | foreach ($function in (Get-ChildItem "$ModuleRoot\internal\functions" -Filter "*.ps1" -Recurse -ErrorAction Ignore)) 63 | { 64 | . Import-ModuleFile -Path $function.FullName 65 | } 66 | 67 | # Import all public functions 68 | foreach ($function in (Get-ChildItem "$ModuleRoot\functions" -Filter "*.ps1" -Recurse -ErrorAction Ignore)) 69 | { 70 | . Import-ModuleFile -Path $function.FullName 71 | } 72 | 73 | # Execute Postimport actions 74 | foreach ($path in (& "$ModuleRoot\internal\scripts\postimport.ps1")) { 75 | . Import-ModuleFile -Path $path 76 | } 77 | 78 | # End it here, do not load compiled code below 79 | return 80 | } 81 | #endregion Load individual files 82 | 83 | #region Load compiled code 84 | "" 85 | #endregion Load compiled code -------------------------------------------------------------------------------- /AutoRest/bin/readme.md: -------------------------------------------------------------------------------- 1 | # bin folder 2 | 3 | The bin folder exists to store binary data. And scripts related to the type system. 4 | 5 | This may include your own C#-based library, third party libraries you want to include (watch the license!), or a script declaring type accelerators (effectively aliases for .NET types) 6 | 7 | For more information on Type Accelerators, see the help on Set-PSFTypeAlias -------------------------------------------------------------------------------- /AutoRest/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 1.0.4 (2024-03-18) 4 | 5 | + Upd: Export-ARCommand - Added `Start-` to the list of verbs that defaults to skip ShouldProcess requirements. 6 | + Fix: Export-ARCommand - ConvertToHashtableCommand is not respected for translating common parameters 7 | 8 | ## 1.0.2 9 | 10 | + Upd: Added support to pass through some common parameters via `PassThruActions` command setting 11 | + Fix: ConvertFrom-ARSwagger errors when parameterset keys are empty 12 | 13 | ## 1.0.0 (2022-04-14) 14 | 15 | + Upd: Added Support to disable any PSSA rule per command config 'PssaRulesIgnored' (string[]) 16 | + Upd: Added Support for ShouldProcess 17 | + Upd: Automatically include a PSScriptAnalyzer exemption for ShouldProcess in commands that have state-changing verbs, unless ShouldProcess is provided for 18 | + Upd: Disabled message integration when parsing swagger files. Added configuration setting to enable it again. Performance optimization. (Thank you @nohwnd; #8) 19 | + Fix: Error when overriding parameters on a secondary parameterset 20 | + Fix: Fails to apply override example help for secondary parametersets 21 | 22 | ## 0.2.0 (2022-02-14) 23 | 24 | + New: Support header parameters (#13 | @Callidus2000) 25 | + New: Support adding parameters to pass through (#13 | @Callidus2000) 26 | + Upd: ConvertFrom-ARSwagger - Added ability to select hashtable processing command (#13 | @Callidus2000) 27 | + Other: Added docs and examples (#13 | @Callidus2000) 28 | 29 | ## 0.1.4 (2021-10-01) 30 | 31 | + Upd: Added option to export commands without help 32 | + Fix: Example not included in help when command has no parameters 33 | + Fix: Parameter-Type defaults to object if not specified 34 | + Fix: Fails to resolve referenced parameter 35 | 36 | ## 0.1.0 (2021-09-30) 37 | 38 | + Initial Release 39 | -------------------------------------------------------------------------------- /AutoRest/en-us/about_AutoRest.help.txt: -------------------------------------------------------------------------------- 1 | TOPIC 2 | about_AutoRest 3 | 4 | SHORT DESCRIPTION 5 | Explains how to use the AutoRest powershell module 6 | 7 | LONG DESCRIPTION 8 | 9 | 10 | KEYWORDS 11 | AutoRest -------------------------------------------------------------------------------- /AutoRest/en-us/strings.psd1: -------------------------------------------------------------------------------- 1 | # This is where the strings go, that are written by 2 | # Write-PSFMessage, Stop-PSFFunction or the PSFramework validation scriptblocks 3 | @{ 4 | 'key' = 'Value' 5 | } -------------------------------------------------------------------------------- /AutoRest/functions/ConvertFrom-ARSwagger.ps1: -------------------------------------------------------------------------------- 1 | function ConvertFrom-ARSwagger { 2 | <# 3 | .SYNOPSIS 4 | Parse a swagger file and generate commands from it. 5 | 6 | .DESCRIPTION 7 | Parse a swagger file and generate commands from it. 8 | Only supports the JSON format of swagger file. 9 | 10 | .PARAMETER Path 11 | Path to the swagger file(s) to process. 12 | 13 | .PARAMETER TransformPath 14 | Path to a folder containing psd1 transform files. 15 | These can be used to override or add to individual entries from the swagger file. 16 | For example, you can add help URI, fix missing descriptions, add parameter help or attributes... 17 | 18 | .PARAMETER RestCommand 19 | Name of the command executing the respective REST queries. 20 | All autogenerated commands will call this command to execute. 21 | 22 | .PARAMETER ConvertToHashtableCommand 23 | In order to make it easier to include a version of `ConvertTo-Hashtable` you can rename the used 24 | function name by using this parameter. Defaults to `ConvertTo-Hashtable`. 25 | 26 | .PARAMETER ModulePrefix 27 | A prefix to add to all commands generated from this command. 28 | 29 | .PARAMETER PathPrefix 30 | Swagger files may include the same first uri segments in all endpoints. 31 | While this could be just passed through, you can also remove them using this parameter. 32 | It is then assumed, that the command used in the RestCommand is aware of this and adds it again to the request. 33 | Example: 34 | All endpoints in the swagger-file start with "/api/" 35 | "/api/users", "/api/machines", "/api/software", ... 36 | In that case, it could make sense to remove the "/api/" part from all commands and just include it in the invokation command. 37 | 38 | .PARAMETER ServiceName 39 | Adds the servicename to the commands generated. 40 | When exported, they will be hardcoded to execute as that service. 41 | This simplifies the configuration of the output, but prevents using multiple connections to different instances or under different privileges at the same time. 42 | 43 | .EXAMPLE 44 | PS C:\> Get-ChildItem .\swaggerfiles | ConvertFrom-ARSwagger -Transformpath .\transform -RestCommand Invoke-ARRestRequest -ModulePrefix Mg -PathPrefix '/api/' 45 | 46 | Picks up all items in the subfolder "swaggerfiles" and converts it to PowerShell command objects. 47 | Applies all transforms in the subfolder transform. 48 | Uses the "Invoke-ARRestRequest" command for all rest requests. 49 | #> 50 | [CmdletBinding()] 51 | param ( 52 | [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] 53 | [PsfValidateScript('PSFramework.Validate.FSPath.File', ErrorString = 'PSFramework.Validate.FSPath.File')] 54 | [Alias('FullName')] 55 | [string] 56 | $Path, 57 | 58 | [PsfValidateScript('PSFramework.Validate.FSPath.Folder', ErrorString = 'PSFramework.Validate.FSPath.Folder')] 59 | [string] 60 | $TransformPath, 61 | 62 | [Parameter(Mandatory = $true)] 63 | [string] 64 | $RestCommand, 65 | 66 | [string] 67 | $ConvertToHashtableCommand = 'ConvertTo-Hashtable', 68 | 69 | [string] 70 | $ModulePrefix, 71 | 72 | [string] 73 | $PathPrefix, 74 | 75 | [string] 76 | $ServiceName 77 | ) 78 | 79 | begin { 80 | #region Functions 81 | function Copy-ParameterConfig { 82 | [CmdletBinding()] 83 | param ( 84 | [Hashtable] 85 | $Config, 86 | 87 | $Parameter 88 | ) 89 | 90 | if ($Config.Help) { $Parameter.Help = $Config.Help } 91 | if ($Config.Name) { $Parameter.Name = $Config.Name } 92 | if ($Config.Alias) { $Parameter.Alias = $Config.Alias } 93 | if ($Config.Weight) { $Parameter.Weight = $Config.Weight } 94 | if ($Config.ParameterType) { $Parameter.ParameterType = $Config.ParameterType } 95 | if ($Config.ContainsKey('ValueFromPipeline')) { $Parameter.ValueFromPipeline = $Config.ValueFromPipeline } 96 | if ($Config.ParameterSet) { $Parameter.ParameterSet = $Config.ParameterSet } 97 | } 98 | 99 | function New-Parameter { 100 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")] 101 | [CmdletBinding()] 102 | param ( 103 | [string] 104 | $Name, 105 | 106 | [string] 107 | $Help, 108 | 109 | [string] 110 | $ParameterType, 111 | 112 | [AllowEmptyString()] 113 | [AllowNull()] 114 | [string] 115 | $ParameterFormat, 116 | 117 | [bool] 118 | $Mandatory, 119 | 120 | [ParameterType] 121 | $Type 122 | ) 123 | 124 | $parameter = [CommandParameter]::new( 125 | $Name, 126 | $Help, 127 | $ParameterType, 128 | $Mandatory, 129 | $Type 130 | ) 131 | if ($parameter.ParameterType -eq "integer") { 132 | $parameter.ParameterType = $ParameterFormat 133 | } 134 | $parameter 135 | } 136 | 137 | function Resolve-ParameterReference { 138 | [CmdletBinding()] 139 | param ( 140 | [string] 141 | $Ref, 142 | 143 | $SwaggerObject 144 | ) 145 | 146 | # "#/components/parameters/top" 147 | $segments = $Ref | Set-String -OldValue '^#/' | Split-String -Separator '/' 148 | $paramValue = $SwaggerObject 149 | foreach ($segment in $segments) { 150 | $paramValue = $paramValue.$segment 151 | } 152 | $paramValue 153 | } 154 | 155 | function Read-Parameters { 156 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '')] 157 | [CmdletBinding()] 158 | param ( 159 | [Command] 160 | $CommandObject, 161 | 162 | $Parameters, 163 | 164 | $SwaggerObject, 165 | 166 | [PSFramework.Message.MessageLevel] 167 | $LogLevel, 168 | 169 | [string] 170 | $ParameterSet 171 | ) 172 | 173 | foreach ($parameter in $Parameters) { 174 | if ($parameter.'$ref') { 175 | $parameter = Resolve-ParameterReference -Ref $parameter.'$ref' -SwaggerObject $SwaggerObject 176 | if (-not $parameter) { 177 | Write-PSFMessage -Level Warning -Message " Unable to resolve referenced parameter $($parameter.'$ref')" 178 | continue 179 | } 180 | } 181 | if ($LogLevel -le [PSFramework.Message.MessageLevel]::Verbose) { 182 | # This is on hot path. Checking if we should write the message in a cheap way. 183 | Write-PSFMessage " Processing Parameter: $($parameter.Name) ($($parameter.in))" 184 | } 185 | switch ($parameter.in) { 186 | #region Body 187 | body { 188 | foreach ($property in $parameter.schema.properties.PSObject.Properties) { 189 | if ($ParameterSet -and $CommandObject.Parameters[$property.Value.title]) { 190 | $CommandObject.Parameters[$property.Value.title].ParameterSet += @($ParameterSet) 191 | continue 192 | } 193 | 194 | $parameterParam = @{ 195 | Name = $property.Value.title 196 | Help = $property.Value.description 197 | ParameterType = $property.Value.type 198 | ParameterFormat = $property.Value.format 199 | Mandatory = $parameter.schema.required -contains $property.Value.title 200 | Type = 'Body' 201 | } 202 | $CommandObject.Parameters[$property.Value.title] = New-Parameter @parameterParam 203 | if ($ParameterSet) { 204 | $commandObject.Parameters[$property.Value.title].ParameterSet = @($ParameterSet) 205 | } 206 | } 207 | } 208 | #endregion Body 209 | 210 | #region Path 211 | path { 212 | if ($ParameterSet -and $CommandObject.Parameters[($parameter.name -replace '\s')]) { 213 | $CommandObject.Parameters[($parameter.name -replace '\s')].ParameterSet += @($ParameterSet) 214 | continue 215 | } 216 | 217 | $parameterParam = @{ 218 | Name = $parameter.Name -replace '\s' 219 | Help = $parameter.Description 220 | ParameterType = 'string' 221 | ParameterFormat = $parameter.format 222 | Mandatory = $parameter.required -as [bool] 223 | Type = 'Path' 224 | } 225 | $CommandObject.Parameters[($parameter.name -replace '\s')] = New-Parameter @parameterParam 226 | if ($ParameterSet) { 227 | $CommandObject.Parameters[($parameter.name -replace '\s')].ParameterSet = @($ParameterSet) 228 | } 229 | } 230 | #endregion Path 231 | 232 | #region Query 233 | query { 234 | if ($CommandObject.Parameters[$parameter.name]) { 235 | $CommandObject.Parameters[$parameter.name].ParameterSet += @($parameterSetName) 236 | continue 237 | } 238 | 239 | $parameterType = $parameter.type 240 | if (-not $parameterType -and $parameter.schema.type) { 241 | $parameterType = $parameter.schema.type 242 | if ($parameter.schema.type -eq "array" -and $parameter.schema.items.type) { 243 | $parameterType = '{0}[]' -f $parameter.schema.items.type 244 | } 245 | } 246 | 247 | $parameterParam = @{ 248 | Name = $parameter.Name 249 | Help = $parameter.Description 250 | ParameterType = $parameterType 251 | ParameterFormat = $parameter.format 252 | Mandatory = $parameter.required -as [bool] 253 | Type = 'Query' 254 | } 255 | $CommandObject.Parameters[$parameter.name] = New-Parameter @parameterParam 256 | if ($ParameterSet) { 257 | $CommandObject.Parameters[$parameter.name].ParameterSet = @($ParameterSet) 258 | } 259 | } 260 | #endregion Query 261 | 262 | #region Header 263 | header { 264 | if ($commandObject.Parameters[$parameter.name]) { 265 | $commandObject.Parameters[$parameter.name].ParameterSet += @($parameterSetName) 266 | continue 267 | } 268 | $parameterType = $parameter.type 269 | if (-not $parameterType -and $parameter.schema.type) { 270 | $parameterType = $parameter.schema.type 271 | if ($parameter.schema.type -eq "array" -and $parameter.schema.items.type) { 272 | $parameterType = '{0}[]' -f $parameter.schema.items.type 273 | } 274 | } 275 | $parameterParam = @{ 276 | Name = $parameter.Name 277 | Help = $parameter.Description 278 | ParameterType = $parameterType 279 | ParameterFormat = $parameter.format 280 | Mandatory = $parameter.required -as [bool] 281 | Type = 'header' 282 | } 283 | $commandObject.Parameters[$parameter.name] = New-Parameter @parameterParam 284 | $commandObject.Parameters[$parameter.name].ParameterSet = @($parameterSetName) 285 | } 286 | #endregion Header 287 | } 288 | } 289 | } 290 | 291 | function Set-ParameterOverrides { 292 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")] 293 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '')] 294 | [CmdletBinding()] 295 | param ( 296 | [hashtable] 297 | $Overrides, 298 | 299 | [Command] 300 | $CommandObject, 301 | 302 | [string] 303 | $CommandKey 304 | ) 305 | 306 | foreach ($parameterName in $Overrides.globalParameters.Keys) { 307 | if (-not $CommandObject.Parameters[$parameterName]) { continue } 308 | 309 | Copy-ParameterConfig -Config $Overrides.globalParameters[$parameterName] -Parameter $CommandObject.Parameters[$parameterName] 310 | } 311 | foreach ($partialPath in $Overrides.scopedParameters.Keys) { 312 | if ($CommandObject.EndpointUrl -notlike $partialPath) { continue } 313 | foreach ($parameterPair in $Overrides.scopedParameters.$($partialPath).GetEnumerator()) { 314 | if (-not $CommandObject.Parameters[$parameterPair.Name]) { continue } 315 | 316 | Copy-ParameterConfig -Parameter $CommandObject.Parameters[$parameterPair.Name] -Config $parameterPair.Value 317 | } 318 | } 319 | foreach ($parameterName in $Overrides.$CommandKey.Parameters.Keys) { 320 | if (-not $CommandObject.Parameters[$parameterName]) { 321 | Write-PSFMessage -Level Warning -Message "Invalid override parameter: $parameterName - unable to find parameter on $($CommandObject.Name)" -Target $commandObject 322 | continue 323 | } 324 | 325 | Copy-ParameterConfig -Config $Overrides.$CommandKey.Parameters[$parameterName] -Parameter $CommandObject.Parameters[$parameterName] 326 | } 327 | } 328 | 329 | function Set-CommandOverrides { 330 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")] 331 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '')] 332 | [CmdletBinding()] 333 | param ( 334 | [hashtable] 335 | $Overrides, 336 | 337 | [Command] 338 | $CommandObject, 339 | 340 | [string] 341 | $CommandKey 342 | ) 343 | 344 | $commandOverrides = $Overrides.$CommandKey 345 | 346 | # Apply Overrides 347 | foreach ($property in $CommandObject.PSObject.Properties) { 348 | if ($property.Name -eq 'Parameters') { continue } 349 | if ($property.Name -eq 'ParameterSets') { 350 | foreach ($key in $commandOverrides.ParameterSets.Keys | Write-Output) { 351 | if (-not $key) { continue } 352 | $CommandObject.ParameterSets[$key] = $commandOverrides.ParameterSets.$key 353 | } 354 | continue 355 | } 356 | $propertyOverride = $commandOverrides.($property.Name) 357 | if ($propertyOverride) { 358 | $property.Value = $propertyOverride 359 | } 360 | } 361 | } 362 | #endregion Functions 363 | 364 | $commands = @{ } 365 | $overrides = @{ } 366 | if ($TransformPath) { 367 | foreach ($file in Get-ChildItem -Path $TransformPath -Filter *.psd1) { 368 | $data = Import-PSFPowerShellDataFile -Path $file.FullName 369 | foreach ($key in $data.Keys) { 370 | $overrides[$key] = $data.$key 371 | } 372 | } 373 | } 374 | 375 | $verbs = @{ 376 | get = "Get" 377 | put = "New" 378 | post = "Set" 379 | patch = "Set" 380 | delete = "Remove" 381 | head = "Invoke" 382 | } 383 | 384 | [PSFramework.Message.MessageLevel]$logLevel = Get-PSFConfigValue -FullName AutoRest.Logging.Level -Fallback "Warning" 385 | } 386 | process { 387 | #region Process Swagger file 388 | foreach ($file in Resolve-PSFPath -Path $Path) { 389 | $data = Get-Content -Path $file | ConvertFrom-Json 390 | foreach ($endpoint in $data.paths.PSObject.Properties | Sort-Object { $_.Name.Length }, Name) { 391 | $endpointPath = ($endpoint.Name -replace "^$PathPrefix" -replace '/{[\w\s\d+-]+}$').Trim("/") 392 | $effectiveEndpointPath = ($endpoint.Name -replace "^$PathPrefix" -replace '\s' ).Trim("/") 393 | foreach ($method in $endpoint.Value.PSObject.Properties) { 394 | $commandKey = $endpointPath, $method.Name -join ":" 395 | if ($logLevel -le [PSFramework.Message.MessageLevel]::Verbose) { 396 | Write-PSFMessage "Processing Command: $($commandKey)" 397 | } 398 | #region Case: Existing Command 399 | if ($commands[$commandKey]) { 400 | $commandObject = $commands[$commandKey] 401 | $parameterSetName = $method.Value.operationId 402 | $commandObject.ParameterSets[$parameterSetName] = $method.Value.description 403 | 404 | Read-Parameters -CommandObject $commandObject -Parameters $method.Value.parameters -SwaggerObject $data -LogLevel $logLevel -ParameterSet $parameterSetName 405 | } 406 | #endregion Case: Existing Command 407 | 408 | #region Case: New Command 409 | else { 410 | $commandNouns = foreach ($element in $endpointPath -split "/") { 411 | if ($element -like "{*}") { continue } 412 | [cultureinfo]::CurrentUICulture.TextInfo.ToTitleCase($element) -replace 's$' -replace '\$' 413 | } 414 | $commandObject = [Command]@{ 415 | Name = "$($verbs[$method.Name])-$($ModulePrefix)$($commandNouns -join '')" 416 | Synopsis = $method.Value.summary 417 | Description = $method.Value.description 418 | Method = $method.Name 419 | EndpointUrl = $effectiveEndpointPath 420 | RestCommand = $RestCommand 421 | ParameterSets = @{ 422 | 'default' = $method.Value.description 423 | } 424 | ConvertToHashtableCommand = $ConvertToHashtableCommand 425 | } 426 | if ($ServiceName) { $commandObject.ServiceName = $ServiceName } 427 | $commands[$commandKey] = $commandObject 428 | 429 | foreach ($property in $commands[$commandKey].PSObject.Properties) { 430 | if ($property.Name -eq 'Parameters') { continue } 431 | if ($overrides.$commandKey.$($property.Name)) { $commandObject.$($property.Name) = $overrides.$commandKey.$($property.Name) } 432 | } 433 | 434 | # Parameters 435 | Read-Parameters -CommandObject $commandObject -Parameters $method.Value.parameters -SwaggerObject $data -LogLevel $logLevel 436 | } 437 | #endregion Case: New Command 438 | 439 | if ($logLevel -le [PSFramework.Message.MessageLevel]::Verbose) { 440 | Write-PSFMessage -Message "Finished processing $($endpointPath) : $($method.Name) --> $($commandObject.Name)" -Target $commandObject -Data @{ 441 | Overrides = $overrides 442 | CommandObject = $commandObject 443 | } -Tag done 444 | } 445 | } 446 | } 447 | } 448 | #endregion Process Swagger file 449 | } 450 | end { 451 | foreach ($pair in $commands.GetEnumerator()) { 452 | Set-ParameterOverrides -Overrides $overrides -CommandObject $pair.Value -CommandKey $pair.Key 453 | Set-CommandOverrides -Overrides $overrides -CommandObject $pair.Value -CommandKey $pair.Key 454 | } 455 | $commands.Values 456 | } 457 | } -------------------------------------------------------------------------------- /AutoRest/functions/Export-ARCommand.ps1: -------------------------------------------------------------------------------- 1 | function Export-ARCommand { 2 | <# 3 | .SYNOPSIS 4 | Writes AutoRest Command objects to file as a function definition. 5 | 6 | .DESCRIPTION 7 | Writes AutoRest Command objects to file as a function definition. 8 | 9 | To generate AutoRest Command objects, use a parsing command such as ConvertFrom-ARSwagger. 10 | 11 | .PARAMETER Path 12 | The Path in which the resulting set of commands should be placed. 13 | 14 | .PARAMETER GroupByEndpoint 15 | By default, each command will be placed in the OutPath folder. 16 | Setting this parameter will instead create a folder for the first element in each endpoint path and group the output by that. 17 | 18 | .PARAMETER Force 19 | Overwrite existing files. 20 | By default, this command will skip files of commands that were already created. 21 | Setting the -Force parameter enforces those being overwritten, updating the command, but discarding any manual edits. 22 | 23 | .PARAMETER NoHelp 24 | Export commands without generating help. 25 | 26 | .PARAMETER Command 27 | The command object(s) to write to file. 28 | Can be generated using the ConvertFrom-ARSwagger command. 29 | 30 | .EXAMPLE 31 | PS C:\> $commands | Export-ARCommand 32 | 33 | Exports all the commands specified to the current folder. 34 | 35 | .EXAMPLE 36 | PS C:\> ConvertFrom-ARSwagger @param | Export-ARCommand -Path C:\Code\modules\MyApi\functions -GroupByEndpoint -Force 37 | 38 | Takes the output of ConvertFrom-ARSwagger and writes it to the specified folder, creating a subfolder for each top-level api endpoint node. 39 | Existing files will be overwritten. 40 | #> 41 | [CmdletBinding()] 42 | param ( 43 | [PsfValidateScript('PSFramework.Validate.FSPath.Folder', ErrorString = 'PSFramework.Validate.FSPath.Folder')] 44 | [string] 45 | $Path = '.', 46 | 47 | [switch] 48 | $GroupByEndpoint, 49 | 50 | [switch] 51 | $Force, 52 | 53 | [switch] 54 | $NoHelp, 55 | 56 | [Parameter(Mandatory = $true, ValueFromPipeline = $true)] 57 | [PSFramework.Utility.TypeTransformationAttribute([Command])] 58 | [Command[]] 59 | $Command 60 | ) 61 | 62 | begin { 63 | $encoding = [System.Text.UTF8Encoding]::new($true) 64 | $targetFolder = Resolve-PSFPath $Path 65 | [PSFramework.Message.MessageLevel]$logLevel = Get-PSFConfigValue -FullName AutoRest.Logging.Level -Fallback "Warning" 66 | } 67 | process { 68 | foreach ($commandObject in $Command) { 69 | $folder = if ($GroupByEndpoint) { 70 | Join-Path -Path (Resolve-PSFPath $Path) -ChildPath ($commandObject.EndpointUrl -split "/" | Select-Object -First 1) 71 | } 72 | else { 73 | $targetFolder 74 | } 75 | 76 | if (-not ([System.IO.Directory]::Exists($folder))) { 77 | $null = New-Item -Path $folder -ItemType Directory -Force 78 | } 79 | 80 | $filePath = Join-Path -Path $folder -ChildPath "$($commandObject.Name).ps1" 81 | if (-not $Force -and (Test-Path -Path $filePath)) { 82 | if ($logLevel -le [PSFramework.Message.MessageLevel]::Verbose) { 83 | Write-PSFMessage -Message "Skipping $($commandObject.Name), as $filePath already exists." -Target $commandObject 84 | } 85 | continue 86 | } 87 | if ($logLevel -le [PSFramework.Message.MessageLevel]::Verbose) { 88 | Write-PSFMessage -Message "Writing $($commandObject.Name) to $filePath" -Target $commandObject 89 | } 90 | try { [System.IO.File]::WriteAllText($filePath, $commandObject.ToCommand($NoHelp.ToBool()), $encoding) } 91 | catch { Write-PSFMessage -Level Warning -Message $_ -ErrorRecord $_ -EnableException $true -PSCmdlet $PSCmdlet -Target $commandObject -Data @{ Path = $filePath } } 92 | } 93 | } 94 | } -------------------------------------------------------------------------------- /AutoRest/functions/readme.md: -------------------------------------------------------------------------------- 1 | # Functions 2 | 3 | This is the folder where the functions go. 4 | 5 | Depending on the complexity of the module, it is recommended to subdivide them into subfolders. 6 | 7 | The module will pick up all .ps1 files recursively -------------------------------------------------------------------------------- /AutoRest/internal/classes/00-ParameterType.ps1: -------------------------------------------------------------------------------- 1 | enum ParameterType { 2 | Body 3 | Path 4 | Query 5 | Header 6 | Misc 7 | } -------------------------------------------------------------------------------- /AutoRest/internal/classes/01-CommandParameter.ps1: -------------------------------------------------------------------------------- 1 | class CommandParameter { 2 | [string]$Name 3 | [string]$SystemName 4 | [string]$Help = '' 5 | [string]$ParameterType = 'string' 6 | [string[]]$ParameterSet = @('default') 7 | [string[]]$Alias 8 | [bool]$Mandatory 9 | [bool]$ValueFromPipeline 10 | [int]$Weight = 1000 11 | [ParameterType]$Type 12 | 13 | [string]ToExample() { 14 | return '-{0} ${1}' -f $this.Name, $this.Name.ToLower() 15 | } 16 | 17 | [string]ToHelp() { 18 | return @' 19 | .PARAMETER {0} 20 | {1} 21 | '@ -f $this.Name, $this.Help 22 | } 23 | 24 | [string]ToParam() { 25 | $sb = [System.Text.StringBuilder]::new() 26 | $mandatoryString = '' 27 | if ($this.Mandatory) { $mandatoryString = 'Mandatory = $true, ' } 28 | $pipelineString = '' 29 | if ($this.ValueFromPipeline) { $pipelineString = 'ValueFromPipeline = $true, ' } 30 | foreach ($set in $this.ParameterSet) { 31 | $null = $sb.AppendLine((" [Parameter({0}{1}ValueFromPipelineByPropertyName = `$true, ParameterSetName = '{2}')]" -f $mandatoryString, $pipelineString, $set)) 32 | } 33 | if ($this.Alias) { $null = $sb.AppendLine(" [Alias($($this.Alias | Add-String "'" "'" | Join-String ','))]") } 34 | $typeName = $this.ParameterType 35 | if (-not $typeName) { $typeName = 'object' } 36 | $null = $sb.AppendLine(" [$typeName]") 37 | $null = $sb.Append(" `$$($this.Name)") 38 | return $sb.ToString() 39 | } 40 | 41 | CommandParameter() { } 42 | 43 | CommandParameter( 44 | [string]$Name 45 | ) { 46 | $this.SystemName = $Name 47 | $this.Name = $Name.Trim('$') | Split-String -Separator "\s" | ForEach-Object { 48 | $_.SubString(0, 1).ToUpper() + $_.Substring(1) 49 | } | Join-String -Separator "" 50 | } 51 | CommandParameter( 52 | [string]$Name, 53 | [string]$Help, 54 | [string]$ParameterType, 55 | [bool]$Mandatory, 56 | [ParameterType]$Type 57 | ) { 58 | $this.SystemName = $Name 59 | $this.Name = $Name.Trim('$') | Split-String -Separator "\s|-" | ForEach-Object { 60 | $_.SubString(0, 1).ToUpper() + $_.Substring(1) 61 | } | Join-String -Separator "" 62 | $this.Help = $Help 63 | $this.ParameterType = $ParameterType 64 | if ($Name -eq '$select' -and $ParameterType -eq 'string') { 65 | $this.ParameterType = 'string[]' 66 | } 67 | $this.Mandatory = $Mandatory 68 | $this.Type = $Type 69 | } 70 | } -------------------------------------------------------------------------------- /AutoRest/internal/classes/02-Command.ps1: -------------------------------------------------------------------------------- 1 | class Command { 2 | [string]$Name 3 | [string]$Synopsis 4 | [string]$Description 5 | [string]$DocumentationUrl = '' 6 | 7 | [string]$Method 8 | [string]$EndpointUrl 9 | [string]$ServiceName 10 | [string[]]$Scopes = @() 11 | 12 | [Hashtable]$Parameters = @{ } 13 | [Hashtable]$ParameterSets = @{ } 14 | 15 | [string]$RestCommand 16 | [string]$ProcessorCommand 17 | 18 | [string]$ShouldProcess 19 | [string]$ShouldProcessTarget 20 | [string[]]$PssaRulesIgnored 21 | [bool]$PassThruActions 22 | 23 | [string]$ConvertToHashtableCommand 24 | 25 | [string]ToExample() { 26 | $format = @' 27 | .EXAMPLE 28 | PS C:\> {0} 29 | 30 | {1} 31 | '@ 32 | $sets = @{ } 33 | foreach ($set in $this.ParameterSets.Keys) { 34 | $sets[$set] = @() 35 | } 36 | foreach ($parameter in $this.Parameters.Values) { 37 | foreach ($set in $parameter.ParameterSet) { 38 | if ($sets[$set]) { continue } 39 | $sets[$set] = @() 40 | } 41 | if (-not $parameter.Mandatory) { continue } 42 | 43 | foreach ($set in $parameter.ParameterSet) { 44 | $sets[$set] += $parameter 45 | } 46 | } 47 | 48 | $texts = foreach ($set in $sets.Keys) { 49 | $descriptionText = '' 50 | if ($this.ParameterSets[$set]) { $descriptionText = $this.ParameterSets[$set] } 51 | 52 | $commandText = (@($this.Name) + @(($sets[$set] | ForEach-Object ToExample))) -join " " 53 | 54 | $format -f $commandText, $descriptionText 55 | } 56 | 57 | return $texts -join "`n`n" 58 | } 59 | 60 | [string]ToHelp() { 61 | $format = @' 62 | <# 63 | .SYNOPSIS 64 | {0} 65 | 66 | .DESCRIPTION 67 | {1} 68 | 69 | {2} 70 | 71 | {3} 72 | 73 | .LINK 74 | {4} 75 | #> 76 | '@ 77 | $parameterHelp = $this.Parameters.Values | Sort-Object Weight | ForEach-Object ToHelp | Join-String -Separator "`n`n" 78 | if ($this.ShouldProcess) { 79 | $shouldPropcessHelp = @' 80 | .PARAMETER Confirm 81 | If this switch is enabled, you will be prompted for confirmation before executing any operations that change state. 82 | 83 | .PARAMETER WhatIf 84 | If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run. 85 | '@ 86 | $parameterHelp = $parameterHelp, $shouldPropcessHelp -join "`n`n" 87 | } 88 | $descriptionText = $this.Description 89 | if ($this.Scopes) { $descriptionText = $descriptionText, (' Scopes required (delegate auth): {0}' -f ($this.Scopes -join ", ")) -join "`n`n" } 90 | return $format -f $this.Synopsis, $descriptionText, $parameterHelp, $this.ToExample(), $this.DocumentationUrl 91 | } 92 | 93 | [string]ToParam() { 94 | if (-not $this.ShouldProcess -and $this.Name -match '^New-|^Remove-|^Set-|^Start-') { 95 | $this.PssaRulesIgnored = @($this.PssaRulesIgnored) + @('PSUseShouldProcessForStateChangingFunctions') 96 | } 97 | $pssaString = '' 98 | foreach ($rule in $this.PssaRulesIgnored) { 99 | if (-not "$rule") { continue } 100 | $pssaString += @" 101 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('$rule', '')] 102 | 103 | "@ 104 | } 105 | 106 | $supportsShouldProcess = '' 107 | if ($this.ShouldProcess) { $supportsShouldProcess = ', SupportsShouldProcess = $true' } 108 | return @" 109 | $pssaString [CmdletBinding(DefaultParameterSetName = 'default'$($supportsShouldProcess))] 110 | param ( 111 | $($this.Parameters.Values | Sort-Object Weight | ForEach-Object ToParam | Join-String ",`n`n") 112 | ) 113 | "@ 114 | } 115 | 116 | [string]ToProcess() { 117 | $bodyString = '@({0})' -f (($this.Parameters.Values | Where-Object Type -EQ Body).Name | Add-String "'" "'" | Join-String ",") 118 | $queryString = '@({0})' -f (($this.Parameters.Values | Where-Object Type -EQ Query).Name | Add-String "'" "'" | Join-String ",") 119 | $headerString = '@({0})' -f (($this.Parameters.Values | Where-Object Type -EQ Header).Name | Add-String "'" "'" | Join-String ",") 120 | [string]$pathReplacement = $this.Parameters.Values | Where-Object { 121 | $_.Type -eq 'Path' -and 122 | $this.EndpointUrl -like "*{$($_.SystemName)}*" 123 | } | Format-String -Format " -Replace '{{{0}}}',`${1}" -Property SystemName, Name | Join-String "" 124 | $pathModifier = '' 125 | if ($optionalParameter = $this.Parameters.Values | Where-Object { $_.Type -eq 'Path' -and $this.EndpointUrl -notlike "*{$($_.SystemName)}*" }) { 126 | $pathModifier = 'if (${0}) {{ $__param.Path += "/${0}" }}' -f $optionalParameter.Name 127 | } 128 | $actionsString = '' 129 | if ($this.PassThruActions) { $actionsString = "`$__param += `$PSBoundParameters | $($this.ConvertToHashtableCommand) -Include 'ErrorAction', 'WarningAction', 'Verbose'" } 130 | $scopesString = '' 131 | if ($this.Scopes) { $scopesString = 'RequiredScopes = {0}' -f ($this.Scopes | Add-String "'" "'" | Join-String ',') } 132 | $processorString = '' 133 | if ($this.ProcessorCommand) { $processorString = " | $($this.ProcessorCommand)" } 134 | $serviceString = '' 135 | if ($this.ServiceName) { $serviceString = "Service = '$($this.ServiceName)'" } 136 | $mappingString = $this.Parameters.Values | Where-Object Type -NE Path | Format-String -Format " '{0}' = '{1}'" -Property Name, SystemName | Join-String "`n" 137 | $miscParameterString = $this.Parameters.Values | Where-Object Type -eq Misc | Format-String -Format ' -{0} ${1}' -Property SystemName, Name | Join-String " " 138 | $shouldProcessString = '' 139 | if ($this.ShouldProcess) { 140 | $target = $this.ShouldProcessTarget 141 | if (-not $target) { $target = '${0}' -f @(@($this.Parameters.Keys).Where{ $_ -like "*ID" })[0] } 142 | if ($target -eq '$') { $target = '' } 143 | $shouldProcessString = ' if (-not $PSCmdlet.ShouldProcess("{0}","{1}")) {{ return }}' -f $target, $this.ShouldProcess 144 | } 145 | 146 | return @" 147 | `$__mapping = @{ 148 | $mappingString 149 | } 150 | 151 | `$__param = @{ 152 | Body = `$PSBoundParameters | $($this.ConvertToHashtableCommand) -Include $bodyString -Mapping `$__mapping 153 | Query = `$PSBoundParameters | $($this.ConvertToHashtableCommand) -Include $queryString -Mapping `$__mapping 154 | Header = `$PSBoundParameters | $($this.ConvertToHashtableCommand) -Include $headerString -Mapping `$__mapping 155 | Path = '$($this.EndpointUrl)'$pathReplacement 156 | Method = '$($this.Method)' 157 | $scopesString 158 | $serviceString 159 | } 160 | $pathModifier 161 | $actionsString 162 | $shouldProcessString 163 | try { $($this.RestCommand) @__param$($miscParameterString)$($processorString) } 164 | catch { `$PSCmdlet.ThrowTerminatingError(`$_) } 165 | "@ 166 | } 167 | 168 | [string]ToCommand([bool]$NoHelp = $false) { 169 | if ($NoHelp) { 170 | return @" 171 | function $($this.Name) { 172 | $($this.ToParam()) 173 | process { 174 | $($this.ToProcess()) 175 | } 176 | } 177 | "@ 178 | } 179 | return @" 180 | function $($this.Name) { 181 | $($this.ToHelp()) 182 | $($this.ToParam()) 183 | process { 184 | $($this.ToProcess()) 185 | } 186 | } 187 | "@ 188 | } 189 | } -------------------------------------------------------------------------------- /AutoRest/internal/configurations/configuration.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | This is an example configuration file 3 | 4 | By default, it is enough to have a single one of them, 5 | however if you have enough configuration settings to justify having multiple copies of it, 6 | feel totally free to split them into multiple files. 7 | #> 8 | 9 | <# 10 | # Example Configuration 11 | Set-PSFConfig -Module 'AutoRest' -Name 'Example.Setting' -Value 10 -Initialize -Validation 'integer' -Handler { } -Description "Example configuration setting. Your module can then use the setting using 'Get-PSFConfigValue'" 12 | #> 13 | 14 | Set-PSFConfig -Module 'AutoRest' -Name 'Import.DoDotSource' -Value $false -Initialize -Validation 'bool' -Description "Whether the module files should be dotsourced on import. By default, the files of this module are read as string value and invoked, which is faster but worse on debugging." 15 | Set-PSFConfig -Module 'AutoRest' -Name 'Import.IndividualFiles' -Value $false -Initialize -Validation 'bool' -Description "Whether the module files should be imported individually. During the module build, all module code is compiled into few files, which are imported instead by default. Loading the compiled versions is faster, using the individual files is easier for debugging and testing out adjustments." 16 | 17 | Set-PSFConfig -Module 'AutoRest' -Name 'Author' -Value $env:USERNAME -Initialize -Validation string -Description 'The user running this module. Will be added to output metadata.' 18 | Set-PSFConfig -Module 'AutoRest' -Name 'Company' -Value 'Contoso ltd.' -Initialize -Validation string -Description 'The company owning the output. Will be added to output metadata.' 19 | Set-PSFConfig -Module 'AutoRest' -Name 'Logging.Level' -Value 'Warning' -Initialize -Validation string -Description 'The maximum level of logging when generating commands from swagger file. Corresponds to PSFramework message levels. Set this to "Critical" to have all actions logged. Enabling this logging costs performance when parsing swagger files, but helps with debugging.' -------------------------------------------------------------------------------- /AutoRest/internal/configurations/readme.md: -------------------------------------------------------------------------------- 1 | # Configurations 2 | 3 | Through the `PSFramework` you have a simple method that allows you to ... 4 | 5 | - Publish settings 6 | - With onboard documentation 7 | - Input validation 8 | - Scripts that run on change of settings 9 | - That can be discovered and updated by the user 10 | - That can be administrated by policy & DSC 11 | 12 | The configuration system is a bit too complex to describe in a help file, you can however visit us at http://psframework.org for detailed guidance. 13 | 14 | An example can be seen in the attached ps1 file -------------------------------------------------------------------------------- /AutoRest/internal/functions/readme.md: -------------------------------------------------------------------------------- 1 | # Functions 2 | 3 | This is the folder where the internal functions go. 4 | 5 | Depending on the complexity of the module, it is recommended to subdivide them into subfolders. 6 | 7 | The module will pick up all .ps1 files recursively -------------------------------------------------------------------------------- /AutoRest/internal/scriptblocks/scriptblocks.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Stored scriptblocks are available in [PsfValidateScript()] attributes. 3 | This makes it easier to centrally provide the same scriptblock multiple times, 4 | without having to maintain it in separate locations. 5 | 6 | It also prevents lengthy validation scriptblocks from making your parameter block 7 | hard to read. 8 | 9 | Set-PSFScriptblock -Name 'AutoRest.ScriptBlockName' -Scriptblock { 10 | 11 | } 12 | #> -------------------------------------------------------------------------------- /AutoRest/internal/scripts/license.ps1: -------------------------------------------------------------------------------- 1 | New-PSFLicense -Product 'AutoRest' -Manufacturer 'Friedrich Weinmann' -ProductVersion $script:ModuleVersion -ProductType Module -Name MIT -Version "1.0.0.0" -Date (Get-Date "2021-06-17") -Text @" 2 | Copyright (c) 2021 Friedrich Weinmann 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | "@ -------------------------------------------------------------------------------- /AutoRest/internal/scripts/postimport.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Add all things you want to run after importing the main function code 3 | 4 | WARNING: ONLY provide paths to files! 5 | 6 | After building the module, this file will be completely ignored, adding anything but paths to files ... 7 | - Will not work after publishing 8 | - Could break the build process 9 | #> 10 | 11 | $moduleRoot = Split-Path (Split-Path $PSScriptRoot) 12 | 13 | # Load Configurations 14 | (Get-ChildItem "$moduleRoot\internal\configurations\*.ps1" -ErrorAction Ignore).FullName 15 | 16 | # Load Scriptblocks 17 | (Get-ChildItem "$moduleRoot\internal\scriptblocks\*.ps1" -ErrorAction Ignore).FullName 18 | 19 | # Load Tab Expansion 20 | (Get-ChildItem "$moduleRoot\internal\tepp\*.tepp.ps1" -ErrorAction Ignore).FullName 21 | 22 | # Load Tab Expansion Assignment 23 | "$moduleRoot\internal\tepp\assignment.ps1" 24 | 25 | # Load License 26 | "$moduleRoot\internal\scripts\license.ps1" -------------------------------------------------------------------------------- /AutoRest/internal/scripts/preimport.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Add all things you want to run before importing the main function code. 3 | 4 | WARNING: ONLY provide paths to files! 5 | 6 | After building the module, this file will be completely ignored, adding anything but paths to files ... 7 | - Will not work after publishing 8 | - Could break the build process 9 | #> 10 | 11 | $moduleRoot = Split-Path (Split-Path $PSScriptRoot) 12 | 13 | # Load the strings used in messages 14 | "$moduleRoot\internal\scripts\strings.ps1" 15 | 16 | # Load all classes 17 | (Get-ChildItem "$moduleRoot\internal\classes\*.ps1" -ErrorAction Ignore).FullName -------------------------------------------------------------------------------- /AutoRest/internal/scripts/strings.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | This file loads the strings documents from the respective language folders. 3 | This allows localizing messages and errors. 4 | Load psd1 language files for each language you wish to support. 5 | Partial translations are acceptable - when missing a current language message, 6 | it will fallback to English or another available language. 7 | #> 8 | Import-PSFLocalizedString -Path "$($script:ModuleRoot)\en-us\*.psd1" -Module 'AutoRest' -Language 'en-US' -------------------------------------------------------------------------------- /AutoRest/internal/tepp/assignment.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | # Example: 3 | Register-PSFTeppArgumentCompleter -Command Get-Alcohol -Parameter Type -Name AutoRest.alcohol 4 | #> -------------------------------------------------------------------------------- /AutoRest/internal/tepp/example.tepp.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | # Example: 3 | Register-PSFTeppScriptblock -Name "AutoRest.alcohol" -ScriptBlock { 'Beer','Mead','Whiskey','Wine','Vodka','Rum (3y)', 'Rum (5y)', 'Rum (7y)' } 4 | #> -------------------------------------------------------------------------------- /AutoRest/internal/tepp/readme.md: -------------------------------------------------------------------------------- 1 | # Tab Expansion 2 | 3 | ## Description 4 | 5 | Modern Tab Expansion was opened to users with the module `Tab Expansion Plus Plus` (TEPP). 6 | 7 | It allows you to define, what options a user is offered when tabbing through input options. This can save a lot of time for the user and is considered a key element in user experience. 8 | 9 | The `PSFramework` offers a simplified way of offering just this, as the two example files show. 10 | 11 | ## Concept 12 | 13 | Custom tab completion is defined in two steps: 14 | 15 | - Define a scriptblock that is run when the user hits `TAB` and provides the strings that are his options. 16 | - Assign that scriptblock to the parameter of a command. You can assign the same scriptblock multiple times. 17 | 18 | ## Structure 19 | 20 | Import order matters. In order to make things work with the default scaffold, follow those rules: 21 | 22 | - All scriptfiles _defining_ completion scriptblocks like this: `*.tepp.ps1` 23 | - Put all your completion assignments in `assignment.ps1` -------------------------------------------------------------------------------- /AutoRest/internal/tools/ConvertTo-Hashtable.ps1: -------------------------------------------------------------------------------- 1 | function ConvertTo-Hashtable { 2 | <# 3 | .SYNOPSIS 4 | Converts an inputobject into a hashtable. 5 | 6 | .DESCRIPTION 7 | Converts an inputobject into a hashtable. 8 | Allows remapping keys as needed. 9 | 10 | .PARAMETER Include 11 | Which properties / keys to include. 12 | Only properties that exist on the input will be included, no matter what. 13 | 14 | .PARAMETER Mapping 15 | A hashtable mapping keys to another name. 16 | This is used to change the keys on hashtables. 17 | Specifically, this allows providing PowerShell-compliant parameter names, while passing them to the REST api how the API wants them. 18 | 19 | .PARAMETER InputObject 20 | The object to convert into a hashtable. 21 | 22 | .EXAMPLE 23 | PS C:\> $PSBoundParameters | ConvertTo-Hashtable -Include Name, Description, ID -Mapping @{ ID = 'objectId; Name = 'name' } 24 | 25 | Converts the $PSBoundParameters system-variable into a regular hashtable, discarding all entries but Name, Description and ID. 26 | "Name" will be renamed to be lowercase if specified, "ID" will be renamed to "objectId" if specified. 27 | #> 28 | [CmdletBinding()] 29 | param ( 30 | [AllowEmptyCollection()] 31 | [string[]] 32 | $Include, 33 | 34 | [Hashtable] 35 | $Mapping = @{ }, 36 | 37 | [Parameter(ValueFromPipeline = $true)] 38 | $InputObject 39 | ) 40 | 41 | process { 42 | $result = @{ } 43 | # Empty includes lead to empty hashtable; Otherwhise it would be the same as $Include='*' 44 | if ($Include) { 45 | if ($InputObject -is [System.Collections.IDictionary]) { 46 | foreach ($pair in $InputObject.GetEnumerator()) { 47 | if ($Include -and $pair.Key -notin $Include) { continue } 48 | if ($Mapping[$pair.Key]) { $result[$Mapping[$pair.Key]] = $pair.Value } 49 | else { $result[$pair.Key] = $pair.Value } 50 | } 51 | } 52 | else { 53 | foreach ($property in $InputObject.PSObject.Properties) { 54 | if ($Include -and $property.Name -notin $Include) { continue } 55 | if ($Mapping[$property.Name]) { $result[$Mapping[$property.Name]] = $property.Value } 56 | else { $result[$property.Name] = $property.Value } 57 | } 58 | } 59 | } 60 | $result 61 | } 62 | } -------------------------------------------------------------------------------- /AutoRest/readme.md: -------------------------------------------------------------------------------- 1 | # PSFModule guidance 2 | 3 | This is a finished module layout optimized for implementing the PSFramework. 4 | 5 | If you don't care to deal with the details, this is what you need to do to get started seeing results: 6 | 7 | - Add the functions you want to publish to `/functions/` 8 | - Update the `FunctionsToExport` node in the module manifest (AutoRest.psd1). All functions you want to publish should be in a list. 9 | - Add internal helper functions the user should not see to `/internal/functions/` 10 | 11 | ## Path Warning 12 | 13 | > If you want your module to be compatible with Linux and MacOS, keep in mind that those OS are case sensitive for paths and files. 14 | 15 | `Import-ModuleFile` is preconfigured to resolve the path of the files specified, so it will reliably convert weird path notations the system can't handle. 16 | Content imported through that command thus need not mind the path separator. 17 | If you want to make sure your code too will survive OS-specific path notations, get used to using `Resolve-path` or the more powerful `Resolve-PSFPath`. -------------------------------------------------------------------------------- /AutoRest/tests/functions/readme.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | This is where the function tests go. 4 | 5 | Make sure to put them in folders reflecting the actual module structure. 6 | 7 | It is not necessary to differentiate between internal and public functions here. -------------------------------------------------------------------------------- /AutoRest/tests/general/FileIntegrity.Exceptions.ps1: -------------------------------------------------------------------------------- 1 | # List of forbidden commands 2 | $global:BannedCommands = @( 3 | 'Write-Host' 4 | 'Write-Verbose' 5 | 'Write-Warning' 6 | 'Write-Error' 7 | 'Write-Output' 8 | 'Write-Information' 9 | 'Write-Debug' 10 | 11 | # Use CIM instead where possible 12 | 'Get-WmiObject' 13 | 'Invoke-WmiMethod' 14 | 'Register-WmiEvent' 15 | 'Remove-WmiObject' 16 | 'Set-WmiInstance' 17 | 18 | # Use Get-WinEvent instead 19 | 'Get-EventLog' 20 | ) 21 | 22 | <# 23 | Contains list of exceptions for banned cmdlets. 24 | Insert the file names of files that may contain them. 25 | 26 | Example: 27 | "Write-Host" = @('Write-PSFHostColor.ps1','Write-PSFMessage.ps1') 28 | #> 29 | $global:MayContainCommand = @{ 30 | "Write-Host" = @() 31 | "Write-Verbose" = @() 32 | "Write-Warning" = @() 33 | "Write-Error" = @() 34 | "Write-Output" = @('ConvertFrom-ARSwagger.ps1') 35 | "Write-Information" = @() 36 | "Write-Debug" = @() 37 | } 38 | 39 | # List of Commands that may have syntax errors 40 | $global:MayContainSyntaxErrors = @( 41 | '01-CommandParameter.ps1' 42 | ) -------------------------------------------------------------------------------- /AutoRest/tests/general/FileIntegrity.Tests.ps1: -------------------------------------------------------------------------------- 1 | $moduleRoot = (Resolve-Path "$global:testroot\..").Path 2 | 3 | . "$global:testroot\general\FileIntegrity.Exceptions.ps1" 4 | 5 | Describe "Verifying integrity of module files" { 6 | BeforeAll { 7 | function Get-FileEncoding 8 | { 9 | <# 10 | .SYNOPSIS 11 | Tests a file for encoding. 12 | 13 | .DESCRIPTION 14 | Tests a file for encoding. 15 | 16 | .PARAMETER Path 17 | The file to test 18 | #> 19 | [CmdletBinding()] 20 | Param ( 21 | [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $True)] 22 | [Alias('FullName')] 23 | [string] 24 | $Path 25 | ) 26 | 27 | if ($PSVersionTable.PSVersion.Major -lt 6) 28 | { 29 | [byte[]]$byte = get-content -Encoding byte -ReadCount 4 -TotalCount 4 -Path $Path 30 | } 31 | else 32 | { 33 | [byte[]]$byte = Get-Content -AsByteStream -ReadCount 4 -TotalCount 4 -Path $Path 34 | } 35 | 36 | if ($byte[0] -eq 0xef -and $byte[1] -eq 0xbb -and $byte[2] -eq 0xbf) { 'UTF8 BOM' } 37 | elseif ($byte[0] -eq 0xfe -and $byte[1] -eq 0xff) { 'Unicode' } 38 | elseif ($byte[0] -eq 0 -and $byte[1] -eq 0 -and $byte[2] -eq 0xfe -and $byte[3] -eq 0xff) { 'UTF32' } 39 | elseif ($byte[0] -eq 0x2b -and $byte[1] -eq 0x2f -and $byte[2] -eq 0x76) { 'UTF7' } 40 | else { 'Unknown' } 41 | } 42 | } 43 | 44 | Context "Validating PS1 Script files" { 45 | $allFiles = Get-ChildItem -Path $moduleRoot -Recurse | Where-Object Name -like "*.ps1" | Where-Object FullName -NotLike "$moduleRoot\tests\*" 46 | 47 | foreach ($file in $allFiles) 48 | { 49 | $name = $file.FullName.Replace("$moduleRoot\", '') 50 | 51 | It "[$name] Should have UTF8 encoding with Byte Order Mark" -TestCases @{ file = $file } { 52 | Get-FileEncoding -Path $file.FullName | Should -Be 'UTF8 BOM' 53 | } 54 | 55 | It "[$name] Should have no trailing space" -TestCases @{ file = $file } { 56 | ($file | Select-String "\s$" | Where-Object { $_.Line.Trim().Length -gt 0}).LineNumber | Should -BeNullOrEmpty 57 | } 58 | 59 | $tokens = $null 60 | $parseErrors = $null 61 | $ast = [System.Management.Automation.Language.Parser]::ParseFile($file.FullName, [ref]$tokens, [ref]$parseErrors) 62 | 63 | if ($file.Name -notin $global:MayContainSyntaxErrors) { 64 | It "[$name] Should have no syntax errors" -TestCases @{ parseErrors = $parseErrors } { 65 | $parseErrors | Should -BeNullOrEmpty 66 | } 67 | } 68 | 69 | foreach ($command in $global:BannedCommands) 70 | { 71 | if ($global:MayContainCommand["$command"] -notcontains $file.Name) 72 | { 73 | It "[$name] Should not use $command" -TestCases @{ tokens = $tokens; command = $command } { 74 | $tokens | Where-Object Text -EQ $command | Should -BeNullOrEmpty 75 | } 76 | } 77 | } 78 | } 79 | } 80 | 81 | Context "Validating help.txt help files" { 82 | $allFiles = Get-ChildItem -Path $moduleRoot -Recurse | Where-Object Name -like "*.help.txt" | Where-Object FullName -NotLike "$moduleRoot\tests\*" 83 | 84 | foreach ($file in $allFiles) 85 | { 86 | $name = $file.FullName.Replace("$moduleRoot\", '') 87 | 88 | It "[$name] Should have UTF8 encoding" -TestCases @{ file = $file } { 89 | Get-FileEncoding -Path $file.FullName | Should -Be 'UTF8 BOM' 90 | } 91 | 92 | It "[$name] Should have no trailing space" -TestCases @{ file = $file } { 93 | ($file | Select-String "\s$" | Where-Object { $_.Line.Trim().Length -gt 0 } | Measure-Object).Count | Should -Be 0 94 | } 95 | } 96 | } 97 | } -------------------------------------------------------------------------------- /AutoRest/tests/general/Help.Exceptions.ps1: -------------------------------------------------------------------------------- 1 | # List of functions that should be ignored 2 | $global:FunctionHelpTestExceptions = @( 3 | 4 | ) 5 | 6 | <# 7 | List of arrayed enumerations. These need to be treated differently. Add full name. 8 | Example: 9 | 10 | "Sqlcollaborative.Dbatools.Connection.ManagementConnectionType[]" 11 | #> 12 | $global:HelpTestEnumeratedArrays = @( 13 | 14 | ) 15 | 16 | <# 17 | Some types on parameters just fail their validation no matter what. 18 | For those it becomes possible to skip them, by adding them to this hashtable. 19 | Add by following this convention: = @() 20 | Example: 21 | 22 | "Get-DbaCmObject" = @("DoNotUse") 23 | #> 24 | $global:HelpTestSkipParameterType = @{ 25 | 26 | } 27 | -------------------------------------------------------------------------------- /AutoRest/tests/general/Help.Tests.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | The original test this is based upon was written by June Blender. 4 | After several rounds of modifications it stands now as it is, but the honor remains hers. 5 | 6 | Thank you June, for all you have done! 7 | 8 | .DESCRIPTION 9 | This test evaluates the help for all commands in a module. 10 | 11 | .PARAMETER SkipTest 12 | Disables this test. 13 | 14 | .PARAMETER CommandPath 15 | List of paths under which the script files are stored. 16 | This test assumes that all functions have their own file that is named after themselves. 17 | These paths are used to search for commands that should exist and be tested. 18 | Will search recursively and accepts wildcards, make sure only functions are found 19 | 20 | .PARAMETER ModuleName 21 | Name of the module to be tested. 22 | The module must already be imported 23 | 24 | .PARAMETER ExceptionsFile 25 | File in which exceptions and adjustments are configured. 26 | In it there should be two arrays and a hashtable defined: 27 | $global:FunctionHelpTestExceptions 28 | $global:HelpTestEnumeratedArrays 29 | $global:HelpTestSkipParameterType 30 | These can be used to tweak the tests slightly in cases of need. 31 | See the example file for explanations on each of these usage and effect. 32 | #> 33 | [CmdletBinding()] 34 | Param ( 35 | [switch] 36 | $SkipTest, 37 | 38 | [string[]] 39 | $CommandPath = @("$global:testroot\..\functions", "$global:testroot\..\internal\functions"), 40 | 41 | [string] 42 | $ModuleName = "AutoRest", 43 | 44 | [string] 45 | $ExceptionsFile = "$global:testroot\general\Help.Exceptions.ps1" 46 | ) 47 | if ($SkipTest) { return } 48 | . $ExceptionsFile 49 | 50 | $includedNames = (Get-ChildItem $CommandPath -Recurse -File | Where-Object Name -like "*.ps1").BaseName 51 | $commandTypes = @('Cmdlet', 'Function') 52 | if ($PSVersionTable.PSEdition -eq 'Desktop' ) { $commandTypes += 'Workflow' } 53 | $commands = Get-Command -Module (Get-Module $ModuleName) -CommandType $commandTypes | Where-Object Name -In $includedNames 54 | 55 | ## When testing help, remember that help is cached at the beginning of each session. 56 | ## To test, restart session. 57 | 58 | 59 | foreach ($command in $commands) { 60 | $commandName = $command.Name 61 | 62 | # Skip all functions that are on the exclusions list 63 | if ($global:FunctionHelpTestExceptions -contains $commandName) { continue } 64 | 65 | # The module-qualified command fails on Microsoft.PowerShell.Archive cmdlets 66 | $Help = Get-Help $commandName -ErrorAction SilentlyContinue 67 | 68 | Describe "Test help for $commandName" { 69 | 70 | # If help is not found, synopsis in auto-generated help is the syntax diagram 71 | It "should not be auto-generated" -TestCases @{ Help = $Help } { 72 | $Help.Synopsis | Should -Not -BeLike '*`[``]*' 73 | } 74 | 75 | # Should be a description for every function 76 | It "gets description for $commandName" -TestCases @{ Help = $Help } { 77 | $Help.Description | Should -Not -BeNullOrEmpty 78 | } 79 | 80 | # Should be at least one example 81 | It "gets example code from $commandName" -TestCases @{ Help = $Help } { 82 | ($Help.Examples.Example | Select-Object -First 1).Code | Should -Not -BeNullOrEmpty 83 | } 84 | 85 | # Should be at least one example description 86 | It "gets example help from $commandName" -TestCases @{ Help = $Help } { 87 | ($Help.Examples.Example.Remarks | Select-Object -First 1).Text | Should -Not -BeNullOrEmpty 88 | } 89 | 90 | Context "Test parameter help for $commandName" { 91 | 92 | $common = 'Debug', 'ErrorAction', 'ErrorVariable', 'InformationAction', 'InformationVariable', 'OutBuffer', 'OutVariable', 'PipelineVariable', 'Verbose', 'WarningAction', 'WarningVariable' 93 | 94 | $parameters = $command.ParameterSets.Parameters | Sort-Object -Property Name -Unique | Where-Object Name -notin $common 95 | $parameterNames = $parameters.Name 96 | $HelpParameterNames = $Help.Parameters.Parameter.Name | Sort-Object -Unique 97 | foreach ($parameter in $parameters) { 98 | $parameterName = $parameter.Name 99 | $parameterHelp = $Help.parameters.parameter | Where-Object Name -EQ $parameterName 100 | 101 | # Should be a description for every parameter 102 | It "gets help for parameter: $parameterName : in $commandName" -TestCases @{ parameterHelp = $parameterHelp } { 103 | $parameterHelp.Description.Text | Should -Not -BeNullOrEmpty 104 | } 105 | 106 | $codeMandatory = $parameter.IsMandatory.toString() 107 | It "help for $parameterName parameter in $commandName has correct Mandatory value" -TestCases @{ parameterHelp = $parameterHelp; codeMandatory = $codeMandatory } { 108 | $parameterHelp.Required | Should -Be $codeMandatory 109 | } 110 | 111 | if ($HelpTestSkipParameterType[$commandName] -contains $parameterName) { continue } 112 | 113 | $codeType = $parameter.ParameterType.Name 114 | 115 | if ($parameter.ParameterType.IsEnum) { 116 | # Enumerations often have issues with the typename not being reliably available 117 | $names = $parameter.ParameterType::GetNames($parameter.ParameterType) 118 | # Parameter type in Help should match code 119 | It "help for $commandName has correct parameter type for $parameterName" -TestCases @{ parameterHelp = $parameterHelp; names = $names } { 120 | $parameterHelp.parameterValueGroup.parameterValue | Should -be $names 121 | } 122 | } 123 | elseif ($parameter.ParameterType.FullName -in $HelpTestEnumeratedArrays) { 124 | # Enumerations often have issues with the typename not being reliably available 125 | $names = [Enum]::GetNames($parameter.ParameterType.DeclaredMembers[0].ReturnType) 126 | It "help for $commandName has correct parameter type for $parameterName" -TestCases @{ parameterHelp = $parameterHelp; names = $names } { 127 | $parameterHelp.parameterValueGroup.parameterValue | Should -be $names 128 | } 129 | } 130 | else { 131 | # To avoid calling Trim method on a null object. 132 | $helpType = if ($parameterHelp.parameterValue) { $parameterHelp.parameterValue.Trim() } 133 | # Parameter type in Help should match code 134 | It "help for $commandName has correct parameter type for $parameterName" -TestCases @{ helpType = $helpType; codeType = $codeType } { 135 | $helpType | Should -be $codeType 136 | } 137 | } 138 | } 139 | foreach ($helpParm in $HelpParameterNames) { 140 | # Shouldn't find extra parameters in help. 141 | It "finds help parameter in code: $helpParm" -TestCases @{ helpParm = $helpParm; parameterNames = $parameterNames } { 142 | $helpParm -in $parameterNames | Should -Be $true 143 | } 144 | } 145 | } 146 | } 147 | } -------------------------------------------------------------------------------- /AutoRest/tests/general/Manifest.Tests.ps1: -------------------------------------------------------------------------------- 1 | Describe "Validating the module manifest" { 2 | $moduleRoot = (Resolve-Path "$global:testroot\..").Path 3 | $manifest = ((Get-Content "$moduleRoot\AutoRest.psd1") -join "`n") | Invoke-Expression 4 | Context "Basic resources validation" { 5 | $files = Get-ChildItem "$moduleRoot\functions" -Recurse -File | Where-Object Name -like "*.ps1" 6 | It "Exports all functions in the public folder" -TestCases @{ files = $files; manifest = $manifest } { 7 | 8 | $functions = (Compare-Object -ReferenceObject $files.BaseName -DifferenceObject $manifest.FunctionsToExport | Where-Object SideIndicator -Like '<=').InputObject 9 | $functions | Should -BeNullOrEmpty 10 | } 11 | It "Exports no function that isn't also present in the public folder" -TestCases @{ files = $files; manifest = $manifest } { 12 | $functions = (Compare-Object -ReferenceObject $files.BaseName -DifferenceObject $manifest.FunctionsToExport | Where-Object SideIndicator -Like '=>').InputObject 13 | $functions | Should -BeNullOrEmpty 14 | } 15 | 16 | It "Exports none of its internal functions" -TestCases @{ moduleRoot = $moduleRoot; manifest = $manifest } { 17 | $files = Get-ChildItem "$moduleRoot\internal\functions" -Recurse -File -Filter "*.ps1" 18 | $files | Where-Object BaseName -In $manifest.FunctionsToExport | Should -BeNullOrEmpty 19 | } 20 | } 21 | 22 | Context "Individual file validation" { 23 | It "The root module file exists" -TestCases @{ moduleRoot = $moduleRoot; manifest = $manifest } { 24 | Test-Path "$moduleRoot\$($manifest.RootModule)" | Should -Be $true 25 | } 26 | 27 | foreach ($format in $manifest.FormatsToProcess) 28 | { 29 | It "The file $format should exist" -TestCases @{ moduleRoot = $moduleRoot; format = $format } { 30 | Test-Path "$moduleRoot\$format" | Should -Be $true 31 | } 32 | } 33 | 34 | foreach ($type in $manifest.TypesToProcess) 35 | { 36 | It "The file $type should exist" -TestCases @{ moduleRoot = $moduleRoot; type = $type } { 37 | Test-Path "$moduleRoot\$type" | Should -Be $true 38 | } 39 | } 40 | 41 | foreach ($assembly in $manifest.RequiredAssemblies) 42 | { 43 | if ($assembly -like "*.dll") { 44 | It "The file $assembly should exist" -TestCases @{ moduleRoot = $moduleRoot; assembly = $assembly } { 45 | Test-Path "$moduleRoot\$assembly" | Should -Be $true 46 | } 47 | } 48 | else { 49 | It "The file $assembly should load from the GAC" -TestCases @{ moduleRoot = $moduleRoot; assembly = $assembly } { 50 | { Add-Type -AssemblyName $assembly } | Should -Not -Throw 51 | } 52 | } 53 | } 54 | 55 | foreach ($tag in $manifest.PrivateData.PSData.Tags) 56 | { 57 | It "Tags should have no spaces in name" -TestCases @{ tag = $tag } { 58 | $tag -match " " | Should -Be $false 59 | } 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /AutoRest/tests/general/PSScriptAnalyzer.Tests.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | Param ( 3 | [switch] 4 | $SkipTest, 5 | 6 | [string[]] 7 | $CommandPath = @("$global:testroot\..\functions", "$global:testroot\..\internal\functions") 8 | ) 9 | 10 | if ($SkipTest) { return } 11 | 12 | $global:__pester_data.ScriptAnalyzer = New-Object System.Collections.ArrayList 13 | 14 | Describe 'Invoking PSScriptAnalyzer against commandbase' { 15 | $commandFiles = foreach ($path in $CommandPath) { Get-ChildItem -Path $path -Recurse | Where-Object Name -like "*.ps1" } 16 | $scriptAnalyzerRules = Get-ScriptAnalyzerRule 17 | 18 | foreach ($file in $commandFiles) 19 | { 20 | Context "Analyzing $($file.BaseName)" { 21 | $analysis = Invoke-ScriptAnalyzer -Path $file.FullName -ExcludeRule PSAvoidTrailingWhitespace, PSShouldProcess 22 | 23 | forEach ($rule in $scriptAnalyzerRules) 24 | { 25 | It "Should pass $rule" -TestCases @{ analysis = $analysis; rule = $rule } { 26 | If ($analysis.RuleName -contains $rule) 27 | { 28 | $analysis | Where-Object RuleName -EQ $rule -outvariable failures | ForEach-Object { $null = $global:__pester_data.ScriptAnalyzer.Add($_) } 29 | 30 | 1 | Should -Be 0 31 | } 32 | else 33 | { 34 | 0 | Should -Be 0 35 | } 36 | } 37 | } 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /AutoRest/tests/general/strings.Exceptions.ps1: -------------------------------------------------------------------------------- 1 | $exceptions = @{ } 2 | 3 | <# 4 | A list of entries that MAY be in the language files, without causing the tests to fail. 5 | This is commonly used in modules that generate localized messages straight from C#. 6 | Specify the full key as it is written in the language files, do not prepend the modulename, 7 | as you would have to in C# code. 8 | 9 | Example: 10 | $exceptions['LegalSurplus'] = @( 11 | 'Exception.Streams.FailedCreate' 12 | 'Exception.Streams.FailedDispose' 13 | ) 14 | #> 15 | $exceptions['LegalSurplus'] = @( 16 | 17 | ) 18 | 19 | <# 20 | A list of entries that MAY be used without being contained in the language file. 21 | It is assumed the strings are provided externally, such as another module this module depends on. 22 | #> 23 | $exceptions['ForeignSourced'] = @( 24 | 'Validate.FSPath.File' 25 | 'Validate.FSPath.Folder' 26 | ) 27 | 28 | $exceptions -------------------------------------------------------------------------------- /AutoRest/tests/general/strings.Tests.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .DESCRIPTION 3 | This test verifies, that all strings that have been used, 4 | are listed in the language files and thus have a message being displayed. 5 | 6 | It also checks, whether the language files have orphaned entries that need cleaning up. 7 | #> 8 | 9 | 10 | 11 | Describe "Testing localization strings" { 12 | $moduleRoot = (Get-Module AutoRest).ModuleBase 13 | $stringsResults = Export-PSMDString -ModuleRoot $moduleRoot 14 | $exceptions = & "$global:testroot\general\strings.Exceptions.ps1" 15 | 16 | foreach ($stringEntry in $stringsResults) { 17 | if ($stringEntry.String -eq "key") { continue } # Skipping the template default entry 18 | It "Should be used & have text: $($stringEntry.String)" -TestCases @{ stringEntry = $stringEntry; exceptions = $exceptions } { 19 | if ($exceptions.LegalSurplus -notcontains $stringEntry.String -and $exceptions.ForeignSourced -notcontains $stringEntry.String) { 20 | $stringEntry.Surplus | Should -BeFalse 21 | } 22 | if ($exceptions.ForeignSourced -notcontains $stringEntry.String) { 23 | $stringEntry.Text | Should -Not -BeNullOrEmpty 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /AutoRest/tests/pester.ps1: -------------------------------------------------------------------------------- 1 | param ( 2 | $TestGeneral = $true, 3 | 4 | $TestFunctions = $true, 5 | 6 | [ValidateSet('None', 'Normal', 'Detailed', 'Diagnostic')] 7 | [Alias('Show')] 8 | $Output = "None", 9 | 10 | $Include = "*", 11 | 12 | $Exclude = "" 13 | ) 14 | 15 | Write-PSFMessage -Level Important -Message "Starting Tests" 16 | 17 | Write-PSFMessage -Level Important -Message "Importing Module" 18 | 19 | $global:testroot = $PSScriptRoot 20 | $global:__pester_data = @{ } 21 | 22 | Remove-Module AutoRest -ErrorAction Ignore 23 | Import-Module "$PSScriptRoot\..\AutoRest.psd1" 24 | Import-Module "$PSScriptRoot\..\AutoRest.psm1" -Force 25 | 26 | # Need to import explicitly so we can use the configuration class 27 | Import-Module Pester 28 | 29 | Write-PSFMessage -Level Important -Message "Creating test result folder" 30 | $null = New-Item -Path "$PSScriptRoot\..\.." -Name TestResults -ItemType Directory -Force 31 | 32 | $totalFailed = 0 33 | $totalRun = 0 34 | 35 | $testresults = @() 36 | $config = [PesterConfiguration]::Default 37 | $config.TestResult.Enabled = $true 38 | 39 | #region Run General Tests 40 | if ($TestGeneral) 41 | { 42 | Write-PSFMessage -Level Important -Message "Modules imported, proceeding with general tests" 43 | foreach ($file in (Get-ChildItem "$PSScriptRoot\general" | Where-Object Name -like "*.Tests.ps1")) 44 | { 45 | if ($file.Name -notlike $Include) { continue } 46 | if ($file.Name -like $Exclude) { continue } 47 | 48 | Write-PSFMessage -Level Significant -Message " Executing $($file.Name)" 49 | $config.TestResult.OutputPath = Join-Path "$PSScriptRoot\..\..\TestResults" "TEST-$($file.BaseName).xml" 50 | $config.Run.Path = $file.FullName 51 | $config.Run.PassThru = $true 52 | $config.Output.Verbosity = $Output 53 | $results = Invoke-Pester -Configuration $config 54 | foreach ($result in $results) 55 | { 56 | $totalRun += $result.TotalCount 57 | $totalFailed += $result.FailedCount 58 | $result.Tests | Where-Object Result -ne 'Passed' | ForEach-Object { 59 | $testresults += [pscustomobject]@{ 60 | Block = $_.Block 61 | Name = "It $($_.Name)" 62 | Result = $_.Result 63 | Message = $_.ErrorRecord.DisplayErrorMessage 64 | } 65 | } 66 | } 67 | } 68 | } 69 | #endregion Run General Tests 70 | 71 | $global:__pester_data.ScriptAnalyzer | Out-Host 72 | 73 | #region Test Commands 74 | if ($TestFunctions) 75 | { 76 | Write-PSFMessage -Level Important -Message "Proceeding with individual tests" 77 | foreach ($file in (Get-ChildItem "$PSScriptRoot\functions" -Recurse -File | Where-Object Name -like "*Tests.ps1")) 78 | { 79 | if ($file.Name -notlike $Include) { continue } 80 | if ($file.Name -like $Exclude) { continue } 81 | 82 | Write-PSFMessage -Level Significant -Message " Executing $($file.Name)" 83 | $config.TestResult.OutputPath = Join-Path "$PSScriptRoot\..\..\TestResults" "TEST-$($file.BaseName).xml" 84 | $config.Run.Path = $file.FullName 85 | $config.Run.PassThru = $true 86 | $config.Output.Verbosity = $Output 87 | $results = Invoke-Pester -Configuration $config 88 | foreach ($result in $results) 89 | { 90 | $totalRun += $result.TotalCount 91 | $totalFailed += $result.FailedCount 92 | $result.Tests | Where-Object Result -ne 'Passed' | ForEach-Object { 93 | $testresults += [pscustomobject]@{ 94 | Block = $_.Block 95 | Name = "It $($_.Name)" 96 | Result = $_.Result 97 | Message = $_.ErrorRecord.DisplayErrorMessage 98 | } 99 | } 100 | } 101 | } 102 | } 103 | #endregion Test Commands 104 | 105 | $testresults | Sort-Object Describe, Context, Name, Result, Message | Format-List 106 | 107 | if ($totalFailed -eq 0) { Write-PSFMessage -Level Critical -Message "All $totalRun tests executed without a single failure!" } 108 | else { Write-PSFMessage -Level Critical -Message "$totalFailed tests out of $totalRun tests failed!" } 109 | 110 | if ($totalFailed -gt 0) 111 | { 112 | throw "$totalFailed / $totalRun tests failed!" 113 | } -------------------------------------------------------------------------------- /AutoRest/tests/readme.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | This is the folder, where all the tests go. 4 | 5 | Those are subdivided in two categories: 6 | 7 | - General 8 | - Function 9 | 10 | ## General Tests 11 | 12 | General tests are function generic and test for general policies. 13 | 14 | These test scan answer questions such as: 15 | 16 | - Is my module following my style guides? 17 | - Does any of my scripts have a syntax error? 18 | - Do my scripts use commands I do not want them to use? 19 | - Do my commands follow best practices? 20 | - Do my commands have proper help? 21 | 22 | Basically, these allow a general module health check. 23 | 24 | These tests are already provided as part of the template. 25 | 26 | ## Function Tests 27 | 28 | A healthy module should provide unit and integration tests for the commands & components it ships. 29 | Only then can be guaranteed, that they will actually perform as promised. 30 | 31 | However, as each such test must be specific to the function it tests, there cannot be much in the way of templates. -------------------------------------------------------------------------------- /AutoRest/transformExamples/Demo-ParameterAdd.psd1: -------------------------------------------------------------------------------- 1 |  2 | @{ 3 | "additionalParameters" = @{ 4 | "admin/cron:Post" = @{ 5 | "MyAdditionalBodyParam" = @{ 6 | "Help" = "Text contained within the help of the parameter" 7 | "ParameterType" = "String" 8 | "SystemName" = "myadditionalbodyparam" 9 | "Mandatory" = $true 10 | "Type" = "Body" 11 | } 12 | "MyAdditionalQueryParam" = @{ 13 | "Help" = "Text contained within the help of the parameter" 14 | "ParameterType" = "String" 15 | "SystemName" = "addQueryParam" 16 | "Mandatory" = $true 17 | "Type" = "Query" 18 | } 19 | } 20 | } 21 | "additionalScopedParameters" = @{ 22 | "admin*" = @{ 23 | "MyAdditionalScopedParam" = @{ 24 | "Help" = "Text contained within the help of the parameter" 25 | "ParameterType" = "String" 26 | "SystemName" = "addScopedParam" 27 | "Mandatory" = $true 28 | "Type" = "Query" 29 | } 30 | } 31 | } 32 | "additionalGlobalParameters" = @{ 33 | "Connection" = @{ 34 | "Help" = "Connection object for authentication" 35 | "ParameterType" = "object" 36 | "Mandatory" = $true 37 | "Type" = "Misc" 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /AutoRest/transformExamples/Demo.psd1: -------------------------------------------------------------------------------- 1 |  2 | @{ 3 | "admin/cron:Post" = @{ 4 | "Name" = "Invoke-GiteaAdminCronTask" 5 | "Synopsis" = "Executes the named cron task" 6 | "Description" = "Description part of the command help" 7 | "DocumentationUrl" = "https://try.gitea.io/api/swagger" 8 | "ServiceName" = "MyGiteaService" 9 | "Scopes" = "CurrentUser" 10 | "RestCommand" = "Invoke-GiteaRestCall" 11 | "ProcessorCommand" = "ConvertFrom-GiteaCronJob" 12 | Parameters = @{ 13 | Task = @{ 14 | Name = "CronTask" 15 | Help = 'ID of the cron task to be invoked.' 16 | } 17 | } 18 | } 19 | "globalParameters" = @{ 20 | Limit = @{ 21 | Name = "PageSize" 22 | Help = 'How many elements should be fetched within one call' 23 | ParameterType = 'int' 24 | } 25 | } 26 | "scopedParameters" = @{ 27 | "admin*" = @{ 28 | Page = @{ 29 | Name = "PageNumber" 30 | } 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /AutoRest/transformExamples/readme.md: -------------------------------------------------------------------------------- 1 | # Transform Files 2 | `ConvertFrom-ARSwagger` has got the parameter `Transformpath`. If used the 3 | command will take any *.psd1 file within the given folder and use the contained 4 | data for transformation of the generated `[Command]` objects. By using this 5 | option it is possible to modify the generated PowerShell functions without 6 | writing any code. 7 | 8 | ### Source for examples 9 | The examples provided within this folder and readme.md use the [swagger definition of Gitea](https://try.gitea.io/api/swagger). You can download the json file from their [TryOut Web-Site](https://try.gitea.io/swagger.v1.json) 10 | 11 | ## Structure of the Transform-Files 12 | Each transformation rule file consists of a PowerShell-Data-File. Those *.psd1 files are handled by [Import-PowerShellDataFile](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/import-powershelldatafile?view=powershell-7.2) 13 | and consist of a simple HashTable definition. Each main entry/key may consist of one of the following: 14 | 15 | ### List of keys for command selecting 16 | 1. `:` 17 | Used for direct transformation of a specific command 18 | 1. `globalParameters` 19 | Contains a Hashtable of parameters which should be modified for all commands 20 | 1. `scopedParameters` 21 | Contains a Hashtable of endpoint-pathes for whom parameters should be modified for all commands 22 | 23 | ## Content of the transformation 24 | As the value to the corresponding key you use a HashTable containing the 25 | 26 | | Key | Description | 27 | | ----------- | ----------- | 28 | | Name | Name of the command to generate | 29 | | Synopsis | Synopsis part of the command help | 30 | | Description | Description part of the command help | 31 | | DocumentationUrl | Link to the online documentation | 32 | | ServiceName | Specific service override to include. Needs to be implemented in the Rest Command used. | 33 | | Scopes | Scopes required in delegate authentication mode. Will be included in help and passed to rest command. | 34 | | RestCommand | Command to use to execute the rest request and handle authentication | 35 | | ProcessorCommand | An additional command used to process the output. This command must be implemented manually. | 36 | | ParameterSets | Hashtable mapping parameterset name to its description. Used in examples. | 37 | | Parameters | HashTable for each parameter to modify. See "Modification of parameters" | 38 | 39 | ## Modification of parameters: 40 | To override individual properties on parameters, create a 41 | child-hashtable. Each key is the system name of the parameter, entries 42 | govern the properties to override. 43 | ```Powershell 44 | Parameters = @{ 45 | MachineID = @{ 46 | Help = 'ID of the machine to get recommendations for.' 47 | } 48 | } 49 | ``` 50 | The following table describes the possible attributes of each parameter: 51 | | Key | Description | 52 | | ----------- | ----------- | 53 | | Help | Text contained within the help of the parameter | 54 | | ParameterType | Type of the parameter variable | 55 | | ParameterSet | {default} | 56 | | Weight | Weight for sorting the parameters, default `1000` | 57 | | Name | Name of the parameter variable | 58 | | SystemName | Name of the parameter as expected by the API service, **should never be modified** | 59 | | Mandatory | `True` or `False` | 60 | | ValueFromPipeline | Should the parameter be accepted from the pipeline, possible Values `True` or `False`, default `False` | 61 | | Type | Type of the parameter; specifies where the parameter should be included in the API call, possible Values `Body`, `Path`,`Query`, `Header` and `Misc` | 62 | 63 | ## Examples 64 | ### Override a specific command 65 | The following transform-content creates the function `Invoke-GiteaAdminCronTask` instead of `Set-GiteaAdminCron`: 66 | ```Powershell 67 | @{ 68 | "admin/cron:Post" = @{ 69 | "Name" = "Invoke-GiteaAdminCronTask" 70 | "Synopsis" = "Executes the named cron task" 71 | "Description" = "Description part of the command help" 72 | "DocumentationUrl" = "https://try.gitea.io/api/swagger" 73 | "ServiceName" = "MyGiteaService" 74 | "Scopes" = "CurrentUser" 75 | "RestCommand" = "Invoke-GiteaRestCall" 76 | "ProcessorCommand" = "ConvertFrom-GiteaCronJob" 77 | } 78 | } 79 | ``` 80 |
81 | Content of Invoke-GiteaAdminCronTask 82 | 83 | ```Powershell 84 | function Invoke-GiteaAdminCronTask { 85 | <# 86 | .SYNOPSIS 87 | Executes the named cron task 88 | 89 | .DESCRIPTION 90 | Description part of the command help 91 | 92 | Scopes required (delegate auth): CurrentUser 93 | 94 | .PARAMETER Task 95 | task to run 96 | 97 | .EXAMPLE 98 | PS C:\> Invoke-GiteaAdminCronTask -Task $task 99 | 100 | 101 | 102 | .LINK 103 | https://try.gitea.io/api/swagger 104 | #> 105 | [CmdletBinding(DefaultParameterSetName = 'default')] 106 | param ( 107 | [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')] 108 | [string] 109 | $Task 110 | ) 111 | process { 112 | $__mapping = @{ 113 | 114 | } 115 | $__body = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping 116 | $__query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping 117 | $__path = 'admin/cron/{task}' -Replace '{task}',$Task 118 | Invoke-GiteaRestCall -Path $__path -Method post -Body $__body -Query $__query -RequiredScopes 'CurrentUser' -Service MyGiteaService | ConvertFrom-GiteaCronJob 119 | } 120 | } 121 | ``` 122 |
123 | 124 | ### Renaming an existing parameter 125 | The code above contains the parameter `Task`. If it should be renamed into `CronTask` you have to add the following code to the transformation: 126 | 127 | ```Powershell 128 | Parameters = @{ 129 | Task = @{ 130 | Name = "CronTask" 131 | Help = 'ID of the cron task to be invoked.' 132 | } 133 | } 134 | ``` 135 |
136 | Complete *.psd1 Code 137 | 138 | ```Powershell 139 | @{ 140 | "admin/cron:Post" = @{ 141 | "Name" = "Invoke-GiteaAdminCronTask" 142 | "Synopsis" = "Executes the named cron task" 143 | "Description" = "Description part of the command help" 144 | "DocumentationUrl" = "https://try.gitea.io/api/swagger" 145 | "ServiceName" = "MyGiteaService" 146 | "Scopes" = "CurrentUser" 147 | "RestCommand" = "Invoke-GiteaRestCall" 148 | "ProcessorCommand" = "ConvertFrom-GiteaCronJob" 149 | Parameters = @{ 150 | Task = @{ 151 | Name = "CronTask" 152 | Help = 'ID of the cron task to be invoked.' 153 | } 154 | } 155 | } 156 | } 157 | ``` 158 | 159 |
160 |
161 | Content of Invoke-GiteaAdminCronTask, v2 162 | 163 | ```Powershell 164 | function Invoke-GiteaAdminCronTask { 165 | <# 166 | .SYNOPSIS 167 | Executes the named cron task 168 | 169 | .DESCRIPTION 170 | Description part of the command help 171 | 172 | Scopes required (delegate auth): CurrentUser 173 | 174 | .PARAMETER CronTask 175 | ID of the cron task to be invoked. 176 | 177 | .EXAMPLE 178 | PS C:\> Invoke-GiteaAdminCronTask -CronTask $crontask 179 | 180 | 181 | 182 | .LINK 183 | https://try.gitea.io/api/swagger 184 | #> 185 | [CmdletBinding(DefaultParameterSetName = 'default')] 186 | param ( 187 | [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')] 188 | [string] 189 | $CronTask 190 | ) 191 | process { 192 | $__mapping = @{ 193 | 194 | } 195 | $__body = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping 196 | $__query = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping 197 | $__path = 'admin/cron/{task}' -Replace '{task}',$CronTask 198 | Invoke-GiteaRestCall -Path $__path -Method post -Body $__body -Query $__query -RequiredScopes 'CurrentUser' -Service MyGiteaService | ConvertFrom-GiteaCronJob 199 | } 200 | } 201 | ``` 202 |
203 | 204 | ## Global parameter modification 205 | If you need to modify a parameter for all generated commands you have to describe the modification under the key `globalParameters`. 206 | 207 | E.g. if the following snippet would rename all Parameters `Limit` to `PageSize` and modify additional aatributes: 208 | 209 | ```Powershell 210 | "globalParameters" = @{ 211 | Limit = @{ 212 | Name = "PageSize" 213 | Help = 'How many elements should be fetched within one call' 214 | ParameterType = 'int' 215 | } 216 | } 217 | ``` 218 |
219 | Content of Invoke-GiteaAdminCronTask, v3, abbreviated 220 | 221 | ```Powershell 222 | function Get-GiteaAdminCron { 223 | <# 224 | .... 225 | .PARAMETER PageSize 226 | How many elements should be fetched within one call 227 | .... 228 | #> 229 | [CmdletBinding(DefaultParameterSetName = 'default')] 230 | param ( 231 | .... 232 | [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'default')] 233 | [int] 234 | $PageSize 235 | ) 236 | process { 237 | $__mapping = @{ 238 | 'Page' = 'page' 239 | 'Connection' = 'Connection' 240 | 'PageSize' = 'limit' 241 | } 242 | $__body = $PSBoundParameters | ConvertTo-HashTable -Include @() -Mapping $__mapping 243 | $__query = $PSBoundParameters | ConvertTo-HashTable -Include @('Page','PageSize') -Mapping $__mapping 244 | $__path = 'admin/cron' 245 | Invoke-ARRestRequest -Path $__path -Method get -Body $__body -Query $__query -Service Dagobert 246 | } 247 | } 248 | ``` 249 |
250 | 251 | ## Global parameter modification with scope 252 | If you need to modify a parameter for all generated commands **within a specific path** you may describe the modification under the key `scopedParameters/[Path-Matching String]`. 253 | 254 | E.g. if the following snippet would rename all Parameters `Page` to `PageNumber` only for entrypoints within the path `admin/*` 255 | 256 | ```Powershell 257 | "scopedParameters" = @{ 258 | "admin*" = @{ 259 | Page = @{ 260 | Name = "PageNumber" 261 | } 262 | } 263 | } 264 | ``` 265 | **This option should only be used with caution, as it can lead to inconsistent naming patterns.** 266 | 267 | ## Adding additional parameters to the existing ones 268 | Sometimes it is necessary to add a functional parameter which is missing in the 269 | swagger documentation. In order to achieve this you can add the parameter 270 | configuration to one of the following keys: 271 | 272 | - `additionalParameters` followed by `:` 273 | - `additionalScopedParameters` followed by the endpoint-path-match 274 | - `additionalGlobalParameters` containing a global parameter which should be 275 | added to every function 276 | 277 | Examples can be found within the file [Demo-ParameterAdd.psd1](Demo-ParameterAdd.psd1). 278 | 279 | ### New Parameter Type 'Misc' 280 | If you need to add a parameter to the call of your RestCommand you can specify it with the ParameterType `Misc`. For example 281 | ```Powershell 282 | "additionalGlobalParameters" = @{ 283 | "Connection" = @{ 284 | "Help" = "Will not be given" 285 | "ParameterType" = "object" 286 | "Mandatory" = $true 287 | "Type" = "Misc" 288 | } 289 | } 290 | ``` 291 | will result in the addition of `-Connection $Connection` 292 | ```Powershell 293 | Invoke-ARRestRequest -Path $__path -Method head -Body $__body -Query $__query -Header $__header -Connection $Connection 294 | ``` 295 | -------------------------------------------------------------------------------- /AutoRest/xml/AutoRest.Format.ps1xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | Foo.Bar 7 | 8 | Foo.Bar 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | Foo 21 | 22 | 23 | Bar 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /AutoRest/xml/AutoRest.Types.ps1xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Deserialized.Foo.Bar 6 | 7 | 8 | PSStandardMembers 9 | 10 | 11 | 12 | TargetTypeForDeserialization 13 | 14 | 15 | Foo.Bar 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Foo.Bar 24 | 25 | 26 | SerializationData 27 | 28 | PSFramework.Serialization.SerializationTypeConverter 29 | GetSerializationData 30 | 31 | 32 | 33 | 34 | PSFramework.Serialization.SerializationTypeConverter 35 | 36 | 37 | -------------------------------------------------------------------------------- /AutoRest/xml/readme.md: -------------------------------------------------------------------------------- 1 | # XML 2 | 3 | This is the folder where project XML files go, notably: 4 | 5 | - Format XML 6 | - Type Extension XML 7 | 8 | External help files should _not_ be placed in this folder! 9 | 10 | ## Notes on Files and Naming 11 | 12 | There should be only one format file and one type extension file per project, as importing them has a notable impact on import times. 13 | 14 | - The Format XML should be named `AutoRest.Format.ps1xml` 15 | - The Type Extension XML should be named `AutoRest.Types.ps1xml` 16 | 17 | ## Tools 18 | 19 | ### New-PSMDFormatTableDefinition 20 | 21 | This function will take an input object and generate format xml for an auto-sized table. 22 | 23 | It provides a simple way to get started with formats. 24 | 25 | ### Get-PSFTypeSerializationData 26 | 27 | ``` 28 | C# Warning! 29 | This section is only interest if you're using C# together with PowerShell. 30 | ``` 31 | 32 | This function generates type extension XML that allows PowerShell to convert types written in C# to be written to file and restored from it without being 'Deserialized'. Also works for jobs or remoting, if both sides have the `PSFramework` module and type extension loaded. 33 | 34 | In order for a class to be eligible for this, it needs to conform to the following rules: 35 | 36 | - Have the `[Serializable]` attribute 37 | - Be public 38 | - Have an empty constructor 39 | - Allow all public properties/fields to be set (even if setting it doesn't do anything) without throwing an exception. 40 | 41 | ``` 42 | non-public properties and fields will be lost in this process! 43 | ``` -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Friedrich Weinmann 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AutoRest (PowerShell) 2 | 3 | Welcome to the home of the AutoRest for PowerShell project. 4 | This toolkit is designed to help with generating client code for Rest APIs. 5 | 6 | ## Installation 7 | 8 | To install this PowerShell module - and you need it only on the machine _generating_ the code, the output itself has no dependencies in this module - run the following line in your PowerShell console of choice: 9 | 10 | ```powershell 11 | Install-Module AutoRest -Scope CurrentUser 12 | ``` 13 | 14 | ## Requirements 15 | 16 | + PowerShell 5.1+ ([PowerShell 7+ recommended](https://aka.ms/pwsh)) 17 | + Module: [PSFramework](https://psframework.org) 18 | + Module: strings 19 | 20 | > All module dependencies are installed automatically with the above install command 21 | 22 | ## Getting started 23 | 24 | For details on how to use each command, see their help, but fundamentally, the system has two steps: 25 | 26 | + Convert source to Command data 27 | + Export Command data to file as command 28 | 29 | Example: 30 | 31 | ```powershell 32 | $paramConvertFromARSwagger = @{ 33 | Transformpath = '.\transform' 34 | RestCommand = 'Invoke-ARRestRequest' 35 | ModulePrefix = 'Mg' 36 | PathPrefix = '/api/' 37 | } 38 | 39 | Get-ChildItem .\swaggerfiles | ConvertFrom-ARSwagger @paramConvertFromARSwagger | Export-ARCommand -Path . 40 | ``` 41 | 42 | > [More Information on Customizing the result using Transforms](https://github.com/FriedrichWeinmann/AutoRest/tree/master/AutoRest/transformExamples) 43 | 44 | ## Authentication & Invocation 45 | 46 | The commands generated from this module assume the actual authentication and request execution is provided externally. 47 | An example authentication component has been provided through the sister project "RestConnect" 48 | -------------------------------------------------------------------------------- /azFunctionResources/clientModule/function.ps1: -------------------------------------------------------------------------------- 1 | function %functionname% 2 | { 3 | %parameter% 4 | 5 | process 6 | { 7 | $invokeParameters = Get-InternalConnectionData -Method '%method%' -Parameter $PSBoundParameters -FunctionName '%condensedname%' 8 | Invoke-RestMethod @invokeParameters | ConvertFrom-PSFClixml 9 | } 10 | } -------------------------------------------------------------------------------- /azFunctionResources/clientModule/functions/Connect-AutoRest.ps1: -------------------------------------------------------------------------------- 1 | function Connect-AutoRest 2 | { 3 | <# 4 | .SYNOPSIS 5 | Configures the connection to the AutoRest Azure Function. 6 | 7 | .DESCRIPTION 8 | Configures the connection to the AutoRest Azure Function. 9 | 10 | .PARAMETER Uri 11 | Url to connect to the AutoRest Azure function. 12 | 13 | .PARAMETER UnprotectedToken 14 | The unencrypted access token to the AutoRest Azure function. ONLY use this from secure locations or non-sensitive functions! 15 | 16 | .PARAMETER ProtectedToken 17 | An encrypted access token to the AutoRest Azure function. Use this to persist an access token in a way only the current user on the current system can access. 18 | 19 | .PARAMETER Register 20 | Using this command, the module will remember the connection settings persistently across PowerShell sessions. 21 | CAUTION: When using unencrypted token data (such as specified through the -UnprotectedToken parameter), the authenticating token will be stored in clear-text! 22 | 23 | .EXAMPLE 24 | PS C:\> Connect-AutoRest -Uri 'https://demofunctionapp.azurewebsites.net/api/' 25 | 26 | Establishes a connection to AutoRest 27 | #> 28 | [CmdletBinding()] 29 | param ( 30 | [string] 31 | $Uri, 32 | 33 | [string] 34 | $UnprotectedToken, 35 | 36 | [System.Management.Automation.PSCredential] 37 | $ProtectedToken, 38 | 39 | [switch] 40 | $Register 41 | ) 42 | 43 | process 44 | { 45 | if (Test-PSFParameterBinding -ParameterName UnprotectedToken) 46 | { 47 | Set-PSFConfig -Module 'AutoRest' -Name 'Client.UnprotectedToken' -Value $UnprotectedToken 48 | if ($Register) { Register-PSFConfig -Module 'AutoRest' -Name 'Client.UnprotectedToken' } 49 | } 50 | if (Test-PSFParameterBinding -ParameterName Uri) 51 | { 52 | Set-PSFConfig -Module 'AutoRest' -Name 'Client.Uri' -Value $Uri 53 | if ($Register) { Register-PSFConfig -Module 'AutoRest' -Name 'Client.Uri' } 54 | } 55 | if (Test-PSFParameterBinding -ParameterName ProtectedToken) 56 | { 57 | Set-PSFConfig -Module 'AutoRest' -Name 'Client.ProtectedToken' -Value $ProtectedToken 58 | if ($Register) { Register-PSFConfig -Module 'AutoRest' -Name 'Client.ProtectedToken' } 59 | } 60 | 61 | } 62 | } -------------------------------------------------------------------------------- /azFunctionResources/clientModule/internal/configurations/connection.ps1: -------------------------------------------------------------------------------- 1 | Set-PSFConfig -Module 'AutoRest' -Name 'Client.Uri' -Value $null -Initialize -Validation 'string' -Description "Url to connect to the AutoRest Azure function" 2 | Set-PSFConfig -Module 'AutoRest' -Name 'Client.UnprotectedToken' -Value '' -Initialize -Validation 'string' -Description "The unencrypted access token to the AutoRest Azure function. ONLY use this from secure locations or non-sensitive functions!" 3 | Set-PSFConfig -Module 'AutoRest' -Name 'Client.ProtectedToken' -Value $null -Initialize -Validation 'credential' -Description "An encrypted access token to the AutoRest Azure function. Use this to persist an access token in a way only the current user on the current system can access." -------------------------------------------------------------------------------- /azFunctionResources/clientModule/internal/functions/Get-InternalConnectionData.ps1: -------------------------------------------------------------------------------- 1 | function Get-InternalConnectionData 2 | { 3 | <# 4 | .SYNOPSIS 5 | Creates parameter hashtables for Invoke-RestMethod calls. 6 | 7 | .DESCRIPTION 8 | Creates parameter hashtables for Invoke-RestMethod calls. 9 | This is the main abstraction layer for public functions. 10 | 11 | .PARAMETER Method 12 | The Rest Method to use when calling this function. 13 | 14 | .PARAMETER Parameters 15 | The PSBoundParameters object. Will be passed online using PowerShell Serialization. 16 | 17 | .PARAMETER FunctionName 18 | The name of the Azure Function to call. 19 | This should always be the condensed name of the function. 20 | #> 21 | [CmdletBinding()] 22 | param ( 23 | [string] 24 | $Method, 25 | 26 | $Parameters, 27 | 28 | [string] 29 | $FunctionName 30 | ) 31 | 32 | process 33 | { 34 | try { $uri = '{0}{1}' -f (Get-PSFConfigValue -FullName 'AutoRest.Client.Uri' -NotNull), $FunctionName } 35 | catch { $PSCmdlet.ThrowTerminatingError($_) } 36 | $header = @{ } 37 | 38 | #region Authentication 39 | $unprotectedToken = Get-PSFConfigValue -FullName 'AutoRest.Client.UnprotectedToken' 40 | $protectedToken = Get-PSFConfigValue -FullName 'AutoRest.Client.ProtectedToken' 41 | 42 | $authenticationDone = $false 43 | if ($protectedToken -and -not $authenticationDone) 44 | { 45 | $uri += '?code={0}' -f $protectedToken.GetNetworkCredential().Password 46 | $authenticationDone = $true 47 | } 48 | if ($unprotectedToken -and -not $authenticationDone) 49 | { 50 | $uri += '?code={0}' -f $unprotectedToken 51 | $authenticationDone = $true 52 | } 53 | if (-not $authenticationDone) 54 | { 55 | throw "No Authentication configured!" 56 | } 57 | #endregion Authentication 58 | 59 | 60 | @{ 61 | Method = $Method 62 | Uri = $uri 63 | Headers = $header 64 | Body = (@{ 65 | __SerializedParameters = ($Parameters | ConvertTo-PSFHashtable | ConvertTo-PSFClixml) 66 | __PSSerialize = $true 67 | } | ConvertTo-Json) 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /azFunctionResources/clientModule/moduleroot.psm1: -------------------------------------------------------------------------------- 1 | $script:ModuleRoot = $PSScriptRoot 2 | 3 | foreach ($file in (Get-ChildItem -Path "$script:ModuleRoot\internal\configurations" -Recurse -Filter '*.ps1')) 4 | { 5 | . $file.FullName 6 | } 7 | foreach ($file in (Get-ChildItem -Path "$script:ModuleRoot\internal\functions" -Recurse -Filter '*.ps1')) 8 | { 9 | . $file.FullName 10 | } 11 | foreach ($file in (Get-ChildItem -Path "$script:ModuleRoot\functions" -Recurse -Filter '*.ps1')) 12 | { 13 | . $file.FullName 14 | } -------------------------------------------------------------------------------- /azFunctionResources/functionOverride/Get-Example.json: -------------------------------------------------------------------------------- 1 | { 2 | "bindings": [ 3 | { 4 | "authLevel": "function", 5 | "type": "httpTrigger", 6 | "direction": "in", 7 | "name": "Request", 8 | "methods": [ 9 | "delete" 10 | ] 11 | }, 12 | { 13 | "type": "http", 14 | "direction": "out", 15 | "name": "Response" 16 | } 17 | ], 18 | "disabled": true 19 | } -------------------------------------------------------------------------------- /azFunctionResources/functionOverride/Get-Example.ps1: -------------------------------------------------------------------------------- 1 | function Get-Example 2 | { 3 | <# 4 | .NOTES 5 | This file will be used to override the auto-generated CLIENT MODULE function implementation. 6 | 7 | Using the vsts-createFunctionClientModule.ps1 task script, you can autogenerate a client module. 8 | That module can be used to connect to the published Azure Function Module. 9 | However sometimes you may want to override the default client function for a given command, 10 | in order to better customize the way it behaves. 11 | 12 | Creating a ps1 file with the name of the specific function in this folder will use this file, 13 | rather than creating a default copy. 14 | 15 | NOTE: 16 | There will be no further automatic change detection! 17 | If you later update the Azure Function, you need to manually update the client function as well. 18 | #> 19 | [CmdletBinding()] 20 | Param ( 21 | 22 | ) 23 | 24 | begin 25 | { 26 | 27 | } 28 | process 29 | { 30 | 31 | } 32 | end 33 | { 34 | 35 | } 36 | } -------------------------------------------------------------------------------- /azFunctionResources/functionOverride/Get-Example.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | # Override the rest methods used for the API endpoint 3 | # RestMethods = 'delete' 4 | 5 | # Override inclusion into client module 6 | # NoClientFunction 7 | } -------------------------------------------------------------------------------- /azFunctionResources/host-az.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0", 3 | "managedDependency": { 4 | "Enabled": true 5 | } 6 | } -------------------------------------------------------------------------------- /azFunctionResources/host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0" 3 | } -------------------------------------------------------------------------------- /azFunctionResources/local.settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "IsEncrypted": false, 3 | "Values": { 4 | "FUNCTIONS_WORKER_RUNTIME": "powershell", 5 | "AzureWebJobsStorage": "--connection string for storage account---" 6 | } 7 | } -------------------------------------------------------------------------------- /azFunctionResources/profile.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | This is the globl profile file for the Azure Function App. 3 | This file will have been executed first, before any function runs. 4 | Use this to create a common execution environment, 5 | but keep in mind that the profile execution time is added to the function startup time for ALL functions. 6 | #> 7 | 8 | if ($env:MSI_SECRET -and (Get-Module -ListAvailable Az.Accounts)) 9 | { 10 | Connect-AzAccount -Identity 11 | } -------------------------------------------------------------------------------- /azFunctionResources/profileFunctions/Convert-AzureFunctionParameter.ps1: -------------------------------------------------------------------------------- 1 | function Convert-AzureFunctionParameter 2 | { 3 | <# 4 | .SYNOPSIS 5 | Extracts the parameters passed into the rest method. 6 | 7 | .DESCRIPTION 8 | Extracts the parameters passed into the rest method of an Azure Function. 9 | Returns a hashtable, similar to what would be found on a $PSBoundParameters variable. 10 | 11 | .PARAMETER Request 12 | The request to process 13 | 14 | .EXAMPLE 15 | PS C:\> Convert-AzureFunctionParameter -Request $request 16 | 17 | Converts the $request object into a regular hashtable. 18 | #> 19 | [OutputType([System.Collections.Hashtable])] 20 | [CmdletBinding()] 21 | param ( 22 | $Request 23 | ) 24 | 25 | $parameterObject = [pscustomobject]@{ 26 | Parameters = @{ } 27 | Serialize = $false 28 | } 29 | 30 | foreach ($key in $Request.Query.Keys) 31 | { 32 | # Do NOT include the authentication key 33 | if ($key -eq 'code') { continue } 34 | $parameterObject.Parameters[$key] = $Request.Query.$key 35 | } 36 | foreach ($key in $Request.Body.Keys) 37 | { 38 | $parameterObject.Parameters[$key] = $Request.Body.$key 39 | } 40 | if ($parameterObject.Parameters.__PSSerialize) 41 | { 42 | $parameterObject.Serialize = $true 43 | $null = $parameterObject.Parameters.Remove('__PSSerialize') 44 | } 45 | if ($parameterObject.Parameters.__SerializedParameters) 46 | { 47 | $parameterObject.Parameters = $parameterObject.Parameters.__SerializedParameters | ConvertFrom-PSFClixml 48 | } 49 | 50 | $parameterObject 51 | } -------------------------------------------------------------------------------- /azFunctionResources/profileFunctions/Write-AzureFunctionOutput.ps1: -------------------------------------------------------------------------------- 1 | function Write-AzureFunctionOutput 2 | { 3 | <# 4 | .SYNOPSIS 5 | Write output equally well from Azure Functions or locally. 6 | 7 | .DESCRIPTION 8 | Write output equally well from Azure Functions or locally. 9 | When calling this command, call return straight after it. 10 | Use Write-AzureFunctionStatus first if an error should be returned, then specify an error text here. 11 | 12 | .PARAMETER Value 13 | The value data to return. 14 | Either an error message 15 | 16 | .PARAMETER Serialize 17 | Return the output object as compressed clixml string. 18 | You can use ConvertFrom-PSFClixml to restore the object on the recipient-side. 19 | 20 | .EXAMPLE 21 | PS C:\> Write-AzureFunctionOutput -Value $result 22 | 23 | Writes the content of $result as output. 24 | 25 | .EXAMPLE 26 | PS C:\> Write-AzureFunctionOutput -Value $result -Serialize 27 | 28 | Writes the content of $result as output. 29 | If called from Azure Functions, it will convert the output as compressed clixml string. 30 | 31 | #> 32 | [CmdletBinding()] 33 | param ( 34 | [Parameter(Mandatory = $true)] 35 | $Value, 36 | 37 | [switch] 38 | $Serialize, 39 | 40 | [System.Net.HttpStatusCode] 41 | $Status = [System.Net.HttpStatusCode]::OK 42 | ) 43 | 44 | if ($Serialize) 45 | { 46 | $Value = $Value | ConvertTo-PSFClixml 47 | } 48 | 49 | Push-OutputBinding -Name Response -Value ( 50 | [HttpResponseContext]@{ 51 | StatusCode = $Status 52 | Body = $Value 53 | } 54 | ) 55 | } -------------------------------------------------------------------------------- /azFunctionResources/readme.md: -------------------------------------------------------------------------------- 1 | # Azure Function Resources 2 | 3 | This folder is used to store Azure Function specific meta data and resources. 4 | 5 | This folder is also used to allow the user to easily create a custom function-specific configuration, for exanmple in order to change the trigger settings. 6 | 7 | To specify custom, 'Per Function' configuration json, just place the desired configuration file as 'functionname.json' into this folder (it does not matter if it is the PowerShell function name or the condensed version used for publishing on Azure). -------------------------------------------------------------------------------- /azFunctionResources/requirements.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | Az = '1.*' 3 | } -------------------------------------------------------------------------------- /azFunctionResources/run.ps1: -------------------------------------------------------------------------------- 1 | param ( 2 | $Request, 3 | 4 | $TriggerMetadata 5 | ) 6 | 7 | $parameterObject = Convert-AzureFunctionParameter -Request $Request 8 | $parameters = $parameterObject.Parameters 9 | try { $data = %functionname% @parameters } 10 | catch 11 | { 12 | Write-AzureFunctionOutput -Value "Failed to execute: $_" -Status InternalServerError 13 | return 14 | } 15 | 16 | Write-AzureFunctionOutput -Value $data -Serialize:$parameterObject.Serialize -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | pool: 2 | name: Hosted VS2017 3 | steps: 4 | - task: PowerShell@2 5 | displayName: Prerequisites 6 | inputs: 7 | targetType: filePath 8 | filePath: './build/vsts-prerequisites.ps1' 9 | 10 | - task: PowerShell@2 11 | displayName: Validate 12 | inputs: 13 | targetType: filePath 14 | filePath: './build/vsts-validate.ps1' 15 | 16 | - task: PublishTestResults@2 17 | displayName: 'Publish Test Results **/TEST-*.xml' 18 | inputs: 19 | testResultsFormat: NUnit 20 | condition: always() 21 | -------------------------------------------------------------------------------- /backlogged/ConvertFrom-ARExcel.ps1: -------------------------------------------------------------------------------- 1 | function ConvertFrom-ARExcel { 2 | [CmdletBinding()] 3 | param ( 4 | [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] 5 | [PsfValidateScript('PSFramework.Validate.FSPath.File', ErrorString = 'PSFramework.Validate.FSPath.File')] 6 | [Alias('FullName')] 7 | [string] 8 | $Path, 9 | 10 | [PsfValidateScript('PSFramework.Validate.FSPath.Folder', ErrorString = 'PSFramework.Validate.FSPath.Folder')] 11 | [string] 12 | $OutPath = '.', 13 | 14 | [Parameter(Mandatory = $true)] 15 | [string] 16 | $RestCommand, 17 | 18 | [switch] 19 | $GroupByEndpoint, 20 | 21 | [switch] 22 | $Force, 23 | 24 | [switch] 25 | $EnableException 26 | ) 27 | 28 | process { 29 | foreach ($entry in Import-Excel -Path $Path) { 30 | if (-not ($entry.Command -and $entry.Endpoint -and $entry.Method)) { 31 | Stop-PSFFunction -String 'ConvertFrom-ARExcel.BadInput' -StringValues $entry -Target $entry -Continue -EnableException $EnableException 32 | } 33 | 34 | $param = @{ 35 | CommandName = $entry.Command 36 | Endpoint = $entry.Endpoint 37 | Method = $entry.Method 38 | ParametersMandatory = $entry.ParametersMandatory | Remove-PSFNull | Split-String -Separator ',|;' | Get-SubString -Trim " " 39 | ParametersOptional = $entry.ParametersOptional | Remove-PSFNull | Split-String -Separator ',|;' | Get-SubString -Trim " " 40 | Filters = $entry.Filters | Remove-PSFNull | Split-String -Separator ',|;' | Get-SubString -Trim " " 41 | Scopes = $entry.Scopes | Remove-PSFNull | Split-String -Separator ',|;' | Get-SubString -Trim " " 42 | ProcessorCommand = $entry.Processor 43 | OutPath = $OutPath 44 | RestCommand = $RestCommand 45 | GroupByEndpoint = $GroupByEndpoint 46 | Force = $Force 47 | } 48 | ConvertTo-ARCommand @param 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /backlogged/ConvertTo-ARCommand.ps1: -------------------------------------------------------------------------------- 1 | function ConvertTo-ARCommand { 2 | [CmdletBinding()] 3 | param ( 4 | [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] 5 | [string] 6 | $CommandName, 7 | 8 | [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] 9 | [string] 10 | $Endpoint, 11 | 12 | [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] 13 | [string] 14 | $Method, 15 | 16 | [Parameter(ValueFromPipelineByPropertyName = $true)] 17 | [AllowEmptyCollection()] 18 | [string[]] 19 | $ParametersMandatory, 20 | 21 | [Parameter(ValueFromPipelineByPropertyName = $true)] 22 | [AllowEmptyCollection()] 23 | [string[]] 24 | $ParametersOptional, 25 | 26 | [Parameter(ValueFromPipelineByPropertyName = $true)] 27 | [AllowEmptyCollection()] 28 | [string[]] 29 | $Filters, 30 | 31 | [Parameter(ValueFromPipelineByPropertyName = $true)] 32 | [string[]] 33 | $Scopes, 34 | 35 | [Parameter(ValueFromPipelineByPropertyName = $true)] 36 | [string] 37 | $ProcessorCommand, 38 | 39 | [string] 40 | $OutPath = '.', 41 | 42 | [Parameter(Mandatory = $true)] 43 | [string] 44 | $RestCommand, 45 | 46 | [switch] 47 | $GroupByEndpoint, 48 | 49 | [switch] 50 | $Force, 51 | 52 | [switch] 53 | $EnableException 54 | ) 55 | 56 | process { 57 | $paramNewCommandBody = $PSBoundParameters | ConvertTo-PSFHashtable -Include RestCommand, Endpoint, Method, ParametersMandatory, ParametersOptional, Scopes, ProcessorCommand 58 | $bodyCode = New-CommandBody @paramNewCommandBody 59 | 60 | $paramNewCommand = @{ 61 | Name = $CommandName 62 | ParametersMandatory = $ParametersMandatory 63 | ParametersOptional = $ParametersOptional 64 | Process = $bodyCode 65 | } 66 | if ($Scopes) { $paramNewCommand.Scopes = $Scopes } 67 | $commandCode = New-Command @paramNewCommand 68 | 69 | $outFolder = Resolve-PSFPath -Path $OutPath 70 | if ($GroupByEndpoint) { 71 | $endpointElement = @($Endpoint -split "/")[0] 72 | $outFolder = Join-Path -Path $outFolder -ChildPath ([System.Globalization.CultureInfo]::CurrentCulture.TextInfo.ToTitleCase($endpointElement)) 73 | } 74 | if (-not (Test-Path -Path $outFolder)) { $null = New-Item -Path $outFolder -ItemType Directory -Force } 75 | $commandPath = Join-Path -Path $outFolder -ChildPath "$($CommandName).ps1" 76 | if (-not $Force -and (Test-Path -Path $commandPath)) { continue } 77 | $encoding = [System.Text.UTF8Encoding]::new($true) 78 | [System.IO.File]::WriteAllText($commandPath, $commandCode, $encoding) 79 | } 80 | } -------------------------------------------------------------------------------- /backlogged/New-Command.ps1: -------------------------------------------------------------------------------- 1 | function New-Command { 2 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")] 3 | [CmdletBinding()] 4 | param ( 5 | [string] 6 | $Name, 7 | 8 | [string[]] 9 | $ParametersMandatory, 10 | 11 | [string[]] 12 | $ParametersOptional, 13 | 14 | [string[]] 15 | $Scopes, 16 | 17 | [string] 18 | $Begin, 19 | 20 | [string] 21 | $Process, 22 | 23 | [string] 24 | $End 25 | ) 26 | 27 | process { 28 | $stringBuilder = [System.Text.StringBuilder]::new() 29 | $null = $stringBuilder.AppendLine("function $Name {") 30 | $null = $stringBuilder.AppendLine((New-CommandHelpBlock -Parameters (@($ParametersMandatory) + @($ParametersOptional)) -Scopes $Scopes)) 31 | $null = $stringBuilder.AppendLine((New-CommandParameterBlock -Mandatory $ParametersMandatory -Optional $ParametersOptional)) 32 | if ($Begin) { 33 | $null = $stringBuilder.AppendLine(' begin {') 34 | $null = $stringBuilder.AppendLine($Begin) 35 | $null = $stringBuilder.AppendLine(' }') 36 | } 37 | if ($Process) { 38 | $null = $stringBuilder.AppendLine(' process {') 39 | $null = $stringBuilder.AppendLine($Process) 40 | $null = $stringBuilder.AppendLine(' }') 41 | } 42 | if ($End) { 43 | $null = $stringBuilder.AppendLine(' end {') 44 | $null = $stringBuilder.AppendLine($End) 45 | $null = $stringBuilder.AppendLine(' }') 46 | } 47 | $null = $stringBuilder.AppendLine('}') 48 | $stringBuilder.ToString() 49 | } 50 | } -------------------------------------------------------------------------------- /backlogged/New-CommandBody.ps1: -------------------------------------------------------------------------------- 1 | function New-CommandBody { 2 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")] 3 | [CmdletBinding()] 4 | param ( 5 | [Parameter(Mandatory = $true)] 6 | [string] 7 | $RestCommand, 8 | 9 | [Parameter(Mandatory = $true)] 10 | [string] 11 | $Endpoint, 12 | 13 | [Parameter(Mandatory = $true)] 14 | [string] 15 | $Method, 16 | 17 | [AllowEmptyCollection()] 18 | [string[]] 19 | $ParametersMandatory, 20 | 21 | [AllowEmptyCollection()] 22 | [string[]] 23 | $ParametersOptional, 24 | 25 | [AllowEmptyCollection()] 26 | [string[]] 27 | $Scopes, 28 | 29 | [AllowEmptyString()] 30 | [string] 31 | $ProcessorCommand 32 | ) 33 | 34 | process { 35 | $stringBuilder = [System.Text.StringBuilder]::new() 36 | $parametersString = '' 37 | if ($ParametersMandatory -or $ParametersOptional) { 38 | $null = $stringBuilder.AppendLine(' $parameters = @{ }') 39 | foreach ($parameter in $ParametersMandatory) { 40 | $null = $stringBuilder.AppendLine((' $parameters.{0} = ${0}') -f $parameter) 41 | } 42 | foreach ($parameter in $ParametersOptional) { 43 | $null = $stringBuilder.AppendLine((' if ($PSBoundParameters.ContainsKey("{0}")) {{ $parameters.{0} = ${0} }}') -f $parameter) 44 | } 45 | $parametersString = ' -Body $parameters' 46 | } 47 | $processorString = '' 48 | if ($ProcessorCommand) { $processorString = " | $ProcessorCommand" } 49 | $scopesString = '' 50 | if ($Scopes) { $scopesString = ' -RequiredScopes ''{0}''' -f ($Scopes -join "','") } 51 | 52 | $null = $stringBuilder.AppendLine(" $RestCommand -Path '$Endpoint' -Method $($Method)$($scopesString)$($parametersString)$($processorString)") 53 | $stringBuilder.ToString() 54 | } 55 | } -------------------------------------------------------------------------------- /backlogged/New-CommandHelpBlock.ps1: -------------------------------------------------------------------------------- 1 | function New-CommandHelpBlock 2 | { 3 | [CmdletBinding()] 4 | Param ( 5 | [String[]] 6 | $Parameters, 7 | 8 | [string[]] 9 | $Scopes 10 | ) 11 | 12 | begin 13 | { 14 | 15 | } 16 | process 17 | { 18 | $stringBuilder = [System.Text.StringBuilder]::new() 19 | $helpString = @' 20 | <# 21 | .SYNOPSIS 22 | 23 | 24 | .DESCRIPTION 25 | 26 | 27 | {0} 28 | 29 | .EXAMPLE 30 | PS C:\> {1} 31 | 32 | 33 | 34 | .NOTES 35 | Scopes needed: {2} 36 | Author: {3} 37 | Company: {4} 38 | #> 39 | '@ 40 | } 41 | end 42 | { 43 | 44 | } 45 | } -------------------------------------------------------------------------------- /backlogged/New-CommandParameterBlock.ps1: -------------------------------------------------------------------------------- 1 | function New-CommandParameterBlock 2 | { 3 | [CmdletBinding()] 4 | param ( 5 | [AllowEmptyCollection()] 6 | [string[]] 7 | $Mandatory, 8 | 9 | [AllowEmptyCollection()] 10 | [string[]] 11 | $Optional 12 | ) 13 | 14 | process 15 | { 16 | $stringBuilder = [System.Text.StringBuilder]::new() 17 | $null = $stringBuilder.AppendLine(' [CmdletBinding()]') 18 | $null = $stringBuilder.AppendLine(' Param (') 19 | $first = $true 20 | 21 | foreach ($parameter in $Mandatory) { 22 | if (-not $first) { 23 | $null = $stringBuilder.AppendLine(',') 24 | $null = $stringBuilder.AppendLine("") 25 | } 26 | $null = $stringBuilder.AppendLine(' [Parameter(Mandatory = $true)]') 27 | $null = $stringBuilder.Append(" `$$parameter") 28 | $first = $false 29 | } 30 | foreach ($parameter in $Optional) { 31 | if (-not $first) { 32 | $null = $stringBuilder.AppendLine(',') 33 | $null = $stringBuilder.AppendLine("") 34 | } 35 | $null = $stringBuilder.Append(" `$$parameter") 36 | $first = $false 37 | } 38 | $null = $stringBuilder.AppendLine("") 39 | $null = $stringBuilder.AppendLine(' )') 40 | $stringBuilder.ToString() 41 | } 42 | } -------------------------------------------------------------------------------- /build/AzureFunction.readme.md: -------------------------------------------------------------------------------- 1 | # Setting up the release pipeline: 2 | 3 | ## Preliminary 4 | 5 | Setting up a release pipeline, set the trigger to do continuous integration against the master branch only. 6 | In Stage 1 set up a tasksequence: 7 | 8 | ## 1) PowerShell Task: Prerequisites 9 | 10 | Have it execute `vsts-prerequisites.ps1` 11 | 12 | ## 2) PowerShell Task: Validate 13 | 14 | Have it execute `vsts-prerequisites.ps1` 15 | 16 | ## 3) PowerShell Task: Build 17 | 18 | Have it execute `vsts-build.ps1`. 19 | The task requires two parameters: 20 | 21 | - `-LocalRepo` 22 | - `-WorkingDirectory $(System.DefaultWorkingDirectory)/_�name�` 23 | 24 | ## 4) Publish Test Results 25 | 26 | Configure task to pick up nunit type of tests (rather than the default junit). 27 | Configure task to execute, even if previous steps failed or the task sequence was cancelled. 28 | 29 | ## 5) PowerShell Task: Package Function 30 | 31 | Have it execute `vsts-packageFunction.ps1` 32 | 33 | ## 6) Azure Function AppDeploy 34 | 35 | Configure to publish to the correct function app. -------------------------------------------------------------------------------- /build/vsts-build.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | This script publishes the module to the gallery. 3 | It expects as input an ApiKey authorized to publish the module. 4 | 5 | Insert any build steps you may need to take before publishing it here. 6 | #> 7 | param ( 8 | $ApiKey, 9 | 10 | $WorkingDirectory, 11 | 12 | $Repository = 'PSGallery', 13 | 14 | [switch] 15 | $LocalRepo, 16 | 17 | [switch] 18 | $SkipPublish, 19 | 20 | [switch] 21 | $AutoVersion 22 | ) 23 | 24 | #region Handle Working Directory Defaults 25 | if (-not $WorkingDirectory) 26 | { 27 | if ($env:RELEASE_PRIMARYARTIFACTSOURCEALIAS) 28 | { 29 | $WorkingDirectory = Join-Path -Path $env:SYSTEM_DEFAULTWORKINGDIRECTORY -ChildPath $env:RELEASE_PRIMARYARTIFACTSOURCEALIAS 30 | } 31 | else { $WorkingDirectory = $env:SYSTEM_DEFAULTWORKINGDIRECTORY } 32 | } 33 | if (-not $WorkingDirectory) { $WorkingDirectory = Split-Path $PSScriptRoot } 34 | #endregion Handle Working Directory Defaults 35 | 36 | # Prepare publish folder 37 | Write-PSFMessage -Level Important -Message "Creating and populating publishing directory" 38 | $publishDir = New-Item -Path $WorkingDirectory -Name publish -ItemType Directory -Force 39 | Copy-Item -Path "$($WorkingDirectory)\AutoRest" -Destination $publishDir.FullName -Recurse -Force 40 | 41 | #region Gather text data to compile 42 | $text = @() 43 | $processed = @() 44 | 45 | # Gather Stuff to run before 46 | foreach ($filePath in (& "$($PSScriptRoot)\..\AutoRest\internal\scripts\preimport.ps1")) 47 | { 48 | if ([string]::IsNullOrWhiteSpace($filePath)) { continue } 49 | 50 | $item = Get-Item $filePath 51 | if ($item.PSIsContainer) { continue } 52 | if ($item.FullName -in $processed) { continue } 53 | $text += [System.IO.File]::ReadAllText($item.FullName) 54 | $processed += $item.FullName 55 | } 56 | 57 | # Gather commands 58 | Get-ChildItem -Path "$($publishDir.FullName)\AutoRest\internal\functions\" -Recurse -File -Filter "*.ps1" | ForEach-Object { 59 | $text += [System.IO.File]::ReadAllText($_.FullName) 60 | } 61 | Get-ChildItem -Path "$($publishDir.FullName)\AutoRest\functions\" -Recurse -File -Filter "*.ps1" | ForEach-Object { 62 | $text += [System.IO.File]::ReadAllText($_.FullName) 63 | } 64 | 65 | # Gather stuff to run afterwards 66 | foreach ($filePath in (& "$($PSScriptRoot)\..\AutoRest\internal\scripts\postimport.ps1")) 67 | { 68 | if ([string]::IsNullOrWhiteSpace($filePath)) { continue } 69 | 70 | $item = Get-Item $filePath 71 | if ($item.PSIsContainer) { continue } 72 | if ($item.FullName -in $processed) { continue } 73 | $text += [System.IO.File]::ReadAllText($item.FullName) 74 | $processed += $item.FullName 75 | } 76 | #endregion Gather text data to compile 77 | 78 | #region Update the psm1 file 79 | $fileData = Get-Content -Path "$($publishDir.FullName)\AutoRest\AutoRest.psm1" -Raw 80 | $fileData = $fileData.Replace('""', '""') 81 | $fileData = $fileData.Replace('""', ($text -join "`n`n")) 82 | [System.IO.File]::WriteAllText("$($publishDir.FullName)\AutoRest\AutoRest.psm1", $fileData, [System.Text.Encoding]::UTF8) 83 | #endregion Update the psm1 file 84 | 85 | #region Updating the Module Version 86 | if ($AutoVersion) 87 | { 88 | Write-PSFMessage -Level Important -Message "Updating module version numbers." 89 | try { [version]$remoteVersion = (Find-Module 'AutoRest' -Repository $Repository -ErrorAction Stop).Version } 90 | catch 91 | { 92 | Stop-PSFFunction -Message "Failed to access $($Repository)" -EnableException $true -ErrorRecord $_ 93 | } 94 | if (-not $remoteVersion) 95 | { 96 | Stop-PSFFunction -Message "Couldn't find AutoRest on repository $($Repository)" -EnableException $true 97 | } 98 | $newBuildNumber = $remoteVersion.Build + 1 99 | [version]$localVersion = (Import-PowerShellDataFile -Path "$($publishDir.FullName)\AutoRest\AutoRest.psd1").ModuleVersion 100 | Update-ModuleManifest -Path "$($publishDir.FullName)\AutoRest\AutoRest.psd1" -ModuleVersion "$($localVersion.Major).$($localVersion.Minor).$($newBuildNumber)" 101 | } 102 | #endregion Updating the Module Version 103 | 104 | #region Publish 105 | if ($SkipPublish) { return } 106 | if ($LocalRepo) 107 | { 108 | # Dependencies must go first 109 | Write-PSFMessage -Level Important -Message "Creating Nuget Package for module: PSFramework" 110 | New-PSMDModuleNugetPackage -ModulePath (Get-Module -Name PSFramework).ModuleBase -PackagePath . 111 | Write-PSFMessage -Level Important -Message "Creating Nuget Package for module: AutoRest" 112 | New-PSMDModuleNugetPackage -ModulePath "$($publishDir.FullName)\AutoRest" -PackagePath . 113 | } 114 | else 115 | { 116 | # Publish to Gallery 117 | Write-PSFMessage -Level Important -Message "Publishing the AutoRest module to $($Repository)" 118 | Publish-Module -Path "$($publishDir.FullName)\AutoRest" -NuGetApiKey $ApiKey -Force -Repository $Repository 119 | } 120 | #endregion Publish -------------------------------------------------------------------------------- /build/vsts-createFunctionClientModule.ps1: -------------------------------------------------------------------------------- 1 |  2 | <# 3 | .SYNOPSIS 4 | Build script that generates a client module for REST API endpoints of a Azure PowerShell Functions project. 5 | 6 | .DESCRIPTION 7 | Build script that generates a client module for REST API endpoints of a Azure PowerShell Functions project. 8 | 9 | .PARAMETER ApiKey 10 | The API key to use to publish the module to a Nuget Repository 11 | 12 | .PARAMETER WorkingDirectory 13 | The root folder from which to build the module. 14 | 15 | .PARAMETER Repository 16 | The name of the repository to publish to. 17 | Defaults to PSGallery. 18 | 19 | .PARAMETER LocalRepo 20 | Instead of publishing to a gallery, drop a nuget package in the root folder. 21 | This package can then be picked up in a later step for publishing to Azure Artifacts. 22 | 23 | .PARAMETER ModuleName 24 | The name to give to the client module. 25 | By default, the client module will be named '.Client'. 26 | 27 | .PARAMETER IncludeFormat 28 | Include the format xml of the source module for the client module. 29 | 30 | .PARAMETER IncludeType 31 | Include the type extension xml of the source module for the client module. 32 | 33 | .PARAMETER IncludeAssembly 34 | Include the binaries of the source module for the client module. 35 | #> 36 | param ( 37 | $ApiKey, 38 | 39 | $WorkingDirectory, 40 | 41 | $Repository = 'PSGallery', 42 | 43 | [switch] 44 | $LocalRepo, 45 | 46 | $ModuleName, 47 | 48 | [switch] 49 | $IncludeFormat, 50 | 51 | [switch] 52 | $IncludeType, 53 | 54 | [switch] 55 | $IncludeAssembly 56 | ) 57 | 58 | #region Handle Working Directory Defaults 59 | if (-not $WorkingDirectory) 60 | { 61 | if ($env:RELEASE_PRIMARYARTIFACTSOURCEALIAS) 62 | { 63 | $WorkingDirectory = Join-Path -Path $env:SYSTEM_DEFAULTWORKINGDIRECTORY -ChildPath $env:RELEASE_PRIMARYARTIFACTSOURCEALIAS 64 | } 65 | else { $WorkingDirectory = $env:SYSTEM_DEFAULTWORKINGDIRECTORY } 66 | } 67 | #endregion Handle Working Directory Defaults 68 | 69 | Write-PSFMessage -Level Host -Message 'Starting Build: Client Module' 70 | $parentModule = 'AutoRest' 71 | if (-not $ModuleName) { $ModuleName = 'AutoRest.Client' } 72 | Write-PSFMessage -Level Host -Message 'Creating Folder Structure' 73 | $workingRoot = New-Item -Path $WorkingDirectory -Name $ModuleName -ItemType Directory 74 | $publishRoot = Join-Path -Path $WorkingDirectory -ChildPath 'publish\AutoRest' 75 | Copy-Item -Path "$($WorkingDirectory)\azFunctionResources\clientModule\functions" -Destination "$($workingRoot.FullName)\" -Recurse 76 | Copy-Item -Path "$($WorkingDirectory)\azFunctionResources\clientModule\internal" -Destination "$($workingRoot.FullName)\" -Recurse 77 | Copy-Item -Path "$($publishRoot)\en-us" -Destination "$($workingRoot.FullName)\" -Recurse 78 | $functionFolder = Get-Item -Path "$($workingRoot.FullName)\functions" 79 | 80 | #region Create Functions 81 | $encoding = [PSFEncoding]'utf8' 82 | $functionsText = Get-Content -Path "$($WorkingDirectory)\azFunctionResources\clientModule\function.ps1" -Raw 83 | 84 | Write-PSFMessage -Level Host -Message 'Creating Functions' 85 | foreach ($functionSourceFile in (Get-ChildItem -Path "$($publishRoot)\functions" -Recurse -Filter '*.ps1')) 86 | { 87 | Write-PSFMessage -Level Host -Message " Processing function: $($functionSourceFile.BaseName)" 88 | $condensedName = $functionSourceFile.BaseName -replace '-', '' 89 | 90 | #region Load Overrides 91 | $override = @{ } 92 | if (Test-Path -Path "$($WorkingDirectory)\azFunctionResources\functionOverride\$($functionSourceFile.BaseName).psd1") 93 | { 94 | $override = Import-PowerShellDataFile -Path "$($WorkingDirectory)\azFunctionResources\functionOverride\$($functionSourceFile.BaseName).psd1" 95 | } 96 | if (Test-Path -Path "$($WorkingDirectory)\azFunctionResources\functionOverride\$($condensedName).psd1") 97 | { 98 | $override = Import-PowerShellDataFile -Path "$($WorkingDirectory)\azFunctionResources\functionOverride\$($condensedName).psd1" 99 | } 100 | if ($override.NoClientFunction) 101 | { 102 | Write-PSFMessage -Level Host -Message " Override 'NoClientFunction' detected, skipping!" 103 | continue 104 | } 105 | 106 | # If there is an definition override, use it and continue 107 | if (Test-Path -Path "$($WorkingDirectory)\azFunctionResources\functionOverride\$($functionSourceFile.BaseName).ps1") 108 | { 109 | Write-PSFMessage -Level Host -Message " Override function definition detected, using override" 110 | Copy-Item -Path "$($WorkingDirectory)\azFunctionResources\functionOverride\$($functionSourceFile.BaseName).ps1" -Destination $functionFolder.FullName 111 | continue 112 | } 113 | 114 | # Figure out the Rest Method to use 115 | $methodName = 'Post' 116 | if ($override.RestMethods) 117 | { 118 | $methodName = $override.RestMethods | Where-Object { $_ -ne 'Get' } | Select-Object -First 1 119 | } 120 | 121 | #endregion Load Overrides 122 | 123 | $currentFunctionsText = $functionsText -replace '%functionname%', $functionSourceFile.BaseName -replace '%condensedname%', $condensedName -replace '%method%', $methodName 124 | 125 | $parsedFunction = Read-PSMDScript -Path $functionSourceFile.FullName 126 | $functionAst = $parsedFunction.Ast.EndBlock.Statements | Where-Object { 127 | $_ -is [System.Management.Automation.Language.FunctionDefinitionAst] 128 | } | Select-Object -First 1 129 | 130 | $end = $functionAst.Body.ParamBlock.Extent.EndOffSet 131 | $start = $functionAst.Body.Extent.StartOffSet + 1 132 | $currentFunctionsText = $currentFunctionsText.Replace('%parameter%', $functionAst.Body.Extent.Text.SubString(1, ($end - $start))) 133 | 134 | Write-PSFMessage -Level Host -Message " Creating file: $($functionFolder.FullName)\$($functionSourceFile.Name)" 135 | [System.IO.File]::WriteAllText("$($functionFolder.FullName)\$($functionSourceFile.Name)", $currentFunctionsText, $encoding) 136 | } 137 | $functionsToExport = (Get-ChildItem -Path $functionFolder.FullName -Recurse -Filter *.ps1).BaseName | Sort-Object 138 | #endregion Create Functions 139 | 140 | #region Create Core Module Files 141 | # Get Manifest of published version, in order to catch build-phase changes such as module version. 142 | $originalManifestData = Import-PowerShellDataFile -Path "$publishRoot\AutoRest.psd1" 143 | $prereqHash = @{ 144 | ModuleName = 'PSFramework' 145 | ModuleVersion = (Get-Module PSFramework).Version 146 | } 147 | $paramNewModuleManifest = @{ 148 | Path = ('{0}\{1}.psd1' -f $workingRoot.FullName, $ModuleName) 149 | FunctionsToExport = $functionsToExport 150 | CompanyName = $originalManifestData.CompanyName 151 | Author = $originalManifestData.Author 152 | Description = $originalManifestData.Description 153 | ModuleVersion = $originalManifestData.ModuleVersion 154 | RootModule = ('{0}.psm1' -f $ModuleName) 155 | Copyright = $originalManifestData.Copyright 156 | TypesToProcess = @() 157 | FormatsToProcess = @() 158 | RequiredAssemblies = @() 159 | RequiredModules = @($prereqHash) 160 | CompatiblePSEditions = 'Core', 'Desktop' 161 | PowerShellVersion = '5.1' 162 | } 163 | 164 | if ($IncludeAssembly) { $paramNewModuleManifest.RequiredAssemblies = $originalManifestData.RequiredAssemblies } 165 | if ($IncludeFormat) { $paramNewModuleManifest.FormatsToProcess = $originalManifestData.FormatsToProcess } 166 | if ($IncludeType) { $paramNewModuleManifest.TypesToProcess = $originalManifestData.TypesToProcess } 167 | Write-PSFMessage -Level Host -Message "Creating Module Manifest for module: $ModuleName" 168 | New-ModuleManifest @paramNewModuleManifest 169 | 170 | Write-PSFMessage -Level Host -Message "Copying additional module files" 171 | Copy-Item -Path "$($WorkingDirectory)\azFunctionResources\clientModule\moduleroot.psm1" -Destination "$($workingRoot.FullName)\$($ModuleName).psm1" 172 | Copy-Item -Path "$($WorkingDirectory)\LICENSE" -Destination "$($workingRoot.FullName)\" 173 | #endregion Create Core Module Files 174 | 175 | #region Transfer Additional Content 176 | if ($IncludeAssembly) 177 | { 178 | Copy-Item -Path "$publishRoot\bin" -Destination "$($workingRoot.FullName)\" -Recurse 179 | } 180 | if ($IncludeFormat -or $IncludeType) 181 | { 182 | Copy-Item -Path "$publishRoot\xml" -Destination "$($workingRoot.FullName)\" -Recurse 183 | } 184 | #endregion Transfer Additional Content 185 | 186 | #region Publish 187 | if ($LocalRepo) 188 | { 189 | # Dependencies must go first 190 | Write-PSFMessage -Level Important -Message "Creating Nuget Package for module: PSFramework" 191 | New-PSMDModuleNugetPackage -ModulePath (Get-Module -Name PSFramework).ModuleBase -PackagePath . -WarningAction SilentlyContinue 192 | Write-PSFMessage -Level Important -Message "Creating Nuget Package for module: AutoRest" 193 | New-PSMDModuleNugetPackage -ModulePath $workingRoot.FullName -PackagePath . -EnableException 194 | } 195 | else 196 | { 197 | # Publish to Gallery 198 | Write-PSFMessage -Level Important -Message "Publishing the AutoRest module to $($Repository)" 199 | Publish-Module -Path $workingRoot.FullName -NuGetApiKey $ApiKey -Force -Repository $Repository 200 | } 201 | #endregion Publish -------------------------------------------------------------------------------- /build/vsts-packageFunction.ps1: -------------------------------------------------------------------------------- 1 |  2 | <# 3 | .SYNOPSIS 4 | Packages an Azure Functions project, ready to release. 5 | 6 | .DESCRIPTION 7 | Packages an Azure Functions project, ready to release. 8 | Should be part of the release pipeline, after ensuring validation. 9 | 10 | Look into the 'AzureFunctionRest' template for generating functions for the module if you do. 11 | 12 | .PARAMETER WorkingDirectory 13 | The root folder to work from. 14 | 15 | .PARAMETER Repository 16 | The name of the repository to use for gathering dependencies from. 17 | #> 18 | param ( 19 | $WorkingDirectory = "$($env:SYSTEM_DEFAULTWORKINGDIRECTORY)\_AutoRest", 20 | 21 | $Repository = 'PSGallery', 22 | 23 | [switch] 24 | $IncludeAZ 25 | ) 26 | 27 | $moduleName = 'AutoRest' 28 | 29 | # Prepare Paths 30 | Write-PSFMessage -Level Host -Message "Creating working folders" 31 | $moduleRoot = Join-Path -Path $WorkingDirectory -ChildPath 'publish' 32 | $workingRoot = New-Item -Path $WorkingDirectory -Name 'working' -ItemType Directory 33 | $modulesFolder = New-Item -Path $workingRoot.FullName -Name Modules -ItemType Directory 34 | 35 | # Fill out the modules folder 36 | Write-PSFMessage -Level Host -Message "Transfering built module data into working directory" 37 | Copy-Item -Path "$moduleRoot\$moduleName" -Destination $modulesFolder.FullName -Recurse -Force 38 | foreach ($dependency in (Import-PowerShellDataFile -Path "$moduleRoot\$moduleName\$moduleName.psd1").RequiredModules) 39 | { 40 | $param = @{ 41 | Repository = $Repository 42 | Name = $dependency.ModuleName 43 | Path = $modulesFolder.FullName 44 | } 45 | if ($dependency -is [string]) { $param['Name'] = $dependency } 46 | if ($dependency.RequiredVersion) 47 | { 48 | $param['RequiredVersion'] = $dependency.RequiredVersion 49 | } 50 | Write-PSFMessage -Level Host -Message "Preparing Dependency: $($param['Name'])" 51 | Save-Module @param 52 | } 53 | 54 | # Generate function configuration 55 | Write-PSFMessage -Level Host -Message 'Generating function configuration' 56 | $runTemplate = Get-Content -Path "$($WorkingDirectory)\azFunctionResources\run.ps1" -Raw 57 | foreach ($functionSourceFile in (Get-ChildItem -Path "$($moduleRoot)\$moduleName\functions" -Recurse -Filter '*.ps1')) 58 | { 59 | Write-PSFMessage -Level Host -Message " Processing function: $functionSourceFile" 60 | $condensedName = $functionSourceFile.BaseName -replace '-', '' 61 | $functionFolder = New-Item -Path $workingRoot.FullName -Name $condensedName -ItemType Directory 62 | 63 | #region Load Overrides 64 | $override = @{ } 65 | if (Test-Path -Path "$($WorkingDirectory)\azFunctionResources\functionOverride\$($functionSourceFile.BaseName).psd1") 66 | { 67 | $override = Import-PowerShellDataFile -Path "$($WorkingDirectory)\azFunctionResources\functionOverride\$($functionSourceFile.BaseName).psd1" 68 | } 69 | if (Test-Path -Path "$($WorkingDirectory)\azFunctionResources\functionOverride\$($condensedName).psd1") 70 | { 71 | $override = Import-PowerShellDataFile -Path "$($WorkingDirectory)\azFunctionResources\functionOverride\$($condensedName).psd1" 72 | } 73 | #endregion Load Overrides 74 | 75 | #region Create Function Configuration 76 | $restMethods = 'get', 'post' 77 | if ($override.RestMethods) { $restMethods = $override.RestMethods } 78 | 79 | Set-Content -Path "$($functionFolder.FullName)\function.json" -Value @" 80 | { 81 | "bindings": [ 82 | { 83 | "authLevel": "function", 84 | "type": "httpTrigger", 85 | "direction": "in", 86 | "name": "Request", 87 | "methods": [ 88 | "$($restMethods -join "`", 89 | `"")" 90 | ] 91 | }, 92 | { 93 | "type": "http", 94 | "direction": "out", 95 | "name": "Response" 96 | } 97 | ], 98 | "disabled": false 99 | } 100 | "@ 101 | #endregion Create Function Configuration 102 | 103 | #region Override Function Configuration 104 | if (Test-Path -Path "$($WorkingDirectory)\azFunctionResources\functionOverride\$($functionSourceFile.BaseName).json") 105 | { 106 | Copy-Item -Path "$($WorkingDirectory)\azFunctionResources\functionOverride\$($functionSourceFile.BaseName).json" -Destination "$($functionFolder.FullName)\function.json" -Force 107 | } 108 | if (Test-Path -Path "$($WorkingDirectory)\azFunctionResources\functionOverride\$($condensedName).json") 109 | { 110 | Copy-Item -Path "$($WorkingDirectory)\azFunctionResources\functionOverride\$($condensedName).json" -Destination "$($functionFolder.FullName)\function.json" -Force 111 | } 112 | #endregion Override Function Configuration 113 | 114 | # Generate the run.ps1 file 115 | $runText = $runTemplate -replace '%functionname%', $functionSourceFile.BaseName 116 | $runText | Set-Content -Path "$($functionFolder.FullName)\run.ps1" -Encoding UTF8 117 | } 118 | 119 | # Transfer common files 120 | Write-PSFMessage -Level Host -Message "Transfering core function data" 121 | if ($IncludeAZ) 122 | { 123 | Copy-Item -Path "$($WorkingDirectory)\azFunctionResources\host-az.json" -Destination "$($workingroot.FullName)\host.json" 124 | Copy-Item -Path "$($WorkingDirectory)\azFunctionResources\requirements.psd1" -Destination "$($workingroot.FullName)\" 125 | } 126 | else 127 | { 128 | Copy-Item -Path "$($WorkingDirectory)\azFunctionResources\host.json" -Destination "$($workingroot.FullName)\" 129 | } 130 | Copy-Item -Path "$($WorkingDirectory)\azFunctionResources\local.settings.json" -Destination "$($workingroot.FullName)\" 131 | 132 | # Build the profile file 133 | $text = @() 134 | $text += Get-Content -Path "$($WorkingDirectory)\azFunctionResources\profile.ps1" -Raw 135 | foreach ($functionFile in (Get-ChildItem "$($WorkingDirectory)\azFunctionResources\profileFunctions" -Recurse)) 136 | { 137 | $text += Get-Content -Path $functionFile.FullName -Raw 138 | } 139 | $text -join "`n`n" | Set-Content "$($workingroot.FullName)\profile.ps1" 140 | 141 | # Zip It 142 | Write-PSFMessage -Level Host -Message "Creating function archive in '$($WorkingDirectory)\$moduleName.zip'" 143 | Compress-Archive -Path "$($workingroot.FullName)\*" -DestinationPath "$($WorkingDirectory)\$moduleName.zip" -Force -------------------------------------------------------------------------------- /build/vsts-prerequisites.ps1: -------------------------------------------------------------------------------- 1 | param ( 2 | [string] 3 | $Repository = 'PSGallery' 4 | ) 5 | 6 | $modules = @("Pester", "PSFramework", "PSModuleDevelopment", "PSScriptAnalyzer") 7 | 8 | # Automatically add missing dependencies 9 | $data = Import-PowerShellDataFile -Path "$PSScriptRoot\..\AutoRest\AutoRest.psd1" 10 | foreach ($dependency in $data.RequiredModules) { 11 | if ($dependency -is [string]) { 12 | if ($modules -contains $dependency) { continue } 13 | $modules += $dependency 14 | } 15 | else { 16 | if ($modules -contains $dependency.ModuleName) { continue } 17 | $modules += $dependency.ModuleName 18 | } 19 | } 20 | 21 | foreach ($module in $modules) { 22 | Write-Host "Installing $module" -ForegroundColor Cyan 23 | Install-Module $module -Force -SkipPublisherCheck -Repository $Repository 24 | Import-Module $module -Force -PassThru 25 | } -------------------------------------------------------------------------------- /build/vsts-validate.ps1: -------------------------------------------------------------------------------- 1 | # Guide for available variables and working with secrets: 2 | # https://docs.microsoft.com/en-us/vsts/build-release/concepts/definitions/build/variables?tabs=powershell 3 | 4 | # Needs to ensure things are Done Right and only legal commits to master get built 5 | 6 | # Run internal pester tests 7 | & "$PSScriptRoot\..\AutoRest\tests\pester.ps1" -------------------------------------------------------------------------------- /library/AutoRest/AutoRest.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27130.2010 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{F922A607-1FAB-457E-ADC7-8A4AE9AB7DC3}") = "AutoRest", "AutoRest\AutoRest.csproj", "{91AD8784-0C2A-4177-972F-3B55D90670B7}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {91AD8784-0C2A-4177-972F-3B55D90670B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {91AD8784-0C2A-4177-972F-3B55D90670B7}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {91AD8784-0C2A-4177-972F-3B55D90670B7}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {91AD8784-0C2A-4177-972F-3B55D90670B7}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {689EAC65-8766-4D97-92EF-E686CEF15F1F} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /library/AutoRest/AutoRest/AutoRest.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net4.5.2 5 | 6 | 7 | 8 | ..\..\..\AutoRest\bin 9 | ..\..\..\AutoRest\bin\AutoRest.xml 10 | 11 | 12 | 13 | ..\..\..\AutoRest\bin 14 | ..\..\..\AutoRest\bin\AutoRest.xml 15 | 16 | 17 | 18 | false 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /library/AutoRest/AutoRest/Class1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace AutoRest 4 | { 5 | public class Class1 6 | { 7 | } 8 | } 9 | --------------------------------------------------------------------------------