diff --git a/CHANGELOG.md b/CHANGELOG.md index 852a720b..d3487cd1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # Change Log +## 2.7 - 2018-05-13 + +More detailed description about the changes can be found on [Our Website](https://atlassianps.org/article/announcement/JiraPS-v2.7.html). + +### IMPROVEMENTS + +- Writing and throwing of errors show better context (#199, [@lipkau][]) +- Improved validation of parameters in `Add-JiraGroupMember` (#250, [@WindowsAdmin92][]) +- Improved casting to `-Fields` by defining it's type as `[PSCustomObject]` (#255, [@lipkau][]) +- Several improvements to the CI pipeline (#252, #257, [@lipkau][]) + +### BUG FIXES + +- Build script was not publishing to the PSGallery (#252, [@lipkau][]) +- Build script was publishing a new tag to repository even in case the build failed (#252, [@lipkau][]) +- Fixed the adding multiple labels and the removal of those in `Set-JiraIssueLabel` (#244, [@lipkau][]) +- Fixed CI icon in README (#245, [@lipkau][]) +- Allow `Get-JiraUser` to return more than 1 result (#246, [@lipkau][]) + ## 2.6 - 2018-05-02 More detailed description about the changes can be found on [Our Website](https://atlassianps.org/article/announcement/JiraPS-v2.6.html). @@ -246,3 +265,4 @@ which is in turn inspired by the [Vagrant](https://github.com/mitchellh/vagrant/ [@lukhase]: https://github.com/lukhase [@padgers]: https://github.com/padgers [@ThePSAdmin]: https://github.com/ThePSAdmin + [@WindowsAdmin92]: https://github.com/WindowsAdmin92 diff --git a/JiraPS.build.ps1 b/JiraPS.build.ps1 index 6eda0bc4..fbc44ac3 100644 --- a/JiraPS.build.ps1 +++ b/JiraPS.build.ps1 @@ -201,6 +201,9 @@ task Test Init, { OutputFormat = "NUnitXml" CodeCoverage = $codeCoverageFiles } + if ($env:APPVEYOR_PULL_REQUEST_NUMBER) { + $parameter["Show"] = "Fails" + } $testResults = Invoke-Pester @parameter If ('AppVeyor' -eq $env:BHBuildSystem) { @@ -217,7 +220,7 @@ task Test Init, { #region Publish # Synopsis: Publish a new release on github and the PSGallery -task Deploy -If { Test-ShouldDeploy } Init, PublishToGallery, TagReplository +task Deploy -If { Test-ShouldDeploy } Init, PublishToGallery, TagReplository, UpdateHomepage # Synipsis: Publish the $release to the PSGallery task PublishToGallery { @@ -249,6 +252,37 @@ task TagReplository GetNextVersion, { $uploadURI = $releaseResponse.upload_url -replace "\{\?name,label\}", "?name=$($packageFile.Name)" $null = Publish-GithubReleaseArtifact -Uri $uploadURI -Path $packageFile } + +# Synopsis: Update the version of this module that the homepage uses +task UpdateHomepage { + try { + Write-Build Gray "git close .../AtlassianPS.github.io --recursive" + cmd /c "git clone https://github.com/AtlassianPS/AtlassianPS.github.io --recursive 2>&1" + + Push-Location "AtlassianPS.github.io/" + + Write-Build Gray "git submodule foreach git pull origin master" + cmd /c "git submodule foreach git pull origin master 2>&1" + + Write-Build Gray "git status -s" + $status = cmd /c "git status -s 2>&1" + + Write-Build Gray $status + if ($status -contains " M modules/$env:BHProjectName") { + Write-Build Gray "git add modules/$env:BHProjectName" + cmd /c "git add modules/$env:BHProjectName 2>&1" + + Write-Build Gray "git commit -m `"Update module $PROJECT_NAME`"" + cmd /c "git commit -m `"Update module $PROJECT_NAME`" 2>&1" + + Write-Build Gray "git push" + cmd /c "git push 2>&1" + } + + Pop-Location + } + catch {} +} #endregion Publish #region Cleaning tasks diff --git a/JiraPS/JiraPS.psd1 b/JiraPS/JiraPS.psd1 index fd3a5af3..9959680a 100644 --- a/JiraPS/JiraPS.psd1 +++ b/JiraPS/JiraPS.psd1 @@ -4,7 +4,7 @@ RootModule = 'JiraPS.psm1' # Version number of this module. - ModuleVersion = '2.6' + ModuleVersion = '2.7' # Supported PSEditions # CompatiblePSEditions = @() diff --git a/JiraPS/Private/Resolve-JiraError.ps1 b/JiraPS/Private/Resolve-JiraError.ps1 index 282b9fc2..a13111b3 100644 --- a/JiraPS/Private/Resolve-JiraError.ps1 +++ b/JiraPS/Private/Resolve-JiraError.ps1 @@ -9,7 +9,9 @@ [Switch] $WriteError, - $Caller = $PSCmdlet + [ValidateNotNullOrEmpty()] + [System.Management.Automation.PSCmdlet] + $Cmdlet = $PSCmdlet ) process { @@ -26,7 +28,7 @@ $i ) $errorItem.ErrorDetails = "Jira encountered an error: [$e]" - $Caller.WriteError($errorItem) + $Cmdlet.WriteError($errorItem) } else { $obj = [PSCustomObject] @{ @@ -53,7 +55,7 @@ $i ) $errorItem.ErrorDetails = "Jira encountered an error: [$k] - $($i.errors.$k)" - $Caller.WriteError($errorItem) + $Cmdlet.WriteError($errorItem) } else { $obj = [PSCustomObject] @{ diff --git a/JiraPS/Private/Test-Captcha.ps1 b/JiraPS/Private/Test-Captcha.ps1 deleted file mode 100644 index 71382e5e..00000000 --- a/JiraPS/Private/Test-Captcha.ps1 +++ /dev/null @@ -1,34 +0,0 @@ -function Test-Captcha { - [CmdletBinding()] - param( - # Response of Invoke-WebRequest - [Parameter( ValueFromPipeline )] - [PSObject]$InputObject, - - $Caller = $PSCmdlet - ) - - begin { - $tokenRequiresCaptcha = "AUTHENTICATION_DENIED" - $LoginReason = "X-Seraph-LoginReason" - } - - process { - Write-Verbose "[$($MyInvocation.MyCommand.Name)] Checking response headers for authentication errors" - Write-DebugMessage "[$($MyInvocation.MyCommand.Name)] Investigating `$InputObject.Headers['$LoginReason']" - - if ($InputObject.Headers -and $InputObject.Headers[$LoginReason]) { - if ( ($InputObject.Headers[$LoginReason] -split ",") -contains $tokenRequiresCaptcha ) { - $errorMessage = @{ - Category = "AuthenticationError" - CategoryActivity = "Authentication" - Message = "JIRA requires you to log on to the website before continuing for security reasons." - } - $Caller.WriteError($errorMessage) - } - } - } - - end { - } -} diff --git a/JiraPS/Private/Test-ServerResponse.ps1 b/JiraPS/Private/Test-ServerResponse.ps1 new file mode 100644 index 00000000..177618a7 --- /dev/null +++ b/JiraPS/Private/Test-ServerResponse.ps1 @@ -0,0 +1,69 @@ +function Test-ServerResponse { + [CmdletBinding()] + <# + .SYNOPSIS + Evauluate the response of the API call + .LINK + https://docs.atlassian.com/software/jira/docs/api/7.6.1/com/atlassian/jira/bc/security/login/LoginReason.html + #> + param ( + # Response of Invoke-WebRequest + [Parameter( ValueFromPipeline )] + [PSObject]$InputObject, + + [ValidateNotNullOrEmpty()] + [System.Management.Automation.PSCmdlet] + $Cmdlet = $PSCmdlet + ) + + begin { + $loginReasonKey = "X-Seraph-LoginReason" + } + + process { + Write-Verbose "[$($MyInvocation.MyCommand.Name)] Checking response headers for authentication errors" + Write-DebugMessage "[$($MyInvocation.MyCommand.Name)] Investigating `$InputObject.Headers['$loginReasonKey']" + + if ($InputObject.Headers -and $InputObject.Headers[$loginReasonKey]) { + $loginReason = $InputObject.Headers[$loginReasonKey] -split "," + + switch ($true) { + {$loginReason -contains "AUTHENTICATED_FAILED"} { + $errorParameter = @{ + ExceptionType = "System.Net.Http.HttpRequestException" + Message = "The user could not be authenticated." + ErrorId = "AuthenticationFailed" + Category = "AuthenticationError" + Cmdlet = $Cmdlet + } + ThrowError @errorParameter + } + {$loginReason -contains "AUTHENTICATION_DENIED"} { + $errorParameter = @{ + ExceptionType = "System.Net.Http.HttpRequestException" + Message = "For security reasons Jira requires you to log on to the website before continuing." + ErrorId = "AuthenticaionDenied" + Category = "AuthenticationError" + Cmdlet = $Cmdlet + } + ThrowError @errorParameter + } + {$loginReason -contains "AUTHORISATION_FAILED"} { + $errorParameter = @{ + ExceptionType = "System.Net.Http.HttpRequestException" + Message = "The user could not be authorised." + ErrorId = "AuthorisationFailed" + Category = "AuthenticationError" + Cmdlet = $Cmdlet + } + ThrowError @errorParameter + } + {$loginReason -contains "OK"} {} # The login was OK + {$loginReason -contains "OUT"} {} # This indicates that person has in fact logged "out" + } + } + } + + end { + } +} diff --git a/JiraPS/Private/ThrowError.ps1 b/JiraPS/Private/ThrowError.ps1 new file mode 100644 index 00000000..621c4f61 --- /dev/null +++ b/JiraPS/Private/ThrowError.ps1 @@ -0,0 +1,65 @@ +function ThrowError { + <# + .SYNOPSIS + Utility to throw a terminating errorrecord + .NOTES + Thanks to Jaykul: + https://github.com/PoshCode/Configuration/blob/master/Source/Metadata.psm1 + #> + param + ( + [Parameter()] + [ValidateNotNullOrEmpty()] + [System.Management.Automation.PSCmdlet] + $Cmdlet = $((Get-Variable -Scope 1 PSCmdlet).Value), + + [Parameter(Mandatory = $true, ParameterSetName = "ExistingException", Position = 1, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] + [Parameter(ParameterSetName = "NewException")] + [ValidateNotNullOrEmpty()] + [System.Exception] + $Exception, + + [Parameter(ParameterSetName = "NewException", Position = 2)] + [ValidateNotNullOrEmpty()] + [System.String] + $ExceptionType = "System.Management.Automation.RuntimeException", + + [Parameter(Mandatory = $true, ParameterSetName = "NewException", Position = 3)] + [ValidateNotNullOrEmpty()] + [System.String] + $Message, + + [Parameter(Mandatory = $false)] + [System.Object] + $TargetObject, + + [Parameter(Mandatory = $true, ParameterSetName = "ExistingException", Position = 10)] + [Parameter(Mandatory = $true, ParameterSetName = "NewException", Position = 10)] + [ValidateNotNullOrEmpty()] + [System.String] + $ErrorId, + + [Parameter(Mandatory = $true, ParameterSetName = "ExistingException", Position = 11)] + [Parameter(Mandatory = $true, ParameterSetName = "NewException", Position = 11)] + [ValidateNotNull()] + [System.Management.Automation.ErrorCategory] + $Category, + + [Parameter(Mandatory = $true, ParameterSetName = "Rethrow", Position = 1)] + [System.Management.Automation.ErrorRecord]$ErrorRecord + ) + process { + if (!$ErrorRecord) { + if ($PSCmdlet.ParameterSetName -eq "NewException") { + if ($Exception) { + $Exception = New-Object $ExceptionType $Message, $Exception + } + else { + $Exception = New-Object $ExceptionType $Message + } + } + $errorRecord = New-Object System.Management.Automation.ErrorRecord $Exception, $ErrorId, $Category, $TargetObject + } + $Cmdlet.ThrowTerminatingError($errorRecord) + } +} diff --git a/JiraPS/Private/WriteError.ps1 b/JiraPS/Private/WriteError.ps1 new file mode 100644 index 00000000..98811b1b --- /dev/null +++ b/JiraPS/Private/WriteError.ps1 @@ -0,0 +1,63 @@ +function WriteError { + <# + .SYNOPSIS + Utility to write an errorrecord to the errstd + .NOTES + Thanks to Jaykul: + https://github.com/PoshCode/Configuration/blob/master/Source/Metadata.psm1 + #> + param + ( + [Parameter()] + [ValidateNotNullOrEmpty()] + [System.Management.Automation.PSCmdlet] + $Cmdlet = $((Get-Variable -Scope 1 PSCmdlet).Value), + + [Parameter(Mandatory = $true, ParameterSetName = "ExistingException", Position = 1, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] + [Parameter(ParameterSetName = "NewException")] + [ValidateNotNullOrEmpty()] + [System.Exception] + $Exception, + + [Parameter(ParameterSetName = "NewException", Position = 2)] + [ValidateNotNullOrEmpty()] + [System.String] + $ExceptionType = "System.Management.Automation.RuntimeException", + + [Parameter(Mandatory = $true, ParameterSetName = "NewException", Position = 3)] + [ValidateNotNullOrEmpty()] + [System.String] + $Message, + + [Parameter(Mandatory = $false)] + [System.Object] + $TargetObject, + + [Parameter(Mandatory = $true, Position = 10)] + [ValidateNotNullOrEmpty()] + [System.String] + $ErrorId, + + [Parameter(Mandatory = $true, Position = 11)] + [ValidateNotNull()] + [System.Management.Automation.ErrorCategory] + $Category, + + [Parameter(Mandatory = $true, ParameterSetName = "Rethrow", Position = 1)] + [System.Management.Automation.ErrorRecord]$ErrorRecord + ) + process { + if (!$ErrorRecord) { + if ($PSCmdlet.ParameterSetName -eq "NewException") { + if ($Exception) { + $Exception = New-Object $ExceptionType $Message, $Exception + } + else { + $Exception = New-Object $ExceptionType $Message + } + } + $errorRecord = New-Object System.Management.Automation.ErrorRecord $Exception, $ErrorId, $Category, $TargetObject + } + $Cmdlet.WriteError($errorRecord) + } +} diff --git a/JiraPS/Public/Invoke-JiraIssueTransition.ps1 b/JiraPS/Public/Invoke-JiraIssueTransition.ps1 index 112c4380..55f7dea7 100644 --- a/JiraPS/Public/Invoke-JiraIssueTransition.ps1 +++ b/JiraPS/Public/Invoke-JiraIssueTransition.ps1 @@ -32,7 +32,7 @@ function Invoke-JiraIssueTransition { [Object] $Transition, - [System.Collections.Hashtable] + [PSCustomObject] $Fields, [Object] diff --git a/JiraPS/Public/Invoke-JiraMethod.ps1 b/JiraPS/Public/Invoke-JiraMethod.ps1 index f2f63f7b..3148b294 100644 --- a/JiraPS/Public/Invoke-JiraMethod.ps1 +++ b/JiraPS/Public/Invoke-JiraMethod.ps1 @@ -31,7 +31,9 @@ function Invoke-JiraMethod { [PSCredential] $Credential, - $Caller = $PSCmdlet + [ValidateNotNullOrEmpty()] + [System.Management.Automation.PSCmdlet] + $Cmdlet = $PSCmdlet ) begin { @@ -121,8 +123,7 @@ function Invoke-JiraMethod { } } - # Test response Headers if Confluence requires a CAPTCHA - Test-Captcha -InputObject $webResponse -Caller $Caller + Test-ServerResponse -InputObject $webResponse -Cmdlet $Cmdlet Write-Debug "[$($MyInvocation.MyCommand.Name)] Executed WebRequest. Access `$webResponse to see details" @@ -172,7 +173,7 @@ function Invoke-JiraMethod { if ($result) { if (Get-Member -Name "Errors" -InputObject $result -ErrorAction SilentlyContinue) { - Resolve-JiraError $result -WriteError -Caller $Caller + Resolve-JiraError $result -WriteError -Cmdlet $Cmdlet } else { Write-Output $result diff --git a/JiraPS/Public/New-JiraIssue.ps1 b/JiraPS/Public/New-JiraIssue.ps1 index 096287b0..df817ad9 100644 --- a/JiraPS/Public/New-JiraIssue.ps1 +++ b/JiraPS/Public/New-JiraIssue.ps1 @@ -34,7 +34,7 @@ function New-JiraIssue { [String[]] $FixVersion, - [Hashtable] + [PSCustomObject] $Fields, [PSCredential] diff --git a/JiraPS/Public/Set-JiraIssue.ps1 b/JiraPS/Public/Set-JiraIssue.ps1 index d747a000..3190bc9c 100644 --- a/JiraPS/Public/Set-JiraIssue.ps1 +++ b/JiraPS/Public/Set-JiraIssue.ps1 @@ -44,7 +44,7 @@ function Set-JiraIssue { [String[]] $Label, - [Hashtable] + [PSCustomObject] $Fields, [String] diff --git a/Tests/Invoke-JiraMethod.Tests.ps1 b/Tests/Invoke-JiraMethod.Tests.ps1 index 417522c2..d04bb5f9 100644 --- a/Tests/Invoke-JiraMethod.Tests.ps1 +++ b/Tests/Invoke-JiraMethod.Tests.ps1 @@ -19,7 +19,7 @@ Describe "Invoke-JiraMethod" { defParam $command 'InFile' defParam $command 'OutFile' defParam $command 'Credential' - defParam $command 'Caller' + defParam $command 'CmdLet' It "Restricts the METHODs to WebRequestMethod" { $methodType = $command.Parameters.Method.ParameterType diff --git a/Tests/JiraPS.Help.Tests.ps1 b/Tests/JiraPS.Help.Tests.ps1 index daf048a9..097d9ca1 100644 --- a/Tests/JiraPS.Help.Tests.ps1 +++ b/Tests/JiraPS.Help.Tests.ps1 @@ -183,6 +183,7 @@ foreach ($command in $commands) { $codeType = $parameter.ParameterType.Name # To avoid calling Trim method on a null object. $helpType = if ($parameterHelp.parameterValue) { $parameterHelp.parameterValue.Trim() } + if ($helpType -eq "PSCustomObject") { $helpType = "PSObject" } $helpType | Should be $codeType } } diff --git a/appveyor.yml b/appveyor.yml index d784751d..b3835125 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -6,7 +6,7 @@ environment: secure: PX1R0Ds6r3TTm+wxVbx8MRAgjCRV/juO3cYSEz1MedB/OXvZ3YkqpQGE+X47bcFT matrix: - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - # waiting on https://github.com/PoshCode/Configuration/issues/15 + # waiting on https://github.com/RamblingCookieMonster/BuildHelpers/pull/60 # - APPVEYOR_BUILD_WORKER_IMAGE: WMF 4 - APPVEYOR_BUILD_WORKER_IMAGE: Ubuntu PowershellVersion: "6.0.0" @@ -16,7 +16,7 @@ environment: matrix: fast_finish: true -version: 2.6.{build} +version: 2.7.{build} # Don't rebuild when I tag a release on GitHub skip_tags: true diff --git a/docs/en-US/commands/Invoke-JiraIssueTransition.md b/docs/en-US/commands/Invoke-JiraIssueTransition.md index f3982ed4..6f2aa5d2 100644 --- a/docs/en-US/commands/Invoke-JiraIssueTransition.md +++ b/docs/en-US/commands/Invoke-JiraIssueTransition.md @@ -16,7 +16,7 @@ Performs an issue transition on a JIRA issue changing it's status ## SYNTAX ```powershell -Invoke-JiraIssueTransition [-Issue] [-Transition] [[-Fields] ] +Invoke-JiraIssueTransition [-Issue] [-Transition] [[-Fields] ] [[-Assignee] ] [[-Comment] ] [[-Credential] ] [-Passthru] [] ``` @@ -128,7 +128,7 @@ Any additional fields that should be updated. Fields must be configured to appear on the transition screen to use this parameter. ```yaml -Type: Hashtable +Type: PSCustomObject Parameter Sets: (All) Aliases: diff --git a/docs/en-US/commands/Invoke-JiraMethod.md b/docs/en-US/commands/Invoke-JiraMethod.md index d6c9b487..47b5e548 100644 --- a/docs/en-US/commands/Invoke-JiraMethod.md +++ b/docs/en-US/commands/Invoke-JiraMethod.md @@ -18,7 +18,7 @@ Invoke a specific call to a Jira REST Api endpoint ```powershell Invoke-JiraMethod [-URI] [[-Method] ] [[-Body] ] [-RawBody] [[-Headers] ] [[-InFile] ] [[-OutFile] ] [-StoreSession] - [[-Credential] ] [[-Caller] ] [] + [[-Credential] ] [[-Cmdlet] ] [] ``` ## DESCRIPTION @@ -311,12 +311,12 @@ Accept pipeline input: False Accept wildcard characters: False ``` -### -Caller +### -Cmdlet Context which will be used for throwing errors. ```yaml -Type: Object +Type: PSCmdlet Parameter Sets: (All) Aliases: diff --git a/docs/en-US/commands/New-JiraIssue.md b/docs/en-US/commands/New-JiraIssue.md index 928867b2..264fb4e6 100644 --- a/docs/en-US/commands/New-JiraIssue.md +++ b/docs/en-US/commands/New-JiraIssue.md @@ -18,7 +18,7 @@ Creates a new issue in JIRA ```powershell New-JiraIssue [-Project] [-IssueType] [-Summary] [[-Priority] ] [[-Description] ] [[-Reporter] ] [[-Labels] ] [[-Parent] ] - [[-FixVersion] ] [[-Fields] ] [[-Credential] ] [-WhatIf] [-Confirm] + [[-FixVersion] ] [[-Fields] ] [[-Credential] ] [-WhatIf] [-Confirm] [] ``` @@ -237,7 +237,7 @@ Any additional fields. See: about_JiraPS_CustomFields ```yaml -Type: Hashtable +Type: PSCustomObject Parameter Sets: (All) Aliases: diff --git a/docs/en-US/commands/Set-JiraIssue.md b/docs/en-US/commands/Set-JiraIssue.md index ed4f6d05..c3a13ee2 100644 --- a/docs/en-US/commands/Set-JiraIssue.md +++ b/docs/en-US/commands/Set-JiraIssue.md @@ -17,7 +17,7 @@ Modifies an existing issue in JIRA ```powershell Set-JiraIssue [-Issue] [[-Summary] ] [[-Description] ] [[-FixVersion] ] - [[-Assignee] ] [[-Label] ] [[-Fields] ] [[-AddComment] ] + [[-Assignee] ] [[-Label] ] [[-Fields] ] [[-AddComment] ] [[-Credential] ] [-PassThru] [-WhatIf] [-Confirm] [] ``` @@ -196,7 +196,7 @@ Any additional fields that should be updated. Inspect [about_JiraPS_CustomFields](../../about/custom-fields.html) for more information. ```yaml -Type: Hashtable +Type: PSCustomObject Parameter Sets: (All) Aliases: