diff --git a/CHANGELOG.md b/CHANGELOG.md index 9cc7342..e395e7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,17 @@ # Changelog -## v0.3.0 - 2017-09-29 +## v0.3.0 +- Generate single Verb-Noun cmdlet for OperationIds like Noun_Verb and Noun_VerbBySomething (#358) +- Move New-HttpClientHandler logic into New-ServiceClient for non-Azure services. (#357) +- Add few verb mappings for Azure RPs (#356) +- Add NoVersionFolder switch parameter on New-PSSwaggerModule cmdlet to not create the version folder. (#355) +- Add all non-complex type properties in output format ps1xml files. (#354) +- Escape '<#' and '#>', and replace '--' with '==' in Header content (#352) +- Use separate PSCmdletOutputItemType variable for getting the output item type of pageable swagger operations. (#351) +- Verb map change: 'Regenerate' to 'New' instead of 'Update' as per the feedback recieved from Azure PowerShell team. (#347) +- [Azure and AzureStack] Use IClientFactory to create ARM Client in Azure PowerShell way. (#348) +- New-ServiceClient error on custom host (#350) +- Add CHANGELOG.md, and minor update for releasing the 0.3.0 version of PSSwagger and PSSwaggerUtility modules. (#345) - Add support for generating proper output type for the Swagger operations with x-ms-pageable extension (#342) - Add support for parameter type references to enum definitions (#341) - Add support for AdditionalProperties Json schema with array type (#339) @@ -21,7 +32,7 @@ - Update Readme and fix an error related to importing the PSSwaggerUtility module (#300) - Support custom x-ms-pageable\NextLinkName field name (#294) -## v0.2.0 - 2017-08-15 +## v0.2.0 * First preview release diff --git a/PSSwagger/AssemblyGenerationHelpers.Resources.psd1 b/PSSwagger/AssemblyGenerationHelpers.Resources.psd1 index 9b00b0c..4ff8f77 100644 --- a/PSSwagger/AssemblyGenerationHelpers.Resources.psd1 +++ b/PSSwagger/AssemblyGenerationHelpers.Resources.psd1 @@ -1,11 +1,3 @@ -######################################################################################### -# -# Copyright (c) Microsoft Corporation. All rights reserved. -# -# Licensed under the MIT license. -# -######################################################################################### - ConvertFrom-StringData @' ###PSLOC diff --git a/PSSwagger/AssemblyGenerationHelpers.ps1 b/PSSwagger/AssemblyGenerationHelpers.ps1 index 5bbbc01..33dcfb3 100644 --- a/PSSwagger/AssemblyGenerationHelpers.ps1 +++ b/PSSwagger/AssemblyGenerationHelpers.ps1 @@ -1,11 +1,3 @@ -######################################################################################### -# -# Copyright (c) Microsoft Corporation. All rights reserved. -# -# Licensed under the MIT license. -# -######################################################################################### - Microsoft.PowerShell.Core\Set-StrictMode -Version Latest Microsoft.PowerShell.Utility\Import-LocalizedData LocalizedData -FileName AssemblyGenerationHelpers.Resources.psd1 diff --git a/PSSwagger/Definitions.psm1 b/PSSwagger/Definitions.psm1 index 39b7aa7..10ef031 100644 --- a/PSSwagger/Definitions.psm1 +++ b/PSSwagger/Definitions.psm1 @@ -988,26 +988,37 @@ function New-SwaggerDefinitionFormatFile $ViewTypeName = $ViewName $TableColumnItemsList = @() $TableColumnItemCount = 0 - $ParametersCount = Get-HashtableKeyCount -Hashtable $FunctionDetails.ParametersTable - $SkipParameterList = @('id', 'tags') $FunctionDetails.ParametersTable.GetEnumerator() | ForEach-Object { - $ParameterDetails = $_.Value - - # Add all properties when definition has 4 or less properties. - # Otherwise add the first 4 properties with basic types by skipping the complex types, id and tags. - if(($ParametersCount -le 4) -or - (($TableColumnItemCount -le 4) -and - ($SkipParameterList -notcontains $ParameterDetails.Name) -and - (-not $ParameterDetails.Type.StartsWith($Namespace, [System.StringComparison]::OrdinalIgnoreCase)))) + # Add all properties otherthan complex typed properties. + # Complex typed properties are not displayed by the PowerShell Format viewer. + if(-not $ParameterDetails.Type.StartsWith($Namespace, [System.StringComparison]::OrdinalIgnoreCase)) { $TableColumnItemsList += $TableColumnItemStr -f ($ParameterDetails.Name) $TableColumnItemCount += 1 } } - $TableColumnHeaders = $null + if(-not $TableColumnItemCount) { + Write-Verbose -Message ($LocalizedData.FormatFileNotRequired -f $FunctionDetails.Name) + return + } + + $TableColumnHeadersList = @() + $DefaultWindowSizeWidth = 120 + # Getting the width value for each property column. Default console window width is 120. + $TableColumnHeaderWidth = [int]($DefaultWindowSizeWidth/$TableColumnItemCount) + + if ($TableColumnItemCount -ge 2) { + 1..($TableColumnItemCount - 1) | ForEach-Object { + $TableColumnHeadersList += $TableColumnHeaderStr -f ($TableColumnHeaderWidth) + } + } + # Allowing the last property to get the remaining column width, this is useful when customer increases the default window width. + $TableColumnHeadersList += $LastTableColumnHeaderStr + + $TableColumnHeaders = $TableColumnHeadersList -join "`r`n" $TableColumnItems = $TableColumnItemsList -join "`r`n" $FormatViewDefinition = $FormatViewDefinitionStr -f ($ViewName, $ViewTypeName, $TableColumnHeaders, $TableColumnItems, $XmlHeaderComment) diff --git a/PSSwagger/GeneratedHelpers.ps1 b/PSSwagger/GeneratedHelpers.ps1 deleted file mode 100644 index 27cc989..0000000 --- a/PSSwagger/GeneratedHelpers.ps1 +++ /dev/null @@ -1,96 +0,0 @@ -######################################################################################### -# -# Copyright (c) Microsoft Corporation. All rights reserved. -# -# Licensed under the MIT license. -# -######################################################################################### -Microsoft.PowerShell.Core\Set-StrictMode -Version Latest - -<# -.DESCRIPTION - Creates AutoRest ServiceClientCredentials for Microsoft Azure using the logged in AzureRM context. -#> -function Get-AzServiceCredential -{ - [CmdletBinding()] - param() - - $AzureContext = & "Get-AzureRmContext" -ErrorAction Stop - $authenticationFactory = New-Object -TypeName Microsoft.Azure.Commands.Common.Authentication.Factories.AuthenticationFactory - if ((Get-Variable -Name PSEdition -ErrorAction Ignore) -and ('Core' -eq $PSEdition)) { - [Action[string]]$stringAction = {param($s)} - $serviceCredentials = $authenticationFactory.GetServiceClientCredentials($AzureContext, $stringAction) - } else { - $serviceCredentials = $authenticationFactory.GetServiceClientCredentials($AzureContext) - } - - $serviceCredentials -} - -<# -.DESCRIPTION - Creates delegating handlers for Microsoft Azure generated modules. -#> -function Get-AzDelegatingHandler -{ - [CmdletBinding()] - param() - - New-Object -TypeName System.Net.Http.DelegatingHandler[] 0 -} - -<# -.DESCRIPTION - Gets the Azure subscription ID from the logged in AzureRM context. -#> -function Get-AzSubscriptionId -{ - [CmdletBinding()] - param() - - $AzureContext = & "Get-AzureRmContext" -ErrorAction Stop - if($AzureContext) - { - if(Get-Member -InputObject $AzureContext.Subscription -Name SubscriptionId) - { - return $AzureContext.Subscription.SubscriptionId - } - else - { - return $AzureContext.Subscription.Id - } - } -} - -<# -.DESCRIPTION - Gets the resource manager URL from the logged in AzureRM context. -#> -function Get-AzResourceManagerUrl -{ - [CmdletBinding()] - param() - - $AzureContext = & "Get-AzureRmContext" -ErrorAction Stop - $AzureContext.Environment.ResourceManagerUrl -} - -<# -.DESCRIPTION - Creates a System.Net.Http.HttpClientHandler for the given credentials and sets preauthentication to true. -#> -function New-HttpClientHandler { - [CmdletBinding()] - param( - [Parameter(Mandatory=$true)] - [PSCredential] - $Credential - ) - - Add-Type -AssemblyName System.Net.Http - $httpClientHandler = New-Object -TypeName System.Net.Http.HttpClientHandler - $httpClientHandler.PreAuthenticate = $true - $httpClientHandler.Credentials = $Credential - $httpClientHandler -} \ No newline at end of file diff --git a/PSSwagger/New-ArmServiceClient.ps1 b/PSSwagger/New-ArmServiceClient.ps1 new file mode 100644 index 0000000..d21496a --- /dev/null +++ b/PSSwagger/New-ArmServiceClient.ps1 @@ -0,0 +1,49 @@ +Microsoft.PowerShell.Core\Set-StrictMode -Version Latest + +<# +.DESCRIPTION + Creates Service Client object. + +.PARAMETER FullClientTypeName + Client type full name. + +.PARAMETER GlobalParameterHashtable + Global parameters to be set on client object. +#> +function New-ServiceClient { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string] + $FullClientTypeName, + + [Parameter(Mandatory = $false)] + [PSCustomObject] + $GlobalParameterHashtable + ) + + # Azure Powershell way + [Microsoft.Azure.Commands.Common.Authentication.Abstractions.IAzureContext]$Context = Get-AzureRmContext + if (-not $Context -or -not $Context.Account) { + Write-Error -Message 'Run Login-AzureRmAccount to login.' -ErrorId 'AzureRmContextError' + return + } + + $Factory = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.ClientFactory + [System.Type[]]$Types = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.IAzureContext], [string] + $CreateArmClientMethod = [Microsoft.Azure.Commands.Common.Authentication.IClientFactory].GetMethod('CreateArmClient', $Types) + $ClientType = $FullClientTypeName -as [Type] + $ClosedMethod = $CreateArmClientMethod.MakeGenericMethod($ClientType) + $Arguments = $Context, [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureEnvironment+Endpoint]::ResourceManager + $Client = $closedMethod.Invoke($Factory, $Arguments) + + if ($GlobalParameterHashtable) { + $GlobalParameterHashtable.GetEnumerator() | ForEach-Object { + if ($_.Value -and (Get-Member -InputObject $Client -Name $_.Key -MemberType Property)) { + $Client."$($_.Key)" = $_.Value + } + } + } + + return $Client +} \ No newline at end of file diff --git a/PSSwagger/New-ServiceClient.ps1 b/PSSwagger/New-ServiceClient.ps1 index 1b84c90..0bfad60 100644 --- a/PSSwagger/New-ServiceClient.ps1 +++ b/PSSwagger/New-ServiceClient.ps1 @@ -23,9 +23,6 @@ Microsoft.PowerShell.Core\Set-StrictMode -Version Latest Command should return a custom hostname string. Overrides the default host in the specification. -.PARAMETER SubscriptionIdCommand - Custom command get SubscriptionId value. - .PARAMETER GlobalParameterHashtable Global parameters to be set on client object. #> @@ -56,10 +53,6 @@ function New-ServiceClient { [string] $HostOverrideCommand, - [Parameter(Mandatory = $false)] - [string] - $SubscriptionIdCommand, - [Parameter(Mandatory = $false)] [PSCustomObject] $GlobalParameterHashtable @@ -75,7 +68,12 @@ function New-ServiceClient { $ClientArgumentList += Invoke-Command @InvokeCommand_parameters if ($AddHttpClientHandler) { - $httpClientHandler = New-HttpClientHandler -Credential $Credential + if(-not ('System.Net.Http.HttpClientHandler' -as [Type])) { + Add-Type -AssemblyName System.Net.Http + } + $httpClientHandler = New-Object -TypeName System.Net.Http.HttpClientHandler + $httpClientHandler.PreAuthenticate = $true + $httpClientHandler.Credentials = $Credential $ClientArgumentList += $httpClientHandler } @@ -85,23 +83,14 @@ function New-ServiceClient { $Client = New-Object -TypeName $FullClientTypeName -ArgumentList $ClientArgumentList if ($HostOverrideCommand) { - $Client.BaseUri = Invoke-Command -ScriptBlock [scriptblock]::Create($HostOverrideCommand) + [scriptblock]$HostOverrideCommand = [scriptblock]::Create($HostOverrideCommand) + $Client.BaseUri = Invoke-Command -ScriptBlock $HostOverrideCommand } if ($GlobalParameterHashtable) { $GlobalParameterHashtable.GetEnumerator() | ForEach-Object { - if (Get-Member -InputObject $Client -Name $_.Key -MemberType Property) { - if ((-not $_.Value) -and ($_.Key -eq 'SubscriptionId')) { - if($SubscriptionIdCommand) { - $Client.SubscriptionId = Invoke-Command -ScriptBlock [scriptblock]::Create($SubscriptionIdCommand) - } - else { - $Client.SubscriptionId = Get-AzSubscriptionId - } - } - else { - $Client."$($_.Key)" = $_.Value - } + if ($_.Value -and (Get-Member -InputObject $Client -Name $_.Key -MemberType Property)) { + $Client."$($_.Key)" = $_.Value } } } diff --git a/PSSwagger/PSCommandVerbMap.ps1 b/PSSwagger/PSCommandVerbMap.ps1 index ad28acc..aa19d9c 100644 --- a/PSSwagger/PSCommandVerbMap.ps1 +++ b/PSSwagger/PSCommandVerbMap.ps1 @@ -19,13 +19,15 @@ $script:PSCommandVerbMap = @{ Acquire = 'Get' Examine = 'Get' Suggest = 'Get' + Retrieve = 'Get' Create = 'New' Generate = 'New' Allocate = 'New' Provision = 'New' Make = 'New' - + Regenerate = 'New' # Alternatives: Redo, Update, Reset + CreateOrUpdate = 'New,Set' Failover = 'Set' Assign = 'Set' @@ -53,10 +55,10 @@ $script:PSCommandVerbMap = @{ Patch = 'Update' Refresh = 'Update' - Regenerate = 'Update' # Alternatives: Redo, New, Reset Reprocess = "Update" # Alternatives: Redo Upgrade = 'Update' Reimage = 'Update' # Alternatives: Format, Reset + Retarget = 'Update' Validate = 'Test' Check = 'Test' @@ -103,12 +105,14 @@ $script:PSCommandVerbMap = @{ Migrate = 'Move' # Alternatives: Export Transfer = 'Move' Name = 'Move' + Reassociate = 'Move' Change = 'Rename' Swap = 'Switch' # Alternatives: Move Execute = 'Invoke' + Perform = 'Invoke' Discover = 'Find' # Alternatives: Search Locate = 'Find' @@ -140,4 +144,8 @@ $script:PSCommandVerbMap = @{ Jump = 'Skip' Separate = 'Split' + + Notify = 'Send' + + Authorize = 'Grant' } \ No newline at end of file diff --git a/PSSwagger/PSSwagger.Constants.ps1 b/PSSwagger/PSSwagger.Constants.ps1 index 5da4695..fe6be5a 100644 --- a/PSSwagger/PSSwagger.Constants.ps1 +++ b/PSSwagger/PSSwagger.Constants.ps1 @@ -59,7 +59,6 @@ if (Test-Path -Path `$ClrPath -PathType Container) { } . (Join-Path -Path `$PSScriptRoot -ChildPath 'New-ServiceClient.ps1') -. (Join-Path -Path `$PSScriptRoot -ChildPath 'GeneratedHelpers.ps1') `$allPs1FilesPath = Join-Path -Path `$PSScriptRoot -ChildPath '$GeneratedCommandsName' | Join-Path -ChildPath '*.ps1' Get-ChildItem -Path `$allPs1FilesPath -Recurse -File | ForEach-Object { . `$_.FullName} @@ -491,7 +490,8 @@ $FormatViewDefinitionStr = @' {1} - {2} + +{2} @@ -518,6 +518,10 @@ $TableColumnHeaderStr = @' '@ +$LastTableColumnHeaderStr = @' + +'@ + $DefaultGeneratedFileHeader = @' Code generated by Microsoft (R) PSSwagger {0} Changes may cause incorrect behavior and will be lost if the code is regenerated. diff --git a/PSSwagger/PSSwagger.Resources.psd1 b/PSSwagger/PSSwagger.Resources.psd1 index 5b3b8f5..ee0a3cf 100644 --- a/PSSwagger/PSSwagger.Resources.psd1 +++ b/PSSwagger/PSSwagger.Resources.psd1 @@ -43,6 +43,7 @@ ConvertFrom-StringData @' GeneratedPathCommand=Generated path command '{0}'. GeneratedDefinitionCommand=Generated command '{0}' for the definition name '{1}'. GeneratedFormatFile=Generated output format file for the definition name '{0}'. + FormatFileNotRequired=It is not required to generated the format file as this definition '{0}' doesn't have non-complex typed properties. DeleteGeneratedFile=Deleting generated file '{0}' ExtractingMetadata=Extracting metadata from generated assembly ExpectedServiceClientTypeNotFound=Unable to find expected service client type: {0} @@ -87,5 +88,6 @@ ConvertFrom-StringData @' InvalidPSMetaFlattenParameter=Flatten property is specified as 'true' for an invalid parameter '{0}' with type '{1}'. InvalidHeaderFileExtension=Header '{0}' file extension should be '.txt'. InvalidHeaderFilePath=The specified value '{0}' for Header parameter is should be a valid file path. + HeaderContentTwoHyphenWarning=The specified Header content has '--', replacing '--' with '=='. ###PSLOC '@ \ No newline at end of file diff --git a/PSSwagger/PSSwagger.psm1 b/PSSwagger/PSSwagger.psm1 index d0163e4..0595470 100644 --- a/PSSwagger/PSSwagger.psm1 +++ b/PSSwagger/PSSwagger.psm1 @@ -72,6 +72,9 @@ Microsoft.PowerShell.Utility\Import-LocalizedData LocalizedData -filename PSSwa .PARAMETER Version Version of the generated PowerShell module. +.PARAMETER NoVersionFolder + Switch to not create the version folder under the generated module folder. + .PARAMETER DefaultCommandPrefix Prefix value to be prepended to cmdlet noun or to cmdlet name without verb. @@ -163,6 +166,10 @@ function New-PSSwaggerModule [Version] $Version = '0.0.1', + [Parameter(Mandatory = $false)] + [switch] + $NoVersionFolder, + [Parameter(Mandatory = $false)] [string] $DefaultCommandPrefix, @@ -449,7 +456,7 @@ function New-PSSwaggerModule $nameSpace = $swaggerDict['info'].NameSpace $models = $swaggerDict['info'].Models - if($PSVersionTable.PSVersion -lt '5.0.0') { + if($NoVersionFolder -or $PSVersionTable.PSVersion -lt '5.0.0') { if (-not $outputDirectory.EndsWith($Name, [System.StringComparison]::OrdinalIgnoreCase)) { $outputDirectory = Join-Path -Path $outputDirectory -ChildPath $Name $SymbolPath = Join-Path -Path $SymbolPath -ChildPath $Name @@ -611,11 +618,14 @@ function New-PSSwaggerModule -Info $swaggerDict['info'] ` -PSHeaderComment $PSHeaderComment - $CopyFilesMap = [ordered]@{ - 'GeneratedHelpers.ps1' = 'GeneratedHelpers.ps1' - 'Test-CoreRequirements.ps1' = 'Test-CoreRequirements.ps1' - 'Test-FullRequirements.ps1' = 'Test-FullRequirements.ps1' - 'New-ServiceClient.ps1' = 'New-ServiceClient.ps1' + $CopyFilesMap = [ordered]@{} + if($UseAzureCsharpGenerator) { + $CopyFilesMap['New-ArmServiceClient.ps1'] = 'New-ServiceClient.ps1' + $CopyFilesMap['Test-FullRequirements.ps1'] = 'Test-FullRequirements.ps1' + $CopyFilesMap['Test-CoreRequirements.ps1'] = 'Test-CoreRequirements.ps1' + } + else { + $CopyFilesMap['New-ServiceClient.ps1'] = 'New-ServiceClient.ps1' } if (-not $AssemblyFileName) { @@ -1108,6 +1118,14 @@ function Get-HeaderContent { } } + # Escape block comment character sequence, if any, using the PowerShell escape character, grave-accent(`). + $HeaderContent = $HeaderContent.Replace('<#', '<`#').Replace('#>', '#`>') + + if ($HeaderContent -match '--') { + Write-Warning -Message $LocalizedData.HeaderContentTwoHyphenWarning + $HeaderContent = $HeaderContent.Replace('--', '==') + } + return $HeaderContent } diff --git a/PSSwagger/Paths.psm1 b/PSSwagger/Paths.psm1 index a771d0e..435348d 100644 --- a/PSSwagger/Paths.psm1 +++ b/PSSwagger/Paths.psm1 @@ -196,6 +196,25 @@ function Get-SwaggerSpecPathInfo } else { $commandNames = Get-PathCommandName -OperationId $operationId } + + # Priority of a parameterset will be used to determine the default parameterset of a cmdlet. + $Priority = 0 + $ParametersCount = Get-HashtableKeyCount -Hashtable $ParametersTable + if($ParametersCount) { + # Priority for parameter sets with mandatory parameters starts at 100 + $Priority = 100 + + $ParametersTable.GetEnumerator() | ForEach-Object { + if($_.Value.ContainsKey('Mandatory') -and $_.Value.Mandatory -eq '$true') { + $Priority++ + } + } + + # If there are no mandatory parameters, use the parameter count as the priority. + if($Priority -eq 100) { + $Priority = $ParametersCount + } + } $ParameterSetDetail = @{ Description = $FunctionDescription @@ -206,7 +225,7 @@ function Get-SwaggerSpecPathInfo OperationType = $operationType EndpointRelativePath = $EndpointRelativePath PathCommonParameters = $PathCommonParameters - Priority = 100 # Default + Priority = $Priority 'x-ms-pageable' = $x_ms_pageableObject } @@ -228,10 +247,6 @@ function Get-SwaggerSpecPathInfo } } - if ($approximateVerb.StartsWith("List")) { - $ParameterSetDetail.Priority = 0 - } - $commandNames | ForEach-Object { $FunctionDetails = @{} if ($PathFunctionDetails.ContainsKey($_.name)) { @@ -445,6 +460,9 @@ function New-SwaggerPath } elseif (-not $x_ms_pageableObject) { $x_ms_pageableObject = $parameterSetDetail.'x-ms-pageable' $x_ms_pageableObject['ReturnType'] = $parameterSetDetail.ReturnType + if($parameterSetDetail.ContainsKey('PSCmdletOutputItemType')) { + $x_ms_pageableObject['PSCmdletOutputItemType'] = $parameterSetDetail.PSCmdletOutputItemType + } if ($x_ms_pageableObject.Containskey('operationName')) { # Search for the cmdlet with a parameter set with the given operationName $pagingFunctionDetails = $PathFunctionDetails.GetEnumerator() | Where-Object { $_.Value.ParameterSetDetails | Where-Object { $_.OperationId -eq $x_ms_pageableObject.operationName }} | Select-Object -First 1 @@ -561,11 +579,15 @@ function New-SwaggerPath $Cmdlet = '' $CmdletParameter = '' $CmdletArgs = '' - $pageType = '' + $pageType = 'Array' + $PSCmdletOutputItemType = '' $resultBlockStr = $resultBlockNoPaging if ($x_ms_pageableObject) { if ($x_ms_pageableObject.ReturnType -ne 'NONE') { $pageType = $x_ms_pageableObject.ReturnType + if($x_ms_pageableObject.ContainsKey('PSCmdletOutputItemType')) { + $PSCmdletOutputItemType = $x_ms_pageableObject.PSCmdletOutputItemType + } } if ($x_ms_pageableObject.ContainsKey('Operations')) { @@ -616,24 +638,25 @@ function New-SwaggerPath } # Process security section - $SubscriptionIdCommand = "" $AuthenticationCommand = "" $AuthenticationCommandArgumentName = '' $hostOverrideCommand = '' $AddHttpClientHandler = $false $securityParametersToAdd = @() $PowerShellCodeGen = $SwaggerMetaDict['PowerShellCodeGen'] - if (($PowerShellCodeGen['ServiceType'] -eq 'azure') -or ($PowerShellCodeGen['ServiceType'] -eq 'azure_stack')) { - $SubscriptionIdCommand = 'Get-AzSubscriptionId' - } - if ($PowerShellCodeGen['CustomAuthCommand']) { - $AuthenticationCommand = $PowerShellCodeGen['CustomAuthCommand'] - } - if ($PowerShellCodeGen['HostOverrideCommand']) { - $hostOverrideCommand = $PowerShellCodeGen['HostOverrideCommand'] + + # CustomAuthCommand and HostOverrideCommand are not required for Arm Services + if (($PowerShellCodeGen['ServiceType'] -ne 'azure') -and ($PowerShellCodeGen['ServiceType'] -eq 'azure_stack')) { + if ($PowerShellCodeGen['CustomAuthCommand']) { + $AuthenticationCommand = $PowerShellCodeGen['CustomAuthCommand'] + } + if ($PowerShellCodeGen['HostOverrideCommand']) { + $hostOverrideCommand = $PowerShellCodeGen['HostOverrideCommand'] + } } + # If the auth function hasn't been set by metadata, try to discover it from the security and securityDefinition objects in the spec - if (-not $AuthenticationCommand) { + if (-not $AuthenticationCommand -and -not $UseAzureCsharpGenerator) { if ($FunctionDetails.ContainsKey('Security')) { # For now, just take the first security object if ($FunctionDetails.Security.Count -gt 1) { @@ -729,7 +752,7 @@ function New-SwaggerPath } } - if (-not $AuthenticationCommand) { + if (-not $AuthenticationCommand -and -not $UseAzureCsharpGenerator) { # At this point, there was no supported security object or overridden auth function, so assume no auth $AuthenticationCommand = 'Get-AutoRestCredential' } @@ -831,7 +854,7 @@ function New-SwaggerPath if ($nonUniqueParameterSets.Length -gt 1) { # Pick the highest priority set among $nonUniqueParameterSets, but really it doesn't matter, cause... # Print warning that this generated cmdlet has ambiguous parameter sets - $defaultParameterSet = $nonUniqueParameterSets | Sort-Object -Property Priority | Select-Object -First 1 + $defaultParameterSet = $nonUniqueParameterSets | Sort-Object {$_.Priority} | Select-Object -First 1 $DefaultParameterSetName = $defaultParameterSet.OperationId $description = $defaultParameterSet.Description $synopsis = $defaultParameterSet.Synopsis @@ -973,13 +996,18 @@ function New-SwaggerPath ParameterGroupsExpressionBlock = $parameterGroupsExpressionBlock SwaggerDict = $SwaggerDict SwaggerMetaDict = $SwaggerMetaDict - AddHttpClientHandler = $AddHttpClientHandler - HostOverrideCommand = $hostOverrideCommand - AuthenticationCommand = $AuthenticationCommand - AuthenticationCommandArgumentName = $AuthenticationCommandArgumentName - SubscriptionIdCommand = $SubscriptionIdCommand FlattenedParametersOnPSCmdlet = $flattenedParametersOnPSCmdlet } + if($AuthenticationCommand) { + $functionBodyParams['AuthenticationCommand'] = $AuthenticationCommand + $functionBodyParams['AuthenticationCommandArgumentName'] = $AuthenticationCommandArgumentName + } + if($AddHttpClientHandler) { + $functionBodyParams['AddHttpClientHandler'] = $AddHttpClientHandler + } + if($hostOverrideCommand) { + $functionBodyParams['hostOverrideCommand'] = $hostOverrideCommand + } if($globalParameters) { $functionBodyParams['GlobalParameters'] = $globalParameters } @@ -988,8 +1016,8 @@ function New-SwaggerPath $bodyObject = $pathGenerationPhaseResult.BodyObject $body = $bodyObject.Body - if($pageType){ - $fullPathDataType = $pageType + if($PSCmdletOutputItemType){ + $fullPathDataType = $PSCmdletOutputItemType $outputTypeBlock = $executionContext.InvokeCommand.ExpandString($outputTypeStr) } else { @@ -1156,12 +1184,12 @@ function Set-ExtendedCodeMetadata { $returnType = $returnType.GenericTypeArguments[0] } + # Note: ReturnType and PSCmdletOutputItemType are currently used for Swagger operations which supports x-ms-pageable. if (($returnType.Name -eq 'IPage`1') -and $returnType.GenericTypeArguments) { - $returnType = $returnType.GenericTypeArguments[0] + $PSCmdletOutputItemTypeString = Convert-GenericTypeToString -Type $returnType.GenericTypeArguments[0] + $parameterSetDetail['PSCmdletOutputItemType'] = $PSCmdletOutputItemTypeString.Trim('[]') } - # Note: ReturnType is currently used for Swagger operations which supports x-ms-pageable. - $returnTypeString = Convert-GenericTypeToString -Type $returnType - $parameterSetDetail['ReturnType'] = $returnTypeString + $parameterSetDetail['ReturnType'] = Convert-GenericTypeToString -Type $returnType $ParamList = @() $oDataQueryFound = $false @@ -1282,7 +1310,7 @@ function Convert-GenericTypeToString { ) if (-not $Type.IsGenericType) { - return $Type.FullName.Trim('[]') + return $Type.FullName } $genericTypeStr = '' diff --git a/PSSwagger/ServiceTypes/azure.PSMeta.json b/PSSwagger/ServiceTypes/azure.PSMeta.json index c3de351..ffc184b 100644 --- a/PSSwagger/ServiceTypes/azure.PSMeta.json +++ b/PSSwagger/ServiceTypes/azure.PSMeta.json @@ -1,7 +1,5 @@ { "info": { - "x-ps-code-generation-settings": { - "customAuthCommand": "Get-AzServiceCredential" - } + "x-ps-code-generation-settings": {} } } \ No newline at end of file diff --git a/PSSwagger/ServiceTypes/azure_stack.PSMeta.json b/PSSwagger/ServiceTypes/azure_stack.PSMeta.json index 8bcd7a5..ffc184b 100644 --- a/PSSwagger/ServiceTypes/azure_stack.PSMeta.json +++ b/PSSwagger/ServiceTypes/azure_stack.PSMeta.json @@ -1,8 +1,5 @@ { "info": { - "x-ps-code-generation-settings": { - "customAuthCommand": "Get-AzServiceCredential", - "hostOverrideCommand": "Get-AzResourceManagerUrl" - } + "x-ps-code-generation-settings": {} } } \ No newline at end of file diff --git a/PSSwagger/SwaggerUtils.psm1 b/PSSwagger/SwaggerUtils.psm1 index 58f7971..8a0f891 100644 --- a/PSSwagger/SwaggerUtils.psm1 +++ b/PSSwagger/SwaggerUtils.psm1 @@ -1402,10 +1402,16 @@ function Get-PathCommandName # This is still empty when a verb match is found that is the entire string, but it might not be worth checking for that case and skipping the below operation $cmdNounSuffix = $UnapprovedVerb.Substring($beginningOfSuffix) # Add command noun suffix only when the current noun doesn't contain it or vice-versa. - if(-not $cmdNoun -or (($cmdNoun -notmatch $cmdNounSuffix) -and ($cmdNounSuffix -notmatch $cmdNoun))) { - $cmdNoun = $cmdNoun + (Get-PascalCasedString -Name $UnapprovedVerb.Substring($beginningOfSuffix)) - } elseif($cmdNounSuffix -match $cmdNoun) { - $cmdNoun = $cmdNounSuffix + if(-not $cmdNoun) { + $cmdNoun = Get-PascalCasedString -Name $cmdNounSuffix + } + elseif(-not $cmdNounSuffix.StartsWith('By', [System.StringComparison]::OrdinalIgnoreCase)) { + if(($cmdNoun -notmatch $cmdNounSuffix) -and ($cmdNounSuffix -notmatch $cmdNoun)) { + $cmdNoun = $cmdNoun + (Get-PascalCasedString -Name $cmdNounSuffix) + } + elseif($cmdNounSuffix -match $cmdNoun) { + $cmdNoun = $cmdNounSuffix + } } } } @@ -1471,7 +1477,7 @@ function Get-PathFunctionBody [string] $HostOverrideCommand, - [Parameter(Mandatory=$true)] + [Parameter(Mandatory=$false)] [string] $AuthenticationCommand, @@ -1479,10 +1485,6 @@ function Get-PathFunctionBody [string] $AuthenticationCommandArgumentName, - [Parameter(Mandatory=$false)] - [string] - $SubscriptionIdCommand, - [Parameter(Mandatory=$true)] [PSCustomObject] $FlattenedParametersOnPSCmdlet diff --git a/Tests/PSSwagger.Unit.Tests.ps1 b/Tests/PSSwagger.Unit.Tests.ps1 index da4aa69..31e272a 100644 --- a/Tests/PSSwagger.Unit.Tests.ps1 +++ b/Tests/PSSwagger.Unit.Tests.ps1 @@ -266,6 +266,24 @@ Describe "PSSwagger Unit Tests" -Tag @('BVT', 'DRT', 'UnitTest', 'P0') { It "Get-HeaderContent should return MICROSOFT_APACHE_NO_CODEGEN header content with '-Header MICROSOFT_APACHE_NO_CODEGEN'" { Get-HeaderContent -SwaggerDict @{Info = @{Header = 'MICROSOFT_APACHE_NO_CODEGEN'}} | Should BeExactly $MicrosoftApacheLicenseHeader } + + It 'Get-HeaderContent should escape <#' { + Get-HeaderContent -SwaggerDict @{Info = @{Header = 'Header content with <#'}} | Should BeExactly 'Header content with <`#' + } + + It 'Get-HeaderContent should escape #>' { + Get-HeaderContent -SwaggerDict @{Info = @{Header = 'Header content with #>'}} | Should BeExactly 'Header content with #`>' + } + + It 'Get-HeaderContent should replace -- with ==' { + Get-HeaderContent -SwaggerDict @{Info = @{Header = 'Header content with --'}} -WarningVariable wv -WarningAction SilentlyContinue | Should BeExactly 'Header content with ==' + $wv | Should not BeNullOrEmpty + $wv.Message -match '==' | Should Be $true + } + + It "Get-HeaderContent should escape '<#' and '#>', and replace '--' with '=='" { + Get-HeaderContent -SwaggerDict @{Info = @{Header = 'Header content with <# PS comment #> and --.'}} -WarningAction SilentlyContinue | Should BeExactly 'Header content with <`# PS comment #`> and ==.' + } } Context "Get-CSharpModelName Unit Tests" { @@ -287,7 +305,7 @@ Describe "PSSwagger Unit Tests" -Tag @('BVT', 'DRT', 'UnitTest', 'P0') { Convert-GenericTypeToString -Type ('System.Int32' -as [type]) | Should BeExactly 'System.Int32' } It "Convert-GenericTypeToString with 'System.String[]' type" { - Convert-GenericTypeToString -Type ('System.String[]' -as [type]) | Should BeExactly 'System.String' + Convert-GenericTypeToString -Type ('System.String[]' -as [type]) | Should BeExactly 'System.String[]' } It "Convert-GenericTypeToString with 'System.Collections.Generic.List[string]' type" { Convert-GenericTypeToString -Type ('System.Collections.Generic.List[string]' -as [type]) | Should BeExactly 'System.Collections.Generic.List[System.String]' diff --git a/Tests/PSSwaggerScenario.Tests.ps1 b/Tests/PSSwaggerScenario.Tests.ps1 index 4884a56..2c1d29b 100644 --- a/Tests/PSSwaggerScenario.Tests.ps1 +++ b/Tests/PSSwaggerScenario.Tests.ps1 @@ -223,7 +223,7 @@ Describe "Optional parameter tests" -Tag ScenarioTest { } It "Generates cmdlet using optional path parameters" { - $results = Get-CupcakeByMaker -Flavor "chocolate" -Maker "bob" + $results = Get-Cupcake -Flavor "chocolate" -Maker "bob" $results.Length | should be 1 } @@ -528,10 +528,10 @@ Describe "AzureExtensions" -Tag @('AzureExtension','ScenarioTest') { } It "Test x-ms-paths generated cmdlets" { - $results = Get-CupcakeById -Id 1 + $results = Get-Cupcake -Id 1 $results.Count | should be 1 - $results = Get-CupcakeByFlavor -Flavor 'vanilla' + $results = Get-Cupcake -Flavor 'vanilla' $results.Count | should be 1 } @@ -546,6 +546,22 @@ Describe "AzureExtensions" -Tag @('AzureExtension','ScenarioTest') { $cmdInfo = Should BeNullOrEmpty $ev.FullyQualifiedErrorId | Should Be 'CommandNotFoundException,Microsoft.PowerShell.Commands.GetCommandCommand' } + + It "Validate default parameterset name of generated cmdlet" { + $CommandInfo = Get-Command -Name Get-Cupcake + $DefaultParameterSet = 'Cupcake_List' + $ParameterSetNames = @( + $DefaultParameterSet, + 'Cupcake_GetById', + 'Cupcake_GetByFlavor' + ) + $CommandInfo.DefaultParameterSet | Should Be $DefaultParameterSet + $CommandInfo.ParameterSets.Count | Should Be $ParameterSetNames.Count + + $ParameterSetNames | ForEach-Object { + $CommandInfo.ParameterSets.Name -contains $_ | Should Be $true + } + } } AfterAll { @@ -1141,4 +1157,32 @@ Describe "Output type scenario tests" -Tag @('OutputType','ScenarioTest') { $CommandInfo = Get-Command -Name Get-IotHubResourceEventHubConsumerGroup -Module $ModuleName $CommandInfo.OutputType.Type.ToString() | Should BeExactly 'System.String' } -} \ No newline at end of file +} + +Describe 'New-PSSwaggerModule cmdlet parameter tests' -Tag @('CmdletParameterTest','ScenarioTest') { + BeforeAll { + $ModuleName = 'Generated.Module.NoVersionFolder' + $SwaggerSpecPath = Join-Path -Path $PSScriptRoot -ChildPath 'Data' | Join-Path -ChildPath 'AzureExtensions' | Join-Path -ChildPath 'AzureExtensionsSpec.json' + $GeneratedPath = Join-Path -Path $PSScriptRoot -ChildPath 'Generated' + $GeneratedModuleBase = Join-Path -Path $GeneratedPath -ChildPath $ModuleName + if (Test-Path -Path $GeneratedModuleBase -PathType Container) { + Remove-Item -Path $GeneratedModuleBase -Recurse -Force + } + } + + It 'Test NoVersionFolder switch parameter' { + $params = @{ + SpecificationPath = $SwaggerSpecPath + Name = $ModuleName + UseAzureCsharpGenerator = $true + Path = $GeneratedPath + NoVersionFolder = $true + ConfirmBootstrap = $true + Verbose = $true + } + Invoke-NewPSSwaggerModuleCommand -NewPSSwaggerModuleParameters $params + + $ModuleInfo = Import-Module $GeneratedModuleBase -Force -PassThru + $ModuleInfo.ModuleBase | Should Be $GeneratedModuleBase + } +} diff --git a/Tests/TestUtilities.psm1 b/Tests/TestUtilities.psm1 index d9d2781..3fdb292 100644 --- a/Tests/TestUtilities.psm1 +++ b/Tests/TestUtilities.psm1 @@ -78,6 +78,9 @@ function Invoke-NewPSSwaggerModuleCommand { $NewPSSwaggerModuleParameters['NoAssembly'] = $false $NewPSSwaggerModuleParameters['ConfirmBootstrap'] = $true } + elseif (-not $NewPSSwaggerModuleParameters.ContainsKey('AssemblyFileName')) { + $NewPSSwaggerModuleParameters['NoAssembly'] = $true + } if ((Get-Variable -Name PSEdition -ErrorAction Ignore) -and ('Core' -eq $PSEdition)) { if ($IncludeAssembly) { diff --git a/docs/commands/New-PSSwaggerModule.md b/docs/commands/New-PSSwaggerModule.md index a1612f7..87db1f5 100644 --- a/docs/commands/New-PSSwaggerModule.md +++ b/docs/commands/New-PSSwaggerModule.md @@ -14,30 +14,30 @@ PowerShell command to generate the PowerShell commands for a given RESTful Web S ### SpecificationPath (Default) ``` New-PSSwaggerModule -SpecificationPath -Path -Name [-Version ] - [-DefaultCommandPrefix ] [-Header ] [-UseAzureCsharpGenerator] [-NoAssembly] - [-PowerShellCorePath ] [-IncludeCoreFxAssembly] [-InstallToolsForAllUsers] [-TestBuild] + [-NoVersionFolder] [-DefaultCommandPrefix ] [-Header ] [-UseAzureCsharpGenerator] + [-NoAssembly] [-PowerShellCorePath ] [-IncludeCoreFxAssembly] [-InstallToolsForAllUsers] [-TestBuild] [-SymbolPath ] [-ConfirmBootstrap] ``` ### SdkAssemblyWithSpecificationPath ``` New-PSSwaggerModule -SpecificationPath -Path -AssemblyFileName - [-ClientTypeName ] [-ModelsName ] -Name [-Version ] + [-ClientTypeName ] [-ModelsName ] -Name [-Version ] [-NoVersionFolder] [-DefaultCommandPrefix ] [-Header ] [-UseAzureCsharpGenerator] ``` ### SdkAssemblyWithSpecificationUri ``` New-PSSwaggerModule -SpecificationUri -Path -AssemblyFileName - [-ClientTypeName ] [-ModelsName ] -Name [-Version ] + [-ClientTypeName ] [-ModelsName ] -Name [-Version ] [-NoVersionFolder] [-DefaultCommandPrefix ] [-Header ] [-UseAzureCsharpGenerator] ``` ### SpecificationUri ``` New-PSSwaggerModule -SpecificationUri -Path -Name [-Version ] - [-DefaultCommandPrefix ] [-Header ] [-UseAzureCsharpGenerator] [-NoAssembly] - [-PowerShellCorePath ] [-IncludeCoreFxAssembly] [-InstallToolsForAllUsers] [-TestBuild] + [-NoVersionFolder] [-DefaultCommandPrefix ] [-Header ] [-UseAzureCsharpGenerator] + [-NoAssembly] [-PowerShellCorePath ] [-IncludeCoreFxAssembly] [-InstallToolsForAllUsers] [-TestBuild] [-SymbolPath ] [-ConfirmBootstrap] ``` @@ -191,6 +191,21 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -NoVersionFolder +Switch to not create the version folder under the generated module folder. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: False +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -DefaultCommandPrefix Prefix value to be prepended to cmdlet noun or to cmdlet name without verb.