├── LICENSE ├── New-ApiObject.ps1 ├── New-ApiParam.ps1 ├── README.md ├── Write-ApiFunctions.ps1 ├── ideas └── classes.ps1 └── projects ├── Consul ├── Extract-Toc.ps1 ├── Get-ApiDocsFromToc.ps1 ├── Load-AngleSharpLibs.ps1 ├── Parse-Doc.ps1 ├── Untitled7.ps1 └── generate-kvFunctions.ps1 ├── ExampleProject ├── ExampleModule.ps1 ├── Get-ExampleModuleUser.ps1 ├── New-ExampleModuleUser.ps1 └── README.md └── Kanboard-PHPApi-PSRAWG ├── Add-AuthenticationParameters.ps1 ├── Add-MainFunctions.ps1 ├── Additional scripts ├── Convert-KanboardResult.ps1 ├── ConvertFrom-UnixTime.ps1 └── New-KanboardCredential.ps1 ├── ConvertFrom-PHPApi.ps1 ├── ConvertFrom-RSTDocumentation-v2.ps1 ├── README.md └── Write-KBApiGenFunctionFiles.ps1 /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 IAN 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. 22 | -------------------------------------------------------------------------------- /New-ApiObject.ps1: -------------------------------------------------------------------------------- 1 | function New-ApiObject { 2 | param ( 3 | # Prefix to add to the nouns aka module prefix 4 | $NounPrefix = '', 5 | $ApiFunctionName, 6 | $Verb, 7 | $Noun, 8 | $ApiFilename, 9 | $ApiSource, 10 | $ApiParamsSource 11 | ) 12 | 13 | $obj = [pscustomobject]@{ 14 | 15 | # as in API we are parsing 16 | ApiFunctionName = $ApiFunctionName 17 | 18 | # PS 19 | Verb = $Verb 20 | Noun = $Noun 21 | 22 | # a more discoverable (in PS terms) alias 23 | FriendlyAlias = $null 24 | 25 | # 26 | SyntaxCheckPass = $false 27 | SyntaxCheckError = $null 28 | 29 | 30 | Params = @() 31 | 32 | 33 | DOC = [pscustomobject]@{ 34 | Synopsis = $null 35 | Description = $null 36 | Params = $null 37 | ResultSucc = $null 38 | ResultFail = $null 39 | Blk = $null 40 | Source = $null 41 | FileName = $null 42 | Parent = $null 43 | } 44 | 45 | 46 | API = [pscustomobject]@{ 47 | # 48 | Params = @() 49 | 50 | # 51 | ParamsSource = $ApiParamsSource 52 | 53 | # API source as-is 54 | Source = $ApiSource 55 | 56 | # API filename what were parsed 57 | FileName = $ApiFilename 58 | 59 | # placeholder for the parent object 60 | Parent = $null 61 | } 62 | 63 | PS = [pscustomobject]@{ 64 | # 65 | NounPrefix = $NounPrefix 66 | 67 | #DefaultParameterSetName 68 | DefaultParameterSetName = $null 69 | 70 | BeginBlock = @() 71 | ProcessBlock = @() 72 | EndBlock = @() 73 | 74 | GenerateHelperFunctions = $true 75 | # placeholder for the parent object 76 | Parent = $null 77 | } 78 | 79 | #Commented out because I don't need them, but they could be useful if there is a complex directory structure 80 | #RelativeName = $thisFile.FullName -replace [regex]::Escape($rootdir) 81 | #FullName = $thisFile.FullName 82 | } 83 | 84 | # Abomination 85 | $obj.PS.parent = $obj 86 | $obj.API.parent = $obj 87 | $obj.DOC.parent = $obj 88 | 89 | # make a 'safe' alias with API function name 90 | $SafeAlias = { 91 | 92 | # inline declaration just to be safe 93 | filter Capitalize { 94 | [Regex]::Replace($_, '^\w', { param($letter) $letter.Value.ToUpper() }) 95 | } 96 | 97 | 'Invoke-{1}{0}' -f ($this.Parent.ApiFunctionName | Capitalize), $this.NounPrefix 98 | } 99 | 100 | $memberParam = @{ 101 | MemberType = 'ScriptProperty' 102 | Name = 'SafeAlias' 103 | Value = $SafeAlias 104 | Force = $true 105 | } 106 | Add-Member -InputObject $obj.PS @memberParam -Verbose 107 | 108 | ### 109 | 110 | # return non $null aliases 111 | $Aliases = { 112 | @($this.SafeAlias, $this.parent.FriendlyAlias | ? {$null -ne $_} ) 113 | } 114 | 115 | $memberParam = @{ 116 | MemberType = 'ScriptProperty' 117 | Name = 'Aliases' 118 | Value = $Aliases 119 | Force = $true 120 | } 121 | Add-Member -InputObject $obj.PS @memberParam 122 | 123 | ### 124 | 125 | # auto make PS function name 126 | $sbPSFunctionName = { 127 | '{0}-{2}{1}' -f $this.Verb, $this.Noun, $this.PS.NounPrefix 128 | } 129 | 130 | $memberParam = @{ 131 | MemberType = 'ScriptProperty' 132 | Name = 'PSFunctionName' 133 | Value = $sbPSFunctionName 134 | Force = $true 135 | } 136 | Add-Member -InputObject $obj @memberParam 137 | 138 | ### 139 | 140 | # auto make PS Parameter Block 141 | $ParameterBlock = { 142 | $arrStr = @() 143 | $arrStr += 'Param (' 144 | 145 | $joinedParams = foreach ($thisParam in $this.parent.Params) { 146 | $thisParam.PSParameter -join [environment]::NewLine 147 | } 148 | 149 | # moar string manipulation magik 150 | $arrStr += $joinedParams -join (',{0}{0}' -f [environment]::NewLine) -split [environment]::NewLine 151 | 152 | $arrStr += ' )' 153 | $arrStr 154 | } 155 | 156 | $memberParam = @{ 157 | MemberType = 'ScriptProperty' 158 | Name = 'ParameterBlock' 159 | Value = $ParameterBlock 160 | Force = $true 161 | } 162 | Add-Member -InputObject $obj.PS @memberParam 163 | 164 | ### 165 | 166 | # auto make PS help block 167 | $HelpBlock = { 168 | 169 | # try to format the help block with the weird PS 3 spaces notation 170 | filter Prepend3Spaces { 171 | $x = $_ 172 | (($x -split '[\n\r]+') | % {$_ -replace '(^.)',' $1'}) -join [System.Environment]::NewLine 173 | } 174 | 175 | $arrStr = @() 176 | 177 | $arrStr += '<#' 178 | $arrStr += '.Synopsis' 179 | 180 | if ($this.parent.Doc.Synopsis) { 181 | $arrStr += '{0}' -f $this.parent.Doc.Synopsis | Prepend3Spaces 182 | } 183 | else { 184 | $arrStr += ' Invoke {0}' -f $this.parent.ApiFunctionName 185 | } 186 | 187 | $arrStr += '.DESCRIPTION' 188 | 189 | if ($this.parent.Doc.Description) { 190 | $arrStr += '{0}' -f $this.parent.Doc.Description | Prepend3Spaces 191 | } 192 | else { 193 | $arrStr += ' Invoke {0}' -f $this.parent.ApiFunctionName 194 | } 195 | 196 | if ($this.Aliases) { 197 | $arrStr += ' Alias: {0}' -f ($this.Aliases -join ', ') 198 | } 199 | 200 | if ($this.parent.Doc.ResultSucc -or $this.parent.Doc.ResultFail) { 201 | 202 | $arrStr += '.OUTPUTS' 203 | 204 | if ($this.parent.Doc.ResultSucc) { 205 | $arrStr += ' Returns {0} on success' -f $this.parent.Doc.ResultSucc 206 | } 207 | 208 | if ($this.parent.Doc.ResultFail) { 209 | $arrStr += ' Returns {0} on failure' -f $this.parent.Doc.ResultFail 210 | } 211 | 212 | } 213 | 214 | $arrStr += '.NOTES' 215 | $arrStr += ' API Function Name: {0}' -f $this.parent.ApiFunctionName 216 | $arrStr += ' PS Module Safe Name: {0}' -f $this.parent.ps.SafeAlias 217 | 218 | if ($this.parent.API.Filename) { 219 | $arrStr += ' Function parsed from: {0}' -f $this.parent.API.Filename 220 | } 221 | 222 | if ($this.parent.Doc.FileName) { 223 | $arrStr += ' Description parsed from: {0}' -f $this.parent.Doc.FileName 224 | } 225 | 226 | $arrStr += '#>' 227 | 228 | $arrStr 229 | 230 | } # END auto make ps help block 231 | 232 | $memberParam = @{ 233 | MemberType = 'ScriptProperty' 234 | Name = 'HelpBlock' 235 | Value = $HelpBlock 236 | Force = $true 237 | } 238 | Add-Member -InputObject $obj.PS @memberParam 239 | 240 | ### 241 | 242 | # auto make PS Header block 243 | $HeaderBlock = { 244 | $arrStr = @() 245 | 246 | $arrStr += "function {0} {{" -f $this.parent.PSFunctionName 247 | 248 | if ($this.DefaultParameterSetName) { 249 | $arrStr += "[CmdletBinding(DefaultParameterSetName='{0}')]" -f $this.DefaultParameterSetName 250 | } 251 | else { 252 | $arrStr += '[CmdletBinding()]' 253 | } 254 | 255 | if ($this.parent.PS.Aliases) { 256 | $arrStr += "[Alias('{0}')]" -f ($this.parent.PS.Aliases -join "', '") 257 | } 258 | 259 | $arrStr 260 | } #end header block 261 | 262 | $memberParam = @{ 263 | MemberType = 'ScriptProperty' 264 | Name = 'HeaderBlock' 265 | Value = $HeaderBlock 266 | Force = $true 267 | } 268 | Add-Member -InputObject $obj.PS @memberParam 269 | 270 | ### 271 | 272 | # auto make PS function footer 273 | $FooterBlock = { 274 | '' 275 | '}} # End {0} function' -f $this.PSFunctionName 276 | } 277 | 278 | $memberParam = @{ 279 | MemberType = 'ScriptProperty' 280 | Name = 'FooterBlock' 281 | Value = $FooterBlock 282 | Force = $true 283 | } 284 | Add-Member -InputObject $obj.PS @memberParam 285 | 286 | ### 287 | 288 | # Compose the main function block 289 | 290 | $MainBlock = { 291 | $arrStr = @() 292 | 293 | filter PrependSpaces { 294 | param ($n = 4) 295 | $x = $_ 296 | $s = ' ' * $n 297 | (($x -split '[\n\r]+') | % {$_ -replace '(^.)',('{0}$1' -f $s)}) -join [System.Environment]::NewLine 298 | } 299 | 300 | if ($this.GenerateHelperFunctions) { 301 | $arrStr += 'Begin {' | PrependSpaces 302 | $this.HelperFunctionsBlock -split [environment]::NewLine | PrependSpaces | % {$arrStr += $_} 303 | $this.BeginBlock -split [environment]::NewLine | PrependSpaces | % {$arrStr += $_} 304 | $arrStr += '} # End begin block' | PrependSpaces 305 | $arrStr += '' 306 | } 307 | elseif ($this.BeginBlock) { 308 | $arrStr += 'Begin {' | PrependSpaces 309 | $this.BeginBlock -split [environment]::NewLine | PrependSpaces | % {$arrStr += $_} 310 | $arrStr += '} # End begin block' | PrependSpaces 311 | $arrStr += '' 312 | } 313 | 314 | if ($this.ProcessBlock) { 315 | $arrStr += 'Process {' | PrependSpaces 316 | $this.ProcessBlock -split [environment]::NewLine | PrependSpaces | % {$arrStr += $_} 317 | $arrStr += '} # End process block' | PrependSpaces 318 | $arrStr += '' 319 | } 320 | 321 | if ($this.EndBlock) { 322 | $arrStr += 'End {' | PrependSpaces 323 | $this.EndBlock -split [environment]::NewLine | PrependSpaces | % {$arrStr += $_} 324 | $arrStr += '} # End end block' | PrependSpaces 325 | $arrStr += '' 326 | } 327 | 328 | $arrStr 329 | } 330 | 331 | $memberParam = @{ 332 | MemberType = 'ScriptProperty' 333 | Name = 'MainBlock' 334 | Value = $MainBlock 335 | Force = $true 336 | } 337 | Add-Member -InputObject $obj.PS @memberParam 338 | 339 | ### 340 | 341 | # Compile all blocks into the one 342 | $FunctionText = { 343 | $sb = [System.Text.StringBuilder]::new() 344 | 345 | filter sb { [void]$sb.AppendLine($_) } 346 | 347 | $this.HelpBlock | sb 348 | $this.HeaderBlock | sb 349 | $this.ParameterBlock | sb 350 | $this.MainBlock | sb 351 | $this.FooterBlock | sb 352 | $sb.ToString() 353 | [void]$sb.Clear() 354 | Remove-Variable sb 355 | 356 | } 357 | 358 | $memberParam = @{ 359 | MemberType = 'ScriptProperty' 360 | Name = 'FunctionText' 361 | Value = $FunctionText 362 | Force = $true 363 | } 364 | Add-Member -InputObject $obj.PS @memberParam 365 | 366 | ### 367 | 368 | # try to create a script block 369 | $ScriptBlock = { 370 | try { 371 | [scriptblock]::Create($this.FunctionText) 372 | 373 | $this.parent.SyntaxCheckPass = $true 374 | } 375 | catch { 376 | $this.parent.SyntaxCheckPass = $false 377 | $this.parent.SyntaxCheckError = $Error[0] 378 | } 379 | } 380 | 381 | $memberParam = @{ 382 | MemberType = 'ScriptProperty' 383 | Name = 'ScriptBlock' 384 | Value = $ScriptBlock 385 | Force = $true 386 | } 387 | Add-Member -InputObject $obj.PS @memberParam 388 | 389 | ### 390 | 391 | # create some helper functions 392 | $HelperFunctionsBlock = { 393 | 394 | # add API parameters handling only if there is any 395 | if ($apiParameters = $this.parent.Params | ? {$_.API -eq $true}) { 396 | function ToArrayDef { 397 | [CmdletBinding()] 398 | param( 399 | [Parameter(Mandatory=$true, 400 | ValueFromPipeline=$true, 401 | Position=0)] 402 | $array 403 | ) 404 | ( $array | % { "'{0}'" -f $_ } ) -join ', ' 405 | } 406 | 407 | # create deafult block with all API parameters 408 | $usage = 'Api' 409 | '# parameters marked as an API parameters' 410 | '' 411 | '${0}Parameters = @({1})' -f $usage, (ToArrayDef $apiParameters.Name) 412 | '$hash{0}Parameters = @{{}}' -f $usage 413 | '' 414 | 'foreach ($par in ${0}Parameters) {{' -f $usage 415 | ' if ($PSBoundParameters.psbase.Keys -contains $par) {{' -f $usage 416 | ' $hash{0}Parameters.Add($par, $PSBoundParameters[$par])' -f $usage 417 | ' }' 418 | ' }' 419 | '' 420 | 421 | 422 | # if there are parameters with a defined usage, create a separate hashtable for each usage 423 | $uniqueUsedIn = @($this.parent.Params | ? {$_.UsedIn} | Select-Object -Property UsedIn -Unique -ExpandProperty UsedIn) 424 | 425 | foreach ($usage in $uniqueUsedIn) { 426 | $paramsWithThisUsage = $this.parent.Params | ? {$_.UsedIn -eq $usage} 427 | 428 | '# parameters marked as used in {0}' -f $usage 429 | '' 430 | '${0}Parameters = @({1})' -f $usage, (ToArrayDef $paramsWithThisUsage.name) 431 | '$hash{0}Parameters = @{{}}' -f $usage 432 | '' 433 | 'foreach ($par in ${0}Parameters) {{' -f $usage 434 | ' if ($PSBoundParameters.psbase.Keys -contains $par) {{' -f $usage 435 | ' $hash{0}Parameters.Add($par, $PSBoundParameters[$par])' -f $usage 436 | ' }' 437 | ' }' 438 | '' 439 | } 440 | 441 | if ($uniqueUsedIn -icontains 'query') { 442 | 'if ($hashQueryParameters) {' 443 | ' $Query = $hashQueryParameters.psbase.Keys | % {' 444 | ' ''{0}={1}'' -f $_, $hashQueryParameters[$_]' 445 | ' }' 446 | ' $Query = ''?'' + ($query -join ''&'')' 447 | '}' 448 | '' 449 | } 450 | } # End if ($apiParameters = $this.parent.Params 451 | 452 | } 453 | 454 | $memberParam = @{ 455 | MemberType = 'ScriptProperty' 456 | Name = 'HelperFunctionsBlock' 457 | Value = $HelperFunctionsBlock 458 | Force = $true 459 | } 460 | Add-Member -InputObject $obj.PS @memberParam 461 | 462 | ### 463 | 464 | #return object 465 | $obj 466 | } -------------------------------------------------------------------------------- /New-ApiParam.ps1: -------------------------------------------------------------------------------- 1 | function New-ApiParam { 2 | param ( 3 | # Name of the parameter 4 | [string]$Name, 5 | 6 | # Used in API query (true) or something else 7 | # this property is used to generate helper functions 8 | [bool]$API = $true, 9 | 10 | # PowerShell/.NET type 11 | $Type = $null, 12 | 13 | # Mark if the parameter is mandatory 14 | [bool]$Required = $false, 15 | 16 | # Text description 17 | $Description, 18 | 19 | # Alias 20 | $Alias, 21 | 22 | # ParameterSetName 23 | $ParameterSetName, 24 | 25 | # Parameter position name 26 | $Position, 27 | 28 | # Where the parameter is used, eg. Headers, Body 29 | $UsedIn = $null, 30 | 31 | # Default value to assign 32 | $DefaultValue = $null 33 | ) 34 | 35 | $obj = [pscustomobject]@{ 36 | Name = $Name 37 | API = $API 38 | Type = $Type 39 | Required = $Required 40 | Description = $Description 41 | Alias = $Alias 42 | ParameterSetName = $ParameterSetName 43 | Position = $Position 44 | UsedIn = $UsedIn 45 | DefaultValue = $DefaultValue 46 | } 47 | 48 | 49 | # create a PS script parameter definition 50 | $PSParameter = { 51 | $arrStr = @() 52 | if ($this.Description) { 53 | $arrStr += ' # {0}' -f $this.Description 54 | } 55 | if ($this.Required -or $this.ParameterSetName) { 56 | $arrParam = @() 57 | 58 | if ($this.Required -eq 'True') { 59 | $arrParam += 'Mandatory=$true' 60 | } 61 | 62 | if ($this.ParameterSetName) { 63 | $arrParam += "ParameterSetName='{0}'" -f $this.ParameterSetName 64 | } 65 | 66 | $arrStr += ' [Parameter({0})]' -f ($arrParam -join ', ') 67 | } # end parameter parameters 68 | 69 | if ($this.Alias) { 70 | $arrStr += " [Alias('{0}')]" -f $this.Alias 71 | } 72 | 73 | $tStr = ' ' 74 | 75 | if ($this.Type) { 76 | $tStr += '[{0}]' -f $this.Type 77 | } 78 | 79 | $tStr += '${0}' -f $this.Name 80 | 81 | if ($this.DefaultValue) { 82 | $tStr += ' = {0}' -f $this.DefaultValue 83 | } 84 | 85 | $arrStr += $tStr 86 | 87 | $arrStr 88 | 89 | } 90 | 91 | $memberParam = @{ 92 | MemberType = 'ScriptProperty' 93 | Name = 'PSParameter' 94 | Value = $PSParameter 95 | Force = $true 96 | } 97 | Add-Member -InputObject $obj @memberParam 98 | 99 | $obj 100 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PowerShell REST API Wrapper Generator 2 | 3 | A little utility project to help with creating PS modules for various REST API services 4 | 5 | This project was originally developed to create https://github.com/al-ign/PS2Kanboard module 6 | 7 | # Usage 8 | 9 | Refer to [projects/ExampleProject](projects/ExampleProject) for the basic usage in the form of a mockup project 10 | 11 | [projects/Kanboard-PHPApi-PSRAWG](projects/Kanboard-PHPApi-PSRAWG) contains original PS2Kanboard generator 12 | 13 | -------------------------------------------------------------------------------- /Write-ApiFunctions.ps1: -------------------------------------------------------------------------------- 1 | # Write-ApiFunctions.ps1 2 | 3 | $outputPath = 'C:\Shares\personal\projects\ModuleName\Public' 4 | 5 | $i = 0 6 | foreach ($thisFunction in $Functions) { 7 | Write-Progress -Activity $thisFunction.PSFunctionName -Status 'Writing' -PercentComplete ($i/$Functions.Count*100) 8 | $i++ 9 | $thisFunction.PS.FunctionText | Set-Content -LiteralPath (Join-Path $outputPath ('{0}.ps1' -f $thisFunction.PSFunctionName )) 10 | } 11 | -------------------------------------------------------------------------------- /ideas/classes.ps1: -------------------------------------------------------------------------------- 1 |  2 | class VerbNoun { 3 | [string]$Verb 4 | [string]$Noun 5 | #[string]$Prefix 6 | hidden $Parent 7 | 8 | [void] AddPrefix () { 9 | $getter = { 10 | return $this.Parent.Prefix 11 | } 12 | 13 | $setter = { 14 | param($string) 15 | $this.Parent.Prefix = $string 16 | } 17 | 18 | $splat = @{ 19 | Name = 'Prefix' 20 | MemberType = 'ScriptProperty' 21 | Value = $getter 22 | SecondValue = $setter 23 | } 24 | 25 | Add-Member -InputObject $this @splat 26 | } 27 | 28 | [string]PSName() { 29 | filter Capitalize { 30 | [Regex]::Replace($_, '^\w', { param($letter) $letter.Value.ToUpper() }) 31 | } 32 | $tmpVerb = $this.Verb | Capitalize 33 | $tmpNoun = $this.Noun | Capitalize 34 | return ('{0}-{2}{1}' -f $tmpVerb, $tmpNoun, $this.Parent.Prefix) 35 | } 36 | 37 | VerbNoun ( 38 | [string]$newVerb, 39 | [string]$newNoun, 40 | $Parent 41 | ) 42 | { 43 | $this.Verb = $newVerb 44 | $this.Noun = $newNoun 45 | $this.Parent = $Parent 46 | $this.AddPrefix() 47 | } 48 | 49 | VerbNoun ( 50 | $Parent 51 | ) 52 | { 53 | $this.Parent = $Parent 54 | $this.AddPrefix() 55 | } 56 | 57 | [string] ToString () { 58 | return $this.PSName() 59 | } 60 | 61 | } 62 | 63 | 64 | class ApiObject { 65 | [string]$name 66 | [VerbNoun]$PSName 67 | [string]$Prefix 68 | 69 | ApiObject() { 70 | $this.PSName = [VerbNoun]::New($this) 71 | } 72 | } 73 | 74 | $b = [ApiObject]::new() 75 | $b.PSName.Verb = 'get' 76 | $b.PSName.Noun = 'rekt' 77 | 78 | $b.Prefix = 'PS' 79 | 'Function name: {0}; Prefix: {1}; PSName.Prefix: {2}' -f $b.PSName, $b.Prefix, $b.PSName.Prefix 80 | 81 | $b.PSName.Prefix = 'Test' 82 | 'Function name: {0}; Prefix: {1}; PSName.Prefix: {2}' -f $b.PSName, $b.Prefix, $b.PSName.Prefix 83 | -------------------------------------------------------------------------------- /projects/Consul/Extract-Toc.ps1: -------------------------------------------------------------------------------- 1 |  2 | $asParser = [AngleSharp.Html.Parser.HtmlParser]::new() 3 | 4 | $iwr = Invoke-WebRequest -UseBasicParsing -Uri https://www.consul.io/api-docs 5 | 6 | $as = $asParser.ParseDocument($iwr.content) 7 | 8 | $FQDN = 'https://www.consul.io' 9 | 10 | $docsnav = $as.All | ? Classlist -contains docs-nav | ? Localname -eq ul 11 | $docsnav = $docsnav.ChildNodes | ? ChildElementCount -gt 0 12 | 13 | $arrToc = @() 14 | 15 | 16 | # no descendant topics 17 | foreach ($single in ($docsnav | ? ChildElementCount -eq 1)) { 18 | $obj = [pscustomobject]@{ 19 | Dir = $null 20 | Name = $single.TextContent 21 | Path = $Single.Attributes |? Name -eq data-testid | select -ExpandProperty value 22 | IWR = $null 23 | AS = $null 24 | } 25 | $obj.Path = '{0}{1}' -f $FQDN, $obj.Path 26 | 27 | $arrToc += $obj 28 | } 29 | 30 | # 'folders' 31 | foreach ($single in ($docsnav | ? ChildElementCount -ne 1)) { 32 | 33 | $single.ChildNodes.ChildNodes | % { 34 | $z = $_ 35 | 36 | # skip container link 37 | if ($z.children.classname -contains 'chevron') { 38 | 39 | } 40 | else { 41 | $obj = [pscustomobject]@{ 42 | Dir = $single.ChildNodes[0].TextContent -replace '^\s*' 43 | Name = $_.TextContent 44 | Path = $_.attributes |? Name -eq data-testid | select -ExpandProperty value 45 | IWR = $null 46 | AS = $null 47 | } 48 | $obj.Path = '{0}{1}' -f $FQDN, $obj.Path 49 | 50 | $arrToc += $obj 51 | } 52 | } 53 | 54 | } 55 | 56 | # exclude pages 57 | $excludeList = '(/features)|(/libraries)|(/index)' 58 | 59 | $arrToc = $arrToc | ? Path -NotMatch $excludeList -------------------------------------------------------------------------------- /projects/Consul/Get-ApiDocsFromToc.ps1: -------------------------------------------------------------------------------- 1 | $asParser = [AngleSharp.Html.Parser.HtmlParser]::new() 2 | 3 | foreach ($topic in $arrToc) { 4 | 5 | 6 | $iwr = Invoke-WebRequest -UseBasicParsing -Uri $topic.Path 7 | $as = $asParser.ParseDocument($iwr.content) 8 | $topic.IWR = $iwr 9 | $topic.AS = $as 10 | Start-Sleep -Seconds 1 11 | } -------------------------------------------------------------------------------- /projects/Consul/Load-AngleSharpLibs.ps1: -------------------------------------------------------------------------------- 1 | gci -Filter 'netstandard2*' -Recurse | gci -Filter *.dll | %{ Add-Type -Path $_.fullname} -------------------------------------------------------------------------------- /projects/Consul/Parse-Doc.ps1: -------------------------------------------------------------------------------- 1 | cls 2 | # requires $arrToc 3 | 4 | $functions = @() 5 | 6 | foreach ($topic in $arrToc[6]) { 7 | 8 | #select all headers 9 | 10 | $Headers = @($topic.AS.all | ? {$_.ClassName -eq 'g-type-display-3'}) 11 | 'Headers: {0}' -f $Headers.Count | Write-Host -BackgroundColor Green -ForegroundColor Black 12 | 13 | $arr = @() 14 | 15 | foreach ($i in (0..$headers.GetUpperBound(0))) { 16 | 17 | $arr += [PSCustomObject]@{ 18 | Dir = $topic.dir 19 | Topic = $Topic.Name 20 | Title = $null 21 | Synopsis = $null 22 | Description = $null 23 | Method = $null 24 | Parameters = @() 25 | 26 | Block = @{ 27 | Header = $Headers[$i] 28 | Method = @() 29 | Parameter = @() 30 | } 31 | Href = $topic.Path 32 | } 33 | } 34 | 35 | foreach ($node in $arr) { 36 | 37 | $node.Synopsis = $node.Block.Header.TextContent -replace '^\W' 38 | 39 | $parent = $node.Block.Header.Parent 40 | $node.Title = $parent.FirstChild.TextContent -replace '^\W' 41 | 42 | $node.Description = $node.Block.Header.NextElementSibling.TextContent 43 | if ($topic.Dir) { 44 | '{2} - {0}: {1}' -f $Topic.Name, $node.Title, $topic.dir | Write-Host -BackgroundColor DarkBlue 45 | } 46 | else { 47 | '{0}: {1}' -f $Topic.Name, $node.Title | Write-Host -BackgroundColor DarkBlue 48 | } 49 | 50 | $node.Synopsis | Write-Host -BackgroundColor DarkGreen 51 | 52 | $node.Description | Write-Host -BackgroundColor DarkYellow 53 | 54 | # iterate over siblings 55 | 56 | $a = $node.Block.Header 57 | 58 | # oh bother 59 | $maxI = 15 60 | for ($i = 0; $i -lt $maxI; $i++) { 61 | write-host ("$I " * 3) -BackgroundColor DarkCyan -NoNewline 62 | $nextElement.ClassName |Write-Host -BackgroundColor DarkGray -ForegroundColor White 63 | $nextElement = $a.NextElementSibling 64 | $nextElement.TextContent -replace '[\r\n]+' 65 | 66 | if ($nextElement.TextContent -match 'MethodPath') { 67 | $node.Block.Method += $nextElement 68 | } 69 | 70 | if ($a.TextContent -match 'Parameters$') { 71 | $node.Block.Parameter += [pscustomobject]@{ 72 | ParameterBlockName = $a.TextContent -replace '^\W' 73 | Block = $nextElement 74 | } 75 | } 76 | 77 | if ($nextElement.ClassName -eq 'g-type-display-3') { 78 | 'Stop loop: Next function header' | Write-Host -BackgroundColor DarkRed 79 | $i = $maxI 80 | } 81 | 82 | if ($nextElement.TextContent -match 'Sample') { 83 | 'Stop loop: Sample' | Write-Host -BackgroundColor DarkRed 84 | $i = $maxI 85 | } 86 | $a = $nextElement 87 | } 88 | 89 | ### 90 | 91 | #parse method block 92 | 93 | if ($node.Block.Method) { 94 | 95 | 96 | $methodTable = $node.Block.Method.ChildNodes.ChildNodes 97 | 98 | #init 99 | $node.Method = @() 100 | 101 | # table header 102 | $thead = $node.Block.Method.ChildNodes | ? TagName -eq THEAD 103 | 104 | # how many COLS are there (zero-based) 105 | $colCount = @($thead.ChildNodes.childnodes).GetUpperBound(0) 106 | 107 | # rows 108 | $tBody = @($node.Block.Method.ChildNodes.ChildNodes | ? TagName -eq TR | ? {$_.FirstElementChild.GetType().Name -notmatch 'TableHeader'}) 109 | 110 | foreach ($row in $tBody) { 111 | 112 | $obj = [pscustomobject]@{} 113 | 114 | foreach ($i in (0..$colCount)) { 115 | 116 | $splat = @{ 117 | InputObject = $obj 118 | NotePropertyName = $thead.ChildNodes.childnodes[$i].TextContent 119 | NotePropertyValue = $row.ChildNodes[$i].TextContent 120 | } 121 | Add-Member @splat 122 | 123 | } 124 | 125 | $node.Method += $obj 126 | 127 | }# End % row 128 | 129 | } # End if $node.Block.Method 130 | 131 | ### 132 | 133 | # parse parameters block 134 | 135 | filter te { 136 | param ($ParameterBlockName) 137 | 138 | $in = $_ 139 | 140 | $regex = '\s*(?\w+)\s+\((?[\w<>]+)\s*:\s*(?.+?)\)(?: - )*\s*(?[\w\W]+)' 141 | 142 | $ss = Select-String -InputObject $in.TextContent -Pattern $regex -AllMatches 143 | 144 | foreach ($s in $ss.Matches) { 145 | $obj = [pscustomobject]@{ 146 | Name = $s.Groups['name'].Value 147 | Type = $s.Groups['type'].Value 148 | Desc = $s.Groups['desc'].Value 149 | Default = $s.Groups['default'].Value 150 | UsedIn = $null 151 | ParameterBlockName = $ParameterBlockName 152 | } 153 | if ($obj.Desc -match ('This is specified as part of the URL as a query parameter' -replace ' ','[\W]')) { 154 | $obj.UsedIn = 'query' 155 | } 156 | $obj 157 | } 158 | } # End filter 159 | 160 | 161 | foreach ($member in $node.Block.Parameter) { 162 | 'Parsing parameter Block "{0}"' -f $member.ParameterBlockName | Write-Host -BackgroundColor DarkMagenta 163 | $node.Parameters += $member.Block.ChildNodes | te -ParameterBlockName $member.ParameterBlockName 164 | } 165 | 'Parameters count: {0}' -f $node.Parameters.Count| Write-Host -BackgroundColor DarkMagenta 166 | 'END OF HEADER' 167 | '' 168 | } 169 | 170 | 'END TOPIC' 171 | 172 | foreach ($member in $arr) { 173 | $functions += $member 174 | } 175 | }# End % topic -------------------------------------------------------------------------------- /projects/Consul/Untitled7.ps1: -------------------------------------------------------------------------------- 1 |  2 | 3 | $text = @' 4 | Create Session 5 | 6 | This endpoint initializes a new session. Sessions must be associated with a node and may be associated with any number of checks. 7 | Method Path Produces 8 | PUT /session/create application/json 9 | 10 | The table below shows this endpoint's support for blocking queries, consistency modes, agent caching, and required ACLs. 11 | Blocking Queries Consistency Modes Agent Caching ACL Required 12 | NO none none session:write 13 | »Parameters 14 | 15 | ns (string: "") Enterprise 16 | - Specifies the namespace to query. If not provided, the namespace will be inferred from the request's ACL token, or will default to the default namespace. This is specified as part of the URL as a query parameter. Added in Consul 1.7.0. 17 | 18 | dc (string: "") - Specifies the datacenter to query. This will default to the datacenter of the agent being queried. This is specified as part of the URL as a query parameter. Using this across datacenters is not recommended. 19 | 20 | LockDelay (string: "15s") - Specifies the duration for the lock delay. This must be greater than 0. 21 | 22 | Node (string: "") - Specifies the name of the node. This must refer to a node that is already registered. 23 | 24 | Name (string: "") - Specifies a human-readable name for the session. 25 | 26 | Checks (array: nil) - specifies a list of associated health check IDs (commonly CheckID in API responses). It is highly recommended that, if you override this list, you include the default serfHealth. 27 | 28 | Behavior (string: "release") - Controls the behavior to take when a session is invalidated. Valid values are: 29 | release - causes any locks that are held to be released 30 | delete - causes any locks that are held to be deleted 31 | 32 | TTL (string: "") - Specifies the duration of a session (between 10s and 86400s). If provided, the session is invalidated if it is not renewed before the TTL expires. The lowest practical TTL should be used to keep the number of managed sessions low. When locks are forcibly expired, such as when following the leader election pattern in an application, sessions may not be reaped for up to double this TTL, so long TTL values (> 1 hour) should be avoided. Valid time units include "s", "m" and "h". 33 | '@ 34 | 35 | $function = [pscustomobject]@{ 36 | Name = $null 37 | Synopsis = $null 38 | Description = $null 39 | Definition = $null 40 | Text = $null 41 | ParamText = $null 42 | ParamApi = $null 43 | Method = $null 44 | Path = $null 45 | Produces = $null 46 | } 47 | 48 | # parameters 49 | $ParamScript = { 50 | 51 | $regex = '\s*(?\w+)\s+\((?[\w<>]+)\s*:\s*(?.+?)\)(?: - )*\s*(?.+)' 52 | $ss = Select-String -InputObject $This.text -Pattern $regex -AllMatches 53 | 54 | $this.paramApi = foreach ($s in $ss.Matches) { 55 | $obj = [pscustomobject]@{ 56 | Name = $s.Groups['name'].Value 57 | Type = $s.Groups['type'].Value 58 | Desc = $s.Groups['desc'].Value 59 | Default = $s.Groups['default'].Value 60 | UsedIn = $null 61 | } 62 | if ($obj.Desc -match 'This is specified as part of the URL as a query parameter') { 63 | $obj.UsedIn = 'query' 64 | } 65 | $obj 66 | } 67 | 68 | } 69 | 70 | $memberParam = @{ 71 | MemberType = 'ScriptProperty' 72 | Name = 'ParamScript' 73 | Value = $ParamScript 74 | Force = $true 75 | } 76 | Add-Member -InputObject $function @memberParam 77 | 78 | # Synopsis 79 | $SynopsisScript = { 80 | if ($this.Text -match '.+') { 81 | $this.Synopsis = $Matches[0] 82 | } 83 | } 84 | 85 | $memberParam = @{ 86 | MemberType = 'ScriptProperty' 87 | Name = 'SynopsisScript' 88 | Value = $SynopsisScript 89 | Force = $true 90 | } 91 | Add-Member -InputObject $function @memberParam 92 | 93 | # MethodScript 94 | $MethodScript = { 95 | $regex = '(?:Method\s+Path\s+Produces)[\r\n]+?(?\w+)\s+(?[\w:/]+)\s+(?[\w:/]+).+?[\r\n]' 96 | if ($this.Text -match $regex) { 97 | $this.Method = $Matches.Method 98 | $this.Path = $Matches.path 99 | $this.Produces = $Matches.produces 100 | } 101 | } 102 | 103 | $memberParam = @{ 104 | MemberType = 'ScriptProperty' 105 | Name = 'MethodScript' 106 | Value = $MethodScript 107 | Force = $true 108 | } 109 | Add-Member -InputObject $function @memberParam 110 | 111 | 112 | $function.Text = $text 113 | 114 | $function.ParamScript 115 | $function.SynopsisScript 116 | $function.MethodScript 117 | $function | fl -------------------------------------------------------------------------------- /projects/Consul/generate-kvFunctions.ps1: -------------------------------------------------------------------------------- 1 | $gen = @() 2 | 3 | foreach ($f in $functions) { 4 | $a = New-ApiObject -NounPrefix Consul -ApiFunctionName (($f.title + ' ' + $f.Synopsis) -replace '\W','-') 5 | $a.Verb = $f.Method[0].Method 6 | $a.Noun = 'Key' 7 | $a.doc.Synopsis = $f.Synopsis 8 | $a.doc.Description = $f.Description 9 | 10 | #$f.Parameters | select name, type,usedin, desc 11 | $par = @() 12 | foreach ($p in $f.Parameters) { 13 | $splat = @{ 14 | Name = $p.name 15 | Type = $p.type 16 | Description = $p.desc -replace '[\r\n]+',' ' 17 | UsedIn = $p.usedIn 18 | } 19 | $par += New-ApiParam @splat 20 | } 21 | 22 | $a.Params = $par 23 | $gen += $A 24 | 25 | 26 | # add Endblock code 27 | $endblock = @' 28 | $splat = @{ 29 | Method = 'zxcMETHODzxc' 30 | Uri = '{0}/v1zxcPATHzxc{1}{2}' -f $ApiUri, $Key, $Query 31 | ContentType = 'application/json; charset=utf-8' 32 | } 33 | 34 | if ($splat.Method -eq 'PUT') { 35 | $splat.Add('Body',$Value) 36 | } 37 | 38 | if ($Token) { 39 | $headers = @{ 40 | 'X-Consul-Token' = $token 41 | } 42 | $splat.Add('Headers',$headers) 43 | } 44 | 45 | $irm = Invoke-RestMethod @splat -Verbose 46 | $irm 47 | '@ -replace 'zxcMETHODzxc',$f.Method[0].Method -replace 'zxcPATHzxc',($f.Method[0].Path -replace '\:\w+') 48 | 49 | $a.ps.EndBlock = $endblock 50 | 51 | 52 | 53 | 54 | 55 | } 56 | 57 | # fix verbs 58 | foreach ($g in $gen) { 59 | switch -Regex ($g.Verb) { 60 | 'GET' {$g.verb = 'Get'} 61 | 'PUT' {$g.verb = 'Set'} 62 | 'DELETE' {$g.verb = 'Remove'} 63 | 64 | } 65 | } 66 | 67 | 68 | # add auth token 69 | foreach ($g in $gen) { 70 | $splat = @{ 71 | Name = 'ApiToken' 72 | Type = 'string' 73 | Description = 'Authentication token' 74 | UsedIn = 'auth' 75 | API = $false 76 | } 77 | $g.Params += New-ApiParam @splat 78 | 79 | $splat = @{ 80 | Name = 'ApiUri' 81 | Type = 'string' 82 | Description = 'Consul URI' 83 | UsedIn = 'auth' 84 | Required = $true 85 | API = $false 86 | } 87 | $g.Params += New-ApiParam @splat 88 | 89 | } 90 | 91 | # add a parameter to supply the value for the key 92 | $gen | ? PSFunctionName -EQ 'Set-ConsulKey' | % { 93 | $g = $_ 94 | $splat = @{ 95 | Name = 'Value' 96 | 97 | Description = 'Key Value' 98 | UsedIn = 'body' 99 | } 100 | $g.Params += New-ApiParam @splat 101 | 102 | $splat = @{ 103 | Name = 'ContentType' 104 | Type = 'string' 105 | Description = 'Content type to use' 106 | #UsedIn = '' 107 | #Required = $true 108 | DefaultValue = "'application/json; charset=utf-8'" 109 | API = $false 110 | } 111 | $g.Params += New-ApiParam @splat 112 | 113 | $g.FriendlyAlias ='New-ConsulKey' 114 | 115 | $g.ps.EndBlock = $g.ps.EndBlock -replace [regex]::Escape(" ContentType = 'application/json; charset=utf-8'"),' ContentType = $ContentType' 116 | 117 | #ContentType = 'application/json' 118 | } 119 | 120 | $gen | ? PSFunctionName -EQ 'Get-ConsulKey' | % { 121 | $g = $_ 122 | $g.Params | where Type -eq bool | % {$_.Type = 'switch'} 123 | 124 | $g.ps.BeginBlock = @' 125 | class ConsulKey { 126 | [int]$CreateIndex 127 | [int]$Flags 128 | [string]$Key 129 | [int]$LockIndex 130 | [int]$ModifyIndex 131 | [string]$Value 132 | 133 | [string] ToASCII () { 134 | return [System.Text.Encoding]::ASCII.GetString( 135 | [System.Convert]::FromBase64String($this.Value) 136 | ) 137 | } 138 | 139 | [string] ToUnicode () { 140 | return [System.Text.Encoding]::Unicode.GetString( 141 | [System.Convert]::FromBase64String($this.Value) 142 | ) 143 | } 144 | 145 | [string] ToUTF8 () { 146 | return [System.Text.Encoding]::UTF8.GetString( 147 | [System.Convert]::FromBase64String($this.Value) 148 | ) 149 | } 150 | 151 | } 152 | '@ 153 | 154 | $getConsulKeyCustom = @' 155 | #map to the class if not recurse 156 | if ($PSBoundParameters.psbase.Keys -match 'keys|recurse|raw') { 157 | $irm 158 | } 159 | else { 160 | foreach ($thisResponse in $irm) { 161 | [ConsulKey]$thisResponse 162 | } 163 | } 164 | '@ 165 | 166 | $g.ps.EndBlock = $g.ps.EndBlock -replace '(\$irm)(?!\s+=\s+Invoke)',$getConsulKeyCustom 167 | } 168 | 169 | # initilize scriptproperty 170 | $gen.ps.ScriptBlock | Out-Null 171 | 172 | $gen | select ApiFunctionName, Verb, PSFunctionName, SyntaxCheckPass | ft -------------------------------------------------------------------------------- /projects/ExampleProject/ExampleModule.ps1: -------------------------------------------------------------------------------- 1 | $functions = @() 2 | 3 | # 1st function 4 | $api = @{ 5 | NounPrefix = 'ExampleModule' 6 | ApiFunctionName = 'createUser' 7 | } 8 | 9 | $api = New-ApiObject @api 10 | $functions += $api 11 | 12 | # parameters 13 | $parameter = @{ 14 | Name = 'Username' 15 | Description = 'Username for the new user' 16 | Type = 'string' 17 | Required = $true 18 | UsedIn = 'Headers' 19 | } 20 | 21 | $api.Params += New-ApiParam @parameter 22 | 23 | $parameter = @{ 24 | Name = 'Password' 25 | Description = 'Password for the new user' 26 | Type = 'string' 27 | Required = $true 28 | UsedIn = 'Headers' 29 | } 30 | 31 | $api.Params += New-ApiParam @parameter 32 | 33 | # for the help block 34 | $api.DOC.Synopsis = 'Create a new user' 35 | 36 | # 2nd function 37 | 38 | $api = @{ 39 | NounPrefix = 'ExampleModule' 40 | ApiFunctionName = 'searchUser' 41 | } 42 | 43 | $api = New-ApiObject @api 44 | 45 | $functions += $api 46 | 47 | # parameters 48 | $parameter = @{ 49 | Name = 'Username' 50 | Description = 'Username to search for' 51 | Type = 'string' 52 | Required = $false 53 | UsedIn = 'Query' 54 | } 55 | 56 | $api.Params += New-ApiParam @parameter 57 | 58 | $parameter = @{ 59 | Name = 'Status' 60 | Description = 'Only search for users with status' 61 | Type = 'int' 62 | Required = $false 63 | UsedIn = 'Query' 64 | } 65 | 66 | $api.Params += New-ApiParam @parameter 67 | 68 | # for the help block 69 | 70 | $api.DOC.Synopsis = 'Find a user by name' 71 | 72 | # add auth token and uri parameter for all functions 73 | 74 | foreach ($f in $functions) { 75 | 76 | $parameter = @{ 77 | Name = 'Token' 78 | Description = 'Auth token to access API' 79 | Type = 'string' 80 | Required = $true 81 | UsedIn = 'Auth' 82 | API = $false 83 | } 84 | 85 | $f.params += New-ApiParam @parameter 86 | 87 | $parameter = @{ 88 | Name = 'Uri' 89 | Description = 'API URI' 90 | Type = 'string' 91 | Required = $true 92 | UsedIn = 'Auth' 93 | API = $false 94 | } 95 | 96 | $f.params += New-ApiParam @parameter 97 | 98 | 99 | } 100 | 101 | # create PoSh verbNouns 102 | foreach ($f in $functions) { 103 | switch -regex ($f.ApiFunctionName) { 104 | 105 | '(create)(.+)' { 106 | $f.Verb = 'New' 107 | $f.Noun = $Matches[2] 108 | } 109 | 110 | '(search)(.+)' { 111 | $f.Verb = 'Get' 112 | $f.Noun = $Matches[2] 113 | } 114 | } 115 | } 116 | 117 | # add actual code 118 | 119 | foreach ($f in $functions) { 120 | 121 | $f.ps.EndBlock = @' 122 | $json = @{ 123 | jsonrpc = '2.0' 124 | function = $f__ApiFunctionName 125 | } 126 | 127 | $json.Add('Token', $hashAuthParameters['Token']) 128 | 129 | if ($hashHeadersParameters.Count -gt 0) { 130 | $json.Add('Params', $hashHeadersParameters) 131 | } 132 | 133 | $json = $json | ConvertTo-Json 134 | 135 | $uri = [uri]::new($hashAuthParameters['Uri']) 136 | $QueryParameters = @('Username', 'Status') 137 | 138 | if ($hashQueryParameters) { 139 | 140 | $query = '?' + ( 141 | ( 142 | $hashQueryParameters.Keys | % { 143 | '{0}={1}' -f $_, $hashQueryParameters.$_ 144 | } 145 | ) -join '&' 146 | ) 147 | 148 | $uri = [uri]::new($uri,[string]$query) 149 | } 150 | 151 | $splat = @{ 152 | Method = 'POST' 153 | Uri = $uri 154 | Body = $json 155 | ContentType = 'application/json' 156 | } 157 | 158 | # $res = Invoke-RestMethod @splat 159 | 160 | write-host "Invoking Invoke-RestMethod:" 161 | 'Uri: {0}' -f $uri 162 | 'Body:' 163 | $splat.body 164 | 165 | '@ -replace '\$f__ApiFunctionName',("'{0}'" -f $f.ApiFunctionName) 166 | 167 | } 168 | 169 | # let's see what we got: 170 | 171 | foreach ($f in $functions) { 172 | 'PS function {0} for API function {1}' -f $f.PSFunctionName, $f.ApiFunctionName | Write-Host -ForegroundColor Green 173 | $f.Params | select Name, API, Type, Required, Description, UsedIn | ft 174 | } 175 | 176 | # write function files 177 | 178 | foreach ($f in $functions) { 179 | 180 | $f.PS.FunctionText | Set-Content -LiteralPath (Join-Path $PSScriptRoot ('{0}.ps1' -f $f.PSFunctionName )) 181 | } -------------------------------------------------------------------------------- /projects/ExampleProject/Get-ExampleModuleUser.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | Find a user by name 4 | .DESCRIPTION 5 | Invoke searchUser 6 | Alias: Invoke-ExampleModuleSearchUser 7 | .NOTES 8 | API Function Name: searchUser 9 | PS Module Safe Name: Invoke-ExampleModuleSearchUser 10 | #> 11 | function Get-ExampleModuleUser { 12 | [CmdletBinding()] 13 | [Alias('Invoke-ExampleModuleSearchUser')] 14 | Param ( 15 | # Username to search for 16 | [string]$Username, 17 | 18 | # Only search for users with status 19 | [int]$Status, 20 | 21 | # Auth token to access API 22 | [Parameter(Mandatory=$true)] 23 | [string]$Token, 24 | 25 | # API URI 26 | [Parameter(Mandatory=$true)] 27 | [string]$Uri 28 | ) 29 | Begin { 30 | $ApiParameters = @('Username', 'Status') 31 | $hashApiParameters = @{} 32 | 33 | foreach ($par in $hashApiParameters) { 34 | if ($PSBoundParameters.Keys -contains $par) { 35 | $hashApiParameters.Add($par, $PSBoundParameters[$par]) 36 | } 37 | } 38 | 39 | $QueryParameters = @('Username', 'Status') 40 | $hashQueryParameters = @{} 41 | 42 | foreach ($par in $QueryParameters) { 43 | if ($PSBoundParameters.Keys -contains $par) { 44 | $hashQueryParameters.Add($par, $PSBoundParameters[$par]) 45 | } 46 | } 47 | 48 | $AuthParameters = @('Token', 'Uri') 49 | $hashAuthParameters = @{} 50 | 51 | foreach ($par in $AuthParameters) { 52 | if ($PSBoundParameters.Keys -contains $par) { 53 | $hashAuthParameters.Add($par, $PSBoundParameters[$par]) 54 | } 55 | } 56 | 57 | } # End begin block 58 | 59 | End { 60 | $json = @{ 61 | jsonrpc = '2.0' 62 | function = 'searchUser' 63 | } 64 | 65 | $json.Add('Token', $hashAuthParameters['Token']) 66 | 67 | if ($hashHeadersParameters.Count -gt 0) { 68 | $json.Add('Params', $hashHeadersParameters) 69 | } 70 | 71 | $json = $json | ConvertTo-Json 72 | 73 | $uri = [uri]::new($hashAuthParameters['Uri']) 74 | $QueryParameters = @('Username', 'Status') 75 | 76 | if ($hashQueryParameters) { 77 | 78 | $query = '?' + ( 79 | ( 80 | $hashQueryParameters.Keys | % { 81 | '{0}={1}' -f $_, $hashQueryParameters.$_ 82 | } 83 | ) -join '&' 84 | ) 85 | 86 | $uri = [uri]::new($uri,[string]$query) 87 | } 88 | 89 | $splat = @{ 90 | Method = 'POST' 91 | Uri = $uri 92 | Body = $json 93 | ContentType = 'application/json' 94 | } 95 | 96 | # $res = Invoke-RestMethod @splat 97 | 98 | write-host "Invoking Invoke-RestMethod:" 99 | 'Uri: {0}' -f $uri 100 | 'Body:' 101 | $splat.body 102 | 103 | } # End end block 104 | 105 | 106 | } # End function 107 | 108 | -------------------------------------------------------------------------------- /projects/ExampleProject/New-ExampleModuleUser.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | Create a new user 4 | .DESCRIPTION 5 | Invoke createUser 6 | Alias: Invoke-ExampleModuleCreateUser 7 | .NOTES 8 | API Function Name: createUser 9 | PS Module Safe Name: Invoke-ExampleModuleCreateUser 10 | #> 11 | function New-ExampleModuleUser { 12 | [CmdletBinding()] 13 | [Alias('Invoke-ExampleModuleCreateUser')] 14 | Param ( 15 | # Username for the new user 16 | [Parameter(Mandatory=$true)] 17 | [string]$Username, 18 | 19 | # Password for the new user 20 | [Parameter(Mandatory=$true)] 21 | [string]$Password, 22 | 23 | # Auth token to access API 24 | [Parameter(Mandatory=$true)] 25 | [string]$Token, 26 | 27 | # API URI 28 | [Parameter(Mandatory=$true)] 29 | [string]$Uri 30 | ) 31 | Begin { 32 | $ApiParameters = @('Username', 'Password') 33 | $hashApiParameters = @{} 34 | 35 | foreach ($par in $hashApiParameters) { 36 | if ($PSBoundParameters.Keys -contains $par) { 37 | $hashApiParameters.Add($par, $PSBoundParameters[$par]) 38 | } 39 | } 40 | 41 | $HeadersParameters = @('Username', 'Password') 42 | $hashHeadersParameters = @{} 43 | 44 | foreach ($par in $HeadersParameters) { 45 | if ($PSBoundParameters.Keys -contains $par) { 46 | $hashHeadersParameters.Add($par, $PSBoundParameters[$par]) 47 | } 48 | } 49 | 50 | $AuthParameters = @('Token', 'Uri') 51 | $hashAuthParameters = @{} 52 | 53 | foreach ($par in $AuthParameters) { 54 | if ($PSBoundParameters.Keys -contains $par) { 55 | $hashAuthParameters.Add($par, $PSBoundParameters[$par]) 56 | } 57 | } 58 | 59 | } # End begin block 60 | 61 | End { 62 | $json = @{ 63 | jsonrpc = '2.0' 64 | function = 'createUser' 65 | } 66 | 67 | $json.Add('Token', $hashAuthParameters['Token']) 68 | 69 | if ($hashHeadersParameters.Count -gt 0) { 70 | $json.Add('Params', $hashHeadersParameters) 71 | } 72 | 73 | $json = $json | ConvertTo-Json 74 | 75 | $uri = [uri]::new($hashAuthParameters['Uri']) 76 | $QueryParameters = @('Username', 'Status') 77 | 78 | if ($hashQueryParameters) { 79 | 80 | $query = '?' + ( 81 | ( 82 | $hashQueryParameters.Keys | % { 83 | '{0}={1}' -f $_, $hashQueryParameters.$_ 84 | } 85 | ) -join '&' 86 | ) 87 | 88 | $uri = [uri]::new($uri,[string]$query) 89 | } 90 | 91 | $splat = @{ 92 | Method = 'POST' 93 | Uri = $uri 94 | Body = $json 95 | ContentType = 'application/json' 96 | } 97 | 98 | # $res = Invoke-RestMethod @splat 99 | 100 | write-host "Invoking Invoke-RestMethod:" 101 | 'Uri: {0}' -f $uri 102 | 'Body:' 103 | $splat.body 104 | 105 | } # End end block 106 | 107 | 108 | } # End function 109 | 110 | -------------------------------------------------------------------------------- /projects/ExampleProject/README.md: -------------------------------------------------------------------------------- 1 | # Example Project 2 | 3 | Mockup project to explain basic features 4 | 5 | # Usage 6 | 7 | Run `ExampleModule.ps1` 8 | 9 | # Generated examples 10 | 11 | `Get-ExampleModuleUser.ps1` and `New-ExampleModuleUser.ps1` are generated by `ExampleModule.ps1` 12 | -------------------------------------------------------------------------------- /projects/Kanboard-PHPApi-PSRAWG/Add-AuthenticationParameters.ps1: -------------------------------------------------------------------------------- 1 |  2 | foreach ($thisFunction in $Functions) { 3 | 4 | if ($thisFunction.Params | ? Name -eq Credential) { 5 | } 6 | else { 7 | $thisFunction.PS.DefaultParameterSetName = 'PlainCredentials' 8 | 9 | $splat = @{ 10 | Description = 'Credential Object' 11 | ParameterSetName = 'CredentialObject' 12 | Required = $true 13 | Name = 'Credential' 14 | API = $false 15 | } 16 | 17 | $thisFunction.params += New-ApiParam @splat 18 | 19 | $splat = @{ 20 | Description = 'Kanboard API Uri' 21 | ParameterSetName='PlainCredentials' 22 | Required = $true 23 | Type = 'string' 24 | Name = 'ApiUri' 25 | API = $false 26 | } 27 | 28 | $thisFunction.params += New-ApiParam @splat 29 | 30 | $splat = @{ 31 | Description = 'API Username, use "jsonrpc" for the global access' 32 | ParameterSetName='PlainCredentials' 33 | Required = $true 34 | Type = 'string' 35 | Name = 'ApiUsername' 36 | API = $false 37 | } 38 | 39 | $thisFunction.params += New-ApiParam @splat 40 | 41 | $splat = @{ 42 | Description = 'API Password or Token' 43 | ParameterSetName='PlainCredentials' 44 | Required = $true 45 | Type = 'string' 46 | Name = 'ApiPassword' 47 | Alias = 'Token' 48 | API = $false 49 | } 50 | 51 | $thisFunction.params += New-ApiParam @splat 52 | 53 | } 54 | } -------------------------------------------------------------------------------- /projects/Kanboard-PHPApi-PSRAWG/Add-MainFunctions.ps1: -------------------------------------------------------------------------------- 1 | #Add-MainFunctions 2 | 3 | foreach ($thisFunction in $Functions) { 4 | 5 | # add credentials handling block 6 | 7 | if ($thisFunction.Params | ? Name -eq Credential) { 8 | $thisFunction.PS.BeginBlock += @' 9 | if ($PSCmdlet.ParameterSetName -eq 'PlainCredentials') { 10 | $Credential = New-KanboardCredential $ApiUri $ApiUsername $ApiPassword 11 | } 12 | '@ 13 | 14 | 15 | } 16 | 17 | # Create JSON block 18 | $thisFunction.PS.EndBlock += @' 19 | $jsonRequestId = Get-Random -Minimum 1 -Maximum ([int]::MaxValue) 20 | 21 | $json = @{ 22 | jsonrpc = '2.0' 23 | method = '$$$Method$$$' 24 | id = $jsonRequestId 25 | } 26 | 27 | # dynamically add user parameters 28 | if ($hashJsonParameters.Count -gt 0) { 29 | $json.Add('params', $hashJsonParameters) 30 | } 31 | 32 | $json = $json | ConvertTo-Json 33 | 34 | if ($PSBoundParameters['Verbose']) { 35 | Write-Verbose $json 36 | } 37 | 38 | '@ -replace [regex]::Escape('$$$Method$$$'),$thisFunction.ApiFunctionName 39 | 40 | $thisFunction.PS.EndBlock += @' 41 | $splat = @{ 42 | Method = 'POST' 43 | Uri = $Credential.Uri 44 | Credential = $Credential.Credential 45 | Body = $json 46 | ContentType = 'application/json' 47 | } 48 | 49 | $res = Invoke-RestMethod @splat 50 | 51 | if ($res.result) { 52 | Convert-KanboardResult $res.result -PassThru 53 | } 54 | 55 | '@ 56 | 57 | } -------------------------------------------------------------------------------- /projects/Kanboard-PHPApi-PSRAWG/Additional scripts/Convert-KanboardResult.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | Convert Kanboard API request to .NET types 4 | .DESCRIPTION 5 | Convert the values of Kanboard API response to the more usable .NET/PS types 6 | .EXAMPLE 7 | $res.result | Convert-KanboardResult 8 | #> 9 | function Convert-KanboardResult { 10 | [CmdletBinding()] 11 | [Alias()] 12 | Param ( 13 | # Param1 help description 14 | [Parameter(Mandatory=$true, 15 | ValueFromPipeline=$true, 16 | Position=0)] 17 | $Data, 18 | [switch]$PassThru 19 | ) 20 | 21 | foreach ($obj in $Data) { 22 | $gm = Get-Member -MemberType NoteProperty -InputObject $obj 23 | 24 | foreach ($np in $gm) { 25 | 26 | # Convert datetime to .NET type 27 | if ($np.Name -match '^(date_|last_modified)') { 28 | 29 | if ($obj.$($np.Name) -gt 0) { 30 | 31 | try { 32 | $obj.$($np.Name) = ConvertFrom-UnixTime $($obj.$($np.Name) ) 33 | } 34 | catch { 35 | Write-Warning ('Failed to convert property "{0}" with value "{1}" to the datetime' -f $np.Name, $obj.$($np.Name)) 36 | } 37 | 38 | } 39 | 40 | } 41 | 42 | } 43 | } 44 | 45 | if ($PassThru) { 46 | $Data 47 | } 48 | 49 | } # End Convert-KanboardResult function 50 | 51 | 52 | -------------------------------------------------------------------------------- /projects/Kanboard-PHPApi-PSRAWG/Additional scripts/ConvertFrom-UnixTime.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | Convert input seconds from unixtime to the local time 4 | .DESCRIPTION 5 | Convert input seconds from unixtime to the local time 6 | .EXAMPLE 7 | 43235345 | ConvertFrom-UnixTime 8 | .EXAMPLE 9 | ConvertFrom-UnixTime 1233545 10 | #> 11 | function ConvertFrom-UnixTime { 12 | [CmdletBinding()] 13 | [OutputType([datetime])] 14 | Param ( 15 | # Unixtime 16 | [Parameter(Mandatory=$true, 17 | ValueFromPipeline=$true, 18 | Position=0)] 19 | [int]$Unixtime 20 | ) 21 | 22 | Process { 23 | [timezone]::CurrentTimeZone.ToLocalTime(([datetime]'1/1/1970').AddSeconds($Unixtime)) 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /projects/Kanboard-PHPApi-PSRAWG/Additional scripts/New-KanboardCredential.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | Create a credential object for use with Kanboard cmdlets 4 | .DESCRIPTION 5 | Credential object for use with Kanboard cmdlets 6 | .EXAMPLE 7 | $KBCredential = New-KanboardCredential https://example.org/jsonrpc.php jsonrpc 44a767f47c8bf7885f6d0a8b052445f9d2618ed3350d2453e2b1baa860b2 8 | Get-KBAllProjects -Credential $kbCred 9 | .EXAMPLE 10 | # using splatting 11 | $splat = @{ 12 | Credential = New-KanboardCredential https://example.org/jsonrpc.php Username Password 13 | } 14 | Get-KBAllUsers @splat 15 | #> 16 | function New-KanboardCredential { 17 | [CmdletBinding()] 18 | [Alias('New-KBCredential')] 19 | [OutputType([hashtable])] 20 | Param ( 21 | # Kanboard API URL 22 | [Parameter(Mandatory=$true, 23 | ValueFromPipelineByPropertyName=$true, 24 | Position=0)] 25 | [string]$ApiUri, 26 | 27 | # Username 28 | [Parameter(Mandatory=$true, 29 | ValueFromPipelineByPropertyName=$true, 30 | Position=1)] 31 | [string]$ApiUsername, 32 | 33 | # Password or API token 34 | [Parameter(Mandatory=$true, 35 | ValueFromPipelineByPropertyName=$true, 36 | Position=2)] 37 | [Alias("Token")] 38 | [string]$ApiPassword 39 | 40 | ) 41 | 42 | $SecureStringApiPassword = ConvertTo-SecureString $ApiPassword -AsPlainText -Force 43 | 44 | $Credential = [System.Management.Automation.PSCredential]::new($ApiUsername, $SecureStringApiPassword) 45 | 46 | @{ 47 | Uri = $ApiUri 48 | Credential = $Credential 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /projects/Kanboard-PHPApi-PSRAWG/ConvertFrom-PHPApi.ps1: -------------------------------------------------------------------------------- 1 | # does what it says 2 | filter Capitalize { 3 | [Regex]::Replace($_, '^\w', { param($letter) $letter.Value.ToUpper() }) 4 | } 5 | 6 | # where the source files are located 7 | $rootdir = 'C:\Shares\personal\projects\kanboard\source\app\Api' 8 | 9 | # get all files 10 | $excludeFiles = @('BaseProcedure.php') 11 | $files = gci -Path $rootdir -Filter '*.php' -Recurse -Exclude $excludeFiles 12 | 13 | 14 | # create functions 15 | $Functions = foreach ($thisFile in $files) { 16 | 17 | $source = (Get-Content $thisFile.FullName) 18 | 19 | # regex magik 20 | $regexFunctionDefinition = 'public\s+function\s+(?\w+)\s*' + '(\((?[^{]*)\))?' 21 | 22 | # do magik 23 | $ss = Select-String -InputObject $source -Pattern $regexFunctionDefinition -AllMatches 24 | 25 | foreach ($thisMatch in $ss.Matches) { 26 | $splat = @{ 27 | NounPrefix = 'KB' 28 | ApiFunctionName = $thisMatch.Groups['name'].Value 29 | ApiParamsSource = $thisMatch.Groups['args'].Value -replace '\s{2,}',' ' 30 | ApiSource = $thisMatch.Groups[0].Value 31 | ApiFileName = $thisFile.Name 32 | } 33 | New-ApiObject @splat 34 | } 35 | } 36 | 37 | # parse and assign verbs and nouns 38 | foreach ($tmp in $Functions) { 39 | # Make PS Verb-Noun pairs 40 | switch -Regex ($tmp.ApiFunctionName) { 41 | '^get(.+)$' { 42 | $tmp.Verb = 'Get' 43 | $tmp.Noun = $Matches[1] 44 | } 45 | 46 | '^remove(.+)$' { 47 | $tmp.Verb = 'Remove' 48 | $tmp.Noun = $Matches[1] 49 | } 50 | 51 | '^create(.+)$' { 52 | $tmp.Verb = 'New' 53 | $tmp.Noun = $Matches[1] 54 | } 55 | 56 | '^update(.+)$' { 57 | $tmp.Verb = 'Set' 58 | $tmp.Noun = $Matches[1] 59 | } 60 | 61 | '^enable(.+)$' { 62 | $tmp.Verb = 'Enable' 63 | $tmp.Noun = $Matches[1] 64 | } 65 | 66 | '^disable(.+)$' { 67 | $tmp.Verb = 'Disable' 68 | $tmp.Noun = $Matches[1] 69 | } 70 | 71 | '^add(.+)$' { 72 | $tmp.Verb = 'Add' 73 | $tmp.Noun = $Matches[1] 74 | } 75 | 76 | '^close(.+)$' { 77 | $tmp.Verb = 'Close' 78 | $tmp.Noun = $Matches[1] 79 | } 80 | 81 | '^set(.+)$' { 82 | $tmp.Verb = 'Set' 83 | $tmp.Noun = $Matches[1] 84 | } 85 | 86 | '^move(.+)$' { 87 | $tmp.Verb = 'Move' 88 | $tmp.Noun = $Matches[1] 89 | } 90 | 91 | '^change(.+)$' { 92 | $tmp.Verb = 'Set' 93 | $tmp.Noun = $Matches[1] 94 | } 95 | 96 | '^save(.+)$' { 97 | $tmp.Verb = 'Set' 98 | $tmp.Noun = $Matches[1] 99 | } 100 | 101 | # Function Specific - should be run after all other replacements 102 | 103 | '^(open|close)(Task)$' { 104 | $tmp.Verb = $Matches[1] | Capitalize 105 | $tmp.Noun = $Matches[2] 106 | $tmp.FriendlyAlias = '{0}-{2}{1}' -f 'Set', ('TaskStatus' + ($Matches[1] | Capitalize)), $tmp.PS.NounPrefix 107 | } 108 | 109 | '^duplicateTaskToProject$' { 110 | $tmp.Verb = 'Copy' 111 | $tmp.Noun = 'TaskToProject' 112 | $tmp.FriendlyAlias = '{0}-{2}{1}' -f 'Duplicate', 'TaskToProject', $tmp.PS.NounPrefix 113 | } 114 | 115 | '^searchTasks$' { 116 | $tmp.Verb = 'Find' 117 | $tmp.Noun = 'Tasks' 118 | $tmp.FriendlyAlias = '{0}-{2}{1}' -f 'Get', 'Tasks', $tmp.PS.NounPrefix 119 | } 120 | 121 | '^moveTaskPosition$' { 122 | $tmp.Verb = 'Set' 123 | $tmp.Noun = 'TaskPosition' 124 | $tmp.FriendlyAlias = '{0}-{2}{1}' -f 'Move', 'TaskPosition', $tmp.PS.NounPrefix 125 | } 126 | 127 | '^downloadTaskFile$' { 128 | $tmp.Verb = 'Invoke' 129 | $tmp.Noun = 'downloadTaskFile' 130 | $tmp.FriendlyAlias = '{0}-{2}{1}' -f 'Get', 'TaskFileDownload', $tmp.PS.NounPrefix 131 | } 132 | 133 | '^downloadProjectFile$' { 134 | $tmp.Verb = 'Invoke' 135 | $tmp.Noun = 'downloadProjectFile' 136 | $tmp.FriendlyAlias = '{0}-{2}{1}' -f 'Get', 'ProjectFileDownload', $tmp.PS.NounPrefix 137 | } 138 | 139 | # default 140 | 141 | default { 142 | $tmp.Verb = 'Invoke' 143 | $tmp.Noun = $tmp.ApiFunctionName 144 | } 145 | 146 | } # end switch 147 | 148 | } 149 | 150 | 151 | 152 | # parse and create parameters 153 | foreach ($tmp in $Functions) { 154 | 155 | # find out how many argumetns are there 156 | $argumentCount = Select-String -InputObject $tmp.Api.ParamsSource -Pattern '\$' -AllMatches 157 | 158 | if ($argumentCount.Matches.Count -eq 1) { 159 | $tmp.Api.Params = @( $tmp.Api.ParamsSource -replace '^\(' -replace '\)$' ) 160 | } 161 | 162 | if ($argumentCount.Matches.Count -ge 2) { 163 | $tmp.Api.Params = @( $tmp.Api.ParamsSource -replace '^\(' -replace '\)$' -split '\s*\,\s*' ) 164 | } 165 | 166 | # another regex magik 167 | if ($tmp.Api.Params.Count -gt 0) { 168 | foreach ($thisArg in $tmp.Api.Params) { 169 | 170 | if ($thisArg -Match '^(?.+\s+)?(?\$.+)\s*=\s*(?.+)$') { 171 | 172 | $splat = @{ 173 | Name = $Matches.Var -replace '^\$' -replace '\s' -replace '^array\$' 174 | UsedIn = 'json' 175 | } 176 | $tmp.Params += New-ApiParam @splat 177 | 178 | } 179 | elseif ($thisArg -match '^(?.+)\s*$') { 180 | $splat = @{ 181 | Name = $Matches.Var -replace '^\$' -replace '\s' -replace '^array\$' 182 | UsedIn = 'json' 183 | } 184 | $tmp.Params += New-ApiParam @splat 185 | } 186 | } 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /projects/Kanboard-PHPApi-PSRAWG/ConvertFrom-RSTDocumentation-v2.ps1: -------------------------------------------------------------------------------- 1 | # where the documentation source is located 2 | $rootdir = 'C:\Shares\personal\projects\kanboard\documentation\source\api' 3 | 4 | # get all files 5 | $excludeFiles = 'examples.rst','authentication.rst','index.rst','introduction.rst' 6 | $files = gci -Path $rootdir -Filter '*.rst' -Recurse -Exclude $excludeFiles 7 | 8 | $Descriptions = @() 9 | 10 | foreach ($thisFile in $files) { 11 | Write-Verbose $thisFile.Name 12 | 13 | # reading the files raw to preserve `retur`ns 14 | 15 | $rst = (Get-Content $thisFile.FullName -Raw) 16 | 17 | # regex magik 18 | $regex = '(?\w+)[\r\n]+(?:\-{2,})[\r\n]+(?[\S\s]+?)\.\.\s*code\:\:' 19 | 20 | # do magik 21 | $ss = Select-String -InputObject $rst -Pattern $regex -AllMatches 22 | 23 | foreach ($thisMatch in @($ss.Matches)) { 24 | 25 | # use named regex groups to fill the values 26 | 27 | $apiFunctionName = $thisMatch.Groups['FunctionName'].Value 28 | $f = $Functions.Where({$_.ApiFunctionName -eq $apiFunctionName}) 29 | $f.Doc.Filename = $thisFile.Name 30 | 31 | $obj = [pscustomobject]@{ 32 | purpose = $null 33 | note = $null 34 | ressucc =$null 35 | resfail =$null 36 | blk = $null 37 | } 38 | 39 | $obj.blk = $thisMatch.Groups['block'].Value 40 | 41 | # not needed 42 | $obj.blk = $obj.blk -replace ([regex]::Escape('Request example') + '.*') 43 | 44 | # trim trailing whitespace 45 | $obj.blk = $obj.blk -replace '([\s])*$' 46 | 47 | # isn't really needed, but helps to track down issues with some lines 48 | $f.DOC.Source = $obj.blk 49 | 50 | # many, many regex magiks 51 | $regexPurpose = '(?\s*\-\s*Purpose\s*:\s*[\W\w\s]+?)(-\s*(Parameter|Result))' 52 | 53 | if ($obj.blk -match $regexPurpose) { 54 | $obj.purpose = $Matches.purp -replace '\n' -replace '\s{2,}',' ' -replace '\s*\-\s*Purpose:\s*' -replace '\*{2}' 55 | $obj.blk = $obj.blk -replace [regex]::Escape($Matches.purp) 56 | } 57 | 58 | # - Note: ... 59 | 60 | $regexNote = '\s*-\s*Note\s*:\s*[\W\w\s]+' 61 | 62 | if ($obj.blk -match $regexNote) { 63 | $obj.note = $Matches[0] -replace '\n' -replace '\s{2,}',' ' -replace '\s*\-\s*Note:\s*' -replace '\*{2}' 64 | $obj.blk = $obj.blk -replace ([regex]::Escape($Matches[0]) + '.*') 65 | } 66 | 67 | # workaround for 1 specific function 68 | # because nobody is doing docs the right way 69 | 70 | $regexNote = 'The user will only be created[\W\w\s]+' 71 | 72 | if ($obj.blk -match $regexNote) { 73 | $obj.note = $Matches[0] -replace '\n',' ' -replace '\s{2,}',' ' -replace '\s*\-\s*Note:\s*' -replace '\*{2}' 74 | $obj.blk = $obj.blk -replace ([regex]::Escape($Matches[0]) + '.*') 75 | } 76 | 77 | # Result on failure: 78 | 79 | $regexResultFail = '\s*-\s*Result\s*on\s*failure:*\s*[\*\W\w\s]+' 80 | 81 | if ($obj.blk -match $regexResultFail) { 82 | $obj.resfail = $Matches[0] -replace '\n' -replace '\s{2,}',' ' -replace '\s*-\s*Result\s*on\s*failure:*\s*' -replace '\*{2}' 83 | $obj.blk = $obj.blk -replace ([regex]::Escape($Matches[0]) + '.*') 84 | } 85 | 86 | # Result on success: 87 | 88 | $regexResultSucc = '\s*-\s*Result\s*on\s*success:*\s*[\*\W\w\s]+' 89 | 90 | if ($obj.blk -match $regexResultSucc) { 91 | $obj.ressucc = $Matches[0] -replace '\n' -replace '\s{2,}',' ' -replace '\s*-\s*Result\s*on\s*success:*\s*' -replace '\*{2}' 92 | $obj.blk = $obj.blk -replace ([regex]::Escape($Matches[0]) + '.*') 93 | } 94 | 95 | # Result... 96 | 97 | $regexResult = '\s*-\s*Result[\*\W\w\s]+' 98 | 99 | if ($obj.blk -match $regexResult) { 100 | $obj.ressucc = $Matches[0] -replace '\n' -replace '\s{2,}',' ' -replace '\s*-\s*Result:*\s*' -replace '\*{2}' 101 | $obj.blk = $obj.blk -replace ([regex]::Escape($Matches[0]) + '.*') 102 | } 103 | 104 | # Parameters: none 105 | 106 | $regexParamNone = '\s*\-\s*Parameters:\s*(\*+)*none(\*+)*[\r\n]*' 107 | 108 | if ($obj.blk -match $regexParamNone) { 109 | $obj.blk = $obj.blk -replace ([regex]::Escape($Matches[0]) + '.*') 110 | } 111 | 112 | # skip parameter extraction if the function definition doesn't have any parameters 113 | 114 | if ($f.params.Count -gt 0) { 115 | 116 | # - parameter [optional description] (type, required) 117 | 118 | $regexParam = '( *\-\s*\*{2,}(?\w+)\*{2,})\s*(?\:*[\s*\w]+)*\s*(?\([\w\W]+?\))' 119 | 120 | $par = Select-String -InputObject $obj.blk -Pattern $regexParam -AllMatches 121 | 122 | $parameters = foreach ($t in $par.Matches) { 123 | 124 | 125 | $paramObj = [pscustomobject]@{ 126 | value = $t.Value 127 | name = $t.groups['name'].Value 128 | def = $t.Groups['def'].Value -replace '^\(' -replace '\)$' -replace '\s{2,}',' ' -replace '[\r\n]' # sigh 129 | desc = $t.Groups['desc'].Value -replace '^\:\s*' 130 | type = $null 131 | required = $false 132 | } 133 | 134 | switch -Regex ($paramObj.def) { 135 | 136 | '(\,\s*)*(required),*\s*' { 137 | $paramObj.Required = $true 138 | $paramObj.def = $paramObj.def -replace [regex]::Escape($Matches[0]) 139 | } 140 | 141 | '(\,\s*)*(optional),*\s*' { 142 | $paramObj.Required = $false 143 | $paramObj.def = $paramObj.def -replace [regex]::Escape($Matches[0]) 144 | } 145 | 146 | }# End switch 147 | 148 | # guess the parameter type 149 | 150 | switch -Regex ($paramObj.def) { 151 | 152 | '^string' { 153 | $paramObj.Type = 'string' 154 | $paramObj.def = $paramObj.def -replace [regex]::Escape($Matches[0]) 155 | } 156 | 157 | '^(integer array)' { 158 | $paramObj.Type = 'int[]' 159 | $paramObj.def = $paramObj.def -replace [regex]::Escape($Matches[0]) 160 | } 161 | 162 | '^(integer|int)' { 163 | $paramObj.Type = 'int' 164 | $paramObj.def = $paramObj.def -replace [regex]::Escape($Matches[0]) 165 | } 166 | 167 | '^\[\]string' { 168 | $paramObj.Type = 'string[]' 169 | $paramObj.def = $paramObj.def -replace [regex]::Escape($Matches[0]) 170 | } 171 | 172 | '^boolean' { 173 | $paramObj.Type = 'bool' 174 | $paramObj.def = $paramObj.def -replace [regex]::Escape($Matches[0]) 175 | } 176 | 177 | '^alphanumeric string' { 178 | $paramObj.Type = 'string' 179 | $paramObj.desc = $paramObj.desc + $_ 180 | $paramObj.def = $paramObj.def -replace [regex]::Escape($Matches[0]) 181 | } 182 | 183 | } # end switch 184 | 185 | # if there is anything anything left in def - move it to the description 186 | 187 | if ($paramObj.def -match '\w.+') { 188 | $paramObj.desc = $paramObj.def 189 | $paramObj.def = $paramObj.def -replace [regex]::Escape($Matches[0]) 190 | } 191 | 192 | $paramObj 193 | } 194 | 195 | $f.doc.Params = $parameters 196 | 197 | # remove the property definition from Blk 198 | 199 | foreach ($p in $parameters) { 200 | $obj.blk = $obj.blk -replace [regex]::Escape($p.value),'!' 201 | } 202 | 203 | } 204 | 205 | # update parameter definitions with data from the documentation 206 | foreach ($p in $f.doc.params) { 207 | 208 | if ($d = $f.Params | ? Name -eq $p.name) { 209 | $d.Description = $p.desc 210 | $d.Required = $p.required 211 | $d.type = $p.type 212 | } 213 | } 214 | 215 | $f.DOC.Synopsis = $obj.purpose 216 | # regex magik. again. 217 | $f.DOC.Description = $f.doc.Source -replace '\*{2}' 218 | $f.DOC.ResultSucc = $obj.ressucc 219 | $f.DOC.ResultFail = $obj.resfail 220 | 221 | $Descriptions += $obj 222 | } 223 | } 224 | 225 | 226 | 227 | $Functions.ps.scriptblock | Out-Null 228 | $Functions | ? ApiFunctionName -eq createUser | tee -va z ; $z.ps.ScriptBlock; #$z.doc 229 | -------------------------------------------------------------------------------- /projects/Kanboard-PHPApi-PSRAWG/README.md: -------------------------------------------------------------------------------- 1 | # Kanboard-PHPApi-PSRAWG 2 | 3 | Project used to generate PS2Kanboard module https://github.com/al-ign/PS2Kanboard 4 | 5 | # Usage 6 | 7 | Clone Kanboard repo 8 | 9 | Fix paths in `ConvertFrom...` scripts 10 | 11 | Load PSRAWG functions: 12 | 13 | . ..\..\New-ApiObject.ps1 14 | . ..\..\New-ApiParam.ps1 15 | 16 | Run the scripts in this order: 17 | 18 | ConvertFrom-PHPApi.ps1 19 | ConvertFrom-RSTDocumentation-v2.ps1 20 | Add-AuthenticationParameters.ps1 21 | Add-MainFunctions.ps1 22 | 23 | Create output folder, copy helper scripts from `Additional scripts` 24 | 25 | Fix path in `Write-KBApiGenFunctionFiles.ps1` and run to write script files 26 | -------------------------------------------------------------------------------- /projects/Kanboard-PHPApi-PSRAWG/Write-KBApiGenFunctionFiles.ps1: -------------------------------------------------------------------------------- 1 | # Write-KBApiGenFunctionFiles.ps1 2 | 3 | $outputPath = 'C:\Shares\personal\amc\projects\kanboard\PS2Kanboard\Public' 4 | 5 | $i = 0 6 | foreach ($thisFunction in $Functions) { 7 | Write-Progress -Activity $thisFunction.PSFunctionName -Status 'Writing' -PercentComplete ($i/$Functions.Count*100) 8 | $i++ 9 | $thisFunction.PS.FunctionText | Set-Content -LiteralPath (Join-Path $outputPath ('{0}.ps1' -f $thisFunction.PSFunctionName )) 10 | } 11 | --------------------------------------------------------------------------------