Private/Generator/ConvertTo-OpenApiGenFunction.ps1
|
function ConvertTo-OpenApiGenFunction { <# .SYNOPSIS Renders the wrapper function for one operation from the Function.ps1 template. .DESCRIPTION Takes the resolved command name, the operation, its parameter model (Get-OpenApiGenParameterModel) and the response type name, and returns the source text of the function: comment-based help, [CmdletBinding()] (ShouldProcess for every method except GET/HEAD/OPTIONS unless the verb is Get or Test, for every DELETE, and for any verb that changes state), [OutputType()] when the response type is known, the param() block and a process block that passes only bound parameters to Invoke-OpenApiRequest. #> [CmdletBinding()] [OutputType([string])] param( [Parameter(Mandatory = $true)] [object]$CommandName, [Parameter(Mandatory = $true)] [object]$Operation, [Parameter(Mandatory = $true)] [object]$ParameterModel, [Parameter()] [AllowNull()] [AllowEmptyString()] [string]$ResponseTypeName, [Parameter(Mandatory = $true)] [string]$Template ) $name = $CommandName.Name $method = ([string]$Operation.Method).ToUpperInvariant() $path = [string]$Operation.Path $operationId = [string]$Operation.OperationId $parameters = @($ParameterModel.Parameters) $stateVerbs = @('New', 'Set', 'Remove', 'Start', 'Stop', 'Restart', 'Reset', 'Update') # Get and Test only read, also when the API takes the request as a POST (a query or a validation # with a body). A DELETE always asks. $readVerbs = @('Get', 'Test') $shouldProcess = ($method -eq 'DELETE') -or ($stateVerbs -contains $CommandName.Verb) -or ((@('GET', 'HEAD', 'OPTIONS') -notcontains $method) -and ($readVerbs -notcontains $CommandName.Verb)) $deprecated = ($Operation.Deprecated -eq $true) #region help $summary = ([string]$Operation.Summary).Trim() $description = ([string]$Operation.Description).Trim() $synopsis = $summary if ($synopsis -eq '' -and $description -ne '') { $synopsis = ($description -split '(?<=\.)\s|\n')[0].Trim() } if ($synopsis -eq '') { $synopsis = "Calls $method $path." } $descriptionText = $description if ($descriptionText -eq '') { $descriptionText = $summary } $callText = "Calls the '$operationId' operation ($method $path)." if ($descriptionText -eq '') { $descriptionText = $callText } else { $descriptionText += "`n`n" + $callText } if ($deprecated) { $descriptionText += "`n`nThis operation is deprecated." } $sections = New-Object -TypeName System.Collections.ArrayList [void]$sections.Add(@{ Keyword = 'SYNOPSIS'; Text = $synopsis }) [void]$sections.Add(@{ Keyword = 'DESCRIPTION'; Text = $descriptionText }) foreach ($parameter in $parameters) { $text = [string]$parameter.Description if ([string]::IsNullOrWhiteSpace($text)) { switch ($parameter.Kind) { 'Spec' { $text = "The '$($parameter.SpecName)' $($parameter.In) parameter." } 'BodyProperty' { $text = "The '$($parameter.SpecName)' property of the request body." } default { $text = 'See the API documentation.' } } } if ($parameter.Deprecated) { $text = 'Deprecated. ' + $text } [void]$sections.Add(@{ Keyword = 'PARAMETER'; Argument = $parameter.Name; Text = $text }) } $example = $name foreach ($parameter in $parameters) { $inDefaultSet = ($null -eq $parameter.ParameterSet) -or ($parameter.ParameterSet -eq 'Parameters') if ($parameter.Mandatory -and $inDefaultSet -and $null -ne $parameter.ExampleText) { $example += ' -' + $parameter.Name + ' ' + $parameter.ExampleText } } [void]$sections.Add(@{ Keyword = 'EXAMPLE'; Text = $example }) $outputs = 'System.Management.Automation.PSObject' if (-not [string]::IsNullOrEmpty($ResponseTypeName)) { $outputs = $ResponseTypeName } [void]$sections.Add(@{ Keyword = 'OUTPUTS'; Text = $outputs }) $notes = "Operation: $operationId ($method $path)`nGenerated by tcs.openapi. Do not edit: put changes in Overrides.ps1." if ($CommandName.IsList) { $notes += "`nThis command lists or searches: it can return more than one object." } if ($deprecated) { $notes += "`nThis operation is deprecated and may be removed from the API." } [void]$sections.Add(@{ Keyword = 'NOTES'; Text = $notes }) if (-not [string]::IsNullOrWhiteSpace([string]$Operation.ExternalDocsUrl)) { [void]$sections.Add(@{ Keyword = 'LINK'; Text = [string]$Operation.ExternalDocsUrl }) } $help = ConvertTo-OpenApiGenHelp -Section $sections.ToArray() -Indent 4 #endregion #region attributes $attributeLines = New-Object -TypeName System.Collections.ArrayList if (@($parameters | Where-Object -FilterScript { $_.Name -match 'password|passphrase' }).Count -gt 0) { [void]$attributeLines.Add(" [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPassword', '', Justification = 'The API takes this value as plain text.')]") } $nounWords = @(Split-OpenApiGenWord -Value $CommandName.Noun) $uncountable = @('data', 'equipment', 'feedback', 'firmware', 'hardware', 'information', 'media', 'metadata', 'news', 'series', 'software', 'species', 'status') if ($nounWords.Count -gt 0 -and $uncountable -contains $nounWords[$nounWords.Count - 1].ToLowerInvariant()) { [void]$attributeLines.Add(" [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '', Justification = 'The noun comes from the API and has no singular form.')]") } $bindingArguments = @() if ($ParameterModel.BodyMode -eq 'Flattened') { $bindingArguments += "DefaultParameterSetName = 'Parameters'" } if ($shouldProcess) { $impact = 'Medium' if ($method -eq 'DELETE') { $impact = 'High' } $bindingArguments += 'SupportsShouldProcess = $true' $bindingArguments += "ConfirmImpact = '$impact'" } [void]$attributeLines.Add(' [CmdletBinding(' + ($bindingArguments -join ', ') + ')]') if (-not [string]::IsNullOrEmpty($ResponseTypeName)) { [void]$attributeLines.Add(' [OutputType(' + (ConvertTo-OpenApiGenLiteral -Value $ResponseTypeName) + ')]') } #endregion #region param block $blocks = @(foreach ($parameter in $parameters) { $lines = @() $arguments = @() if ($parameter.Mandatory) { $arguments += 'Mandatory = $true' } if ($null -ne $parameter.ParameterSet) { $arguments += 'ParameterSetName = ' + (ConvertTo-OpenApiGenLiteral -Value $parameter.ParameterSet) } if ($parameter.Pipeline) { $arguments += 'ValueFromPipelineByPropertyName = $true' } $lines += ' [Parameter(' + ($arguments -join ', ') + ')]' if (@($parameter.Aliases).Count -gt 0) { $lines += ' [Alias(' + ((@($parameter.Aliases) | ForEach-Object -Process { ConvertTo-OpenApiGenLiteral -Value $_ }) -join ', ') + ')]' } foreach ($attribute in @($parameter.Attributes)) { $lines += ' ' + $attribute } $lines += ' [' + $parameter.TypeName + ']' $lines += ' $' + $parameter.Name $lines -join "`n" }) $parameterBlock = $blocks -join ",`n`n" #endregion #region process block $mapped = @($parameters | Where-Object -FilterScript { $_.Kind -eq 'Spec' -or $_.Kind -eq 'BodyProperty' }) if ($mapped.Count -eq 0) { $parameterMap = '@{}' } else { $width = 0 foreach ($parameter in $mapped) { $width = [math]::Max($width, (ConvertTo-OpenApiGenLiteral -Value $parameter.Name).Length) } $mapLines = @('@{') foreach ($parameter in $mapped) { $key = (ConvertTo-OpenApiGenLiteral -Value $parameter.Name).PadRight($width) $mapLines += ' ' + $key + ' = @(' + (ConvertTo-OpenApiGenLiteral -Value $parameter.In) + ', ' + (ConvertTo-OpenApiGenLiteral -Value $parameter.SpecName) + ')' } $mapLines += ' }' $parameterMap = $mapLines -join "`n" } $options = New-Object -TypeName System.Collections.ArrayList $locations = @( @('path', 'PathParameters'), @('query', 'QueryParameters'), @('header', 'HeaderParameters'), @('cookie', 'CookieParameters') ) foreach ($location in $locations) { if (@($parameters | Where-Object -FilterScript { $_.Kind -eq 'Spec' -and $_.In -eq $location[0] }).Count -gt 0) { [void]$options.Add(" `$tcsRequest['$($location[1])'] = `$tcsValues['$($location[0])']") } } if ($ParameterModel.BodyMode -eq 'Flattened') { [void]$options.Add(" if (`$PSCmdlet.ParameterSetName -eq 'Body') {") [void]$options.Add(" `$tcsRequest['Body'] = `$Body") [void]$options.Add(' }') if ($ParameterModel.BodyRequired) { [void]$options.Add(' else {') } else { [void]$options.Add(" elseif (`$tcsValues['body'].Count -gt 0) {") } [void]$options.Add(" `$tcsRequest['Body'] = `$tcsValues['body']") [void]$options.Add(' }') } elseif ($ParameterModel.BodyMode -eq 'Body') { [void]$options.Add(" if (`$PSBoundParameters.ContainsKey('Body')) {") [void]$options.Add(" `$tcsRequest['Body'] = `$Body") [void]$options.Add(' }') } if (@($ParameterModel.ContentTypes).Count -gt 0) { [void]$options.Add(" `$tcsRequest['ContentType'] = " + (ConvertTo-OpenApiGenLiteral -Value @($ParameterModel.ContentTypes)[0])) if (@($parameters | Where-Object -FilterScript { $_.Kind -eq 'ContentType' }).Count -gt 0) { [void]$options.Add(" if (`$PSBoundParameters.ContainsKey('ContentType')) {") [void]$options.Add(" `$tcsRequest['ContentType'] = `$ContentType") [void]$options.Add(' }') } } foreach ($switchName in @('All', 'OutFile', 'Raw')) { if (@($parameters | Where-Object -FilterScript { $_.Kind -eq $switchName }).Count -gt 0) { [void]$options.Add(" if (`$PSBoundParameters.ContainsKey('$switchName')) {") [void]$options.Add(" `$tcsRequest['$switchName'] = `$$switchName") [void]$options.Add(' }') } } $shouldProcessText = '' if ($shouldProcess) { $shouldProcessText = @( ' $tcsTarget = ' + (ConvertTo-OpenApiGenLiteral -Value $path) " foreach (`$tcsKey in @(`$tcsValues['path'].Keys)) {" " `$tcsTarget = `$tcsTarget.Replace('{' + `$tcsKey + '}', [string]`$tcsValues['path'][`$tcsKey])" ' }' ' if (-not $PSCmdlet.ShouldProcess($tcsTarget, ' + (ConvertTo-OpenApiGenLiteral -Value $method) + ')) {' ' return' ' }' ) -join "`n" } #endregion return Expand-OpenApiGenTemplate -Template $Template -Value @{ FunctionName = $name Help = $help Attributes = ($attributeLines -join "`n") Parameters = $parameterBlock ParameterMap = $parameterMap OperationIdLiteral = (ConvertTo-OpenApiGenLiteral -Value $operationId) RequestOptions = ($options -join "`n") ShouldProcess = $shouldProcessText } } |