From 63d50d7a1763211b5a0693dcc748e6ebb09b196e Mon Sep 17 00:00:00 2001 From: Michel Perfetti Date: Tue, 6 Jun 2017 09:28:05 +0200 Subject: [PATCH 1/8] Add Sonarqube endpoint --- Team.psd1 | 1 + src/serviceendpoints.psm1 | 55 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/Team.psd1 b/Team.psd1 index 5f7157907..31b9c4232 100644 --- a/Team.psd1 +++ b/Team.psd1 @@ -82,6 +82,7 @@ # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. FunctionsToExport = @('Add-AzureRMServiceEndpoint', + 'Add-SonarQubeEndpoint', 'Add-Build', 'Add-BuildDefinition', 'Add-Project', diff --git a/src/serviceendpoints.psm1 b/src/serviceendpoints.psm1 index 2ef915e6e..36188b476 100644 --- a/src/serviceendpoints.psm1 +++ b/src/serviceendpoints.psm1 @@ -133,6 +133,59 @@ function Remove-ServiceEndpoint { } } +function Add-SonarQubeEndpoint { + [CmdletBinding()] + param( + [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)] + [string] $endpointName, + [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)] + [string] $url, + [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)] + [string] $token + ) + + DynamicParam { + _buildProjectNameDynamicParam + } + + Process { + # Bind the parameter to a friendly variable + $ProjectName = $PSBoundParameters["ProjectName"] + + # Build the url + $url = _buildURL -projectName $projectName + + $obj = @{ + authorization=@{ + parameters=@{ + username=$token; + password='' + }; + scheme='UsernamePassword' + }; + data=@{ + }; + name=$endpointName; + type='sonarqube'; + url=$url + } + + $body = $obj | ConvertTo-Json + + # Call the REST API + if (_useWindowsAuthenticationOnPremise) { + $resp = Invoke-RestMethod -UserAgent (_getUserAgent) -Method Post -Body $body -ContentType "application/json" -Uri $url -UseDefaultCredentials + } else { + $resp = Invoke-RestMethod -UserAgent (_getUserAgent) -Method Post -Body $body -ContentType "application/json" -Uri $url -Headers @{Authorization = "Basic $env:TEAM_PAT"} + } + + _trackProgress -projectName $projectName -resp $resp -title 'Creating Service Endpoint' -msg "Creating $endpointName" + + return Get-ServiceEndpoint -projectName $projectName -id $resp.id + } +} + + function Add-AzureRMServiceEndpoint { [CmdletBinding()] param( @@ -254,4 +307,4 @@ function Get-ServiceEndpoint { } } -Export-ModuleMember -Alias * -Function Get-ServiceEndpoint, Add-AzureRMServiceEndpoint, Remove-ServiceEndpoint \ No newline at end of file +Export-ModuleMember -Alias * -Function Get-ServiceEndpoint, Add-AzureRMServiceEndpoint, Remove-ServiceEndpoint, Add-SonarQubeEndpoint \ No newline at end of file From 656ca0cd93e29a67a27a4ceafac7b40bfe0dfb5c Mon Sep 17 00:00:00 2001 From: Michel Perfetti Date: Tue, 6 Jun 2017 15:51:57 +0200 Subject: [PATCH 2/8] Fix parameter override --- src/serviceendpoints.psm1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/serviceendpoints.psm1 b/src/serviceendpoints.psm1 index 36188b476..b925441ae 100644 --- a/src/serviceendpoints.psm1 +++ b/src/serviceendpoints.psm1 @@ -139,7 +139,7 @@ function Add-SonarQubeEndpoint { [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)] [string] $endpointName, [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)] - [string] $url, + [string] $sonarqubeUrl, [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)] [string] $token ) @@ -167,7 +167,7 @@ function Add-SonarQubeEndpoint { }; name=$endpointName; type='sonarqube'; - url=$url + url=$sonarqubeUrl } $body = $obj | ConvertTo-Json From 19f3d4cf048c77d47623d57c37cbf86824dd9d4f Mon Sep 17 00:00:00 2001 From: Michel Perfetti Date: Tue, 6 Jun 2017 22:15:07 +0200 Subject: [PATCH 3/8] Help for Add-SonarqubeEndpoint --- en-US/Team-Help.xml | 151 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) diff --git a/en-US/Team-Help.xml b/en-US/Team-Help.xml index 3ec7c9fa7..543769c40 100644 --- a/en-US/Team-Help.xml +++ b/en-US/Team-Help.xml @@ -365,6 +365,157 @@ you do not have to pass the ProjectName with each call. + + + + Add-SonarQubeEndpoint + + + + + + + Add + SonarQubeEndpoint + + + + + + + + + Add-SonarQubeEndpoint + + + ProjectName + + + Specifies the team project for which this function operates. + + You can tab complete from a list of available projects. + + You can use Set-DefaultProject to set a default project so + you do not have to pass the ProjectName with each call. + + + String + + + + Token + + + Access token + + + String + + + SonarqubeUrl + + + URL of the sonarqube server + + + String + + + EndpointName + + + + String + + + + + + + SonarQubeUrl + + + + String + + String + + + + + + Token + + + + String + + String + + + + + + + EndpointName + + + + String + + String + + + + + + + ProjectName + + + Specifies the team project for which this function operates. + + You can tab complete from a list of available projects. + + You can use Set-DefaultProject to set a default project so + you do not have to pass the ProjectName with each call. + + + String + + String + + + + + + + + + + + System.String + + + + + + + + + + + + + System.Object + + + + + + + + From 0cbd6b16f7a8080ed8f6d0dee36bd03d40d5b2da Mon Sep 17 00:00:00 2001 From: Michel Perfetti Date: Fri, 16 Jun 2017 10:25:55 +0200 Subject: [PATCH 4/8] Add test and update the help file (formatting) --- en-US/Team-Help.xml | 261 ++++++++++++++++---------------- test/serviceendpoints.Tests.ps1 | 31 ++++ 2 files changed, 161 insertions(+), 131 deletions(-) diff --git a/en-US/Team-Help.xml b/en-US/Team-Help.xml index 543769c40..7a68839dc 100644 --- a/en-US/Team-Help.xml +++ b/en-US/Team-Help.xml @@ -365,32 +365,32 @@ you do not have to pass the ProjectName with each call. - - - - Add-SonarQubeEndpoint - - - - - - - Add - SonarQubeEndpoint - - - - - - - - - Add-SonarQubeEndpoint - - - ProjectName - - + + + + Add-SonarQubeEndpoint + + + + + + + Add + SonarQubeEndpoint + + + + + + + + + Add-SonarQubeEndpoint + + + ProjectName + + Specifies the team project for which this function operates. You can tab complete from a list of available projects. @@ -398,81 +398,80 @@ you do not have to pass the ProjectName with each call. You can use Set-DefaultProject to set a default project so you do not have to pass the ProjectName with each call. - - String - - - - Token - - + + String + + + + Token + + Access token - - String - - - SonarqubeUrl - - + + String + + + SonarqubeUrl + + URL of the sonarqube server - - String - - - EndpointName - - - - String - - - - - - - SonarQubeUrl - - - - String - - String - - - - - - Token - - - - String - - String - - - - - - - EndpointName - - - - String - - String - - - - - - - ProjectName - - + + String + + + EndpointName + + + + String + + + + + + + SonarQubeUrl + + + + String + + String + + + + + + Token + + + + String + + String + + + + + + EndpointName + + + + String + + String + + + + + + + ProjectName + + Specifies the team project for which this function operates. You can tab complete from a list of available projects. @@ -480,42 +479,42 @@ you do not have to pass the ProjectName with each call. You can use Set-DefaultProject to set a default project so you do not have to pass the ProjectName with each call. - - String - - String - - - - - - - - - - + + String + + String + + + + + + + + + + System.String - - - - - - - - - - - - System.Object - - - - - - - - + + + + + + + + + + + + System.Object + + + + + + + + diff --git a/test/serviceendpoints.Tests.ps1 b/test/serviceendpoints.Tests.ps1 index 1fa615944..2c704c5e8 100644 --- a/test/serviceendpoints.Tests.ps1 +++ b/test/serviceendpoints.Tests.ps1 @@ -71,5 +71,36 @@ InModuleScope serviceendpoints { Assert-MockCalled Invoke-RestMethod -Exactly -Scope It -Times 1 -ParameterFilter { $Method -eq 'Post' } } } + + Context 'Add-SonarQubeEndpoint' { + Mock Write-Progress + Mock Invoke-RestMethod { $returnObject = $false + return @{id = '23233-2342'} } -ParameterFilter { $Method -eq 'Post'} + Mock Invoke-RestMethod { + + # This $i is in the module. Because we use InModuleScope + # we can see it + if ($i -gt 9) { + return @{ + isReady = $true + operationStatus = @{state = 'Ready'} + } + } + + return @{ + isReady = $false + createdBy = @{} + authorization = @{} + data = @{} + operationStatus = @{state = 'InProgress'} + } + } + + It 'should create a new SonarQube Serviceendpoint' { + Add-SonarQubeEndpoint -projectName 'project' -endpointName 'PM_DonovanBrown' -sonarqubeUrl 'http://mysonarserver.local' -token '72f988bf-86f1-41af-91ab-2d7cd011db47' + + Assert-MockCalled Invoke-RestMethod -Exactly -Scope It -Times 1 -ParameterFilter { $Method -eq 'Post' } + } + } } } \ No newline at end of file From c56ceba88ab0f34c2880cf9f1d4ef6acdcca404b Mon Sep 17 00:00:00 2001 From: Donovan Brown Date: Fri, 16 Jun 2017 21:26:37 +0200 Subject: [PATCH 5/8] Adding some polish to the help. --- en-US/Team-Help.xml | 78 ++++++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 43 deletions(-) diff --git a/en-US/Team-Help.xml b/en-US/Team-Help.xml index 7a68839dc..71ed62cad 100644 --- a/en-US/Team-Help.xml +++ b/en-US/Team-Help.xml @@ -1,6 +1,6 @@  - + @@ -210,7 +210,7 @@ Remove-BuildDefinition Add-AzureRMServiceEndpoint - + Adds a new Azure Resource Manager service endpoint. @@ -220,7 +220,7 @@ Remove-BuildDefinition - + The cmdlet adds a new connection between TFS/VSTS and Azure using the Azure Resource Manager connection type. @@ -264,9 +264,10 @@ you do not have to pass the ProjectName with each call. EndpointName - + The name displayed on the services page. In VSTS this is the Connection Name. String + @@ -311,14 +312,14 @@ you do not have to pass the ProjectName with each call. EndpointName - + The name displayed on the services page. In VSTS this is the Connection Name. String String - + @@ -370,7 +371,7 @@ you do not have to pass the ProjectName with each call. Add-SonarQubeEndpoint - + Adds a new SonarQube service endpoint. @@ -380,7 +381,7 @@ you do not have to pass the ProjectName with each call. - + The cmdlet adds a new connection between TFS/VSTS and a SonarQube server using the SonarQube connection type. This is only used when using the SonarQube tasks. Using SonarQube with the Maven tasks uses a Generic Connection type. @@ -390,102 +391,93 @@ you do not have to pass the ProjectName with each call. ProjectName - - Specifies the team project for which this function operates. - - You can tab complete from a list of available projects. - - You can use Set-DefaultProject to set a default project so - you do not have to pass the ProjectName with each call. - + Specifies the team project for which this function operates. + You can tab complete from a list of available projects. + You can use Set-DefaultProject to set a default project so +you do not have to pass the ProjectName with each call. String - + Token - - Access token - + Authentication Token generated by SonarQube. String + - SonarqubeUrl + SonarQubeUrl - - URL of the sonarqube server - + URL of the sonarqube server. String + EndpointName - + The name displayed on the services page. In VSTS this is the Connection Name. String + - + SonarQubeUrl - + URL of the sonarqube server. String String - + - + Token - + Authentication Token generated by SonarQube. String String - + EndpointName - + The name displayed on the services page. In VSTS this is the Connection Name. String String - + ProjectName - - Specifies the team project for which this function operates. - - You can tab complete from a list of available projects. - - You can use Set-DefaultProject to set a default project so - you do not have to pass the ProjectName with each call. - + Specifies the team project for which this function operates. + You can tab complete from a list of available projects. + You can use Set-DefaultProject to set a default project so +you do not have to pass the ProjectName with each call. String String - + @@ -5706,5 +5698,5 @@ to pass the ProjectName with each call. - + \ No newline at end of file From e4f6a267784fb5d2223f54c97843aa8acbca91e7 Mon Sep 17 00:00:00 2001 From: Michel Perfetti Date: Mon, 26 Jun 2017 10:15:42 +0200 Subject: [PATCH 6/8] Add check on sonarqube extension installation and securestring handling --- src/serviceendpoints.psm1 | 118 +++++++++++++++++++------------- test/serviceendpoints.Tests.ps1 | 2 +- 2 files changed, 73 insertions(+), 47 deletions(-) diff --git a/src/serviceendpoints.psm1 b/src/serviceendpoints.psm1 index b925441ae..ce519a91d 100644 --- a/src/serviceendpoints.psm1 +++ b/src/serviceendpoints.psm1 @@ -134,56 +134,82 @@ function Remove-ServiceEndpoint { } function Add-SonarQubeEndpoint { - [CmdletBinding()] - param( - [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)] - [string] $endpointName, - [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)] - [string] $sonarqubeUrl, - [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)] - [string] $token - ) - - DynamicParam { - _buildProjectNameDynamicParam - } - - Process { - # Bind the parameter to a friendly variable - $ProjectName = $PSBoundParameters["ProjectName"] - - # Build the url - $url = _buildURL -projectName $projectName - - $obj = @{ - authorization=@{ - parameters=@{ - username=$token; - password='' + [CmdletBinding()] + param( + [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] + [string] $endpointName, + [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] + [string] $sonarqubeUrl, + [parameter(ParameterSetName = 'Plain', Mandatory = $true, Position = 2, HelpMessage = 'Personal Access Token')] + [string] $personalAccessToken, + [parameter(ParameterSetName = 'Secure', Mandatory = $true, HelpMessage = 'Personal Access Token')] + [securestring] $securePersonalAccessToken + ) + + DynamicParam { + _buildProjectNameDynamicParam + } + + Process { + + + if ($personalAccessToken) { + $token = $personalAccessToken + } + else { + $credential = New-Object System.Management.Automation.PSCredential "nologin", $securePersonalAccessToken + $token = $credential.GetNetworkCredential().Password + } + # Bind the parameter to a friendly variable + $ProjectName = $PSBoundParameters["ProjectName"] + + # Build the url + $url = _buildURL -projectName $projectName + + $obj = @{ + authorization = @{ + parameters = @{ + username = $token; + password = '' + }; + scheme = 'UsernamePassword' }; - scheme='UsernamePassword' - }; - data=@{ - }; - name=$endpointName; - type='sonarqube'; - url=$sonarqubeUrl - } + data = @{ + }; + name = $endpointName; + type = 'sonarqube'; + url = $sonarqubeUrl + } - $body = $obj | ConvertTo-Json + $body = $obj | ConvertTo-Json - # Call the REST API - if (_useWindowsAuthenticationOnPremise) { - $resp = Invoke-RestMethod -UserAgent (_getUserAgent) -Method Post -Body $body -ContentType "application/json" -Uri $url -UseDefaultCredentials - } else { - $resp = Invoke-RestMethod -UserAgent (_getUserAgent) -Method Post -Body $body -ContentType "application/json" -Uri $url -Headers @{Authorization = "Basic $env:TEAM_PAT"} - } - - _trackProgress -projectName $projectName -resp $resp -title 'Creating Service Endpoint' -msg "Creating $endpointName" + try { + + # Call the REST API + if (_useWindowsAuthenticationOnPremise) { + $resp = Invoke-RestMethod -UserAgent (_getUserAgent) -Method Post -Body $body -ContentType "application/json" -Uri $url -UseDefaultCredentials + } + else { + $resp = Invoke-RestMethod -UserAgent (_getUserAgent) -Method Post -Body $body -ContentType "application/json" -Uri $url -Headers @{Authorization = "Basic $env:TEAM_PAT"} + } + + } + catch [System.Net.WebException] { + if ($_.Exception.status -eq "ProtocolError") { + $errorDetails = ConvertFrom-Json $_.ErrorDetails + [string] $message = $errorDetails.message + if ($message.StartsWith("Endpoint type couldn't be recognized 'sonarqube'")) { + Write-Error -Message "The Sonarqube extension not installed" + return + } + } + throw + } + _trackProgress -projectName $projectName -resp $resp -title 'Creating Service Endpoint' -msg "Creating $endpointName" - return Get-ServiceEndpoint -projectName $projectName -id $resp.id - } -} + return Get-ServiceEndpoint -projectName $projectName -id $resp.id + } + } function Add-AzureRMServiceEndpoint { diff --git a/test/serviceendpoints.Tests.ps1 b/test/serviceendpoints.Tests.ps1 index 2c704c5e8..e71218635 100644 --- a/test/serviceendpoints.Tests.ps1 +++ b/test/serviceendpoints.Tests.ps1 @@ -97,7 +97,7 @@ InModuleScope serviceendpoints { } It 'should create a new SonarQube Serviceendpoint' { - Add-SonarQubeEndpoint -projectName 'project' -endpointName 'PM_DonovanBrown' -sonarqubeUrl 'http://mysonarserver.local' -token '72f988bf-86f1-41af-91ab-2d7cd011db47' + Add-SonarQubeEndpoint -projectName 'project' -endpointName 'PM_DonovanBrown' -sonarqubeUrl 'http://mysonarserver.local' -personalAccessToken '72f988bf-86f1-41af-91ab-2d7cd011db47' Assert-MockCalled Invoke-RestMethod -Exactly -Scope It -Times 1 -ParameterFilter { $Method -eq 'Post' } } From 1c73ad1a2b8d0a02957ad7f2db845a42bf855c36 Mon Sep 17 00:00:00 2001 From: Donovan Brown Date: Mon, 10 Jul 2017 16:48:01 -0400 Subject: [PATCH 7/8] Added DefaultParameterSetName Without a Default set I was getting 'Parameter set cannot be resolved using the specified named parameters.' Added url of extension to error message This way people would know where to download the requirements. Changed parameter name in Add-TeamAccount to match yours. Your name choice made it much clearer which to use on the command line. --- src/serviceendpoints.psm1 | 4 ++-- src/team.psm1 | 13 +++++-------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/serviceendpoints.psm1 b/src/serviceendpoints.psm1 index ce519a91d..6c44058af 100644 --- a/src/serviceendpoints.psm1 +++ b/src/serviceendpoints.psm1 @@ -134,7 +134,7 @@ function Remove-ServiceEndpoint { } function Add-SonarQubeEndpoint { - [CmdletBinding()] + [CmdletBinding(DefaultParameterSetName = 'Secure')] param( [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [string] $endpointName, @@ -199,7 +199,7 @@ function Add-SonarQubeEndpoint { $errorDetails = ConvertFrom-Json $_.ErrorDetails [string] $message = $errorDetails.message if ($message.StartsWith("Endpoint type couldn't be recognized 'sonarqube'")) { - Write-Error -Message "The Sonarqube extension not installed" + Write-Error -Message "The Sonarqube extension not installed. Please install from https://marketplace.visualstudio.com/items?itemName=SonarSource.sonarqube" return } } diff --git a/src/team.psm1 b/src/team.psm1 index b4be3a26e..4f0fc6958 100644 --- a/src/team.psm1 +++ b/src/team.psm1 @@ -62,7 +62,7 @@ function Add-TeamAccount { [parameter(ParameterSetName='Plain', Mandatory=$true, Position=2, HelpMessage='Personal Access Token')] [string] $PersonalAccessToken, [parameter(ParameterSetName='Secure', Mandatory=$true, HelpMessage='Personal Access Token')] - [securestring] $PAT + [securestring] $SecurePersonalAccessToken ) DynamicParam { @@ -133,26 +133,23 @@ function Add-TeamAccount { } $UsingWindowsAuth = $PSBoundParameters[$ParameterName2] - if (!($PAT) -and !($PersonalAccessToken) -and !($UsingWindowsAuth)) { + if (!($SecurePersonalAccessToken) -and !($PersonalAccessToken) -and !($UsingWindowsAuth)) { Write-Error "Personal Access Token must be provided if you are not using Windows Authentication; please see the help." } } else { $Level = "Process" } - - if ($PAT) { + if ($SecurePersonalAccessToken) { # Convert the securestring to a normal string # this was the one technique that worked on Mac, Linux and Windows - $credential = New-Object System.Management.Automation.PSCredential $account,$PAT + $credential = New-Object System.Management.Automation.PSCredential $account,$SecurePersonalAccessToken $_pat = $credential.GetNetworkCredential().Password } else { $_pat = $PersonalAccessToken } - - # If they only gave an account name add visualstudio.com if($Account.ToLower().Contains('http') -eq $false) { $Account = "https://$($Account).visualstudio.com" @@ -160,7 +157,7 @@ function Add-TeamAccount { $encodedPat = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes(":$_pat")) - # If no PAT is entered, and on windows, are we using default credentials for REST calls + # If no SecurePersonalAccessToken is entered, and on windows, are we using default credentials for REST calls if ((!$_pat) -and (_isOnWindows) -and ($UsingWindowsAuth)) { Write-Verbose "Using Default Windows Credentials for authentication; no Personal Access Token required" $encodedPat = "" From 1f87f769a8607a54dab1d0178a9859eba29c78e2 Mon Sep 17 00:00:00 2001 From: Donovan Brown Date: Mon, 10 Jul 2017 16:52:55 -0400 Subject: [PATCH 8/8] Updating version to 0.1.23 --- Team.psd1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Team.psd1 b/Team.psd1 index 31b9c4232..56bf8e6ce 100644 --- a/Team.psd1 +++ b/Team.psd1 @@ -13,7 +13,7 @@ RootModule = '' # Version number of this module. - ModuleVersion = '0.1.22' + ModuleVersion = '0.1.23' # Supported PSEditions # CompatiblePSEditions = @()