diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..497cc3fe --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,32 @@ +--- +name: Bug report +about: Create a report to help us improve + +--- + +## Description + + +## Steps To Reproduce + +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +## Expected behavior + + +## Screenshots + + +## Your Environment + + +> ```powershell +> Get-Module JiraPS -ListAvailable | select version +> $PSVersionTable +> ``` + +## Possible Solution + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..066b2d92 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,17 @@ +--- +name: Feature request +about: Suggest an idea for this project + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/CHANGELOG.md b/CHANGELOG.md index d3487cd1..2c494f58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,29 @@ # Change Log +## 2.8 - 2018-06-28 + +More detailed description about the changes can be found on [Our Website](https://atlassianps.org/article/announcement/JiraPS-v2.8.html). + +### IMPROVEMENTS + +- Added support for paginated response from API server by means of `-Paging` (#291, [@lipkau[]]) +- Added full set of functions to manage Filter Permissions (#289, [@lipkau[]]) +- Added `-Id` parameter to `Remove-JiraFilter` (#288, [@lipkau[]]) +- Changed logic of `Get-JiraUser` to return multiple results for a search (#272, [@lipkau[]]) +- Added posts for homepage to the module's repository (#268, [@lipkau[]]) +- Improved handling of _Credentials_ (#271, [@lipkau[]]) +- Added missing interactions with _Filters_ (#266, [@lipkau[]]) +- Added `Remove-JiraIssue` (#265, [@hmmwhatsthisdo[]]) +- Improved Build script (to deploy changes to the homepage) (#259, [@lipkau[]]) + +### BUG FIXES + +- Reverted `Add-JiraIssueAttachament` as JiraPS v2.7 broke it (#287, [@lipkau[]]) +- Fixed resolving of Remote Link (#286, [@lipkau[]]) +- Improved error handling for ErrorDetails and non-JSON/HTML responses (#277, [@hmmwhatsthisdo[]]) +- Fully support Powershell v3 (#273, [@lipkau[]]) +- Fixed parameter used in documentation but not in code (#263, [@lipkau[]]) + ## 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). @@ -258,6 +282,7 @@ which is in turn inspired by the [Vagrant](https://github.com/mitchellh/vagrant/ [@colhal]: https://github.com/colhal [@Dejulia489]: https://github.com/Dejulia489 [@ebekker]: https://github.com/ebekker + [@hmmwhatsthisdo]: https://github.com/hmmwhatsthisdo [@jkknorr]: https://github.com/jkknorr [@kittholland]: https://github.com/kittholland [@LiamLeane]: https://github.com/LiamLeane diff --git a/JiraPS.build.ps1 b/JiraPS.build.ps1 index fbc44ac3..32e9a038 100644 --- a/JiraPS.build.ps1 +++ b/JiraPS.build.ps1 @@ -51,6 +51,10 @@ task InstallDependencies { # Synopsis: Ensure the build environment is all ready to go task Init { Set-BuildEnvironment -BuildOutput '$ProjectPath/Release' -ErrorAction SilentlyContinue + # BuildHelpers does not write the project name in the correct caps + if ($env:APPVEYOR_PROJECT_NAME) { + $env:BHProjectName = $env:APPVEYOR_PROJECT_NAME + } Add-ToModulePath -Path $env:BHBuildOutput }, GetNextVersion @@ -173,6 +177,7 @@ task UpdateManifest GetNextVersion, { # Synopsis: Create a ZIP file with this build task Package GenerateRelease, { + Remove-Item "$env:BHBuildOutput\$env:BHProjectName.zip" -ErrorAction SilentlyContinue $null = Compress-Archive -Path "$env:BHBuildOutput\$env:BHProjectName" -DestinationPath "$env:BHBuildOutput\$env:BHProjectName.zip" } #endregion BuildRelease @@ -201,9 +206,7 @@ task Test Init, { OutputFormat = "NUnitXml" CodeCoverage = $codeCoverageFiles } - if ($env:APPVEYOR_PULL_REQUEST_NUMBER) { - $parameter["Show"] = "Fails" - } + $parameter["Show"] = "Fails" $testResults = Invoke-Pester @parameter If ('AppVeyor' -eq $env:BHBuildSystem) { @@ -215,6 +218,12 @@ task Test Init, { catch { throw $_ } + + Set-BuildEnvironment -BuildOutput '$ProjectPath/Release' -ErrorAction SilentlyContinue + # BuildHelpers does not write the project name in the correct caps + if ($env:APPVEYOR_PROJECT_NAME) { + $env:BHProjectName = $env:APPVEYOR_PROJECT_NAME + } }, RemoveTestResults, RemoveConfig #endregion @@ -257,23 +266,22 @@ task TagReplository GetNextVersion, { 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" + $null = 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" + $null = 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" + $null = 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 commit -m `"Update module $env:BHProjectName`"" + cmd /c "git commit -m `"Update module $env:BHProjectName`" 2>&1" Write-Build Gray "git push" cmd /c "git push 2>&1" @@ -281,13 +289,13 @@ task UpdateHomepage { Pop-Location } - catch {} + catch { Write-Warning "Failed to deploy to homepage"} } #endregion Publish #region Cleaning tasks # Synopsis: Clean the working dir -task Clean RemoveGeneratedFiles, RemoveTestResults, RemoveConfig +task Clean Init, RemoveGeneratedFiles, RemoveTestResults, RemoveConfig # Synopsis: Remove generated and temp files. task RemoveGeneratedFiles { diff --git a/JiraPS/JiraPS.psd1 b/JiraPS/JiraPS.psd1 index 9959680a..6dba9ffe 100644 --- a/JiraPS/JiraPS.psd1 +++ b/JiraPS/JiraPS.psd1 @@ -4,7 +4,7 @@ RootModule = 'JiraPS.psm1' # Version number of this module. - ModuleVersion = '2.7' + ModuleVersion = '2.8' # Supported PSEditions # CompatiblePSEditions = @() diff --git a/JiraPS/JiraPS.psm1 b/JiraPS/JiraPS.psm1 index 383636c1..69f83146 100644 --- a/JiraPS/JiraPS.psm1 +++ b/JiraPS/JiraPS.psm1 @@ -10,10 +10,37 @@ # Load Web assembly when needed # PowerShell Core has the assembly preloaded if (!("System.Web.HttpUtility" -as [Type])) { - Add-Type -Assembly System.Web + Add-Type -AssemblyName "System.Web" +} +# Load System.Net.Http when needed +# PowerShell Core has the assembly preloaded +if (!("System.Net.Http.HttpRequestException" -as [Type])) { + Add-Type -AssemblyName "System.Net.Http" +} +if (!("System.Net.Http" -as [Type])) { + Add-Type -Assembly System.Net.Http } #endregion Dependencies +#region Configuration +$script:DefaultContentType = "application/json; charset=utf-8" +$script:DefaultPageSize = 25 +$script:DefaultHeaders= @{ "Accept-Charset" = "utf-8" } +# Bug in PSv3's .Net API +if ($PSVersionTable.PSVersion.Major -gt 3) { + $script:DefaultHeaders["Accept"] = "application/json" +} +$script:PagingContainers = @( + "comments" + "dashboards" + "groups" + "issues" + "values" + "worklogs" +) +$script:SessionTransformationMethod = "ConvertTo-JiraSession" +#endregion Configuration + #region LoadFunctions $PublicFunctions = @( Get-ChildItem -Path "$PSScriptRoot/Public/*.ps1" -ErrorAction SilentlyContinue ) $PrivateFunctions = @( Get-ChildItem -Path "$PSScriptRoot/Private/*.ps1" -ErrorAction SilentlyContinue ) @@ -24,15 +51,14 @@ foreach ($file in @($PublicFunctions + $PrivateFunctions)) { . $file.FullName } catch { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Function not found"), - 'Load.Function', - [System.Management.Automation.ErrorCategory]::ObjectNotFound, - $file - ) + $exception = ([System.ArgumentException]"Function not found") + $errorId = "Load.Function" + $errorCategory = 'ObjectNotFound' + $errorTarget = $file + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Failed to import function $($file.BaseName)" throw $errorItem } } -Export-ModuleMember -Function $PublicFunctions.BaseName +Export-ModuleMember -Function $PublicFunctions.BaseName -Alias * #endregion LoadFunctions diff --git a/JiraPS/Private/ConvertTo-GetParameter.ps1 b/JiraPS/Private/ConvertTo-GetParameter.ps1 new file mode 100644 index 00000000..70018373 --- /dev/null +++ b/JiraPS/Private/ConvertTo-GetParameter.ps1 @@ -0,0 +1,22 @@ +function ConvertTo-GetParameter { + <# + .SYNOPSIS + Generate the GET parameter string for an URL from a hashtable + #> + [CmdletBinding()] + param ( + [Parameter( Position = 0, Mandatory = $true, ValueFromPipeline = $true )] + [hashtable]$InputObject + ) + + process { + Write-Verbose "[$($MyInvocation.MyCommand.Name)] Making HTTP get parameter string out of a hashtable" + Write-Verbose ($InputObject | Out-String) + [string]$parameters = "?" + foreach ($key in $InputObject.Keys) { + $value = $InputObject[$key] + $parameters += "$key=$($value)&" + } + $parameters -replace ".$" + } +} diff --git a/JiraPS/Private/ConvertTo-JiraFilter.ps1 b/JiraPS/Private/ConvertTo-JiraFilter.ps1 index 6eb2be6f..15f1b635 100644 --- a/JiraPS/Private/ConvertTo-JiraFilter.ps1 +++ b/JiraPS/Private/ConvertTo-JiraFilter.ps1 @@ -3,7 +3,10 @@ function ConvertTo-JiraFilter { param( [Parameter( ValueFromPipeline )] [PSObject[]] - $InputObject + $InputObject, + + [PSObject[]] + $FilterPermissions ) process { @@ -11,17 +14,22 @@ function ConvertTo-JiraFilter { Write-Debug "[$($MyInvocation.MyCommand.Name)] Converting `$InputObject to custom object" $props = @{ - 'ID' = $i.id - 'Name' = $i.name - 'JQL' = $i.jql - 'RestUrl' = $i.self - 'ViewUrl' = $i.viewUrl - 'SearchUrl' = $i.searchUrl - 'Favourite' = $i.favourite - - 'SharePermission' = $i.sharePermissions - 'SharedUser' = $i.sharedUsers - 'Subscription' = $i.subscriptions + 'ID' = $i.id + 'Name' = $i.name + 'JQL' = $i.jql + 'RestUrl' = $i.self + 'ViewUrl' = $i.viewUrl + 'SearchUrl' = $i.searchUrl + 'Favourite' = $i.favourite + 'FilterPermissions' = @() + + 'SharePermission' = $i.sharePermissions + 'SharedUser' = $i.sharedUsers + 'Subscription' = $i.subscriptions + } + + if ($FilterPermissions) { + $props.FilterPermissions = @(ConvertTo-JiraFilterPermission ($FilterPermissions)) } if ($i.description) { diff --git a/JiraPS/Private/ConvertTo-JiraFilterPermission.ps1 b/JiraPS/Private/ConvertTo-JiraFilterPermission.ps1 new file mode 100644 index 00000000..95cd1225 --- /dev/null +++ b/JiraPS/Private/ConvertTo-JiraFilterPermission.ps1 @@ -0,0 +1,39 @@ +function ConvertTo-JiraFilterPermission { + [CmdletBinding()] + param( + [Parameter( ValueFromPipeline )] + [PSObject[]] + $InputObject + ) + + process { + foreach ($i in $InputObject) { + Write-Debug "[$($MyInvocation.MyCommand.Name)] Converting `$InputObject to custom object" + + $props = @{ + 'ID' = $i.id + 'Type' = $i.type + 'Group' = $null + 'Project' = $null + 'Role' = $null + } + if ($i.group) { + $props["Group"] = ConvertTo-JiraGroup $i.group + } + if ($i.project) { + $props["Project"] = ConvertTo-JiraProject $i.project + } + if ($i.role) { + $props["Role"] = ConvertTo-JiraProjectRole $i.role + } + + $result = New-Object -TypeName PSObject -Property $props + $result.PSObject.TypeNames.Insert(0, 'JiraPS.FilterPermission') + $result | Add-Member -MemberType ScriptMethod -Name 'ToString' -Force -Value { + Write-Output "$($this.Type)" + } + + Write-Output $result + } + } +} diff --git a/JiraPS/Private/ConvertTo-JiraProjectRole.ps1 b/JiraPS/Private/ConvertTo-JiraProjectRole.ps1 new file mode 100644 index 00000000..aef189f7 --- /dev/null +++ b/JiraPS/Private/ConvertTo-JiraProjectRole.ps1 @@ -0,0 +1,30 @@ +function ConvertTo-JiraProjectRole { + [CmdletBinding()] + param( + [Parameter( ValueFromPipeline )] + [PSObject[]] + $InputObject + ) + + process { + foreach ($i in $InputObject) { + Write-Debug "[$($MyInvocation.MyCommand.Name)] Converting `$InputObject to custom object" + + $props = @{ + 'ID' = $i.id + 'Name' = $i.name + 'Description' = $i.description + 'Actors' = $i.actors + 'RestUrl' = $i.self + } + + $result = New-Object -TypeName PSObject -Property $props + $result.PSObject.TypeNames.Insert(0, 'JiraPS.ProjectRole') + $result | Add-Member -MemberType ScriptMethod -Name "ToString" -Force -Value { + Write-Output "$($this.Name)" + } + + Write-Output $result + } + } +} diff --git a/JiraPS/Private/ConvertTo-ParameterHash.ps1 b/JiraPS/Private/ConvertTo-ParameterHash.ps1 new file mode 100644 index 00000000..1bdd42c0 --- /dev/null +++ b/JiraPS/Private/ConvertTo-ParameterHash.ps1 @@ -0,0 +1,31 @@ +function ConvertTo-ParameterHash { + [CmdletBinding( DefaultParameterSetName = 'ByString' )] + param ( + # URI from which to use the query + [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'ByUri' )] + [Uri] + $Uri, + + # Query string + [Parameter( Position = 0, Mandatory, ParameterSetName = 'ByString' )] + [String] + $Query + ) + + process { + $GetParameter = @{} + + if ($Uri) { + $Query = $Uri.Query + } + + if ($Query -match "^\?.+") { + $Query.TrimStart("?").Split("&") | ForEach-Object { + $key, $value = $_.Split("=") + $GetParameter.Add($key, $value) + } + } + + Write-Output $GetParameter + } +} diff --git a/JiraPS/Private/Invoke-WebRequest.ps1 b/JiraPS/Private/Invoke-WebRequest.ps1 index be45751c..9393d2f9 100644 --- a/JiraPS/Private/Invoke-WebRequest.ps1 +++ b/JiraPS/Private/Invoke-WebRequest.ps1 @@ -30,7 +30,7 @@ function Invoke-WebRequest { [pscredential] [System.Management.Automation.CredentialAttribute()] - ${Credential}, + ${Credential} = [System.Management.Automation.PSCredential]::Empty, [switch] ${UseDefaultCredentials}, @@ -68,7 +68,7 @@ function Invoke-WebRequest { [pscredential] [System.Management.Automation.CredentialAttribute()] - ${ProxyCredential}, + ${ProxyCredential} = [System.Management.Automation.PSCredential]::Empty, [switch] ${ProxyUseDefaultCredentials}, @@ -94,7 +94,7 @@ function Invoke-WebRequest { ${PassThru}) begin { - if ($Credential) { + if ($Credential -and ($Credential -ne [System.Management.Automation.PSCredential]::Empty)) { $SecureCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes( $('{0}:{1}' -f $Credential.UserName, $Credential.GetNetworkCredential().Password) )) diff --git a/JiraPS/Private/Join-Hashtable.ps1 b/JiraPS/Private/Join-Hashtable.ps1 new file mode 100644 index 00000000..ada8d555 --- /dev/null +++ b/JiraPS/Private/Join-Hashtable.ps1 @@ -0,0 +1,38 @@ +function Join-Hashtable { + <# + .SYNOPSIS + Combines multiple hashtables into a single table. + + .DESCRIPTION + Combines multiple hashtables into a single table. + On multiple identic keys, the last wins. + + .EXAMPLE + PS C:\> Join-Hashtable -Hashtable $Hash1, $Hash2 + + Merges the hashtables contained in $Hash1 and $Hash2 into a single hashtable. +#> + [CmdletBinding()] + Param ( + # The tables to merge. + [Parameter( Mandatory, ValueFromPipeline )] + [AllowNull()] + [System.Collections.IDictionary[]] + $Hashtable + ) + begin { + $table = @{ } + } + + process { + foreach ($item in $Hashtable) { + foreach ($key in $item.Keys) { + $table[$key] = $item[$key] + } + } + } + + end { + $table + } +} diff --git a/JiraPS/Private/Resolve-DefaultParameterValue.ps1 b/JiraPS/Private/Resolve-DefaultParameterValue.ps1 new file mode 100644 index 00000000..5614ded2 --- /dev/null +++ b/JiraPS/Private/Resolve-DefaultParameterValue.ps1 @@ -0,0 +1,75 @@ +function Resolve-DefaultParameterValue { + <# + .SYNOPSIS + Used to filter and process default parameter values. + + .DESCRIPTION + This command picks all the default parameter values from a reference hashtable. + It then filters all that match a specified command and binds them to that specific command, narrowing its focus. + These get merged into either a new or a specified hashtable and returned. + + .EXAMPLE + PS C:\> Resolve-DefaultParameterValue -Reference $global:PSDefaultParameterValues -CommandName 'Invoke-WebRequest' + + Returns a hashtable containing all default parameter values in the global scope affecting the command 'Invoke-WebRequest'. +#> + [CmdletBinding()] + param ( + # The hashtable to pick default parameter valeus from. + [Parameter( Mandatory )] + [Hashtable] + $Reference, + + # The commands to pick default parameter values for. + [Parameter( Mandatory )] + [String[]] + $CommandName, + + # The target hashtable to merge results into. + # By default an empty hashtable is used. + [Hashtable] + $Target = @{ }, + + # Only resolve for specific parameter names. + [String[]] + $ParameterName = "*" + ) + + begin { + $defaultItems = New-Object -TypeName System.Collections.ArrayList + + foreach ($key in $Reference.Keys) { + $null = $defaultItems.Add( + [PSCustomObject]@{ + Key = $key + Value = $Reference[$key] + Command = $key.Split(":")[0] + Parameter = $key.Split(":")[1] + } + ) + } + } + + process { + foreach ($command in $CommandName) { + foreach ($item in $defaultItems) { + if ($command -notlike $item.Command) { continue } + + foreach ($parameter in $ParameterName) { + if ($item.Parameter -like $parameter) { + if ($parameter -ne "*") { + $Target["$($command):$($parameter)"] = $item.Value + } + else { + $Target["$($command):$($item.Parameter)"] = $item.Value + } + } + } + } + } + } + + end { + $Target + } +} diff --git a/JiraPS/Private/Resolve-ErrorWebResponse.ps1 b/JiraPS/Private/Resolve-ErrorWebResponse.ps1 new file mode 100644 index 00000000..853a30b1 --- /dev/null +++ b/JiraPS/Private/Resolve-ErrorWebResponse.ps1 @@ -0,0 +1,100 @@ +function Resolve-ErrorWebResponse { + [CmdletBinding()] + param ( + $Exception, + + $StatusCode, + + [ValidateNotNullOrEmpty()] + [System.Management.Automation.PSCmdlet] + $Cmdlet = $PSCmdlet + ) + + begin { + Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started" + + Write-DebugMessage "[$($MyInvocation.MyCommand.Name)] ParameterSetName: $($PsCmdlet.ParameterSetName)" + Write-DebugMessage "[$($MyInvocation.MyCommand.Name)] PSBoundParameters: $($PSBoundParameters | Out-String)" + + # Powershell v6+ populates the body of the response into the exception + if ($Exception.ErrorDetails) { + $responseBody = $Exception.ErrorDetails.Message + } + # Powershell v5.1- has the body of the response in a Stream in the Exception Response + else { + $readStream = New-Object -TypeName System.IO.StreamReader -ArgumentList ($Exception.Exception.Response.GetResponseStream()) + $responseBody = $readStream.ReadToEnd() + $readStream.Close() + } + + $exception = "Invalid Server Response" + $errorId = "InvalidResponse.Status$($StatusCode.value__)" + $errorCategory = "InvalidResult" + + if ($responseBody) { + # Clear the body in case it is not a JSON (but rather html) + if ($responseBody -match "^[\s\t]*\") { + Write-DebugMessage "[$($MyInvocation.MyCommand.Name)] Content is HTML - replacing it with a generic json" + $responseBody = '{"errorMessages": "Invalid server response. HTML returned."}' + } + + Write-Verbose "[$($MyInvocation.MyCommand.Name)] Retrieved body of HTTP response for more information about the error (`$responseBody)" + Write-Debug "[$($MyInvocation.MyCommand.Name)] Got the following error as `$responseBody" + + try { + $responseObject = ConvertFrom-Json -InputObject $responseBody -ErrorAction Stop + + foreach ($_error in ($responseObject.errorMessages + $responseObject.errors)) { + # $_error is a PSCustomObject - therefore can't be $false + if (-not $_error.ToString()) { break } + + $writeErrorSplat = @{ + Exception = $exception + ErrorId = $errorId + Category = $errorCategory + Message = $_error + TargetObject = $targetObject + Cmdlet = $Cmdlet + } + WriteError @writeErrorSplat + } + } + catch [ArgumentException] { + Write-DebugMessage "[$($MyInvocation.MyCommand.Name)] `$responseBody could not be converted from JSON" + $writeErrorSplat = @{ + Exception = $exception + ErrorId = $errorId + Category = $errorCategory + Message = $responseBody + TargetObject = $targetObject + Cmdlet = $Cmdlet + } + WriteError @writeErrorSplat + } + catch { + $writeErrorSplat = @{ + Exception = $exception + ErrorId = $errorId + Category = $errorCategory + Message = "An unknown error ocurred." + TargetObject = $targetObject + Cmdlet = $Cmdlet + } + WriteError @writeErrorSplat + } + } + else { + Write-DebugMessage "[$($MyInvocation.MyCommand.Name)] Response had no Body. Using `$StatusCode for generic error" + $writeErrorSplat = @{ + Exception = $exception + ErrorId = $errorId + Category = $errorCategory + Message = "Server responsed with $StatusCode" + Cmdlet = $Cmdlet + } + WriteError @writeErrorSplat + } + + Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function ended" + } +} diff --git a/JiraPS/Private/Resolve-FilePath.ps1 b/JiraPS/Private/Resolve-FilePath.ps1 new file mode 100644 index 00000000..ea89f087 --- /dev/null +++ b/JiraPS/Private/Resolve-FilePath.ps1 @@ -0,0 +1,35 @@ +function Resolve-FilePath { + <# + .SYNOPSIS + Resolve a path to it's full path + + .DESCRIPTION + Resolve relative paths and PSDrive paths to the full path + + .LINK + https://github.com/pester/Pester/blob/5796c95e4d6ff5528b8e14865e3f25e40f01bd65/Functions/TestResults.ps1#L13-L27 + #> + [CmdletBinding()] + param( + # Path to be resolved + [Parameter( Mandatory, ValueFromPipeline )] + [ValidateNotNullOrEmpty()] + [Alias("PSPath", "LiteralPath")] + [String] + $Path + ) + + process { + $folder = Split-Path -Path $Path -Parent + $file = Split-Path -Path $Path -Leaf + + if ( -not ([String]::IsNullOrEmpty($folder))) { + $folderResolved = Resolve-Path -Path $folder + } + else { + $folderResolved = Resolve-Path -Path $ExecutionContext.SessionState.Path.CurrentFileSystemLocation + } + + Join-Path -Path $folderResolved.ProviderPath -ChildPath $file + } +} diff --git a/JiraPS/Private/Resolve-FullPath.ps1 b/JiraPS/Private/Resolve-FullPath.ps1 new file mode 100644 index 00000000..75e4af72 --- /dev/null +++ b/JiraPS/Private/Resolve-FullPath.ps1 @@ -0,0 +1,35 @@ +function Resolve-FullPath { + [CmdletBinding()] + param ( + # Path to be resolved. + # Can be realtive or absolute. + # Resolves PSDrives + [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName )] + [ValidateNotNullOrEmpty()] + [ValidateScript( + { + if (-not (Test-Path $_ -PathType Leaf)) { + $errorItem = [System.Management.Automation.ErrorRecord]::new( + ([System.ArgumentException]"File not found"), + 'ParameterValue.FileNotFound', + [System.Management.Automation.ErrorCategory]::ObjectNotFound, + $_ + ) + $errorItem.ErrorDetails = "No file could be found with the provided path '$_'." + $PSCmdlet.ThrowTerminatingError($errorItem) + } + else { + return $true + } + } + )] + [Alias( 'FullName', 'PSPath' )] + [String] + $Path + ) + + process { + Write-Verbose "[$($MyInvocation.MyCommand.Name)] Resolving path $Path" + $PSCmdlet.GetUnresolvedProviderPathFromPSPath($Path) + } +} diff --git a/JiraPS/Private/Resolve-JiraError.ps1 b/JiraPS/Private/Resolve-JiraError.ps1 index a13111b3..b87bfda2 100644 --- a/JiraPS/Private/Resolve-JiraError.ps1 +++ b/JiraPS/Private/Resolve-JiraError.ps1 @@ -21,12 +21,11 @@ if ($i.errorMessages) { foreach ($e in $i.errorMessages) { if ($WriteError) { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Server responded with Error"), - "ServerResponse", - [System.Management.Automation.ErrorCategory]::NotSpecified, - $i - ) + $exception = ([System.ArgumentException]"Server responded with Error") + $errorId = "ServerResponse" + $errorCategory = 'NotSpecified' + $errorTarget = $i + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Jira encountered an error: [$e]" $Cmdlet.WriteError($errorItem) } @@ -48,12 +47,11 @@ $keys = (Get-Member -InputObject $i.errors | Where-Object -FilterScript {$_.MemberType -eq 'NoteProperty'}).Name foreach ($k in $keys) { if ($WriteError) { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Server responded with Error"), - "ServerResponse.$k", - [System.Management.Automation.ErrorCategory]::NotSpecified, - $i - ) + $exception = ([System.ArgumentException]"Server responded with Error") + $errorId = "ServerResponse.$k" + $errorCategory = 'NotSpecified' + $errorTarget = $i + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Jira encountered an error: [$k] - $($i.errors.$k)" $Cmdlet.WriteError($errorItem) } diff --git a/JiraPS/Private/Resolve-JiraIssueObject.ps1 b/JiraPS/Private/Resolve-JiraIssueObject.ps1 index 737fa307..12afb5d3 100644 --- a/JiraPS/Private/Resolve-JiraIssueObject.ps1 +++ b/JiraPS/Private/Resolve-JiraIssueObject.ps1 @@ -10,12 +10,11 @@ function Resolve-JiraIssueObject { [ValidateScript( { if (("JiraPS.Issue" -notin $_.PSObject.TypeNames) -and (($_ -isnot [String]))) { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid Type for Parameter"), - 'ParameterType.NotJiraIssue', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $_ - ) + $exception = ([System.ArgumentException]"Invalid Type for Parameter") #fix code highlighting] + $errorId = 'ParameterType.NotJiraIssue' + $errorCategory = 'InvalidArgument' + $errorTarget = $_ + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Wrong object type provided for Issue. Expected [JiraPS.Issue] or [String], but was $($_.GetType().Name)" $PSCmdlet.ThrowTerminatingError($errorItem) } @@ -28,8 +27,10 @@ function Resolve-JiraIssueObject { $InputObject, # Authentication credentials - [PSCredential] - $Credential + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty ) # As we are not able to use proper type casting in the parameters, this is a workaround diff --git a/JiraPS/Public/Add-JiraFilterPermission.ps1 b/JiraPS/Public/Add-JiraFilterPermission.ps1 new file mode 100644 index 00000000..74dc3de4 --- /dev/null +++ b/JiraPS/Public/Add-JiraFilterPermission.ps1 @@ -0,0 +1,80 @@ +function Add-JiraFilterPermission { + # .ExternalHelp ..\JiraPS-help.xml + [CmdletBinding( SupportsShouldProcess, DefaultParameterSetName = 'ByInputObject' )] + # [OutputType( [JiraPS.FilterPermission] )] + param( + [Parameter( Position = 0, Mandatory, ValueFromPipeline, ParameterSetName = 'ByInputObject' )] + [ValidateNotNullOrEmpty()] + [PSTypeName('JiraPS.Filter')] + $Filter, + + [Parameter( Position = 0, Mandatory, ValueFromPipeline, ParameterSetName = 'ById')] + [ValidateNotNullOrEmpty()] + [UInt32[]] + $Id, + + [Parameter( Mandatory )] + [ValidateNotNullOrEmpty()] + [ValidateSet('Group', 'Project', 'ProjectRole', 'Authenticated', 'Global')] + [String]$Type, + + [Parameter()] + [String]$Value, + + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty + ) + + begin { + Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started" + + $resourceURi = "{0}/permission" + } + + process { + Write-DebugMessage "[$($MyInvocation.MyCommand.Name)] ParameterSetName: $($PsCmdlet.ParameterSetName)" + Write-DebugMessage "[$($MyInvocation.MyCommand.Name)] PSBoundParameters: $($PSBoundParameters | Out-String)" + + if ($PSCmdlet.ParameterSetName -eq 'ById') { + $Filter = Get-JiraFilter -Id $Id + } + + $body = @{ + type = $Type.ToLower() + } + switch ($Type) { + "Group" { + $body["groupname"] = $Value + } + "Project" { + $body["projectId"] = $Value + } + "ProjectRole" { + $body["projectRoleId"] = $Value + } + "Authenticated" { } + "Global" { } + } + + foreach ($_filter in $Filter) { + $parameter = @{ + URI = $resourceURi -f $_filter.RestURL + Method = "POST" + Body = ConvertTo-Json $body + Credential = $Credential + } + Write-Debug "[$($MyInvocation.MyCommand.Name)] Invoking JiraMethod with `$parameter" + if ($PSCmdlet.ShouldProcess($_filter.Name, "Add Permission [$Type - $Value]")) { + $result = Invoke-JiraMethod @parameter + + Write-Output (ConvertTo-JiraFilter -InputObject $_filter -FilterPermissions $result) + } + } + } + + end { + Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete" + } +} diff --git a/JiraPS/Public/Add-JiraGroupMember.ps1 b/JiraPS/Public/Add-JiraGroupMember.ps1 index fff2b5dc..7287f39e 100644 --- a/JiraPS/Public/Add-JiraGroupMember.ps1 +++ b/JiraPS/Public/Add-JiraGroupMember.ps1 @@ -1,4 +1,5 @@ function Add-JiraGroupMember { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding( SupportsShouldProcess )] param( [Parameter( Mandatory, ValueFromPipeline )] @@ -16,8 +17,10 @@ function Add-JiraGroupMember { Once we have custom classes, this can also accept ValueFromPipeline #> - [PSCredential] - $Credential, + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty, [Switch] $PassThru diff --git a/JiraPS/Public/Add-JiraIssueAttachment.ps1 b/JiraPS/Public/Add-JiraIssueAttachment.ps1 index 2b40a114..72756b16 100644 --- a/JiraPS/Public/Add-JiraIssueAttachment.ps1 +++ b/JiraPS/Public/Add-JiraIssueAttachment.ps1 @@ -1,4 +1,5 @@ function Add-JiraIssueAttachment { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding( SupportsShouldProcess )] param( [Parameter( Mandatory )] @@ -6,12 +7,11 @@ function Add-JiraIssueAttachment { [ValidateScript( { if (("JiraPS.Issue" -notin $_.PSObject.TypeNames) -and (($_ -isnot [String]))) { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid Type for Parameter"), - 'ParameterType.NotJiraIssue', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $_ - ) + $exception = ([System.ArgumentException]"Invalid Type for Parameter") #fix code highlighting] + $errorId = 'ParameterType.NotJiraIssue' + $errorCategory = 'InvalidArgument' + $errorTarget = $_ + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Wrong object type provided for Issue. Expected [JiraPS.Issue] or [String], but was $($_.GetType().Name)" $PSCmdlet.ThrowTerminatingError($errorItem) <# @@ -36,12 +36,11 @@ function Add-JiraIssueAttachment { [ValidateScript( { if (-not (Test-Path $_ -PathType Leaf)) { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"File not found"), - 'ParameterValue.FileNotFound', - [System.Management.Automation.ErrorCategory]::ObjectNotFound, - $_ - ) + $exception = ([System.ArgumentException]"File not found") #fix code highlighting] + $errorId = 'ParameterValue.FileNotFound' + $errorCategory = 'ObjectNotFound' + $errorTarget = $_ + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "No file could be found with the provided path '$_'." $PSCmdlet.ThrowTerminatingError($errorItem) } @@ -54,8 +53,10 @@ function Add-JiraIssueAttachment { [String[]] $FilePath, - [PSCredential] - $Credential, + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty, [Switch] $PassThru @@ -72,12 +73,11 @@ function Add-JiraIssueAttachment { Write-DebugMessage "[$($MyInvocation.MyCommand.Name)] PSBoundParameters: $($PSBoundParameters | Out-String)" if (@($Issue).Count -ne 1) { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"invalid Issue provided"), - 'ParameterValue.JiraIssue', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $_ - ) + $exception = ([System.ArgumentException]"invalid Issue provided") + $errorId = 'ParameterValue.JiraIssue' + $errorCategory = 'InvalidArgument' + $errorTarget = $_ + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Only one Issue can be provided at a time." $PSCmdlet.ThrowTerminatingError($errorItem) } @@ -85,15 +85,38 @@ function Add-JiraIssueAttachment { # Find the proper object for the Issue $issueObj = Resolve-JiraIssueObject -InputObject $Issue -Credential $Credential - $parameter = @{ - URI = $resourceURi -f $issueObj.RestURL - Method = "POST" - Credential = $Credential - } - foreach ($file in $FilePath) { - $parameter["InFile"] = $file + $file = Resolve-FilePath -Path $file + + $enc = [System.Text.Encoding]::GetEncoding("iso-8859-1") + $boundary = [System.Guid]::NewGuid().ToString() + + $fileName = Split-Path -Path $file -Leaf + $readFile = [System.IO.File]::ReadAllBytes($file) + $fileEnc = $enc.GetString($readFile) + + $bodyLines = @' +--{0} +Content-Disposition: form-data; name="file"; filename="{1}" +Content-Type: application/octet-stream +{2} +--{0}-- +'@ -f $boundary, $fileName, $fileEnc + + $headers = @{ + 'X-Atlassian-Token' = 'nocheck' + 'Content-Type' = "multipart/form-data; boundary=`"$boundary`"" + } + + $parameter = @{ + URI = $resourceURi -f $issueObj.RestURL + Method = "POST" + Body = $bodyLines + Headers = $headers + RawBody = $true + Credential = $Credential + } Write-Debug "[$($MyInvocation.MyCommand.Name)] Invoking JiraMethod with `$parameter" if ($PSCmdlet.ShouldProcess($IssueObj.Key, "Adding attachment '$($fileName)'.")) { $rawResult = Invoke-JiraMethod @parameter diff --git a/JiraPS/Public/Add-JiraIssueComment.ps1 b/JiraPS/Public/Add-JiraIssueComment.ps1 index f12b7b33..5b99915c 100644 --- a/JiraPS/Public/Add-JiraIssueComment.ps1 +++ b/JiraPS/Public/Add-JiraIssueComment.ps1 @@ -1,4 +1,5 @@ function Add-JiraIssueComment { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding( SupportsShouldProcess )] param( [Parameter( Mandatory )] @@ -11,12 +12,11 @@ function Add-JiraIssueComment { [ValidateScript( { if (("JiraPS.Issue" -notin $_.PSObject.TypeNames) -and (($_ -isnot [String]))) { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid Type for Parameter"), - 'ParameterType.NotJiraIssue', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $_ - ) + $exception = ([System.ArgumentException]"Invalid Type for Parameter") #fix code highlighting] + $errorId = 'ParameterType.NotJiraIssue' + $errorCategory = 'InvalidArgument' + $errorTarget = $_ + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Wrong object type provided for Issue. Expected [JiraPS.Issue] or [String], but was $($_.GetType().Name)" $PSCmdlet.ThrowTerminatingError($errorItem) <# @@ -37,8 +37,10 @@ function Add-JiraIssueComment { [String] $VisibleRole = 'All Users', - [PSCredential] - $Credential + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty ) begin { diff --git a/JiraPS/Public/Add-JiraIssueLink.ps1 b/JiraPS/Public/Add-JiraIssueLink.ps1 index ed8fca9c..1a1dbf9a 100644 --- a/JiraPS/Public/Add-JiraIssueLink.ps1 +++ b/JiraPS/Public/Add-JiraIssueLink.ps1 @@ -1,4 +1,5 @@ function Add-JiraIssueLink { +# .ExternalHelp ..\JiraPS-help.xml [CmdletBinding( SupportsShouldProcess )] param( [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName )] @@ -6,12 +7,11 @@ function Add-JiraIssueLink { [ValidateScript( { if (("JiraPS.Issue" -notin $_.PSObject.TypeNames) -and (($_ -isnot [String]))) { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid Type for Parameter"), - 'ParameterType.NotJiraIssue', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $_ - ) + $exception = ([System.ArgumentException]"Invalid Type for Parameter") #fix code highlighting] + $errorId = 'ParameterType.NotJiraIssue' + $errorCategory = 'InvalidArgument' + $errorTarget = $_ + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Wrong object type provided for Issue. Expected [JiraPS.Issue] or [String], but was $($_.GetType().Name)" $PSCmdlet.ThrowTerminatingError($errorItem) <# @@ -36,12 +36,11 @@ function Add-JiraIssueLink { ($objectProperties.Name -contains "type") -and (($objectProperties.Name -contains "outwardIssue") -or ($objectProperties.Name -contains "inwardIssue")) )) { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid Parameter"), - 'ParameterProperties.Incomplete', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $_ - ) + $exception = ([System.ArgumentException]"Invalid Parameter") #fix code highlighting] + $errorId = 'ParameterProperties.Incomplete' + $errorCategory = 'InvalidArgument' + $errorTarget = $_ + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "The IssueLink provided does not contain the information needed." $PSCmdlet.ThrowTerminatingError($errorItem) <# @@ -60,8 +59,10 @@ function Add-JiraIssueLink { [String] $Comment, - [PSCredential] - $Credential + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty ) begin { diff --git a/JiraPS/Public/Add-JiraIssueWatcher.ps1 b/JiraPS/Public/Add-JiraIssueWatcher.ps1 index f5bd9378..fb930288 100644 --- a/JiraPS/Public/Add-JiraIssueWatcher.ps1 +++ b/JiraPS/Public/Add-JiraIssueWatcher.ps1 @@ -1,4 +1,5 @@ function Add-JiraIssueWatcher { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding( SupportsShouldProcess )] param( [Parameter( Mandatory )] @@ -14,12 +15,11 @@ [ValidateScript( { if (("JiraPS.Issue" -notin $_.PSObject.TypeNames) -and (($_ -isnot [String]))) { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid Type for Parameter"), - 'ParameterType.NotJiraIssue', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $_ - ) + $exception = ([System.ArgumentException]"Invalid Type for Parameter") #fix code highlighting] + $errorId = 'ParameterType.NotJiraIssue' + $errorCategory = 'InvalidArgument' + $errorTarget = $_ + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Wrong object type provided for Issue. Expected [JiraPS.Issue] or [String], but was $($_.GetType().Name)" $PSCmdlet.ThrowTerminatingError($errorItem) <# @@ -36,8 +36,10 @@ [Object] $Issue, - [PSCredential] - $Credential + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty ) begin { diff --git a/JiraPS/Public/Add-JiraIssueWorklog.ps1 b/JiraPS/Public/Add-JiraIssueWorklog.ps1 index b9e6100b..edb067a2 100644 --- a/JiraPS/Public/Add-JiraIssueWorklog.ps1 +++ b/JiraPS/Public/Add-JiraIssueWorklog.ps1 @@ -1,4 +1,5 @@ function Add-JiraIssueWorklog { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding( SupportsShouldProcess )] param( [Parameter( Mandatory )] @@ -11,12 +12,11 @@ function Add-JiraIssueWorklog { [ValidateScript( { if (("JiraPS.Issue" -notin $_.PSObject.TypeNames) -and (($_ -isnot [String]))) { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid Type for Parameter"), - 'ParameterType.NotJiraIssue', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $_ - ) + $exception = ([System.ArgumentException]"Invalid Type for Parameter") #fix code highlighting] + $errorId = 'ParameterType.NotJiraIssue' + $errorCategory = 'InvalidArgument' + $errorTarget = $_ + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Wrong object type provided for Issue. Expected [JiraPS.Issue] or [String], but was $($_.GetType().Name)" $PSCmdlet.ThrowTerminatingError($errorItem) <# @@ -45,8 +45,10 @@ function Add-JiraIssueWorklog { [String] $VisibleRole = 'All Users', - [PSCredential] - $Credential + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty ) begin { diff --git a/JiraPS/Public/Format-Jira.ps1 b/JiraPS/Public/Format-Jira.ps1 index 58e2d257..2d01b244 100644 --- a/JiraPS/Public/Format-Jira.ps1 +++ b/JiraPS/Public/Format-Jira.ps1 @@ -1,4 +1,5 @@ function Format-Jira { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding()] [OutputType([System.String])] param( diff --git a/JiraPS/Public/Get-JiraComponent.ps1 b/JiraPS/Public/Get-JiraComponent.ps1 index 04c218d7..4374669b 100644 --- a/JiraPS/Public/Get-JiraComponent.ps1 +++ b/JiraPS/Public/Get-JiraComponent.ps1 @@ -1,4 +1,5 @@ function Get-JiraComponent { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding(DefaultParameterSetName = 'ByID')] param( [Parameter( Position = 0, Mandatory, ValueFromPipeline, ParameterSetName = 'ByProject' )] @@ -6,12 +7,11 @@ function Get-JiraComponent { [ValidateScript( { if (("JiraPS.Project" -notin $_.PSObject.TypeNames) -and (($_ -isnot [String]))) { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid Type for Parameter"), - 'ParameterType.NotJiraProject', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $_ - ) + $exception = ([System.ArgumentException]"Invalid Type for Parameter") #fix code highlighting] + $errorId = 'ParameterType.NotJiraProject' + $errorCategory = 'InvalidArgument' + $errorTarget = $_ + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Wrong object type provided for Issue. Expected [JiraPS.Project] or [String], but was $($_.GetType().Name)" $PSCmdlet.ThrowTerminatingError($errorItem) <# @@ -36,8 +36,10 @@ function Get-JiraComponent { [Int[]] $ComponentId, - [PSCredential] - $Credential + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty ) begin { diff --git a/JiraPS/Public/Get-JiraConfigServer.ps1 b/JiraPS/Public/Get-JiraConfigServer.ps1 index 63caef8d..36b5bd87 100644 --- a/JiraPS/Public/Get-JiraConfigServer.ps1 +++ b/JiraPS/Public/Get-JiraConfigServer.ps1 @@ -1,4 +1,5 @@ function Get-JiraConfigServer { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding()] [OutputType([System.String])] param( @@ -26,12 +27,11 @@ function Get-JiraConfigServer { } if (-not (Test-Path -Path $ConfigFile)) { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.IO.FileNotFoundException]"Could not find $ConfigFile"), - 'ConfigFile.NotFound', - [System.Management.Automation.ErrorCategory]::ObjectNotFound, - $ConfigFile - ) + $exception = ([System.IO.FileNotFoundException]"Could not find $ConfigFile") + $errorId = 'ConfigFile.NotFound' + $errorCategory = 'ObjectNotFound' + $errorTarget = $ConfigFile + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Config file [$ConfigFile] does not exist. Use Set-JiraConfigServer first to define the configuration file." $PSCmdlet.ThrowTerminatingError($errorItem) } @@ -41,12 +41,11 @@ function Get-JiraConfigServer { $xmlConfig = $xml.DocumentElement if ($xmlConfig.LocalName -ne 'Config') { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.IO.FileFormatException]"XML had not the expected format"), - 'ConfigFile.UnexpectedElement', - [System.Management.Automation.ErrorCategory]::ParserError, - $ConfigFile - ) + $exception = ([System.IO.FileFormatException]"XML had not the expected format") + $errorId = 'ConfigFile.UnexpectedElement' + $errorCategory = ParserError + $errorTarget = $ConfigFile + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Unexpected document element [$($xmlConfig.LocalName)] in configuration file [$ConfigFile]. You may need to delete the config file and recreate it using Set-JiraConfigServer." $PSCmdlet.ThrowTerminatingError($errorItem) } @@ -55,12 +54,11 @@ function Get-JiraConfigServer { Write-Output $xmlConfig.Server } else { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.UriFormatException]"Could not find URI"), - 'ConfigFile.EmptyElement', - [System.Management.Automation.ErrorCategory]::OpenError, - $ConfigFile - ) + $exception = ([System.UriFormatException]"Could not find URI") + $errorId = 'ConfigFile.EmptyElement' + $errorCategory = OpenError + $errorTarget = $ConfigFile + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "No Server element is defined in the config file. Use Set-JiraConfigServer to define one." $PSCmdlet.ThrowTerminatingError($errorItem) } diff --git a/JiraPS/Public/Get-JiraField.ps1 b/JiraPS/Public/Get-JiraField.ps1 index 21697071..fd55df9f 100644 --- a/JiraPS/Public/Get-JiraField.ps1 +++ b/JiraPS/Public/Get-JiraField.ps1 @@ -1,12 +1,15 @@ function Get-JiraField { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding( DefaultParameterSetName = '_All' )] param( [Parameter( Position = 0, Mandatory, ValueFromPipeline, ParameterSetName = '_Search' )] [String[]] $Field, - [PSCredential] - $Credential + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty ) begin { diff --git a/JiraPS/Public/Get-JiraFilter.ps1 b/JiraPS/Public/Get-JiraFilter.ps1 index 4ca4d6b8..412c161c 100644 --- a/JiraPS/Public/Get-JiraFilter.ps1 +++ b/JiraPS/Public/Get-JiraFilter.ps1 @@ -1,4 +1,5 @@ function Get-JiraFilter { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding(DefaultParameterSetName = 'ByFilterID')] param( [Parameter( Position = 0, Mandatory, ParameterSetName = 'ByFilterID' )] @@ -16,12 +17,11 @@ [ValidateScript( { if (("JiraPS.Filter" -notin $_.PSObject.TypeNames) -and (($_ -isnot [String]))) { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid Type for Parameter"), - 'ParameterType.NotJiraFilter', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $_ - ) + $exception = ([System.ArgumentException]"Invalid Type for Parameter") #fix code highlighting] + $errorId = 'ParameterType.NotJiraFilter' + $errorCategory = 'InvalidArgument' + $errorTarget = $_ + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Wrong object type provided for Filter. Expected [JiraPS.Filter] or [String], but was $($_.GetType().Name)" $PSCmdlet.ThrowTerminatingError($errorItem) <# @@ -37,8 +37,15 @@ [Object[]] $InputObject, - [PSCredential] - $Credential + [Parameter( Mandatory, ParameterSetName = 'MyFavorite' )] + [Alias('Favourite')] + [Switch] + $Favorite, + + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty ) begin { @@ -86,6 +93,17 @@ Write-Output (Get-JiraFilter -Id $thisId -Credential $Credential) } } + "MyFavorite" { + $parameter = @{ + URI = $resourceURi -f "favourite" + Method = "GET" + Credential = $Credential + } + Write-Debug "[$($MyInvocation.MyCommand.Name)] Invoking JiraMethod with `$parameter" + $result = Invoke-JiraMethod @parameter + + Write-Output (ConvertTo-JiraFilter -InputObject $result) + } } } diff --git a/JiraPS/Public/Get-JiraFilterPermission.ps1 b/JiraPS/Public/Get-JiraFilterPermission.ps1 new file mode 100644 index 00000000..4f04f707 --- /dev/null +++ b/JiraPS/Public/Get-JiraFilterPermission.ps1 @@ -0,0 +1,52 @@ +function Get-JiraFilterPermission { + # .ExternalHelp ..\JiraPS-help.xml + [CmdletBinding( DefaultParameterSetName = 'ById' )] + # [OutputType( [JiraPS.FilterPermission] )] + param( + [Parameter( Position = 0, Mandatory, ValueFromPipeline, ParameterSetName = 'ByInputObject' )] + [ValidateNotNullOrEmpty()] + [PSTypeName('JiraPS.Filter')] + $Filter, + + [Parameter( Position = 0, Mandatory, ValueFromPipeline, ParameterSetName = 'ById')] + [ValidateNotNullOrEmpty()] + [UInt32[]] + $Id, + + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty + ) + + begin { + Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started" + + $resourceURi = "{0}/permission" + } + + process { + Write-DebugMessage "[$($MyInvocation.MyCommand.Name)] ParameterSetName: $($PsCmdlet.ParameterSetName)" + Write-DebugMessage "[$($MyInvocation.MyCommand.Name)] PSBoundParameters: $($PSBoundParameters | Out-String)" + + if ($PSCmdlet.ParameterSetName -eq 'ById') { + $Filter = Get-JiraFilter -Id $Id + } + + foreach ($_filter in $Filter) { + $parameter = @{ + URI = $resourceURi -f $_filter.RestURL + Method = "GET" + Credential = $Credential + } + Write-Debug "[$($MyInvocation.MyCommand.Name)] Invoking JiraMethod with `$parameter" + $result = Invoke-JiraMethod @parameter + + Write-Output (ConvertTo-JiraFilter -InputObject $_filter -FilterPermissions $result) + } + } + + end { + Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete" + } +} diff --git a/JiraPS/Public/Get-JiraGroup.ps1 b/JiraPS/Public/Get-JiraGroup.ps1 index 43f74bac..dc4e395e 100644 --- a/JiraPS/Public/Get-JiraGroup.ps1 +++ b/JiraPS/Public/Get-JiraGroup.ps1 @@ -1,4 +1,5 @@ function Get-JiraGroup { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding()] param( [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName )] @@ -7,8 +8,10 @@ function Get-JiraGroup { [String[]] $GroupName, - [PSCredential] - $Credential + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty ) begin { diff --git a/JiraPS/Public/Get-JiraGroupMember.ps1 b/JiraPS/Public/Get-JiraGroupMember.ps1 index 1c0ac0df..0ffe0579 100644 --- a/JiraPS/Public/Get-JiraGroupMember.ps1 +++ b/JiraPS/Public/Get-JiraGroupMember.ps1 @@ -1,17 +1,17 @@ function Get-JiraGroupMember { - [CmdletBinding()] + # .ExternalHelp ..\JiraPS-help.xml + [CmdletBinding( SupportsPaging )] param( [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName )] [ValidateNotNullOrEmpty()] [ValidateScript( { if (("JiraPS.Group" -notin $_.PSObject.TypeNames) -and (($_ -isnot [String]))) { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid Type for Parameter"), - 'ParameterType.NotJiraGroup', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $_ - ) + $exception = ([System.ArgumentException]"Invalid Type for Parameter") #fix code highlighting] + $errorId = 'ParameterType.NotJiraGroup' + $errorCategory = 'InvalidArgument' + $errorTarget = $_ + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Wrong object type provided for Group. Expected [JiraPS.Group] or [String], but was $($_.GetType().Name)" $PSCmdlet.ThrowTerminatingError($errorItem) <# @@ -27,34 +27,33 @@ function Get-JiraGroupMember { [Object[]] $Group, - [ValidateRange(0, [Int]::MaxValue)] - [Int] + [Switch] + $IncludeInactive, + + [UInt32] $StartIndex = 0, - [ValidateRange(0, [Int]::MaxValue)] - [Int] - $MaxResults = 0, + [UInt32] + $MaxResults, + + [UInt32] + $PageSize = $script:DefaultPageSize, - [PSCredential] - $Credential + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty ) begin { Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started" - # This is a parameter in Get-JiraIssue, but in testing, JIRA doesn't - # reliably return more than 50 results at a time. - $pageSize = 50 + $server = Get-JiraConfigServer -ErrorAction Stop - if ($MaxResults -eq 0) { - Write-DebugMessage "[$($MyInvocation.MyCommand.Name)] MaxResults was not specified. Using loop mode to obtain all members." - $loopMode = $true - } - else { - $loopMode = $false - if ($MaxResults -gt 50) { - Write-Warning "JIRA's API may not properly support MaxResults values higher than 50 for this method. If you receive inconsistent results, do not pass the MaxResults parameter to this function to return all results." - } + $resourceURi = "$server/rest/api/latest/group/member" + + if ($PageSize -gt 50) { + Write-Warning "JIRA's API may not properly support MaxResults values higher than 50 for this method. If you receive inconsistent results, do not pass the MaxResults parameter to this function to return all results." } } @@ -68,49 +67,37 @@ function Get-JiraGroupMember { Write-Verbose "[$($MyInvocation.MyCommand.Name)] Processing [$_group]" Write-Debug "[$($MyInvocation.MyCommand.Name)] Processing `$_group [$_group]" - if ($loopMode) { - # Using the Size property of the group object, iterate - # through all users in a given group. - - $totalResults = $_group.Size - $allUsers = New-Object -TypeName System.Collections.ArrayList - - for ($i = 0; $i -lt $totalResults; $i = $i + $PageSize) { - if ($PageSize -gt ($i + $totalResults)) { - $thisPageSize = $totalResults - $i - } - else { - $thisPageSize = $PageSize - } - $percentComplete = ($i / $totalResults) * 100 - Write-Progress -Activity "$($MyInvocation.MyCommand.Name)" -Status "Obtaining members ($i - $($i + $thisPageSize) of $totalResults)..." -PercentComplete $percentComplete - - Write-DebugMessage "[$($MyInvocation.MyCommand.Name)] Obtaining members $i - $($i + $thisPageSize)..." - $thisSection = Get-JiraGroupMember -Group $_group -StartIndex $i -MaxResults $thisPageSize -Credential $Credential - - foreach ($_user in $thisSection) { - [void] $allUsers.Add($_user) - } + $parameter = @{ + URI = $resourceURi + Method = "GET" + GetParameter = @{ + groupname = $_group.Name + maxResults = $PageSize } - - Write-Progress -Activity "$($MyInvocation.MyCommand.Name)" -Completed - Write-Output ($allUsers.ToArray()) + OutputType = "JiraUser" + Paging = $true + Credential = $Credential + } + if ($IncludeInactive) { + $parameter["includeInactiveUsers"] = $true } - else { - # Since user is an expandable property of the returned - # group from JIRA, JIRA doesn't use the MaxResults argument - # found in other REST endpoints. Instead, we need to pass - # expand=users[0:15] for users 0-15 (inclusive). - $parameter = @{ - URI = '{0}&expand=users[{1}:{2}]' -f $_group.RestUrl, $StartIndex, ($StartIndex + $MaxResults) - Method = "GET" - Credential = $Credential - } - Write-Debug "[$($MyInvocation.MyCommand.Name)] Invoking JiraMethod with `$parameter" - $result = Invoke-JiraMethod @parameter - Write-Output (ConvertTo-JiraGroup -InputObject $result).Member + # Paging + ($PSCmdlet.PagingParameters | Get-Member -MemberType Property).Name | ForEach-Object { + $parameter[$_] = $PSCmdlet.PagingParameters.$_ + } + # Make `SupportsPaging` be backwards compatible + if ($StartIndex) { + Write-Warning "[$($MyInvocation.MyCommand.Name)] The parameter '-StartIndex' has been marked as deprecated. For more information, plase read the help." + $parameter["Skip"] = $StartIndex } + if ($MaxResults) { + Write-Warning "[$($MyInvocation.MyCommand.Name)] The parameter '-MaxResults' has been marked as deprecated. For more information, plase read the help." + $parameter["First"] = $MaxResults + } + + Write-Debug "[$($MyInvocation.MyCommand.Name)] Invoking JiraMethod with `$parameter" + Invoke-JiraMethod @parameter } } diff --git a/JiraPS/Public/Get-JiraIssue.ps1 b/JiraPS/Public/Get-JiraIssue.ps1 index 552a3af7..517b83fb 100644 --- a/JiraPS/Public/Get-JiraIssue.ps1 +++ b/JiraPS/Public/Get-JiraIssue.ps1 @@ -1,8 +1,10 @@ function Get-JiraIssue { - [CmdletBinding(DefaultParameterSetName = 'ByIssueKey')] + # .ExternalHelp ..\JiraPS-help.xml + [CmdletBinding( SupportsPaging, DefaultParameterSetName = 'ByIssueKey' )] param( [Parameter( Position = 0, Mandatory, ParameterSetName = 'ByIssueKey' )] [ValidateNotNullOrEmpty()] + [Alias('Issue')] [String[]] $Key, @@ -11,12 +13,11 @@ function Get-JiraIssue { [ValidateScript( { if (("JiraPS.Issue" -notin $_.PSObject.TypeNames) -and (($_ -isnot [String]))) { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid Type for Parameter"), - 'ParameterType.NotJiraIssue', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $_ - ) + $exception = ([System.ArgumentException]"Invalid Type for Parameter") #fix code highlighting] + $errorId = 'ParameterType.NotJiraIssue' + $errorCategory = 'InvalidArgument' + $errorTarget = $_ + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Wrong object type provided for Issue. Expected [JiraPS.Issue] or [String], but was $($_.GetType().Name)" $PSCmdlet.ThrowTerminatingError($errorItem) } @@ -44,12 +45,11 @@ function Get-JiraIssue { [ValidateScript( { if (("JiraPS.Filter" -notin $_.PSObject.TypeNames) -and (($_ -isnot [String]))) { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid Type for Parameter"), - 'ParameterType.NotJiraFilter', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $_ - ) + $exception = ([System.ArgumentException]"Invalid Type for Parameter") #fix code highlighting] + $errorId = 'ParameterType.NotJiraFilter' + $errorCategory = 'InvalidArgument' + $errorTarget = $_ + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Wrong object type provided for Filter. Expected [JiraPS.Filter] or [String], but was $($_.GetType().Name)" $PSCmdlet.ThrowTerminatingError($errorItem) <# @@ -67,21 +67,23 @@ function Get-JiraIssue { [Parameter( ParameterSetName = 'ByJQL' )] [Parameter( ParameterSetName = 'ByFilter' )] - [Int] + [UInt32] $StartIndex = 0, [Parameter( ParameterSetName = 'ByJQL' )] [Parameter( ParameterSetName = 'ByFilter' )] - [Int] + [UInt32] $MaxResults = 0, [Parameter( ParameterSetName = 'ByJQL' )] [Parameter( ParameterSetName = 'ByFilter' )] - [Int] - $PageSize = 50, + [UInt32] + $PageSize = $script:DefaultPageSize, - [PSCredential] - $Credential + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty ) begin { @@ -89,17 +91,8 @@ function Get-JiraIssue { $server = Get-JiraConfigServer -ErrorAction Stop - if (($PSCmdlet.ParameterSetName -in @('ByJQL', 'ByFilter')) -and $MaxResults -eq 0) { - Write-DebugMessage "[$($MyInvocation.MyCommand.Name)] Using loop mode to obtain all results" - $MaxResults = 1 - $loopMode = $true - } - else { - $loopMode = $false - } - $resourceURi = "$server/rest/api/latest/issue/{0}?expand=transitions" - $searchURi = "$server/rest/api/latest/search?jql={0}&validateQuery=true&expand=transitions&startAt={1}&maxResults={2}" + $searchURi = "$server/rest/api/latest/search" } process { @@ -133,70 +126,72 @@ function Get-JiraIssue { } } 'ByJQL' { - $escapedQuery = ConvertTo-URLEncoded $Query - $parameter = @{ - URI = $searchURi -f $escapedQuery, $StartIndex, $MaxResults - Method = "GET" - Credential = $Credential + URI = $searchURi + Method = "GET" + GetParameter = @{ + jql = (ConvertTo-URLEncoded $Query) + validateQuery = $true + expand = "transitions" + maxResults = $PageSize + } + OutputType = "JiraIssue" + Paging = $true + Credential = $Credential + } + # Paging + ($PSCmdlet.PagingParameters | Get-Member -MemberType Property).Name | ForEach-Object { + $parameter[$_] = $PSCmdlet.PagingParameters.$_ + } + # Make `SupportsPaging` be backwards compatible + if ($StartIndex) { + Write-Warning "[$($MyInvocation.MyCommand.Name)] The parameter '-StartIndex' has been marked as deprecated. For more information, plase read the help." + $parameter["Skip"] = $StartIndex + } + if ($MaxResults) { + Write-Warning "[$($MyInvocation.MyCommand.Name)] The parameter '-MaxResults' has been marked as deprecated. For more information, plase read the help." + $parameter["First"] = $MaxResults } - Write-Debug "[$($MyInvocation.MyCommand.Name)] Invoking JiraMethod with `$parameter" - $result = Invoke-JiraMethod @parameter - - if ($result) { - # {"startAt":0,"maxResults":50,"total":0,"issues":[]} - - if ($loopMode) { - $totalResults = $result.total - - Write-DebugMessage "[$($MyInvocation.MyCommand.Name)] Paging through all issues (loop mode)" - $allIssues = New-Object -TypeName System.Collections.ArrayList - - for ($i = 0; $i -lt $totalResults; $i = $i + $PageSize) { - $percentComplete = ($i / $totalResults) * 100 - Write-Progress -Activity "$($MyInvocation.MyCommand.Name)" -Status "Obtaining issues ($i - $($i + $PageSize))..." -PercentComplete $percentComplete - Write-DebugMessage "[$($MyInvocation.MyCommand.Name)] Obtaining issues $i - $($i + $PageSize)..." - $thisSection = Get-JiraIssue -Query $Query -StartIndex $i -MaxResults $PageSize -Credential $Credential - foreach ($t in $thisSection) { - [void] $allIssues.Add($t) - } - } - Write-Progress -Activity "$($MyInvocation.MyCommand.Name)" -Status 'Obtaining issues' -Completed - Write-Output ($allIssues.ToArray()) - } - elseif ($result.total -gt 0) { - Write-Output (ConvertTo-JiraIssue -InputObject $result.issues) - } - else { - $errorMessage = @{ - Category = "ObjectNotFound" - CategoryActivity = "Searching for resource" - Message = "The JQL query did not return any results" - } - Write-Error @errorMessage - } - } + Write-Debug "[$($MyInvocation.MyCommand.Name)] Invoking JiraMethod with `$parameter" + Invoke-JiraMethod @parameter } 'ByFilter' { $filterObj = Get-JiraFilter -InputObject $Filter -Credential $Credential -ErrorAction Stop - $jql = $filterObj.JQL <# #ToDo:CustomClass Once we have custom classes, this will no longer be necessary #> - # MaxResults would have been set to 1 in the Begin block if it - # was not supplied as a parameter. We don't want to explicitly - # invoke this method recursively with a MaxResults value of 1 - # if it wasn't initially provided to us. - if ($loopMode) { - Write-Output (Get-JiraIssue -Query $jql -Credential $Credential) + $parameter = @{ + URI = $filterObj.SearchUrl + Method = "GET" + GetParameter = @{ + validateQuery = $true + expand = "transitions" + maxResults = $PageSize + } + OutputType = "JiraIssue" + Paging = $true + Credential = $Credential } - else { - Write-Output (Get-JiraIssue -Query $jql -Credential $Credential -MaxResults $MaxResults) + # Paging + ($PSCmdlet.PagingParameters | Get-Member -MemberType Property).Name | ForEach-Object { + $parameter[$_] = $PSCmdlet.PagingParameters.$_ + } + # Make `SupportsPaging` be backwards compatible + if ($StartIndex) { + Write-Warning "[$($MyInvocation.MyCommand.Name)] The parameter '-StartIndex' has been marked as deprecated. For more information, plase read the help." + $parameter["Skip"] = $StartIndex } + if ($MaxResults) { + Write-Warning "[$($MyInvocation.MyCommand.Name)] The parameter '-MaxResults' has been marked as deprecated. For more information, plase read the help." + $parameter["First"] = $MaxResults + } + + Write-Debug "[$($MyInvocation.MyCommand.Name)] Invoking JiraMethod with `$parameter" + Invoke-JiraMethod @parameter } } } diff --git a/JiraPS/Public/Get-JiraIssueAttachment.ps1 b/JiraPS/Public/Get-JiraIssueAttachment.ps1 index e1241e41..cf833b16 100644 --- a/JiraPS/Public/Get-JiraIssueAttachment.ps1 +++ b/JiraPS/Public/Get-JiraIssueAttachment.ps1 @@ -1,4 +1,5 @@ function Get-JiraIssueAttachment { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding()] param( [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName )] @@ -6,12 +7,11 @@ function Get-JiraIssueAttachment { [ValidateScript( { if (("JiraPS.Issue" -notin $_.PSObject.TypeNames) -and (($_ -isnot [String]))) { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid Type for Parameter"), - 'ParameterType.NotJiraIssue', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $_ - ) + $exception = ([System.ArgumentException]"Invalid Type for Parameter") #fix code highlighting] + $errorId = 'ParameterType.NotJiraIssue' + $errorCategory = [System.Management.Automation.ErrorCategory]::InvalidArgument + $errorTarget = $_ + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Wrong object type provided for Issue. Expected [JiraPS.Issue] or [String], but was $($_.GetType().Name)" $PSCmdlet.ThrowTerminatingError($errorItem) <# @@ -31,8 +31,10 @@ function Get-JiraIssueAttachment { [String] $FileName, - [PSCredential] - $Credential + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty ) begin { diff --git a/JiraPS/Public/Get-JiraIssueComment.ps1 b/JiraPS/Public/Get-JiraIssueComment.ps1 index 17c8bb53..24956cd3 100644 --- a/JiraPS/Public/Get-JiraIssueComment.ps1 +++ b/JiraPS/Public/Get-JiraIssueComment.ps1 @@ -1,4 +1,5 @@ function Get-JiraIssueComment { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding()] param( [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName )] @@ -6,12 +7,11 @@ function Get-JiraIssueComment { [ValidateScript( { if (("JiraPS.Issue" -notin $_.PSObject.TypeNames) -and (($_ -isnot [String]))) { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid Type for Parameter"), - 'ParameterType.NotJiraIssue', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $_ - ) + $exception = ([System.ArgumentException]"Invalid Type for Parameter") #fix code highlighting] + $errorId = 'ParameterType.NotJiraIssue' + $errorCategory = 'InvalidArgument' + $errorTarget = $_ + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Wrong object type provided for Issue. Expected [JiraPS.Issue] or [String], but was $($_.GetType().Name)" $PSCmdlet.ThrowTerminatingError($errorItem) <# @@ -28,8 +28,10 @@ function Get-JiraIssueComment { [Object] $Issue, - [PSCredential] - $Credential + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty ) begin { @@ -44,14 +46,18 @@ function Get-JiraIssueComment { $issueObj = Resolve-JiraIssueObject -InputObject $Issue -Credential $Credential $parameter = @{ - URI = "{0}/comment" -f $issueObj.RestURL - Method = "GET" - Credential = $Credential + URI = "{0}/comment" -f $issueObj.RestURL + Method = "GET" + GetParameter = @{ + maxResults = $PageSize + } + OutputType = "JiraComment" + Paging = $true + Credential = $Credential } - Write-Debug "[$($MyInvocation.MyCommand.Name)] Invoking JiraMethod with `$parameter" - $result = Invoke-JiraMethod @parameter - Write-Output (ConvertTo-JiraComment -InputObject $result.comments) + Write-Debug "[$($MyInvocation.MyCommand.Name)] Invoking JiraMethod with `$parameter" + Invoke-JiraMethod @parameter } end { diff --git a/JiraPS/Public/Get-JiraIssueCreateMetadata.ps1 b/JiraPS/Public/Get-JiraIssueCreateMetadata.ps1 index 3caf79f1..f1a03ab0 100644 --- a/JiraPS/Public/Get-JiraIssueCreateMetadata.ps1 +++ b/JiraPS/Public/Get-JiraIssueCreateMetadata.ps1 @@ -1,4 +1,5 @@ function Get-JiraIssueCreateMetadata { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding()] param( [Parameter( Mandatory )] @@ -9,8 +10,10 @@ function Get-JiraIssueCreateMetadata { [String] $IssueType, - [PSCredential] - $Credential + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty ) begin { @@ -74,12 +77,11 @@ function Get-JiraIssueCreateMetadata { Write-Output (ConvertTo-JiraCreateMetaField -InputObject $result) } else { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"No results"), - 'IssueMetadata.ObjectNotFound', - [System.Management.Automation.ErrorCategory]::ObjectNotFound, - $Project - ) + $exception = ([System.ArgumentException]"No results") + $errorId = 'IssueMetadata.ObjectNotFound' + $errorCategory = 'ObjectNotFound' + $errorTarget = $Project + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "No metadata found for project $Project and issueType $IssueType." Throw $errorItem } diff --git a/JiraPS/Public/Get-JiraIssueEditMetadata.ps1 b/JiraPS/Public/Get-JiraIssueEditMetadata.ps1 index fa567744..a44655c6 100644 --- a/JiraPS/Public/Get-JiraIssueEditMetadata.ps1 +++ b/JiraPS/Public/Get-JiraIssueEditMetadata.ps1 @@ -1,4 +1,5 @@ function Get-JiraIssueEditMetadata { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding()] param( [Parameter( Mandatory )] @@ -9,8 +10,10 @@ function Get-JiraIssueEditMetadata { Once we have custom classes, this should be a JiraPS.Issue #> - [PSCredential] - $Credential + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty ) begin { @@ -77,12 +80,11 @@ function Get-JiraIssueEditMetadata { Write-Output (ConvertTo-JiraEditMetaField -InputObject $result) } else { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"No results"), - 'IssueMetadata.ObjectNotFound', - [System.Management.Automation.ErrorCategory]::ObjectNotFound, - $Project - ) + $exception = ([System.ArgumentException]"No results") + $errorId = 'IssueMetadata.ObjectNotFound' + $errorCategory = 'ObjectNotFound' + $errorTarget = $Project + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "No metadata found for project $Project and issueType $IssueType." Throw $errorItem } diff --git a/JiraPS/Public/Get-JiraIssueLink.ps1 b/JiraPS/Public/Get-JiraIssueLink.ps1 index 93c84f2f..36959a52 100644 --- a/JiraPS/Public/Get-JiraIssueLink.ps1 +++ b/JiraPS/Public/Get-JiraIssueLink.ps1 @@ -1,11 +1,15 @@ function Get-JiraIssueLink { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding()] param( [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName )] [Int[]] $Id, - [PSCredential] $Credential + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty ) begin { @@ -22,12 +26,11 @@ function Get-JiraIssueLink { # Validate input object from Pipeline if (($_) -and ("JiraPS.IssueLink" -notin $_.PSObject.TypeNames)) { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid Parameter"), - 'ParameterProperties.WrongObjectType', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $Id - ) + $exception = ([System.ArgumentException]"Invalid Parameter") + $errorId = 'ParameterProperties.WrongObjectType' + $errorCategory = [System.Management.Automation.ErrorCategory]::InvalidArgument + $errorTarget = $Id + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "The IssueLink provided did not match the constraints." $PSCmdlet.ThrowTerminatingError($errorItem) } diff --git a/JiraPS/Public/Get-JiraIssueLinkType.ps1 b/JiraPS/Public/Get-JiraIssueLinkType.ps1 index 6beecaca..0ca0d39a 100644 --- a/JiraPS/Public/Get-JiraIssueLinkType.ps1 +++ b/JiraPS/Public/Get-JiraIssueLinkType.ps1 @@ -1,4 +1,5 @@ function Get-JiraIssueLinkType { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding( DefaultParameterSetName = '_All' )] param( [Parameter( Position = 0, Mandatory, ParameterSetName = '_Search' )] @@ -6,12 +7,11 @@ function Get-JiraIssueLinkType { [ValidateScript( { if (("JiraPS.IssueLinkType" -notin $_.PSObject.TypeNames) -and (($_ -isnot [String])) -and (($_ -isnot [Int]))) { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid Type for Parameter"), - 'ParameterType.NotJiraIssueLinkType', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $_ - ) + $exception = ([System.ArgumentException]"Invalid Type for Parameter") #fix code highlighting] + $errorId = 'ParameterType.NotJiraIssueLinkType' + $errorCategory = 'InvalidArgument' + $errorTarget = $_ + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Wrong object type provided for IssueLinkType. Expected [JiraPS.IssueLinkType], [String] or [Int], but was $($_.GetType().Name)" $PSCmdlet.ThrowTerminatingError($errorItem) <# @@ -27,8 +27,10 @@ function Get-JiraIssueLinkType { [Object] $LinkType, - [PSCredential] - $Credential + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty ) begin { diff --git a/JiraPS/Public/Get-JiraIssueType.ps1 b/JiraPS/Public/Get-JiraIssueType.ps1 index 0c9e67bf..41df6832 100644 --- a/JiraPS/Public/Get-JiraIssueType.ps1 +++ b/JiraPS/Public/Get-JiraIssueType.ps1 @@ -1,12 +1,15 @@ function Get-JiraIssueType { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding( DefaultParameterSetName = '_All' )] param( [Parameter( Position = 0, Mandatory, ValueFromPipeline, ParameterSetName = '_Search' )] [String[]] $IssueType, - [PSCredential] - $Credential + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty ) begin { diff --git a/JiraPS/Public/Get-JiraIssueWatcher.ps1 b/JiraPS/Public/Get-JiraIssueWatcher.ps1 index ab3b74f2..2d671933 100644 --- a/JiraPS/Public/Get-JiraIssueWatcher.ps1 +++ b/JiraPS/Public/Get-JiraIssueWatcher.ps1 @@ -1,4 +1,5 @@ function Get-JiraIssueWatcher { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding()] param( [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName )] @@ -6,12 +7,11 @@ [ValidateScript( { if (("JiraPS.Issue" -notin $_.PSObject.TypeNames) -and (($_ -isnot [String]))) { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid Type for Parameter"), - 'ParameterType.NotJiraIssue', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $_ - ) + $exception = ([System.ArgumentException]"Invalid Type for Parameter") #fix code highlight] + $errorId = 'ParameterType.NotJiraIssue' + $errorCategory = 'InvalidArgument' + $errorTarget = $_ + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Wrong object type provided for Issue. Expected [JiraPS.Issue] or [String], but was $($_.GetType().Name)" $PSCmdlet.ThrowTerminatingError($errorItem) <# @@ -28,8 +28,10 @@ [Object] $Issue, - [PSCredential] - $Credential + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty ) begin { diff --git a/JiraPS/Public/Get-JiraPriority.ps1 b/JiraPS/Public/Get-JiraPriority.ps1 index 784bad8c..441c1bf4 100644 --- a/JiraPS/Public/Get-JiraPriority.ps1 +++ b/JiraPS/Public/Get-JiraPriority.ps1 @@ -1,12 +1,15 @@ function Get-JiraPriority { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding( DefaultParameterSetName = '_All' )] param( [Parameter( Position = 0, Mandatory, ValueFromPipeline, ParameterSetName = '_Search' )] [Int[]] $Id, - [PSCredential] - $Credential + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty ) begin { diff --git a/JiraPS/Public/Get-JiraProject.ps1 b/JiraPS/Public/Get-JiraProject.ps1 index 195a9fd3..c107663b 100644 --- a/JiraPS/Public/Get-JiraProject.ps1 +++ b/JiraPS/Public/Get-JiraProject.ps1 @@ -1,12 +1,15 @@ function Get-JiraProject { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding( DefaultParameterSetName = '_All' )] param( [Parameter( Position = 0, Mandatory, ValueFromPipeline, ParameterSetName = '_Search' )] [String[]] $Project, - [PSCredential] - $Credential + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty ) begin { diff --git a/JiraPS/Public/Get-JiraRemoteLink.ps1 b/JiraPS/Public/Get-JiraRemoteLink.ps1 index ece03eca..769f1c75 100644 --- a/JiraPS/Public/Get-JiraRemoteLink.ps1 +++ b/JiraPS/Public/Get-JiraRemoteLink.ps1 @@ -1,4 +1,5 @@ function Get-JiraRemoteLink { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding()] param( [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName )] @@ -6,12 +7,11 @@ function Get-JiraRemoteLink { [ValidateScript( { if (("JiraPS.Issue" -notin $_.PSObject.TypeNames) -and (($_ -isnot [String]))) { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid Type for Parameter"), - 'ParameterType.NotJiraIssue', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $_ - ) + $exception = ([System.ArgumentException]"Invalid Type for Parameter") #fix code highlighting] + $errorId = 'ParameterType.NotJiraIssue' + $errorCategory = 'InvalidArgument' + $errorTarget = $_ + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Wrong object type provided for Issue. Expected [JiraPS.Issue] or [String], but was $($_.GetType().Name)" $PSCmdlet.ThrowTerminatingError($errorItem) <# @@ -31,8 +31,10 @@ function Get-JiraRemoteLink { [Int] $LinkId, - [PSCredential] - $Credential + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty ) begin { @@ -63,7 +65,7 @@ function Get-JiraRemoteLink { Write-Debug "[$($MyInvocation.MyCommand.Name)] Invoking JiraMethod with `$parameter" $result = Invoke-JiraMethod @parameter - Write-Output (ConvertTo-JiraIssueLinkType -InputObject $result) + Write-Output (ConvertTo-JiraLink -InputObject $result) } } diff --git a/JiraPS/Public/Get-JiraServerInformation.ps1 b/JiraPS/Public/Get-JiraServerInformation.ps1 index 0f1cbb6b..aee87a3a 100644 --- a/JiraPS/Public/Get-JiraServerInformation.ps1 +++ b/JiraPS/Public/Get-JiraServerInformation.ps1 @@ -1,8 +1,11 @@ function Get-JiraServerInformation { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding()] param( - [PSCredential] - $Credential + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty ) begin { diff --git a/JiraPS/Public/Get-JiraSession.ps1 b/JiraPS/Public/Get-JiraSession.ps1 index 4e53f9ba..578cd07c 100644 --- a/JiraPS/Public/Get-JiraSession.ps1 +++ b/JiraPS/Public/Get-JiraSession.ps1 @@ -1,4 +1,5 @@ function Get-JiraSession { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding()] param() diff --git a/JiraPS/Public/Get-JiraUser.ps1 b/JiraPS/Public/Get-JiraUser.ps1 index cc2bdc1f..807d7587 100644 --- a/JiraPS/Public/Get-JiraUser.ps1 +++ b/JiraPS/Public/Get-JiraUser.ps1 @@ -1,4 +1,5 @@ function Get-JiraUser { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding( DefaultParameterSetName = 'Self' )] param( [Parameter( Position = 0, Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'ByUserName' )] @@ -13,8 +14,20 @@ function Get-JiraUser { [Switch] $IncludeInactive, - [PSCredential] - $Credential + [Parameter( ParameterSetName = 'ByUserName' )] + [ValidateRange(1, 1000)] + [UInt32] + $MaxResults = 50, + + [Parameter( ParameterSetName = 'ByUserName' )] + [ValidateNotNullOrEmpty()] + [UInt64] + $Skip = 0, + + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty ) begin { @@ -28,6 +41,12 @@ function Get-JiraUser { if ($IncludeInactive) { $searchResourceUri += "&includeInactive=true" } + if ($MaxResults) { + $searchResourceUri += "&maxResults=$MaxResults" + } + if ($Skip) { + $searchResourceUri += "&startAt=$Skip" + } } process { diff --git a/JiraPS/Public/Get-JiraVersion.ps1 b/JiraPS/Public/Get-JiraVersion.ps1 index 9e00ba9b..116f96b3 100644 --- a/JiraPS/Public/Get-JiraVersion.ps1 +++ b/JiraPS/Public/Get-JiraVersion.ps1 @@ -1,5 +1,6 @@ function Get-JiraVersion { - [CmdletBinding( DefaultParameterSetName = 'byId' )] + # .ExternalHelp ..\JiraPS-help.xml + [CmdletBinding( SupportsPaging, DefaultParameterSetName = 'byId' )] param( [Parameter( Mandatory, ParameterSetName = 'byId' )] [Int[]] @@ -22,10 +23,25 @@ [Parameter( ParameterSetName = 'byInputProject' )] [Alias('Versions')] [String[]] - $Name, - - [PSCredential] - $Credential + $Name = "*", + + [Parameter( ParameterSetName = 'byProject')] + [Parameter( ParameterSetName = 'byInputProject')] + [ValidateSet("sequence", + "name", + "startDate", + "releaseDate" + )] + [String] + $Sort = "name", + + [UInt32] + $PageSize = $script:DefaultPageSize, + + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty ) begin { @@ -73,18 +89,32 @@ $projectData = Get-JiraProject -Project $_project -Credential $Credential $parameter = @{ - URI = $resourceURi -f "project/$($projectData.key)/versions" - Method = "GET" - Credential = $Credential + URI = $resourceURi -f "project/$($projectData.key)/version" + Method = "GET" + GetParameter = @{ + orderBy = $Sort + maxResults = $PageSize + } + Paging = $true + OutputType = "JiraVersion" + Credential = $Credential + } + # Paging + ($PSCmdlet.PagingParameters | Get-Member -MemberType Property).Name | ForEach-Object { + $parameter[$_] = $PSCmdlet.PagingParameters.$_ } + Write-Debug "[$($MyInvocation.MyCommand.Name)] Invoking JiraMethod with `$parameter" $result = Invoke-JiraMethod @parameter - if ($Name) { - $result = $result | Where-Object {$_.Name -in $Name} + $result | Where-Object { + $__ = $_.Name + Write-DebugMessage ($__ | Out-String) + $Name | Foreach-Object { + Write-Verbose "[$($MyInvocation.MyCommand.Name)] Matching $_ against $($__)" + $__ -like $_ + } } - - Write-Output (ConvertTo-JiraVersion -InputObject $result) } } } diff --git a/JiraPS/Public/Invoke-JiraIssueTransition.ps1 b/JiraPS/Public/Invoke-JiraIssueTransition.ps1 index 55f7dea7..e9f96d70 100644 --- a/JiraPS/Public/Invoke-JiraIssueTransition.ps1 +++ b/JiraPS/Public/Invoke-JiraIssueTransition.ps1 @@ -1,4 +1,5 @@ function Invoke-JiraIssueTransition { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding()] param( [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName )] @@ -6,12 +7,11 @@ function Invoke-JiraIssueTransition { [ValidateScript( { if (("JiraPS.Issue" -notin $_.PSObject.TypeNames) -and (($_ -isnot [String]))) { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid Type for Parameter"), - 'ParameterType.NotJiraIssue', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $_ - ) + $exception = ([System.ArgumentException]"Invalid Type for Parameter") #fix code highlighting] + $errorId = 'ParameterType.NotJiraIssue' + $errorCategory = 'InvalidArgument' + $errorTarget = $_ + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Wrong object type provided for Issue. Expected [JiraPS.Issue] or [String], but was $($_.GetType().Name)" $PSCmdlet.ThrowTerminatingError($errorItem) <# @@ -41,8 +41,10 @@ function Invoke-JiraIssueTransition { [String] $Comment, - [PSCredential] - $Credential, + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty, [Switch] $Passthru @@ -69,12 +71,11 @@ function Invoke-JiraIssueTransition { $transitionId = [Int]"$Transition" } catch { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid Type for Parameter"), - 'ParameterType.NotJiraTransition', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $Transition - ) + $exception = ([System.ArgumentException]"Invalid Type for Parameter") + $errorId = 'ParameterType.NotJiraTransition' + $errorCategory = 'InvalidArgumenty' + $errorTarget = $Transition + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTargetError $errorItem.ErrorDetails = "Wrong object type provided for Transition. Expected [JiraPS.Transition] or [Int], but was $($Transition.GetType().Name)" $PSCmdlet.ThrowTerminatingError($errorItem) } @@ -82,12 +83,11 @@ function Invoke-JiraIssueTransition { Write-DebugMessage "[$($MyInvocation.MyCommand.Name)] Checking that the issue can perform the given transition" if (($issueObj.Transition.Id) -notcontains $transitionId) { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid value for Parameter"), - 'ParameterValue.InvalidTransition', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $Issue - ) + $exception = ([System.ArgumentException]"Invalid value for Parameter") + $errorId = 'ParameterValue.InvalidTransition' + $errorCategory = 'InvalidArgument' + $errorTarget = $Issue + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "The specified Jira issue cannot perform transition [$transitionId]. Check the issue's Transition property and provide a transition valid for its current state." $PSCmdlet.ThrowTerminatingError($errorItem) } @@ -115,12 +115,11 @@ function Invoke-JiraIssueTransition { $validAssignee = $true } else { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid value for Parameter"), - 'ParameterValue.InvalidAssignee', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $Assignee - ) + $exception = ([System.ArgumentException]"Invalid value for Parameter") + $errorId = 'ParameterValue.InvalidAssignee' + $errorCategory = 'InvalidArgument' + $errorTarget = $Assignee + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Unable to validate Jira user [$Assignee]. Use Get-JiraUser for more details." $PSCmdlet.ThrowTerminatingError($errorItem) } @@ -160,12 +159,11 @@ function Invoke-JiraIssueTransition { }) } else { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid value for Parameter"), - 'ParameterValue.InvalidFields', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $Fields - ) + $exception = ([System.ArgumentException]"Invalid value for Parameter") + $errorId = 'ParameterValue.InvalidFields' + $errorCategory = 'InvalidArgument' + $errorTarget = $Fields + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Unable to identify field [$name] from -Fields hashtable. Use Get-JiraField for more information." $PSCmdlet.ThrowTerminatingError($errorItem) } diff --git a/JiraPS/Public/Invoke-JiraMethod.ps1 b/JiraPS/Public/Invoke-JiraMethod.ps1 index 3148b294..61448d6e 100644 --- a/JiraPS/Public/Invoke-JiraMethod.ps1 +++ b/JiraPS/Public/Invoke-JiraMethod.ps1 @@ -1,6 +1,6 @@ function Invoke-JiraMethod { - #Requires -Version 3 - [CmdletBinding()] + # .ExternalHelp ..\JiraPS-help.xml + [CmdletBinding( SupportsPaging )] param ( [Parameter( Mandatory )] @@ -19,6 +19,12 @@ function Invoke-JiraMethod { [Hashtable] $Headers = @{}, + [Hashtable] + $GetParameter = @{}, + + [Switch] + $Paging, + [String] $InFile, @@ -28,9 +34,21 @@ function Invoke-JiraMethod { [Switch] $StoreSession, - [PSCredential] - $Credential, + [ValidateSet( + "JiraComment", + "JiraIssue", + "JiraUser", + "JiraVersion" + )] + [String] + $OutputType, + + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty, + # [Parameter( DontShow )] [ValidateNotNullOrEmpty()] [System.Management.Automation.PSCmdlet] $Cmdlet = $PSCmdlet @@ -39,28 +57,58 @@ function Invoke-JiraMethod { begin { Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started" - # pass input to local variable - # this allows to use the PSBoundParameters for recursion - $_headers = @{ # Set any default headers - "Accept" = "application/json" - "Accept-Charset" = "utf-8" - } - foreach ($item in $Headers.Key) { $_headers[$item] = $Headers[$item] } - } + Set-TlsLevel -Tls12 - process { Write-DebugMessage "[$($MyInvocation.MyCommand.Name)] ParameterSetName: $($PsCmdlet.ParameterSetName)" Write-DebugMessage "[$($MyInvocation.MyCommand.Name)] PSBoundParameters: $($PSBoundParameters | Out-String)" # load DefaultParameters for Invoke-WebRequest # as the global PSDefaultParameterValues is not used - $PSDefaultParameterValues = $global:PSDefaultParameterValues + $PSDefaultParameterValues = Resolve-DefaultParameterValue -Reference $global:PSDefaultParameterValues -CommandName 'Invoke-WebRequest' + + #region Headers + # Construct the Headers with the folling priority: + # - Headers passes as parameters + # - User's Headers in $PSDefaultParameterValues + # - Module's default Headers + $_headers = Join-Hashtable -Hashtable $script:DefaultHeaders, $PSDefaultParameterValues["Invoke-WebRequest:Headers"], $Headers + #endregion Headers + + #region Manage URI + # Amend query from URI with GetParameter + $uriQuery = ConvertTo-ParameterHash -Uri $Uri + $internalGetParameter = Join-Hashtable $uriQuery, $GetParameter + + # And remove it from URI + [Uri]$Uri = $Uri.GetLeftPart("Path") + $PaginatedUri = $Uri + + # Use default PageSize + if (-not $internalGetParameter.ContainsKey("maxResults")) { + $internalGetParameter["maxResults"] = $script:DefaultPageSize + } + + # Append GET parameters to URi + $offset = 0 + if ($PSCmdlet.PagingParameters) { + if ($PSCmdlet.PagingParameters.Skip) { + $internalGetParameter["startAt"] = $PSCmdlet.PagingParameters.Skip + $offset = $PSCmdlet.PagingParameters.Skip + } + if ($PSCmdlet.PagingParameters.First -lt $internalGetParameter["maxResults"]) { + $internalGetParameter["maxResults"] = $PSCmdlet.PagingParameters.First + } + } + + [Uri]$PaginatedUri = "{0}{1}" -f $PaginatedUri, (ConvertTo-GetParameter $internalGetParameter) + #endregion Manage URI + #region Constructe IWR Parameter $splatParameters = @{ - Uri = $Uri + Uri = $PaginatedUri Method = $Method Headers = $_headers - ContentType = "application/json; charset=utf-8" + ContentType = $script:DefaultContentType UseBasicParsing = $true Credential = $Credential ErrorAction = "Stop" @@ -69,8 +117,8 @@ function Invoke-JiraMethod { if ($_headers.ContainsKey("Content-Type")) { $splatParameters["ContentType"] = $_headers["Content-Type"] + $splatParameters["Headers"].Remove("Content-Type") $_headers.Remove("Content-Type") - $splatParameters["Headers"] = $_headers } if ($Body) { @@ -84,81 +132,140 @@ function Invoke-JiraMethod { } } + if ((-not $Credential) -or ($Credential -eq [System.Management.Automation.PSCredential]::Empty)) { + $splatParameters.Remove("Credential") + if ($session = Get-JiraSession -ErrorAction SilentlyContinue) { + $splatParameters["WebSession"] = $session.WebSession + } + } + if ($StoreSession) { $splatParameters["SessionVariable"] = "newSessionVar" $splatParameters.Remove("WebSession") } - if ($session = Get-JiraSession -ErrorAction SilentlyContinue) { - if (-not ($Credential)) { - $splatParameters["WebSession"] = $session.WebSession - $splatParameters.Remove("Credential") - } - } - if ($InFile) { $splatParameters["InFile"] = $InFile } if ($OutFile) { $splatParameters["OutFile"] = $OutFile } + #endregion Constructe IWR Parameter - # Invoke the API - Write-Verbose "[$($MyInvocation.MyCommand.Name)] $($splatParameters.Method) $($splatParameters.Uri)" - Write-Debug "[$($MyInvocation.MyCommand.Name)] Invoke-WebRequest with `$splatParameters: $($splatParameters | Out-String)" + #region Execute the actual query try { + Write-Verbose "[$($MyInvocation.MyCommand.Name)] $($splatParameters.Method) $($splatParameters.Uri)" + Write-Debug "[$($MyInvocation.MyCommand.Name)] Invoke-WebRequest with `$splatParameters: $($splatParameters | Out-String)" + # Invoke the API $webResponse = Invoke-WebRequest @splatParameters } catch { Write-Verbose "[$($MyInvocation.MyCommand.Name)] Failed to get an answer from the server" - # Invoke-WebRequest is hard-coded to throw an exception if the Web request returns a 4xx or 5xx error. - # This is the best workaround I can find to retrieve the actual results of the request. - $webResponse = $_ - if ($webResponse.ErrorDetails) { - # In PowerShellCore (v6+), the response body is available as string - $responseBody = $webResponse.ErrorDetails.Message - } - else { - $webResponse = $webResponse.Exception.Response - } - } - Test-ServerResponse -InputObject $webResponse -Cmdlet $Cmdlet + $exception = $_ + $webResponse = $exception.Exception.Response + } Write-Debug "[$($MyInvocation.MyCommand.Name)] Executed WebRequest. Access `$webResponse to see details" + Test-ServerResponse -InputObject $webResponse -Cmdlet $Cmdlet + #endregion Execute the actual query + } + process { if ($webResponse) { # In PowerShellCore (v6+) the StatusCode of an exception is somewhere else if (-not ($statusCode = $webResponse.StatusCode)) { - $statusCode = $webresponse.Exception.Response.StatusCode + $statusCode = $webResponse.Exception.Response.StatusCode } Write-Verbose "[$($MyInvocation.MyCommand.Name)] Status code: $($statusCode)" + #region Code 400+ if ($statusCode.value__ -ge 400) { - Write-Warning "Jira returned HTTP error $($statusCode.value__) - $($statusCode)" - - if ((!($responseBody)) -and ($webResponse | Get-Member -Name "GetResponseStream")) { - # Retrieve body of HTTP response - this contains more useful information about exactly why the error occurred - $readStream = New-Object -TypeName System.IO.StreamReader -ArgumentList ($webResponse.GetResponseStream()) - $responseBody = $readStream.ReadToEnd() - $readStream.Close() - - # Clear the body in case it is not a JSON (but rather html) - if ($responseBody -match "^[\s\t]*\") { $responseBody = '{"errorMessages": "Invalid server response. HTML returned."}' } - - Write-Verbose "[$($MyInvocation.MyCommand.Name)] Retrieved body of HTTP response for more information about the error (`$responseBody)" - Write-Debug "[$($MyInvocation.MyCommand.Name)] Got the following error as `$responseBody" - $result = ConvertFrom-Json -InputObject $responseBody - } - + Resolve-ErrorWebResponse -Exception $exception -StatusCode $statusCode -Cmdlet $Cmdlet } + #endregion Code 400+ + + #region Code 399- else { if ($StoreSession) { - return ConvertTo-JiraSession -Session $newSessionVar -Username $Credential.UserName + return & $script:SessionTransformationMethod -Session $newSessionVar -Username $Credential.UserName } if ($webResponse.Content) { - $result = ConvertFrom-Json -InputObject $webResponse.Content + $response = ConvertFrom-Json ([Text.Encoding]::UTF8.GetString($webResponse.RawContentStream.ToArray())) + + if ($Paging) { + # Remove Parameters that don't need propagation + $script:PSDefaultParameterValues.Remove("$($MyInvocation.MyCommand.Name):IncludeTotalCount") + $null = $PSBoundParameters.Remove("Paging") + $null = $PSBoundParameters.Remove("Skip") + if (-not $PSBoundParameters["GetParameter"]) { + $PSBoundParameters["GetParameter"] = $internalGetParameter + } + + $total = 0 + do { + Write-Verbose "[$($MyInvocation.MyCommand.Name)] Invoking pagination [currentTotal: $total]" + + foreach ($container in $script:PagingContainers) { + if (($response) -and ($response | Get-Member -Name $container)) { + Write-DebugMessage "[$($MyInvocation.MyCommand.Name)] Extracting data from [$container] containter" + $result = $response.$container + } + } + + $total += @($result).Count + $pageSize = $response.maxResults + + if ($total -gt $PSCmdlet.PagingParameters.First) { + Write-DebugMessage "[$($MyInvocation.MyCommand.Name)] Only output the first $($PSCmdlet.PagingParameters.First % $pageSize) of page" + $result = $result | Select-Object -First ($PSCmdlet.PagingParameters.First % $pageSize) + } + + $converter = "ConvertTo-$($OutputType)" + if (Test-Path function:\$converter) { + # Results shall be casted to custom objects (see ValidateSet) + Write-Debug "[$($MyInvocation.MyCommand.Name)] Outputting `$result as $($OutputType)" + Write-Output ($result | & $converter) + } + else { + Write-Debug "[$($MyInvocation.MyCommand.Name)] Outputting `$result" + Write-Output ($result) + } + + if ($total -ge $PSCmdlet.PagingParameters.First) { + Write-DebugMessage "[$($MyInvocation.MyCommand.Name)] Stopping paging, as $total reached $($PSCmdlet.PagingParameters.First)" + break + } + + # calculate the size of the next page + $PSBoundParameters["GetParameter"]["startAt"] = $total + $offset + $expectedTotal = $PSBoundParameters["GetParameter"]["startAt"] + $pageSize + if ($expectedTotal -gt $PSCmdlet.PagingParameters.First) { + $reduceBy = $expectedTotal - $PSCmdlet.PagingParameters.First + $PSBoundParameters["GetParameter"]["maxResults"] = $pageSize - $reduceBy + } + + # Inquire the next page + $response = Invoke-JiraMethod @PSBoundParameters + + # Expand data container of paged results + $result = @() + foreach ($container in $script:PagingContainers) { + if (($response) -and ($response | Get-Member -Name $container)) { + $result = $response.$container + } + } + } while ($result.Count) + + if ($PSCmdlet.PagingParameters.IncludeTotalCount) { + [double]$Accuracy = 1.0 + $PSCmdlet.PagingParameters.NewTotalCount($total, $Accuracy) + } + } + else { + Write-Output $response + } } else { # No content, although statusCode < 400 @@ -166,22 +273,15 @@ function Invoke-JiraMethod { Write-Verbose "[$($MyInvocation.MyCommand.Name)] No content was returned from." } } + #endregion Code 399- } else { Write-Verbose "[$($MyInvocation.MyCommand.Name)] No Web result object was returned from. This is unusual!" } - - if ($result) { - if (Get-Member -Name "Errors" -InputObject $result -ErrorAction SilentlyContinue) { - Resolve-JiraError $result -WriteError -Cmdlet $Cmdlet - } - else { - Write-Output $result - } - } } - end { + Set-TlsLevel -Revert + Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function ended" } } diff --git a/JiraPS/Public/New-JiraFilter.ps1 b/JiraPS/Public/New-JiraFilter.ps1 new file mode 100644 index 00000000..5a66a671 --- /dev/null +++ b/JiraPS/Public/New-JiraFilter.ps1 @@ -0,0 +1,68 @@ +function New-JiraFilter { + # .ExternalHelp ..\JiraPS-help.xml + [CmdletBinding( SupportsShouldProcess )] + param( + [Parameter( Mandatory, ValueFromPipelineByPropertyName )] + [ValidateNotNullOrEmpty()] + [String] + $Name, + + [Parameter( ValueFromPipelineByPropertyName )] + [String] + $Description, + + [Parameter( Mandatory, ValueFromPipelineByPropertyName )] + [ValidateNotNullOrEmpty()] + [String] + $JQL, + + [Parameter( ValueFromPipelineByPropertyName )] + [Alias('Favourite')] + [Switch] + $Favorite, + + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty + ) + + begin { + Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started" + + $server = Get-JiraConfigServer -ErrorAction Stop + + $resourceURi = "$server/rest/api/latest/filter" + } + + process { + Write-DebugMessage "[$($MyInvocation.MyCommand.Name)] ParameterSetName: $($PsCmdlet.ParameterSetName)" + Write-DebugMessage "[$($MyInvocation.MyCommand.Name)] PSBoundParameters: $($PSBoundParameters | Out-String)" + + $requestBody = @{ + Name = $Name + JQL = $JQL + } + if ($PSCmdlet.MyInvocation.BoundParameters.ContainsKey("Description")) { + $requestBody["description"] = $Description + } + $requestBody["favourite"] = [Bool]$Favorite + + $parameter = @{ + URI = $resourceURi + Method = "POST" + Body = ConvertTo-Json -InputObject $requestBody + Credential = $Credential + } + Write-Debug "[$($MyInvocation.MyCommand.Name)] Invoking JiraMethod with `$parameter" + if ($PSCmdlet.ShouldProcess($Name, "Creating new Filter")) { + $result = Invoke-JiraMethod @parameter + + Write-Output (ConvertTo-JiraFilter -InputObject $result) + } + } + + end { + Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete" + } +} diff --git a/JiraPS/Public/New-JiraGroup.ps1 b/JiraPS/Public/New-JiraGroup.ps1 index e2f9a94b..956709b8 100644 --- a/JiraPS/Public/New-JiraGroup.ps1 +++ b/JiraPS/Public/New-JiraGroup.ps1 @@ -1,4 +1,5 @@ function New-JiraGroup { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding( SupportsShouldProcess )] param( [Parameter( Mandatory )] @@ -6,8 +7,10 @@ [String[]] $GroupName, - [PSCredential] - $Credential + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty ) begin { diff --git a/JiraPS/Public/New-JiraIssue.ps1 b/JiraPS/Public/New-JiraIssue.ps1 index df817ad9..717b6e07 100644 --- a/JiraPS/Public/New-JiraIssue.ps1 +++ b/JiraPS/Public/New-JiraIssue.ps1 @@ -1,4 +1,5 @@ function New-JiraIssue { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding( SupportsShouldProcess )] param( [Parameter( Mandatory )] @@ -37,8 +38,10 @@ function New-JiraIssue { [PSCustomObject] $Fields, - [PSCredential] - $Credential + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty ) begin { @@ -107,12 +110,11 @@ function New-JiraIssue { $requestBody["$id"] = $value } else { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid value for Parameter"), - 'ParameterValue.InvalidFields', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $Fields - ) + $exception = ([System.ArgumentException]"Invalid value for Parameter") + $errorId = 'ParameterValue.InvalidFields' + $errorCategory = 'InvalidArgument' + $errorTarget = $Fields + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Unable to identify field [$name] from -Fields hashtable. Use Get-JiraField for more information." $PSCmdlet.ThrowTerminatingError($errorItem) } @@ -126,12 +128,11 @@ function New-JiraIssue { Write-DebugMessage "[$($MyInvocation.MyCommand.Name)] Required field (id=[$($c.Id)], name=[$($c.Name)]) was provided (value=[$($requestBody.$($c.Id))])" } else { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid or missing value Parameter"), - 'ParameterValue.CreateMetaFailure', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $Fields - ) + $exception = ([System.ArgumentException]"Invalid or missing value Parameter") + $errorId = 'ParameterValue.CreateMetaFailure' + $errorCategory = 'InvalidArgument' + $errorTarget = $Fields + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Jira's metadata for project [$Project] and issue type [$IssueType] specifies that a field is required that was not provided (name=[$($c.Name)], id=[$($c.Id)]). Use Get-JiraIssueCreateMetadata for more information." $PSCmdlet.ThrowTerminatingError($errorItem) } diff --git a/JiraPS/Public/New-JiraSession.ps1 b/JiraPS/Public/New-JiraSession.ps1 index cf5f32aa..899074e5 100644 --- a/JiraPS/Public/New-JiraSession.ps1 +++ b/JiraPS/Public/New-JiraSession.ps1 @@ -1,9 +1,11 @@ function New-JiraSession { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding()] [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseShouldProcessForStateChangingFunctions', '')] param( [Parameter( Mandatory )] - [PSCredential] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] $Credential, [Hashtable] @@ -25,6 +27,7 @@ function New-JiraSession { $parameter = @{ URI = $resourceURi Method = "GET" + Headers = $Headers StoreSession = $true Credential = $Credential } diff --git a/JiraPS/Public/New-JiraUser.ps1 b/JiraPS/Public/New-JiraUser.ps1 index 774fa71d..45b8423f 100644 --- a/JiraPS/Public/New-JiraUser.ps1 +++ b/JiraPS/Public/New-JiraUser.ps1 @@ -1,4 +1,5 @@ function New-JiraUser { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding( SupportsShouldProcess )] param( [Parameter( Mandatory )] @@ -16,8 +17,10 @@ function New-JiraUser { [Boolean] $Notify = $true, - [PSCredential] - $Credential + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty ) begin { diff --git a/JiraPS/Public/New-JiraVersion.ps1 b/JiraPS/Public/New-JiraVersion.ps1 index fef2e100..287c6125 100644 --- a/JiraPS/Public/New-JiraVersion.ps1 +++ b/JiraPS/Public/New-JiraVersion.ps1 @@ -1,4 +1,5 @@ function New-JiraVersion { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding( SupportsShouldProcess, DefaultParameterSetName = 'byObject' )] param( [Parameter( Position = 0, Mandatory, ValueFromPipeline, ParameterSetName = 'byObject' )] @@ -6,12 +7,11 @@ [ValidateScript( { if (("JiraPS.Version" -notin $_.PSObject.TypeNames) -and (($_ -isnot [String]))) { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid Type for Parameter"), - 'ParameterType.NotJiraVersion', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $_ - ) + $exception = ([System.ArgumentException]"Invalid Type for Parameter") #fix code highlighting] + $errorId = 'ParameterType.NotJiraVersion' + $errorCategory = 'InvalidArgument' + $errorTarget = $_ + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Wrong object type provided for Version. Expected [JiraPS.Version] or [String], but was $($_.GetType().Name)" $PSCmdlet.ThrowTerminatingError($errorItem) <# @@ -41,12 +41,11 @@ {"JiraPS.Project" -in $Input.PSObject.TypeNames} { return $true } {$Input -is [String]} { return $true} Default { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid Type for Parameter"), - 'ParameterType.NotJiraProject', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $Input - ) + $exception = ([System.ArgumentException]"Invalid Type for Parameter") #fix code highlighting] + $errorId = 'ParameterType.NotJiraProject' + $errorCategory = 'InvalidArgument' + $errorTarget = $Input + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Wrong object type provided for Project. Expected [JiraPS.Project] or [String], but was $($Input.GetType().Name)" $PSCmdlet.ThrowTerminatingError($errorItem) <# @@ -80,8 +79,10 @@ [DateTime] $StartDate, - [PSCredential] - $Credential + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty ) begin { diff --git a/JiraPS/Public/Remove-JiraFilter.ps1 b/JiraPS/Public/Remove-JiraFilter.ps1 new file mode 100644 index 00000000..39bff272 --- /dev/null +++ b/JiraPS/Public/Remove-JiraFilter.ps1 @@ -0,0 +1,51 @@ +function Remove-JiraFilter { + # .ExternalHelp ..\JiraPS-help.xml + [CmdletBinding( ConfirmImpact = "Medium", SupportsShouldProcess, DefaultParameterSetName = 'ByInputObject' )] + param( + [Parameter( Position = 0, Mandatory, ValueFromPipeline, ParameterSetName = 'ByInputObject' )] + [ValidateNotNullOrEmpty()] + [PSTypeName('JiraPS.Filter')] + $InputObject, + + [Parameter( Position = 0, Mandatory, ValueFromPipeline, ParameterSetName = 'ById')] + [ValidateNotNullOrEmpty()] + [UInt32[]] + $Id, + + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty + ) + + begin { + Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started" + } + + process { + Write-DebugMessage "[$($MyInvocation.MyCommand.Name)] ParameterSetName: $($PsCmdlet.ParameterSetName)" + Write-DebugMessage "[$($MyInvocation.MyCommand.Name)] PSBoundParameters: $($PSBoundParameters | Out-String)" + + if ($PSCmdlet.ParameterSetName -eq 'ById') { + $InputObject = foreach ($_id in $Id) { + Get-JiraFilter -Id $_id + } + } + + foreach ($filter in $InputObject) { + $parameter = @{ + URI = $filter.RestURL + Method = "DELETE" + Credential = $Credential + } + Write-Debug "[$($MyInvocation.MyCommand.Name)] Invoking JiraMethod with `$parameter" + if ($PSCmdlet.ShouldProcess($filter.Name, "Deleting Filter")) { + Invoke-JiraMethod @parameter + } + } + } + + end { + Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete" + } +} diff --git a/JiraPS/Public/Remove-JiraFilterPermission.ps1 b/JiraPS/Public/Remove-JiraFilterPermission.ps1 new file mode 100644 index 00000000..d07d5573 --- /dev/null +++ b/JiraPS/Public/Remove-JiraFilterPermission.ps1 @@ -0,0 +1,86 @@ +function Remove-JiraFilterPermission { + # .ExternalHelp ..\JiraPS-help.xml + [CmdletBinding( SupportsShouldProcess, DefaultParameterSetName = 'ByFilterId' )] + param( + [Parameter( Position = 0, Mandatory, ValueFromPipeline, ParameterSetName = 'ByFilterObject' )] + [ValidateNotNullOrEmpty()] + [ValidateScript( + { + if (@($Filter).Count -gt 1) { + $exception = ([System.ArgumentException]"Invalid Parameter") + $errorId = 'ParameterType.TooManyFilters' + $errorCategory = 'InvalidArgument' + $errorTarget = $_ + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget + $errorItem.ErrorDetails = "Only one Filter can be passed at a time." + $PSCmdlet.ThrowTerminatingError($errorItem) + } + elseif (@($_.FilterPermissions).Count -lt 1) { + $exception = ([System.ArgumentException]"Invalid Type for Parameter") + $errorId = 'ParameterType.FilterWithoutPermission' + $errorCategory = 'InvalidArgument' + $errorTarget = $_ + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget + $errorItem.ErrorDetails = "The Filter provided does not contain any Permission to delete." + $PSCmdlet.ThrowTerminatingError($errorItem) + } + else { + return $true + } + } + )] + [PSTypeName('JiraPS.Filter')] + $Filter, + + [Parameter( Position = 0, Mandatory, ParameterSetName = 'ByFilterId' )] + [ValidateNotNullOrEmpty()] + [Alias('Id')] + [UInt32] + $FilterId, + + # TODO: [Parameter( Position = 1, ParameterSetName = 'ByFilterObject')] + [Parameter( Position = 1, Mandatory, ParameterSetName = 'ByFilterId')] + [ValidateNotNullOrEmpty()] + [UInt32[]] + $PermissionId, + + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty + ) + + begin { + Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started" + } + + process { + Write-DebugMessage "[$($MyInvocation.MyCommand.Name)] ParameterSetName: $($PsCmdlet.ParameterSetName)" + Write-DebugMessage "[$($MyInvocation.MyCommand.Name)] PSBoundParameters: $($PSBoundParameters | Out-String)" + + switch ($PSCmdlet.ParameterSetName) { + "ByFilterObject" { + $PermissionId = $Filter.FilterPermissions.Id + } + "ByFilterId" { + $Filter = Get-JiraFilter -Id $FilterId + } + } + + foreach ($_permissionId in $PermissionId) { + $parameter = @{ + URI = "{0}/permission/{1}" -f $Filter.RestURL, $_permissionId + Method = "DELETE" + Credential = $Credential + } + Write-Debug "[$($MyInvocation.MyCommand.Name)] Invoking JiraMethod with `$parameter" + if ($PSCmdlet.ShouldProcess($InputObject.Type, "Remove Permission")) { + Invoke-JiraMethod @parameter + } + } + } + + end { + Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete" + } +} diff --git a/JiraPS/Public/Remove-JiraGroup.ps1 b/JiraPS/Public/Remove-JiraGroup.ps1 index 92ff06cc..63aa2616 100644 --- a/JiraPS/Public/Remove-JiraGroup.ps1 +++ b/JiraPS/Public/Remove-JiraGroup.ps1 @@ -1,4 +1,5 @@ function Remove-JiraGroup { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding( SupportsShouldProcess, ConfirmImpact = 'High' )] param( [Parameter( Mandatory, ValueFromPipeline )] @@ -6,12 +7,11 @@ [ValidateScript( { if (("JiraPS.Group" -notin $_.PSObject.TypeNames) -and (($_ -isnot [String]))) { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid Type for Parameter"), - 'ParameterType.NotJiraGroup', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $_ - ) + $exception = ([System.ArgumentException]"Invalid Type for Parameter") #fix code highlighting] + $errorId = 'ParameterType.NotJiraGroup' + $errorCategory = 'InvalidArgument' + $errorTarget = $_ + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Wrong object type provided for Group. Expected [JiraPS.Group] or [String], but was $($_.GetType().Name)" $PSCmdlet.ThrowTerminatingError($errorItem) <# @@ -28,8 +28,10 @@ [Object[]] $Group, - [PSCredential] - $Credential, + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty, [Switch] $Force diff --git a/JiraPS/Public/Remove-JiraGroupMember.ps1 b/JiraPS/Public/Remove-JiraGroupMember.ps1 index 28f1f70e..f82133fe 100644 --- a/JiraPS/Public/Remove-JiraGroupMember.ps1 +++ b/JiraPS/Public/Remove-JiraGroupMember.ps1 @@ -1,4 +1,5 @@ function Remove-JiraGroupMember { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding( SupportsShouldProcess, ConfirmImpact = 'High' )] param( [Parameter( Mandatory, ValueFromPipeline )] @@ -6,12 +7,11 @@ function Remove-JiraGroupMember { [ValidateScript( { if (("JiraPS.Group" -notin $_.PSObject.TypeNames) -and (($_ -isnot [String]))) { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid Type for Parameter"), - 'ParameterType.NotJiraGroup', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $_ - ) + $exception = ([System.ArgumentException]"Invalid Type for Parameter") #fix code highlighting] + $errorId = 'ParameterType.NotJiraGroup' + $errorCategory = 'InvalidArgument' + $errorTarget = $_ + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Wrong object type provided for Group. Expected [JiraPS.Group] or [String], but was $($_.GetType().Name)" $PSCmdlet.ThrowTerminatingError($errorItem) <# @@ -33,12 +33,11 @@ function Remove-JiraGroupMember { [ValidateScript( { if (("JiraPS.User" -notin $_.PSObject.TypeNames) -and (($_ -isnot [String]))) { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid Type for Parameter"), - 'ParameterType.UotJirauser', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $_ - ) + $exception = ([System.ArgumentException]"Invalid Type for Parameter") #fix code highlighting] + $errorId = 'ParameterType.UotJirauser' + $errorCategory = 'InvalidArgument' + $errorTarget = $_ + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Wrong object type provided for User. Expected [JiraPS.User] or [String], but was $($_.GetType().Name)" $PSCmdlet.ThrowTerminatingError($errorItem) <# @@ -55,8 +54,10 @@ function Remove-JiraGroupMember { [Object[]] $User, - [PSCredential] - $Credential, + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty, [Switch] $PassThru, diff --git a/JiraPS/Public/Remove-JiraIssue.ps1 b/JiraPS/Public/Remove-JiraIssue.ps1 new file mode 100644 index 00000000..075823f9 --- /dev/null +++ b/JiraPS/Public/Remove-JiraIssue.ps1 @@ -0,0 +1,117 @@ +function Remove-JiraIssue { + # .ExternalHelp ..\JiraPS-help.xml + [CmdletBinding( + ConfirmImpact = 'High', + SupportsShouldProcess, + DefaultParameterSetName = "ByInputObject" + )] + param ( + [Parameter( + Mandatory, + ValueFromPipeline, + Position = 0, + ParameterSetName = "ByInputObject" + )] + [Alias( + "Issue" + )] + [PSTypeName("JiraPS.Issue")] + [Object[]] + $InputObject, + + # The issue's ID number or key. + [Parameter( + Mandatory, + Position = 0, + ParameterSetName = "ByIssueId" + )] + [ValidateNotNullOrEmpty()] + [Alias( + "Id", + "Key", + "issueIdOrKey" + )] + [String[]] + $IssueId, + + [Switch] + [Alias("deleteSubtasks")] + $IncludeSubTasks, + + [System.Management.Automation.CredentialAttribute()] + [System.Management.Automation.PSCredential] + $Credential = [System.Management.Automation.PSCredential]::Empty, + + [Switch] + $Force + ) + + begin { + Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started" + + $server = Get-JiraConfigServer -ErrorAction Stop + + $resourceURi = "$server/rest/api/latest/issue/{0}?deleteSubtasks={1}" + + if ($Force) { + Write-DebugMessage "[$($MyInvocation.MyCommand.Name)] -Force was passed. Backing up current ConfirmPreference [$ConfirmPreference] and setting to None" + $oldConfirmPreference = $ConfirmPreference + $ConfirmPreference = 'None' + } + } + + process { + + Write-DebugMessage "[$($MyInvocation.MyCommand.Name)] ParameterSetName: $($PsCmdlet.ParameterSetName)" + Write-DebugMessage "[$($MyInvocation.MyCommand.Name)] PSBoundParameters: $($PSBoundParameters | Out-String)" + + switch ($PsCmdlet.ParameterSetName) { + "ByInputObject" { $PrimaryIterator = $InputObject } + "ByIssueId" { $PrimaryIterator = $IssueID } + } + + foreach ($issueItem in $PrimaryIterator) { + + if ($PsCmdlet.ParameterSetName -eq "ByIssueId") { + $_issue = Get-JiraIssue -Key $issueItem -Credential $Credential -ErrorAction Stop + } Else { + $_issue = $issueItem + } + + Write-Verbose "[$($MyInvocation.MyCommand.Name)] Processing [$_issue]" + Write-Debug "[$($MyInvocation.MyCommand.Name)] Processing `$issueItem [$_issue]" + + + + $parameter = @{ + URI = $resourceURi -f $_issue.Key,$IncludeSubTasks + Method = "DELETE" + Credential = $Credential + Cmdlet = $PsCmdlet + } + + + If ($IncludeSubTasks) { + $ActionText = "Remove issue and sub-tasks" + } Else { + $ActionText = "Remove issue" + } + + if ($PSCmdlet.ShouldProcess($_issue.ToString(), $ActionText)) { + + Write-Debug "[$($MyInvocation.MyCommand.Name)] Invoking JiraMethod with `$parameter" + Invoke-JiraMethod @parameter + } + } + + } + + end { + if ($Force) { + Write-DebugMessage "[$($MyInvocation.MyCommand.Name)] Restoring ConfirmPreference to [$oldConfirmPreference]" + $ConfirmPreference = $oldConfirmPreference + } + + Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete" + } +} diff --git a/JiraPS/Public/Remove-JiraIssueAttachment.ps1 b/JiraPS/Public/Remove-JiraIssueAttachment.ps1 index cb9c8ca0..b3a270c1 100644 --- a/JiraPS/Public/Remove-JiraIssueAttachment.ps1 +++ b/JiraPS/Public/Remove-JiraIssueAttachment.ps1 @@ -1,4 +1,5 @@ function Remove-JiraIssueAttachment { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding( ConfirmImpact = 'High', SupportsShouldProcess, DefaultParameterSetName = 'byId' )] param( [Parameter( Position = 0, Mandatory, ValueFromPipelineByPropertyName, ParameterSetName = 'byId' )] @@ -12,12 +13,11 @@ function Remove-JiraIssueAttachment { [ValidateScript( { if (("JiraPS.Issue" -notin $_.PSObject.TypeNames) -and (($_ -isnot [String]))) { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid Type for Parameter"), - 'ParameterType.NotJiraIssue', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $_ - ) + $exception = ([System.ArgumentException]"Invalid Type for Parameter") #fix code highlighting] + $errorId = 'ParameterType.NotJiraIssue' + $errorCategory = 'InvalidArgument' + $errorTarget = $_ + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Wrong object type provided for Issue. Expected [JiraPS.Issue] or [String], but was $($_.GetType().Name)" $PSCmdlet.ThrowTerminatingError($errorItem) <# @@ -38,8 +38,10 @@ function Remove-JiraIssueAttachment { [String[]] $FileName, - [PSCredential] - $Credential, + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty, [Switch] $Force @@ -85,12 +87,11 @@ function Remove-JiraIssueAttachment { Write-Debug "[$($MyInvocation.MyCommand.Name)] Processing `$Issue [$Issue]" if (@($Issue).Count -ne 1) { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"invalid Issue provided"), - 'ParameterValue.JiraIssue', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $_ - ) + $exception = ([System.ArgumentException]"invalid Issue provided") + $errorId = 'ParameterValue.JiraIssue' + $errorCategory = 'InvalidArgument' + $errorTarget = $_ + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Only one Issue can be provided at a time." $PSCmdlet.ThrowTerminatingError($errorItem) } diff --git a/JiraPS/Public/Remove-JiraIssueLink.ps1 b/JiraPS/Public/Remove-JiraIssueLink.ps1 index 48be0107..33c9e133 100644 --- a/JiraPS/Public/Remove-JiraIssueLink.ps1 +++ b/JiraPS/Public/Remove-JiraIssueLink.ps1 @@ -1,4 +1,5 @@ function Remove-JiraIssueLink { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding( SupportsShouldProcess, ConfirmImpact = 'Medium' )] param( [Parameter( Mandatory, ValueFromPipeline )] @@ -11,12 +12,11 @@ function Remove-JiraIssueLink { {("JiraPS.Issue" -in $Input.PSObject.TypeNames) -and ("issueLinks" -in $objectProperties.Name)} { return $true } {("JiraPS.IssueLink" -in $Input.PSObject.TypeNames) -and ("Id" -in $objectProperties.Name)} { return $true } default { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid Type for Parameter"), - 'ParameterType.NotJiraIssue', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $Input - ) + $exception = ([System.ArgumentException]"Invalid Type for Parameter") #fix code highlighting] + $errorId = 'ParameterType.NotJiraIssue' + $errorCategory = 'InvalidArgument' + $errorTarget = $Input + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Wrong object type provided for Issue. Expected [JiraPS.Issue], [JiraPS.IssueLink] or [String], but was $($Input.GetType().Name)" $PSCmdlet.ThrowTerminatingError($errorItem) <# @@ -30,8 +30,10 @@ function Remove-JiraIssueLink { [Object[]] $IssueLink, - [PSCredential] - $Credential + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty ) begin { diff --git a/JiraPS/Public/Remove-JiraIssueWatcher.ps1 b/JiraPS/Public/Remove-JiraIssueWatcher.ps1 index bf5a66a3..1d8ff434 100644 --- a/JiraPS/Public/Remove-JiraIssueWatcher.ps1 +++ b/JiraPS/Public/Remove-JiraIssueWatcher.ps1 @@ -1,4 +1,5 @@ function Remove-JiraIssueWatcher { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding( SupportsShouldProcess )] param( [Parameter( Mandatory )] @@ -10,12 +11,11 @@ [ValidateScript( { if (("JiraPS.Issue" -notin $_.PSObject.TypeNames) -and (($_ -isnot [String]))) { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid Type for Parameter"), - 'ParameterType.NotJiraIssue', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $_ - ) + $exception = ([System.ArgumentException]"Invalid Type for Parameter") #fix code highlighting] + $errorId = 'ParameterType.NotJiraIssue' + $errorCategory = 'InvalidArgument' + $errorTarget = $_ + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Wrong object type provided for Issue. Expected [JiraPS.Issue] or [String], but was $($_.GetType().Name)" $PSCmdlet.ThrowTerminatingError($errorItem) <# @@ -32,8 +32,10 @@ [Object] $Issue, - [PSCredential] - $Credential + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty ) begin { diff --git a/JiraPS/Public/Remove-JiraRemoteLink.ps1 b/JiraPS/Public/Remove-JiraRemoteLink.ps1 index 7f82c003..710b14db 100644 --- a/JiraPS/Public/Remove-JiraRemoteLink.ps1 +++ b/JiraPS/Public/Remove-JiraRemoteLink.ps1 @@ -1,4 +1,5 @@ function Remove-JiraRemoteLink { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding( ConfirmImpact = 'High', SupportsShouldProcess )] param( [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName )] @@ -6,12 +7,11 @@ function Remove-JiraRemoteLink { [ValidateScript( { if (("JiraPS.Issue" -notin $_.PSObject.TypeNames) -and (($_ -isnot [String]))) { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid Type for Parameter"), - 'ParameterType.NotJiraIssue', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $_ - ) + $exception = ([System.ArgumentException]"Invalid Type for Parameter") #fix code highlighting] + $errorId = 'ParameterType.NotJiraIssue' + $errorCategory = 'InvalidArgument' + $errorTarget = $_ + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Wrong object type provided for Issue. Expected [JiraPS.Issue] or [String], but was $($_.GetType().Name)" $PSCmdlet.ThrowTerminatingError($errorItem) <# @@ -32,8 +32,10 @@ function Remove-JiraRemoteLink { [Int[]] $LinkId, - [PSCredential] - $Credential, + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty, [Switch] $Force diff --git a/JiraPS/Public/Remove-JiraSession.ps1 b/JiraPS/Public/Remove-JiraSession.ps1 index 08cd039e..1f19c3dc 100644 --- a/JiraPS/Public/Remove-JiraSession.ps1 +++ b/JiraPS/Public/Remove-JiraSession.ps1 @@ -1,4 +1,5 @@ function Remove-JiraSession { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding()] [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseShouldProcessForStateChangingFunctions', '')] param( diff --git a/JiraPS/Public/Remove-JiraUser.ps1 b/JiraPS/Public/Remove-JiraUser.ps1 index a7628322..3dc6cf21 100644 --- a/JiraPS/Public/Remove-JiraUser.ps1 +++ b/JiraPS/Public/Remove-JiraUser.ps1 @@ -1,4 +1,5 @@ function Remove-JiraUser { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding( ConfirmImpact = 'High', SupportsShouldProcess )] param( [Parameter( Mandatory, ValueFromPipeline )] @@ -6,12 +7,11 @@ function Remove-JiraUser { [ValidateScript( { if (("JiraPS.User" -notin $_.PSObject.TypeNames) -and (($_ -isnot [String]))) { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid Type for Parameter"), - 'ParameterType.NotJiraUser', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $_ - ) + $exception = ([System.ArgumentException]"Invalid Type for Parameter") #fix code highlighting] + $errorId = 'ParameterType.NotJiraUser' + $errorCategory = 'InvalidArgument' + $errorTarget = $_ + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Wrong object type provided for User. Expected [JiraPS.User] or [String], but was $($_.GetType().Name)" $PSCmdlet.ThrowTerminatingError($errorItem) <# @@ -28,8 +28,10 @@ function Remove-JiraUser { [Object[]] $User, - [PSCredential] - $Credential, + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty, [Switch] $Force diff --git a/JiraPS/Public/Remove-JiraVersion.ps1 b/JiraPS/Public/Remove-JiraVersion.ps1 index 94c0ebcc..8974c16b 100644 --- a/JiraPS/Public/Remove-JiraVersion.ps1 +++ b/JiraPS/Public/Remove-JiraVersion.ps1 @@ -1,4 +1,5 @@ function Remove-JiraVersion { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding( ConfirmImpact = 'High', SupportsShouldProcess )] param( [Parameter( Mandatory, ValueFromPipeline )] @@ -6,12 +7,11 @@ [ValidateScript( { if (("JiraPS.Version" -notin $_.PSObject.TypeNames) -and (($_ -isnot [Int]))) { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid Type for Parameter"), - 'ParameterType.NotJiraVersion', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $_ - ) + $exception = ([System.ArgumentException]"Invalid Type for Parameter") #fix code highlighting] + $errorId = 'ParameterType.NotJiraVersion' + $errorCategory = 'InvalidArgument' + $errorTarget = $_ + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Wrong object type provided for Version. Expected [JiraPS.Version] or [Int], but was $($_.GetType().Name)" $PSCmdlet.ThrowTerminatingError($errorItem) <# @@ -27,8 +27,10 @@ [Object[]] $Version, - [PSCredential] - $Credential, + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty, [Switch] $Force diff --git a/JiraPS/Public/Set-JiraConfigServer.ps1 b/JiraPS/Public/Set-JiraConfigServer.ps1 index 7fad90cf..00c85b5e 100644 --- a/JiraPS/Public/Set-JiraConfigServer.ps1 +++ b/JiraPS/Public/Set-JiraConfigServer.ps1 @@ -1,4 +1,5 @@ function Set-JiraConfigServer { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding()] [System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseShouldProcessForStateChangingFunctions', '')] param( @@ -44,12 +45,11 @@ function Set-JiraConfigServer { $xmlConfig = $xml.DocumentElement if ($xmlConfig.LocalName -ne 'Config') { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid Document"), - 'InvalidObject.InvalidDocument', - [System.Management.Automation.ErrorCategory]::InvalidData, - $_ - ) + $exception = ([System.ArgumentException]"Invalid Document") + $errorId = 'InvalidObject.InvalidDocument' + $errorCategory = 'InvalidData' + $errorTarget = $_ + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Unexpected document element [$($xmlConfig.LocalName)] in configuration file. You may need to delete the config file and recreate it using this function." $PSCmdlet.ThrowTerminatingError($errorItem) } diff --git a/JiraPS/Public/Set-JiraFilter.ps1 b/JiraPS/Public/Set-JiraFilter.ps1 new file mode 100644 index 00000000..ac22ba86 --- /dev/null +++ b/JiraPS/Public/Set-JiraFilter.ps1 @@ -0,0 +1,76 @@ +function Set-JiraFilter { + # .ExternalHelp ..\JiraPS-help.xml + [CmdletBinding( SupportsShouldProcess )] + param( + [Parameter( Mandatory, ValueFromPipeline )] + [ValidateNotNullOrEmpty()] + [PSTypeName('JiraPS.Filter')] + $InputObject, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [String] + $Name, + + [Parameter()] + [String] + $Description, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [String] + $JQL, + + [Parameter()] + [Alias('Favourite')] + [Bool] + $Favorite, + + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty + ) + + begin { + Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started" + } + + process { + Write-DebugMessage "[$($MyInvocation.MyCommand.Name)] ParameterSetName: $($PsCmdlet.ParameterSetName)" + Write-DebugMessage "[$($MyInvocation.MyCommand.Name)] PSBoundParameters: $($PSBoundParameters | Out-String)" + + $requestBody = @{} + if ($PSCmdlet.MyInvocation.BoundParameters.ContainsKey("Name")) { + $requestBody["name"] = $Name + } + if ($PSCmdlet.MyInvocation.BoundParameters.ContainsKey("Description")) { + $requestBody["description"] = $Description + } + if ($PSCmdlet.MyInvocation.BoundParameters.ContainsKey("JQL")) { + $requestBody["jql"] = $JQL + } + if ($PSCmdlet.MyInvocation.BoundParameters.ContainsKey("Favorite")) { + $requestBody["favourite"] = $Favorite + } + + if ($requestBody.Keys.Count) { + $parameter = @{ + URI = $InputObject.RestURL + Method = "PUT" + Body = ConvertTo-Json -InputObject $requestBody + Credential = $Credential + } + Write-Debug "[$($MyInvocation.MyCommand.Name)] Invoking JiraMethod with `$parameter" + if ($PSCmdlet.ShouldProcess($InputObject.Name, "Update Filter")) { + $result = Invoke-JiraMethod @parameter + + Write-Output (ConvertTo-JiraFilter -InputObject $result) + } + } + } + + end { + Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete" + } +} diff --git a/JiraPS/Public/Set-JiraIssue.ps1 b/JiraPS/Public/Set-JiraIssue.ps1 index 3190bc9c..7f219d0d 100644 --- a/JiraPS/Public/Set-JiraIssue.ps1 +++ b/JiraPS/Public/Set-JiraIssue.ps1 @@ -1,4 +1,5 @@ function Set-JiraIssue { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding( SupportsShouldProcess )] param( [Parameter( Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName )] @@ -6,12 +7,11 @@ function Set-JiraIssue { [ValidateScript( { if (("JiraPS.Issue" -notin $_.PSObject.TypeNames) -and (($_ -isnot [String]))) { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid Type for Parameter"), - 'ParameterType.NotJiraIssue', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $_ - ) + $exception = ([System.ArgumentException]"Invalid Type for Parameter") #fix code highlighting] + $errorId = 'ParameterType.NotJiraIssue' + $errorCategory = 'InvalidArgument' + $errorTarget = $_ + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Wrong object type provided for Issue. Expected [JiraPS.Issue] or [String], but was $($_.GetType().Name)" $PSCmdlet.ThrowTerminatingError($errorItem) <# @@ -50,8 +50,10 @@ function Set-JiraIssue { [String] $AddComment, - [PSCredential] - $Credential, + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty, [Switch] $PassThru @@ -88,12 +90,11 @@ function Set-JiraIssue { $validAssignee = $true } else { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid value for Parameter"), - 'ParameterValue.InvalidAssignee', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $Assignee - ) + $exception = ([System.ArgumentException]"Invalid value for Parameter") + $errorId = 'ParameterValue.InvalidAssignee' + $errorCategory = 'InvalidArgument' + $errorTarget = $Assignee + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Unable to validate Jira user [$Assignee]. Use Get-JiraUser for more details." $PSCmdlet.ThrowTerminatingError($errorItem) } diff --git a/JiraPS/Public/Set-JiraIssueLabel.ps1 b/JiraPS/Public/Set-JiraIssueLabel.ps1 index 2a726568..4139f5c6 100644 --- a/JiraPS/Public/Set-JiraIssueLabel.ps1 +++ b/JiraPS/Public/Set-JiraIssueLabel.ps1 @@ -1,4 +1,5 @@ function Set-JiraIssueLabel { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding( SupportsShouldProcess, DefaultParameterSetName = 'ReplaceLabels' )] param( [Parameter( Position = 0, Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName )] @@ -6,12 +7,11 @@ [ValidateScript( { if (("JiraPS.Issue" -notin $_.PSObject.TypeNames) -and (($_ -isnot [String]))) { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid Type for Parameter"), - 'ParameterType.NotJiraIssue', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $_ - ) + $exception = ([System.ArgumentException]"Invalid Type for Parameter") #fix code highlighting] + $errorId = 'ParameterType.NotJiraIssue' + $errorCategory = 'InvalidArgument' + $errorTarget = $_ + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Wrong object type provided for Issue. Expected [JiraPS.Issue] or [String], but was $($_.GetType().Name)" $PSCmdlet.ThrowTerminatingError($errorItem) <# @@ -45,8 +45,10 @@ [Switch] $Clear, - [PSCredential] - $Credential, + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty, [Switch] $PassThru diff --git a/JiraPS/Public/Set-JiraUser.ps1 b/JiraPS/Public/Set-JiraUser.ps1 index 02675c94..b89b7eb0 100644 --- a/JiraPS/Public/Set-JiraUser.ps1 +++ b/JiraPS/Public/Set-JiraUser.ps1 @@ -1,4 +1,5 @@ function Set-JiraUser { + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding( SupportsShouldProcess, DefaultParameterSetName = 'ByNamedParameters' )] param( [Parameter( Position = 0, Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName )] @@ -6,12 +7,11 @@ function Set-JiraUser { [ValidateScript( { if (("JiraPS.User" -notin $_.PSObject.TypeNames) -and (($_ -isnot [String]))) { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid Type for Parameter"), - 'ParameterType.NotJiraUser', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $_ - ) + $exception = ([System.ArgumentException]"Invalid Type for Parameter") #fix code highlighting] + $errorId = 'ParameterType.NotJiraUser' + $errorCategory = 'InvalidArgument' + $errorTarget = $_ + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Wrong object type provided for User. Expected [JiraPS.User] or [String], but was $($_.GetType().Name)" $PSCmdlet.ThrowTerminatingError($errorItem) <# @@ -41,12 +41,11 @@ function Set-JiraUser { return $true } else { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid Argument"), - 'ParameterValue.NotEmail', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $Issue - ) + $exception = ([System.ArgumentException]"Invalid Argument") #fix code highlighting] + $errorId = 'ParameterValue.NotEmail' + $errorCategory = 'InvalidArgument' + $errorTarget = $Issue + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "The value provided does not look like an email address." $PSCmdlet.ThrowTerminatingError($errorItem) return $false @@ -60,8 +59,10 @@ function Set-JiraUser { [Hashtable] $Property, - [PSCredential] - $Credential, + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty, [Switch] $PassThru diff --git a/JiraPS/Public/Set-JiraVersion.ps1 b/JiraPS/Public/Set-JiraVersion.ps1 index f6b1eaa3..18c104ce 100644 --- a/JiraPS/Public/Set-JiraVersion.ps1 +++ b/JiraPS/Public/Set-JiraVersion.ps1 @@ -1,36 +1,17 @@ function Set-JiraVersion { - <# - .SYNOPSIS - Modifies an existing Version in JIRA - .DESCRIPTION - This function modifies the Version for an existing Project in JIRA. - .EXAMPLE - Get-JiraVersion -Project $Project -Name "Old-Name" | Set-JiraVersion -Name 'New-Name' - This example assigns the modifies the existing version with a new name 'New-Name'. - .EXAMPLE - Get-JiraVersion -ID 162401 | Set-JiraVersion -Description 'Descriptive String' - This example assigns the modifies the existing version with a new name 'New-Name'. - .INPUTS - [JiraPS.Version] - .OUTPUTS - [JiraPS.Version] - .NOTES - This function requires either the -Credential parameter to be passed or a persistent JIRA session. See New-JiraSession for more details. If neither are supplied, this function will run with anonymous access to JIRA. - #> + # .ExternalHelp ..\JiraPS-help.xml [CmdletBinding( SupportsShouldProcess )] param( - # Version to be changed [Parameter( Mandatory, ValueFromPipeline )] [ValidateNotNullOrEmpty()] [ValidateScript( { if (("JiraPS.Version" -notin $_.PSObject.TypeNames) -and (($_ -isnot [String]))) { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid Type for Parameter"), - 'ParameterType.NotJiraVersion', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $_ - ) + $exception = ([System.ArgumentException]"Invalid Type for Parameter") #fix code highlighting] + $errorId = 'ParameterType.NotJiraVersion' + $errorCategory = 'InvalidArgument' + $errorTarget = $_ + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Wrong object type provided for Version. Expected [JiraPS.Version] or [String], but was $($_.GetType().Name)" $PSCmdlet.ThrowTerminatingError($errorItem) <# @@ -46,41 +27,32 @@ [Object[]] $Version, - # New Name of the Version. [String] $Name, - # New Description of the Version. [String] $Description, - # New value for Archived. [Bool] $Archived, - # New value for Released. [Bool] $Released, - # New Date of the release. [DateTime] $ReleaseDate, - # New Date of the user release. [DateTime] $StartDate, - # The new Project where this version should be in. - # This can be the ID of the Project, or the Project Object [ValidateScript( { if (("JiraPS.Project" -notin $_.PSObject.TypeNames) -and (($_ -isnot [String]))) { - $errorItem = [System.Management.Automation.ErrorRecord]::new( - ([System.ArgumentException]"Invalid Type for Parameter"), - 'ParameterType.NotJiraProject', - [System.Management.Automation.ErrorCategory]::InvalidArgument, - $_ - ) + $exception = ([System.ArgumentException]"Invalid Type for Parameter") #fix code highlighting] + $errorId = 'ParameterType.NotJiraProject' + $errorCategory = 'InvalidArgument' + $errorTarget = $_ + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget $errorItem.ErrorDetails = "Wrong object type provided for Project. Expected [JiraPS.Project] or [String], but was $($_.GetType().Name)" $PSCmdlet.ThrowTerminatingError($errorItem) <# @@ -96,10 +68,10 @@ [Object] $Project, - # Credentials to use to connect to JIRA. - # If not specified, this function will use anonymous access. - [PSCredential] - $Credential + [Parameter()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty ) begin { diff --git a/Tests/Add-JiraFilterPermission.Tests.ps1 b/Tests/Add-JiraFilterPermission.Tests.ps1 new file mode 100644 index 00000000..d1f947c5 --- /dev/null +++ b/Tests/Add-JiraFilterPermission.Tests.ps1 @@ -0,0 +1,291 @@ +Describe 'Add-JiraFilterPermission' { + BeforeAll { + Remove-Module JiraPS -ErrorAction SilentlyContinue + Import-Module "$PSScriptRoot/../JiraPS" -Force -ErrorAction Stop + } + + InModuleScope JiraPS { + + . "$PSScriptRoot/Shared.ps1" + + #region Definitions + $jiraServer = "https://jira.example.com" + + $permissionJSON = @" +[ + { + "id": 10000, + "type": "global" + }, + { + "id": 10010, + "type": "project", + "project": { + "self": "$jiraServer/jira/rest/api/2/project/EX", + "id": "10000", + "key": "EX", + "name": "Example", + "avatarUrls": { + "48x48": "$jiraServer/jira/secure/projectavatar?size=large&pid=10000", + "24x24": "$jiraServer/jira/secure/projectavatar?size=small&pid=10000", + "16x16": "$jiraServer/jira/secure/projectavatar?size=xsmall&pid=10000", + "32x32": "$jiraServer/jira/secure/projectavatar?size=medium&pid=10000" + }, + "projectCategory": { + "self": "$jiraServer/jira/rest/api/2/projectCategory/10000", + "id": "10000", + "name": "FIRST", + "description": "First Project Category" + }, + "simplified": false + } + }, + { + "id": 10010, + "type": "project", + "project": { + "self": "$jiraServer/jira/rest/api/2/project/MKY", + "id": "10002", + "key": "MKY", + "name": "Example", + "avatarUrls": { + "48x48": "$jiraServer/jira/secure/projectavatar?size=large&pid=10002", + "24x24": "$jiraServer/jira/secure/projectavatar?size=small&pid=10002", + "16x16": "$jiraServer/jira/secure/projectavatar?size=xsmall&pid=10002", + "32x32": "$jiraServer/jira/secure/projectavatar?size=medium&pid=10002" + }, + "projectCategory": { + "self": "$jiraServer/jira/rest/api/2/projectCategory/10000", + "id": "10000", + "name": "FIRST", + "description": "First Project Category" + }, + "simplified": false + }, + "role": { + "self": "$jiraServer/jira/rest/api/2/project/MKY/role/10360", + "name": "Developers", + "id": 10360, + "description": "A project role that represents developers in a project", + "actors": [ + { + "id": 10240, + "displayName": "jira-developers", + "type": "atlassian-group-role-actor", + "name": "jira-developers" + }, + { + "id": 10241, + "displayName": "Fred F. User", + "type": "atlassian-user-role-actor", + "name": "fred" + } + ] + } + }, + { + "id": 10010, + "type": "group", + "group": { + "name": "jira-administrators", + "self": "$jiraServer/jira/rest/api/2/group?groupname=jira-administrators" + } + } +] +"@ + #endregion Definitions + + #region Mocks + Mock Get-JiraConfigServer -ModuleName JiraPS { + $jiraServer + } + + Mock ConvertTo-JiraFilter -ModuleName JiraPS { + $i = New-Object -TypeName PSCustomObject + $i.PSObject.TypeNames.Insert(0, 'JiraPS.Filter') + $i + } + + Mock ConvertTo-JiraFilterPermission -ModuleName JiraPS { + $i = (ConvertFrom-Json $permissionJSON) + $i.PSObject.TypeNames.Insert(0, 'JiraPS.FilterPermission') + $i + } + + Mock Get-JiraFilter -ModuleName JiraPS { + foreach ($_id in $Id) { + $object = New-Object -TypeName PSCustomObject -Property @{ + id = $_id + RestUrl = "$jiraServer/rest/api/latest/filter/$_id" + } + $object.PSObject.TypeNames.Insert(0, 'JiraPS.Filter') + $object + } + } + + Mock Invoke-JiraMethod -ModuleName JiraPS -ParameterFilter {$Method -eq 'Post' -and $URI -like "$jiraServer/rest/api/*/filter/*/permission"} { + ShowMockInfo 'Invoke-JiraMethod' 'Method', 'Uri', 'Body' + ConvertFrom-Json $permissionJSON + } + + Mock Invoke-JiraMethod -ModuleName JiraPS { + ShowMockInfo 'Invoke-JiraMethod' 'Method', 'Uri' + throw "Unidentified call to Invoke-JiraMethod" + } + #endregion Mocks + + Context "Sanity checking" { + $command = Get-Command -Name Add-JiraFilterPermission + + defParam $command 'Filter' + defParam $command 'Id' + defParam $command 'Type' + defParam $command 'Value' + defParam $command 'Credential' + } + + Context "Behavior testing" { + It "Adds share permission to Filter Object" { + { + Add-JiraFilterPermission -Filter (Get-JiraFilter -Id 12844) -Type "Global" + } | Should Not Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It -ParameterFilter { + $Method -eq 'Post' -and + $URI -like '*/rest/api/*/filter/12844/permission' + } + + Assert-MockCalled -CommandName ConvertTo-JiraFilter -ModuleName JiraPS -Exactly -Times 1 -Scope It + } + + It "Adds share permission to FilterId" { + { + Add-JiraFilterPermission -Id 12844 -Type "Global" + } | Should Not Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It -ParameterFilter { + $Method -eq 'Post' -and + $URI -like '*/rest/api/*/filter/12844/permission' + } + + Assert-MockCalled -CommandName ConvertTo-JiraFilter -ModuleName JiraPS -Exactly -Times 1 -Scope It + } + } + + Context "Input testing" { + It "requires -Filter to be a JiraPS.Filter" { + { Add-JiraFilterPermission -Filter 1 -Type "Global" } | Should -Throw + { Add-JiraFilterPermission -Filter "lorem" -Type "Global" } | Should -Throw + { Add-JiraFilterPermission -Filter (Get-Date) -Type "Global" } | Should -Throw + + { Add-JiraFilterPermission -Filter (Get-JiraFilter -Id 1) -Type "Global" } | Should -Not -Throw + } + + It "allows a JiraPS.Filter to be passed over the pipeline" { + { Get-JiraFilter -Id 1 | Add-JiraFilterPermission -Type "Global" } | Should -Not -Throw + } + + It "can process multiple Filters" { + $filters = 1..5 | ForEach-Object { Get-JiraFilter -Id 1 } + $filters.Count | Should -Be 5 + + { Add-JiraFilterPermission -Filter $filters -Type "Global" } | Should -Not -Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 5 -Scope It + } + + It "can find a filter by it's Id" { + { Add-JiraFilterPermission -Id 1 -Type "Global" } | Should -Not -Throw + + Assert-MockCalled -CommandName Get-JiraFilter -ModuleName JiraPS -Exactly -Times 1 -Scope It + } + + It "allows for the filter's Id to be passed over the pipeline" { + { 1,2 | Add-JiraFilterPermission -Type "Global" } | Should -Not -Throw + + Assert-MockCalled -CommandName Get-JiraFilter -ModuleName JiraPS -Exactly -Times 2 -Scope It + } + + It "can process mutiple FilterIds" { + { Add-JiraFilterPermission -Id 1,2,3,4,5 -Type "Global" } | Should -Not -Throw + + Assert-MockCalled -CommandName Get-JiraFilter -ModuleName JiraPS -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 5 -Scope It + } + + It "accepts the 5 known permission types" { + { Add-JiraFilterPermission -Id 1 -Type "lorem" } | Should -Throw + { Add-JiraFilterPermission -Id 1 -Type "Invalid" } | Should -Throw + + { Add-JiraFilterPermission -Id 1 -Type "Global" } | Should -Not -Throw + { Add-JiraFilterPermission -Id 1 -Type "Group" } | Should -Not -Throw + { Add-JiraFilterPermission -Id 1 -Type "Project" } | Should -Not -Throw + { Add-JiraFilterPermission -Id 1 -Type "ProjectRole" } | Should -Not -Throw + { Add-JiraFilterPermission -Id 1 -Type "Authenticated" } | Should -Not -Throw + } + + It "does not validate -Value" { + { Add-JiraFilterPermission -Id 1 -Type "Global" -Value "invalid" } | Should -Not -Throw + { Add-JiraFilterPermission -Id 1 -Type "Group" -Value "not a group" } | Should -Not -Throw + { Add-JiraFilterPermission -Id 1 -Type "Project" -Value "not a project" } | Should -Not -Throw + { Add-JiraFilterPermission -Id 1 -Type "ProjectRole" -Value "not a Role" } | Should -Not -Throw + { Add-JiraFilterPermission -Id 1 -Type "Authenticated" -Value "invalid" } | Should -Not -Throw + } + + It "constructs a valid request Body for type 'Global'" { + { Add-JiraFilterPermission -Id 12844 -Type "Global" } | Should -Not -Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It -ParameterFilter { + $Method -eq 'Post' -and + $URI -like '*/rest/api/*/filter/12844/permission' -and + $Body -match '"type":\s*"global"' -and + $Body -notmatch ',' + } + } + + It "constructs a valid request Body for type 'Authenticated'" { + { Add-JiraFilterPermission -Id 12844 -Type "Authenticated" } | Should -Not -Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It -ParameterFilter { + $Method -eq 'Post' -and + $URI -like '*/rest/api/*/filter/12844/permission' -and + $Body -match '"type":\s*"authenticated"' -and + $Body -notmatch "," + } + } + + It "constructs a valid request Body for type 'Group'" { + { Add-JiraFilterPermission -Id 12844 -Type "Group" -Value "administrators" } | Should -Not -Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It -ParameterFilter { + $Method -eq 'Post' -and + $URI -like '*/rest/api/*/filter/12844/permission' -and + $Body -match '"type":\s*"group"' -and + $Body -match '"groupname":\s*"administrators"' + } + } + + It "constructs a valid request Body for type 'Project'" { + { Add-JiraFilterPermission -Id 12844 -Type "Project" -Value "11822" } | Should -Not -Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It -ParameterFilter { + $Method -eq 'Post' -and + $URI -like '*/rest/api/*/filter/12844/permission' -and + $Body -match '"type":\s*"project"' -and + $Body -match '"projectId":\s*"11822"' + } + } + + It "constructs a valid request Body for type 'ProjectRole'" { + { Add-JiraFilterPermission -Id 12844 -Type "ProjectRole" -Value "11822" } | Should -Not -Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It -ParameterFilter { + $Method -eq 'Post' -and + $URI -like '*/rest/api/*/filter/12844/permission' -and + $Body -match '"type":\s*"projectRole"' -and + $Body -match '"projectRoleId":\s*"11822"' + } + } + } + } +} diff --git a/Tests/ConvertTo-JiraFilter.Tests.ps1 b/Tests/ConvertTo-JiraFilter.Tests.ps1 index c53fc172..0aa7e6a4 100644 --- a/Tests/ConvertTo-JiraFilter.Tests.ps1 +++ b/Tests/ConvertTo-JiraFilter.Tests.ps1 @@ -52,8 +52,97 @@ Describe "ConvertTo-JiraFilter" { } '@ + $samplePermission = @" +[ + { + "id": 10000, + "type": "global" + }, + { + "id": 10010, + "type": "project", + "project": { + "self": "$jiraServer/jira/rest/api/2/project/EX", + "id": "10000", + "key": "EX", + "name": "Example", + "avatarUrls": { + "48x48": "$jiraServer/jira/secure/projectavatar?size=large&pid=10000", + "24x24": "$jiraServer/jira/secure/projectavatar?size=small&pid=10000", + "16x16": "$jiraServer/jira/secure/projectavatar?size=xsmall&pid=10000", + "32x32": "$jiraServer/jira/secure/projectavatar?size=medium&pid=10000" + }, + "projectCategory": { + "self": "$jiraServer/jira/rest/api/2/projectCategory/10000", + "id": "10000", + "name": "FIRST", + "description": "First Project Category" + }, + "simplified": false + } + }, + { + "id": 10010, + "type": "project", + "project": { + "self": "$jiraServer/jira/rest/api/2/project/MKY", + "id": "10002", + "key": "MKY", + "name": "Example", + "avatarUrls": { + "48x48": "$jiraServer/jira/secure/projectavatar?size=large&pid=10002", + "24x24": "$jiraServer/jira/secure/projectavatar?size=small&pid=10002", + "16x16": "$jiraServer/jira/secure/projectavatar?size=xsmall&pid=10002", + "32x32": "$jiraServer/jira/secure/projectavatar?size=medium&pid=10002" + }, + "projectCategory": { + "self": "$jiraServer/jira/rest/api/2/projectCategory/10000", + "id": "10000", + "name": "FIRST", + "description": "First Project Category" + }, + "simplified": false + }, + "role": { + "self": "$jiraServer/jira/rest/api/2/project/MKY/role/10360", + "name": "Developers", + "id": 10360, + "description": "A project role that represents developers in a project", + "actors": [ + { + "id": 10240, + "displayName": "jira-developers", + "type": "atlassian-group-role-actor", + "name": "jira-developers" + }, + { + "id": 10241, + "displayName": "Fred F. User", + "type": "atlassian-user-role-actor", + "name": "fred" + } + ] + } + }, + { + "id": 10010, + "type": "group", + "group": { + "name": "jira-administrators", + "self": "$jiraServer/jira/rest/api/2/group?groupname=jira-administrators" + } + } +] +"@ + + Mock ConvertTo-JiraFilterPermission -ModuleName JiraPS { + $i = New-Object -TypeName PSCustomObject -Property @{ Id = 1111 } + $i.PSObject.TypeNames.Insert(0, 'JiraPS.FilterPermission') + $i + } + $sampleObject = ConvertFrom-Json -InputObject $sampleJson - $r = ConvertTo-JiraFilter -InputObject $sampleObject + $r = ConvertTo-JiraFilter -InputObject $sampleObject -FilterPermission $samplePermission It "Creates a PSObject out of JSON input" { $r | Should Not BeNullOrEmpty @@ -67,7 +156,17 @@ Describe "ConvertTo-JiraFilter" { defProp $r 'RestUrl' 'https://jira.atlassian.com/rest/api/latest/filter/12844' defProp $r 'ViewUrl' 'https://jira.atlassian.com/secure/IssueNavigator.jspa?mode=hide&requestId=12844' defProp $r 'SearchUrl' 'https://jira.atlassian.com/rest/api/latest/search?jql=project+%3D+10240+AND+issuetype+%3D+1+ORDER+BY+key+DESC' - defProp $r 'Favorite' $false + defProp $r 'Favourite' $false + It "Defines the 'Favorite' property as an alias of 'Favourite'" { + ($r | Get-Member -Name Favorite).MemberType | Should -Be "AliasProperty" + } + It "Uses output type of 'JiraPS.FilterPermission' for property 'FilterPermissions'" { + checkType $r.FilterPermissions 'JiraPS.FilterPermission' + } + + It "uses ConvertTo-JiraFilterPermission" { + Assert-MockCalled -CommandName ConvertTo-JiraFilterPermission -Module JiraPS -Exactly -Times 1 -Scope Describe + } } } diff --git a/Tests/ConvertTo-JiraFilterPermission.Tests.ps1 b/Tests/ConvertTo-JiraFilterPermission.Tests.ps1 new file mode 100644 index 00000000..2c4f1475 --- /dev/null +++ b/Tests/ConvertTo-JiraFilterPermission.Tests.ps1 @@ -0,0 +1,117 @@ +Describe "ConvertTo-JiraFilterPermission" { + + Import-Module "$PSScriptRoot/../JiraPS" -Force -ErrorAction Stop + + InModuleScope JiraPS { + + . "$PSScriptRoot/Shared.ps1" + + $sampleJson = @" +[ + { + "id": 10000, + "type": "global" + }, + { + "id": 10010, + "type": "project", + "project": { + "self": "$jiraServer/jira/rest/api/2/project/EX", + "id": "10000", + "key": "EX", + "name": "Example", + "avatarUrls": { + "48x48": "$jiraServer/jira/secure/projectavatar?size=large&pid=10000", + "24x24": "$jiraServer/jira/secure/projectavatar?size=small&pid=10000", + "16x16": "$jiraServer/jira/secure/projectavatar?size=xsmall&pid=10000", + "32x32": "$jiraServer/jira/secure/projectavatar?size=medium&pid=10000" + }, + "projectCategory": { + "self": "$jiraServer/jira/rest/api/2/projectCategory/10000", + "id": "10000", + "name": "FIRST", + "description": "First Project Category" + }, + "simplified": false + } + }, + { + "id": 10010, + "type": "project", + "project": { + "self": "$jiraServer/jira/rest/api/2/project/MKY", + "id": "10002", + "key": "MKY", + "name": "Example", + "avatarUrls": { + "48x48": "$jiraServer/jira/secure/projectavatar?size=large&pid=10002", + "24x24": "$jiraServer/jira/secure/projectavatar?size=small&pid=10002", + "16x16": "$jiraServer/jira/secure/projectavatar?size=xsmall&pid=10002", + "32x32": "$jiraServer/jira/secure/projectavatar?size=medium&pid=10002" + }, + "projectCategory": { + "self": "$jiraServer/jira/rest/api/2/projectCategory/10000", + "id": "10000", + "name": "FIRST", + "description": "First Project Category" + }, + "simplified": false + }, + "role": { + "self": "$jiraServer/jira/rest/api/2/project/MKY/role/10360", + "name": "Developers", + "id": 10360, + "description": "A project role that represents developers in a project", + "actors": [ + { + "id": 10240, + "displayName": "jira-developers", + "type": "atlassian-group-role-actor", + "name": "jira-developers" + }, + { + "id": 10241, + "displayName": "Fred F. User", + "type": "atlassian-user-role-actor", + "name": "fred" + } + ] + } + }, + { + "id": 10010, + "type": "group", + "group": { + "name": "jira-administrators", + "self": "$jiraServer/jira/rest/api/2/group?groupname=jira-administrators" + } + } +] +"@ + + $sampleObject = ConvertFrom-Json -InputObject $sampleJson + $r = ConvertTo-JiraFilterPermission -InputObject $sampleObject + + It "Creates a PSObject out of JSON input" { + $r | Should -Not -BeNullOrEmpty + } + + checkPsType $r 'JiraPS.FilterPermission' + + defProp $r 'Id' @(10000, 10010, 10010, 10010) + defProp $r 'Type' @('global', 'project', 'project', 'group') + It "Defines the 'Group' property of type 'JiraPS.Group'" { + checkType $r[3].Group 'JiraPS.Group' + $r.Group.Name | Should -Be 'jira-administrators' + } + It "Defines the 'Project' property of type 'JiraPS.Project'" { + checkType $r[1].Project 'JiraPS.Project' + $r.Project.Name | Should -Be @('Example', 'Example') + } + + It "Defines the 'Role' property of type 'JiraPS.ProjectRole'" { + checkType $r[2].Role 'JiraPS.ProjectRole' + $r.Role.Name | Should -Be 'Developers' + } + } +} diff --git a/Tests/ConvertTo-JiraProjectRole.Tests.ps1 b/Tests/ConvertTo-JiraProjectRole.Tests.ps1 new file mode 100644 index 00000000..acee0c95 --- /dev/null +++ b/Tests/ConvertTo-JiraProjectRole.Tests.ps1 @@ -0,0 +1,49 @@ +Describe "ConvertTo-JiraProjectRole" { + + Import-Module "$PSScriptRoot/../JiraPS" -Force -ErrorAction Stop + + InModuleScope JiraPS { + + . "$PSScriptRoot/Shared.ps1" + + $sampleJson = @" +[ + { + "self": "http://www.example.com/jira/rest/api/2/project/MKY/role/10360", + "name": "Developers", + "id": 10360, + "description": "A project role that represents developers in a project", + "actors": [ + { + "id": 10240, + "displayName": "jira-developers", + "type": "atlassian-group-role-actor", + "name": "jira-developers" + }, + { + "id": 10241, + "displayName": "Fred F. User", + "type": "atlassian-user-role-actor", + "name": "fred" + } + ] + } +] +"@ + + $sampleObject = ConvertFrom-Json -InputObject $sampleJson + $r = ConvertTo-JiraProjectRole -InputObject $sampleObject + + It "Creates a PSObject out of JSON input" { + $r | Should -Not -BeNullOrEmpty + } + + checkPsType $r 'JiraPS.ProjectRole' + + defProp $r 'Id' 10360 + defProp $r 'Name' "Developers" + defProp $r 'Description' "A project role that represents developers in a project" + hasProp $r 'Actors' + defProp $r 'RestUrl' "http://www.example.com/jira/rest/api/2/project/MKY/role/10360" + } +} diff --git a/Tests/Get-JiraFilter.Tests.ps1 b/Tests/Get-JiraFilter.Tests.ps1 index f3fd631e..9106035c 100644 --- a/Tests/Get-JiraFilter.Tests.ps1 +++ b/Tests/Get-JiraFilter.Tests.ps1 @@ -1,34 +1,37 @@ Describe 'Get-JiraFilter' { - - Import-Module "$PSScriptRoot/../JiraPS" -Force -ErrorAction Stop + BeforeAll { + Remove-Module JiraPS -ErrorAction SilentlyContinue + Import-Module "$PSScriptRoot/../JiraPS" -Force -ErrorAction Stop + } InModuleScope JiraPS { . "$PSScriptRoot/Shared.ps1" + #region Definitions $jiraServer = "https://jira.example.com" - $response = @' + $responseFilter = @" { - "self": "https://jira.atlassian.com/rest/api/latest/filter/12844", + "self": "$jiraServer/rest/api/latest/filter/12844", "id": "12844", "name": "All JIRA Bugs", "owner": { - "self": "https://jira.atlassian.com/rest/api/2/user?username=scott@atlassian.com", + "self": "$jiraServer/rest/api/2/user?username=scott@atlassian.com", "key": "scott@atlassian.com", "name": "scott@atlassian.com", "avatarUrls": { - "16x16": "https://jira.atlassian.com/secure/useravatar?size=xsmall&avatarId=10612", - "24x24": "https://jira.atlassian.com/secure/useravatar?size=small&avatarId=10612", - "32x32": "https://jira.atlassian.com/secure/useravatar?size=medium&avatarId=10612", - "48x48": "https://jira.atlassian.com/secure/useravatar?avatarId=10612" + "16x16": "$jiraServer/secure/useravatar?size=xsmall&avatarId=10612", + "24x24": "$jiraServer/secure/useravatar?size=small&avatarId=10612", + "32x32": "$jiraServer/secure/useravatar?size=medium&avatarId=10612", + "48x48": "$jiraServer/secure/useravatar?avatarId=10612" }, "displayName": "Scott Farquhar [Atlassian]", "active": true }, "jql": "project = 10240 AND issuetype = 1 ORDER BY key DESC", - "viewUrl": "https://jira.atlassian.com/secure/IssueNavigator.jspa?mode=hide&requestId=12844", - "searchUrl": "https://jira.atlassian.com/rest/api/latest/search?jql=project+%3D+10240+AND+issuetype+%3D+1+ORDER+BY+key+DESC", + "viewUrl": "$jiraServer/secure/IssueNavigator.jspa?mode=hide&requestId=12844", + "searchUrl": "$jiraServer/rest/api/latest/search?jql=project+%3D+10240+AND+issuetype+%3D+1+ORDER+BY+key+DESC", "favourite": false, "sharePermissions": [ { @@ -51,36 +54,82 @@ "end-index": 0 } } -'@ +"@ + + $responseFilterCollection = @" +[ + { + "self": "$jiraServer/rest/api/latest/filter/13844", + "id": "13844", + "name": "Filter 1", + "jql": "project = 10240 AND issuetype = 1 ORDER BY key DESC", + "favourite": true + }, + { + "self": "$jiraServer/rest/api/latest/filter/14844", + "id": "14844", + "name": "Filter 2", + "jql": "project = 10240 AND issuetype = 1 ORDER BY key DESC", + "favourite": true + }, + { + "self": "$jiraServer/rest/api/latest/filter/15844", + "id": "15844", + "name": "Filter 3", + "jql": "project = 10240 AND issuetype = 1 ORDER BY key DESC", + "favourite": true + } +] +"@ + #endregion Definitions - Mock Get-JiraConfigServer { + #region Mocks + Mock Get-JiraConfigServer -ModuleName JiraPS { $jiraServer } - Mock Invoke-JiraMethod -ModuleName JiraPS -ParameterFilter {$Method -eq 'Get' -and $URI -eq "$jiraServer/rest/api/latest/filter/12345"} { + Mock ConvertTo-JiraFilter -ModuleName JiraPS { + foreach ($i in $InputObject) { + $i.PSObject.TypeNames.Insert(0, 'JiraPS.Filter') + $i + } + } + + Mock Invoke-JiraMethod -ModuleName JiraPS -ParameterFilter {$Method -eq 'Get' -and $URI -like "$jiraServer/rest/api/*/filter/12345"} { + ShowMockInfo 'Invoke-JiraMethod' 'Method', 'Uri' + ConvertFrom-Json $responseFilter + } + + Mock Invoke-JiraMethod -ModuleName JiraPS -ParameterFilter {$Method -eq 'Get' -and $URI -like "$jiraServer/rest/api/*/filter/67890"} { ShowMockInfo 'Invoke-JiraMethod' 'Method', 'Uri' - ConvertFrom-Json $response + ConvertFrom-Json $responseFilter } - Mock Invoke-JiraMethod -ModuleName JiraPS -ParameterFilter {$Method -eq 'Get' -and $URI -eq "$jiraServer/rest/api/latest/filter/67890"} { + + Mock Invoke-JiraMethod -ModuleName JiraPS -ParameterFilter {$Method -eq 'Get' -and $URI -like "$jiraServer/rest/api/*/filter/favourite"} { ShowMockInfo 'Invoke-JiraMethod' 'Method', 'Uri' - ConvertFrom-Json $response + ConvertFrom-Json $responseFilterCollection } - Mock Invoke-JiraMethod -ModuleName JiraPS -ParameterFilter {$Method -eq 'Get' -and $URI -like "$jiraServer/rest/api/latest/filter/*"} { + + Mock Invoke-JiraMethod -ModuleName JiraPS -ParameterFilter {$Method -eq 'Get' -and $URI -like "$jiraServer/rest/api/*/filter/*"} { ShowMockInfo 'Invoke-JiraMethod' 'Method', 'Uri' - ConvertFrom-Json $response + ConvertFrom-Json $responseFilter } Mock Invoke-JiraMethod -ModuleName JiraPS { ShowMockInfo 'Invoke-JiraMethod' 'Method', 'Uri' throw "Unidentified call to Invoke-JiraMethod" } + #endregion Mocks Context "Sanity checking" { $command = Get-Command -Name Get-JiraFilter defParam $command 'Id' defParam $command 'InputObject' + defParam $command 'Favorite' defParam $command 'Credential' + + defAlias $command 'Favourite' 'Favorite' } Context "Behavior testing" { @@ -95,29 +144,45 @@ { Get-JiraFilter -Id 12345 } | Should Not Throw Assert-MockCalled -CommandName ConvertTo-JiraFilter -ModuleName JiraPS } + + It "Finds all favorite filters of the user" { + { Get-JiraFilter -Favorite } | Should Not Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It -ParameterFilter {$Method -eq 'Get' -and $URI -like '*/rest/api/*/filter/favourite'} + } } Context "Input testing" { - $sampleFilter = ConvertTo-JiraFilter ( ConvertFrom-Json $response ) + $sampleFilter = ConvertTo-JiraFilter (ConvertFrom-Json $responseFilter) It "Accepts a filter ID for the -Filter parameter" { - { Get-JiraFilter -Id 12345 } | Should Not Throw + { Get-JiraFilter -Id "12345" } | Should Not Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It + } + + It "Accepts a filter ID without the -Filter parameter" { + { Get-JiraFilter "12345" } | Should Not Throw + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It } It "Accepts multiple filter IDs to the -Filter parameter" { { Get-JiraFilter -Id '12345', '67890' } | Should Not Throw + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It -ParameterFilter {$Method -eq 'Get' -and $URI -like '*/rest/api/*/filter/12345'} Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It -ParameterFilter {$Method -eq 'Get' -and $URI -like '*/rest/api/*/filter/67890'} } It "Accepts a JiraPS.Filter object to the InputObject parameter" { { Get-JiraFilter -InputObject $sampleFilter } | Should Not Throw + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It -ParameterFilter {$Method -eq 'Get' -and $URI -like '*rest/api/*/filter/12844'} } It "Accepts a JiraPS.Filter object via pipeline" { { $sampleFilter | Get-JiraFilter } | Should Not Throw + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It -ParameterFilter {$Method -eq 'Get' -and $URI -like '*rest/api/*/filter/12844'} } } diff --git a/Tests/Get-JiraFilterPermission.Tests.ps1 b/Tests/Get-JiraFilterPermission.Tests.ps1 new file mode 100644 index 00000000..71ff2533 --- /dev/null +++ b/Tests/Get-JiraFilterPermission.Tests.ps1 @@ -0,0 +1,125 @@ +Describe 'Get-JiraFilterPermission' { + BeforeAll { + Remove-Module JiraPS -ErrorAction SilentlyContinue + Import-Module "$PSScriptRoot/../JiraPS" -Force -ErrorAction Stop + } + + InModuleScope JiraPS { + + . "$PSScriptRoot/Shared.ps1" + + #region Definitions + $jiraServer = "https://jira.example.com" + + $sampleResponse = @" +{ + "id": 10000, + "type": "global" +} +"@ + #endregion Definitions + + #region Mocks + Mock Get-JiraConfigServer -ModuleName JiraPS { + $jiraServer + } + + Mock ConvertTo-JiraFilter -ModuleName JiraPS { } + + Mock Get-JiraFilter -ModuleName JiraPS { + foreach ($_id in $Id) { + $basicFilter = New-Object -TypeName PSCustomObject -Property @{ + Id = $Id + RestUrl = "$jiraServer/rest/api/2/filter/$Id" + } + $basicFilter.PSObject.TypeNames.Insert(0, 'JiraPS.Filter') + $basicFilter + } + } + + Mock Invoke-JiraMethod -ModuleName JiraPS -ParameterFilter {$Method -eq 'Get' -and $URI -like "$jiraServer/rest/api/*/filter/*/permission"} { + ShowMockInfo 'Invoke-JiraMethod' 'Method', 'Uri' + ConvertFrom-Json $sampleResponse + } + + Mock Invoke-JiraMethod -ModuleName JiraPS { + ShowMockInfo 'Invoke-JiraMethod' 'Method', 'Uri' + throw "Unidentified call to Invoke-JiraMethod" + } + #endregion Mocks + + Context "Sanity checking" { + $command = Get-Command -Name Get-JiraFilterPermission + + defParam $command 'Filter' + defParam $command 'Id' + defParam $command 'Credential' + } + + Context "Behavior testing" { + It "Retrieves the permissions of a Filter by Object" { + { Get-JiraFilter -Id 23456 | Get-JiraFilterPermission } | Should -Not -Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It -ParameterFilter { + $Method -eq 'Get' -and + $URI -like '*/rest/api/*/filter/23456/permission' + } + } + + It "Retrieves the permissions of a Filter by Id" { + { 23456 | Get-JiraFilterPermission } | Should -Not -Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It -ParameterFilter { + $Method -eq 'Get' -and + $URI -like '*/rest/api/*/filter/23456/permission' + } + } + } + + Context "Input testing" { + It "finds the filter by Id" { + { Get-JiraFilterPermission -Id 23456 } | Should -Not -Throw + + Assert-MockCalled -CommandName Get-JiraFilter -ModuleName JiraPS -Exactly -Times 1 -Scope It + } + + It "does not accept negative Ids" { + { Get-JiraFilterPermission -Id -1 } | Should -Throw + } + + It "can process multiple Ids" { + { Get-JiraFilterPermission -Id 23456, 23456 } | Should -Not -Throw + + Assert-MockCalled -CommandName Get-JiraFilter -ModuleName JiraPS -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 2 -Scope It + } + + It "allows for the filter to be passed over the pipeline" { + { Get-JiraFilter -Id 23456 | Get-JiraFilterPermission } | Should -Not -Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It + } + + It "can ony process one Filter objects" { + $filter = @() + $filter += Get-JiraFilter -Id 23456 + $filter += Get-JiraFilter -Id 23456 + + { Get-JiraFilterPermission -Filter $filter } | Should -Not -Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 2 -Scope It + } + + It "resolves positional parameters" { + { Get-JiraFilterPermission 23456 } | Should -Not -Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It + + $filter = Get-JiraFilter -Id 23456 + { Get-JiraFilterPermission $filter } | Should -Not -Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 2 -Scope It + } + } + } +} diff --git a/Tests/Get-JiraGroupMember.Tests.ps1 b/Tests/Get-JiraGroupMember.Tests.ps1 index 825baa9c..08f24ffc 100644 --- a/Tests/Get-JiraGroupMember.Tests.ps1 +++ b/Tests/Get-JiraGroupMember.Tests.ps1 @@ -6,26 +6,17 @@ . "$PSScriptRoot/Shared.ps1" - Mock Get-JiraConfigServer { + #region Mocks + Mock Get-JiraConfigServer -ModuleName JiraPS { 'https://jira.example.com' } - # If we don't override this in a context or test, we don't want it to - # actually try to query a JIRA instance - Mock Invoke-JiraMethod { - ShowMockInfo 'Invoke-JiraMethod' 'Method', 'Uri' - throw "Unidentified call to Invoke-JiraMethod" - } - - Mock Invoke-JiraMethod -ModuleName JiraPS -ParameterFilter { $Method -eq 'Get' -and $URI -like '*/rest/api/*/group?groupname=testgroup*' } { - ShowMockInfo 'Invoke-JiraMethod' 'Method', 'Uri' - ConvertFrom-Json @' -{ - "Name": "testgroup", - "RestUrl": "https://jira.example.com/rest/api/2/group?groupname=testgroup", - "Size": 2 -} -'@ + Mock Get-JiraUser -ModuleName JiraPS { + $object = [PSCustomObject] @{ + 'Name' = 'username' + } + $object.PSObject.TypeNames.Insert(0, 'JiraPS.User') + return $object } Mock Get-JiraGroup -ModuleName JiraPS { @@ -38,104 +29,132 @@ Write-Output $obj } + Mock Invoke-JiraMethod -ModuleName JiraPS -ParameterFilter { $Method -eq 'Get' -and $URI -like '*/rest/api/*/group/member' -and $GetParameter["groupname"] -eq "testgroup" } { + ShowMockInfo 'Invoke-JiraMethod' 'Method', 'Uri' + ConvertFrom-Json @' +{ +"Name": "testgroup", +"RestUrl": "https://jira.example.com/rest/api/2/group?groupname=testgroup", +"Size": 2 +} +'@ + } + + # If we don't override this in a context or test, we don't want it to + # actually try to query a JIRA instance + Mock Invoke-JiraMethod { + ShowMockInfo 'Invoke-JiraMethod' 'Method', 'Uri' + throw "Unidentified call to Invoke-JiraMethod" + } + #endregion Mocks + Context "Sanity checking" { $command = Get-Command -Name Get-JiraGroupMember defParam $command 'Group' + defParam $command 'IncludeInactive' defParam $command 'StartIndex' defParam $command 'MaxResults' defParam $command 'Credential' } Context "Behavior testing" { - Mock Invoke-JiraMethod -ModuleName JiraPS { - ShowMockInfo 'Invoke-JiraMethod' 'Method', 'Uri' - } - - Mock Get-JiraUser -ModuleName JiraPS { - $object = [PSCustomObject] @{ - 'Name' = 'username' - } - $object.PSObject.TypeNames.Insert(0, 'JiraPS.User') - return $object - } It "Obtains members about a provided group in JIRA" { { Get-JiraGroupMember -Group testgroup } | Should Not Throw - Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It -ParameterFilter { $Method -eq 'Get' -and $URI -like '*/rest/api/*/group?groupname=testgroup&expand=users*' } - } - It "Supports the -StartIndex and -MaxResults parameters to page through search results" { - { Get-JiraGroupMember -Group testgroup -StartIndex 10 -MaxResults 50 } | Should Not Throw - # Expected: expand=users[10:60] (start index of 10, last index of 10+50) - # https://docs.atlassian.com/jira/REST/6.4.12/#d2e2307 - # Also, -like doesn't seem to "like" square brackets - Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It -ParameterFilter { $Method -eq 'Get' -and $URI -like '*/rest/api/*/group?groupname=testgroup&expand=users*10:60*' } + $assertMockCalledSplat = @{ + CommandName = 'Invoke-JiraMethod' + ModuleName = 'JiraPS' + ParameterFilter = { + $Method -eq 'Get' -and + $URI -like '*/rest/api/*/group/member' + } + Scope = 'It' + Exactly = $true + Times = 1 + } + Assert-MockCalled @assertMockCalledSplat } - It "Returns all issues via looping if -MaxResults is not specified" { - - # In order to test this, we'll need a slightly more elaborate - # mock that actually returns some data. - - Mock Invoke-JiraMethod -ModuleName JiraPS { - ShowMockInfo 'Invoke-JiraMethod' 'Method', 'Uri' - ConvertFrom-Json -InputObject @' -{ - "name": "testgroup", - "self": "https://jira.example.com/rest/api/2/group?groupname=testgroup", - "users": { - "size": 2, - "items": [ - { - "self": "https://jira.example.com/rest/api/2/user?username=testuser1", - "key": "testuser1", - "name": "testuser1", - "emailAddress": "testuser1@example.com", - "displayName": "Test User 1", - "active": true - }, - { - "self": "https://jira.example.com/rest/api/2/user?username=testuser2", - "key": "testuser2", - "name": "testuser2", - "emailAddress": "testuser2@example.com", - "displayName": "Test User 2", - "active": true - } - ], - "max-results": 50, - "start-index": 0, - "end-index": 0 - }, - "expand": "users" -} -'@ + It "Supports the -StartIndex parameters to page through search results" { + { Get-JiraGroupMember -Group testgroup -StartIndex 10 } | Should Not Throw + + $assertMockCalledSplat = @{ + CommandName = 'Invoke-JiraMethod' + ModuleName = 'JiraPS' + ParameterFilter = { + $Method -eq 'Get' -and + $URI -like '*/rest/api/*/group/member' -and + $PSCmdlet.PagingParameters.Skip -eq 10 + } + Scope = 'It' + Exactly = $true + Times = 1 } + Assert-MockCalled @assertMockCalledSplat + } - { Get-JiraGroupMember -Group testgroup } | Should Not Throw - - Assert-MockCalled -CommandName Get-JiraGroup -Exactly -Times 1 -Scope It -ParameterFilter { $GroupName -eq 'testgroup' } - Assert-MockCalled -CommandName Invoke-JiraMethod -Exactly -Times 1 -Scope It -ParameterFilter { $Method -eq 'Get' -and $URI -like '*/rest/api/*/group?groupname=testgroup&expand=users*0:2*' } - + It "Supports the -MaxResults parameters to page through search results" { + { Get-JiraGroupMember -Group testgroup -MaxResults 50 } | Should Not Throw + + $assertMockCalledSplat = @{ + CommandName = 'Invoke-JiraMethod' + ModuleName = 'JiraPS' + ParameterFilter = { + $Method -eq 'Get' -and + $URI -like '*/rest/api/*/group/member' -and + $PSCmdlet.PagingParameters.First -eq 50 + } + Scope = 'It' + Exactly = $true + Times = 1 + } + Assert-MockCalled @assertMockCalledSplat } } Context "Input testing" { It "Accepts a group name for the -Group parameter" { { Get-JiraGroupMember -Group testgroup } | Should Not Throw - Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It -ParameterFilter { $Method -eq 'Get' -and $URI -like '*/rest/api/*/group?groupname=testgroup&expand=users*' } + + $assertMockCalledSplat = @{ + CommandName = 'Invoke-JiraMethod' + ModuleName = 'JiraPS' + ParameterFilter = { + $Method -eq 'Get' -and + $URI -like '*/rest/api/*/group/member' -and + $GetParameter["groupname"] -eq "testgroup" + } + Scope = 'It' + Exactly = $true + Times = 1 + } + Assert-MockCalled @assertMockCalledSplat } It "Accepts a group object for the -InputObject parameter" { $group = Get-JiraGroup -GroupName testgroup { Get-JiraGroupMember -Group $group } | Should Not Throw - Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It -ParameterFilter { $Method -eq 'Get' -and $URI -like '*/rest/api/*/group?groupname=testgroup&expand=users*' } + + $assertMockCalledSplat = @{ + CommandName = 'Invoke-JiraMethod' + ModuleName = 'JiraPS' + ParameterFilter = { + $Method -eq 'Get' -and + $URI -like '*/rest/api/*/group/member' -and + $GetParameter["groupname"] -eq "testgroup" + } + Scope = 'It' + Exactly = $true + Times = 1 + } + Assert-MockCalled @assertMockCalledSplat # We called Get-JiraGroup once manually, and it should be - # called twice by Get-JiraGroupMember. - Assert-MockCalled -CommandName Get-JiraGroup -Exactly -Times 3 -Scope It + # called once by Get-JiraGroupMember. + Assert-MockCalled -CommandName Get-JiraGroup -Exactly -Times 2 -Scope It } } } diff --git a/Tests/Get-JiraIssue.Tests.ps1 b/Tests/Get-JiraIssue.Tests.ps1 index 89cd5bdc..8ecd54fc 100644 --- a/Tests/Get-JiraIssue.Tests.ps1 +++ b/Tests/Get-JiraIssue.Tests.ps1 @@ -27,6 +27,7 @@ } '@ + #region Mocks Mock Get-JiraConfigServer { $jiraServer } @@ -36,7 +37,7 @@ ConvertFrom-Json $response } - Mock Invoke-JiraMethod -ParameterFilter { $Method -eq 'Get' -and $URI -like "$jiraServer/rest/api/latest/search?jql=$jqlEscaped*" } { + Mock Invoke-JiraMethod -ParameterFilter { $Method -eq 'Get' -and $URI -like "$jiraServer/rest/api/latest/search" -and $GetParameter["jql"] -eq $jqlEscaped } { ShowMockInfo 'Invoke-JiraMethod' 'Method', 'Uri' ConvertFrom-Json $response } @@ -53,6 +54,7 @@ $object.PSObject.TypeNames.Insert(0, 'JiraPS.User') return $object } + #endregion Mocks Context "Sanity checking" { $command = Get-Command -Name Get-JiraIssue @@ -71,34 +73,95 @@ It "Obtains information about a provided issue in JIRA" { { Get-JiraIssue -Key TEST-001 } | Should Not Throw - Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Times 1 -Scope It -ParameterFilter { $Method -eq 'Get' -and $URI -like '*/rest/api/*/issue/TEST-001*' } + + $assertMockCalledSplat = @{ + CommandName = 'Invoke-JiraMethod' + ModuleName = 'JiraPS' + ParameterFilter = { + $Method -eq 'Get' -and + $URI -like '*/rest/api/*/issue/TEST-001*' + } + Scope = 'It' + Exactly = $true + Times = 1 + } + Assert-MockCalled @assertMockCalledSplat } It "Uses JQL to search for issues if the -Query parameter is used" { { Get-JiraIssue -Query $jql } | Should Not Throw - Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Times 1 -Scope It -ParameterFilter { $Method -eq 'Get' -and $URI -like "*/rest/api/latest/search?jql=$jqlEscaped*" } + + $assertMockCalledSplat = @{ + CommandName = 'Invoke-JiraMethod' + ModuleName = 'JiraPS' + ParameterFilter = { + $Method -eq 'Get' -and + $URI -like "*/rest/api/*/search" -and + $GetParameter["jql"] -eq $jqlEscaped + } + Scope = 'It' + Exactly = $true + Times = 1 + } + Assert-MockCalled @assertMockCalledSplat } It "Supports the -StartIndex and -MaxResults parameters to page through search results" { { Get-JiraIssue -Query $jql -StartIndex 10 -MaxResults 50 } | Should Not Throw - Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Times 1 -Scope It -ParameterFilter { $Method -eq 'Get' -and $URI -like "*/rest/api/latest/search?jql=$jqlEscaped*startAt=10&maxResults=50*" } + + $assertMockCalledSplat = @{ + CommandName = 'Invoke-JiraMethod' + ModuleName = 'JiraPS' + ParameterFilter = { + $Method -eq 'Get' -and + $URI -like "*/rest/api/*/search" -and + $GetParameter["jql"] -eq $jqlEscaped -and + $PSCmdlet.PagingParameters.Skip -eq 10 + $PSCmdlet.PagingParameters.First -eq 50 + } + Scope = 'It' + Exactly = $true + Times = 1 + } + Assert-MockCalled @assertMockCalledSplat } It "Returns all issues via looping if -MaxResults is not specified" { { Get-JiraIssue -Query $jql -PageSize 25 } | Should Not Throw - # This should call Invoke-JiraMethod once for one issue (to get the MaxResults value)... - Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Times 1 -Scope It -ParameterFilter { $Method -eq 'Get' -and $URI -like "*/rest/api/latest/search?jql=$jqlEscaped*maxResults=1*" } - - # ...and once more with the MaxResults set to the PageSize parameter - Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Times 1 -Scope It -ParameterFilter { $Method -eq 'Get' -and $URI -like "*/rest/api/latest/search?jql=$jqlEscaped*startAt=0&maxResults=25" } + $assertMockCalledSplat = @{ + CommandName = 'Invoke-JiraMethod' + ModuleName = 'JiraPS' + ParameterFilter = { + $Method -eq 'Get' -and + $URI -like "*/rest/api/*/search" -and + $GetParameter["jql"] -eq $jqlEscaped -and + $GetParameter["maxResults"] -eq 25 + } + Scope = 'It' + Exactly = $true + Times = 1 + } + Assert-MockCalled @assertMockCalledSplat } } Context "Input testing" { It "Accepts an issue key for the -Key parameter" { { Get-JiraIssue -Key TEST-001 } | Should Not Throw - Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Times 1 -Scope It -ParameterFilter { $Method -eq 'Get' -and $URI -like '*/rest/api/*/issue/TEST-001*' } + + $assertMockCalledSplat = @{ + CommandName = 'Invoke-JiraMethod' + ModuleName = 'JiraPS' + ParameterFilter = { + $Method -eq 'Get' -and + $URI -like "*/rest/api/*/issue/TEST-001*" + } + Scope = 'It' + Exactly = $true + Times = 1 + } + Assert-MockCalled @assertMockCalledSplat } It "Accepts an issue object for the -InputObject parameter" { @@ -110,7 +173,19 @@ # Should call Get-JiraIssue using the -Key parameter, so our URL should reflect the key we provided { Get-JiraIssue -InputObject $Issue } | Should Not Throw - Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Times 1 -Scope It -ParameterFilter { $Method -eq 'Get' -and $URI -like '*/rest/api/*/issue/TEST-001*' } + + $assertMockCalledSplat = @{ + CommandName = 'Invoke-JiraMethod' + ModuleName = 'JiraPS' + ParameterFilter = { + $Method -eq 'Get' -and + $URI -like "*/rest/api/*/issue/TEST-001*" + } + Scope = 'It' + Exactly = $true + Times = 1 + } + Assert-MockCalled @assertMockCalledSplat } } } diff --git a/Tests/Get-JiraIssueComment.Tests.ps1 b/Tests/Get-JiraIssueComment.Tests.ps1 index edcfa074..471af529 100644 --- a/Tests/Get-JiraIssueComment.Tests.ps1 +++ b/Tests/Get-JiraIssueComment.Tests.ps1 @@ -31,6 +31,8 @@ Describe "Get-JiraIssueComment" { ] } "@ + + #region Mocks Mock Get-JiraConfigServer -ModuleName JiraPS { Write-Output $jiraServer } @@ -52,7 +54,7 @@ Describe "Get-JiraIssueComment" { # Obtaining comments from an issue...this is IT-3676 in the test environment Mock Invoke-JiraMethod -ModuleName JiraPS -ParameterFilter {$Method -eq 'Get' -and $URI -eq "$jiraServer/rest/api/latest/issue/$issueID/comment"} { ShowMockInfo 'Invoke-JiraMethod' 'Method', 'Uri' - ConvertFrom-Json -InputObject $restResult + (ConvertFrom-Json -InputObject $restResult).comments } # Generic catch-all. This will throw an exception if we forgot to mock something. @@ -60,6 +62,7 @@ Describe "Get-JiraIssueComment" { ShowMockInfo 'Invoke-JiraMethod' 'Method', 'Uri' throw "Unidentified call to Invoke-JiraMethod" } + #endregion Mocks ############# # Tests @@ -67,11 +70,11 @@ Describe "Get-JiraIssueComment" { It "Obtains all Jira comments from a Jira issue if the issue key is provided" { $comments = Get-JiraIssueComment -Issue $issueKey + $comments | Should Not BeNullOrEmpty @($comments).Count | Should Be 1 $comments.ID | Should Be 90730 $comments.Body | Should Be 'Test comment' - $comments.RestUrl | Should Be "$jiraServer/rest/api/2/issue/$issueID/comment/90730" # Get-JiraIssue should be called to identify the -Issue parameter Assert-MockCalled -CommandName Get-JiraIssue -ModuleName JiraPS -Exactly -Times 1 -Scope It @@ -84,15 +87,19 @@ Describe "Get-JiraIssueComment" { It "Obtains all Jira comments from a Jira issue if the Jira object is provided" { $issue = Get-JiraIssue -Key $issueKey $comments = Get-JiraIssueComment -Issue $issue + $comments | Should Not BeNullOrEmpty $comments.ID | Should Be 90730 + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It } It "Handles pipeline input from Get-JiraIssue" { $comments = Get-JiraIssue -Key $issueKey | Get-JiraIssueComment + $comments | Should Not BeNullOrEmpty $comments.ID | Should Be 90730 + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It } } diff --git a/Tests/Get-JiraPriority.Tests.ps1 b/Tests/Get-JiraPriority.Tests.ps1 index 65679ae9..31abccc6 100644 --- a/Tests/Get-JiraPriority.Tests.ps1 +++ b/Tests/Get-JiraPriority.Tests.ps1 @@ -62,6 +62,10 @@ Describe "Get-JiraPriority" { Write-Output $jiraServer } + Mock ConvertTo-JiraPriority -ModuleName JiraPS { + $InputObject + } + Mock Invoke-JiraMethod -ModuleName JiraPS -ParameterFilter {$Method -eq 'Get' -and $URI -eq "$jiraServer/rest/api/latest/priority"} { ConvertFrom-Json $restResultAll } @@ -71,7 +75,7 @@ Describe "Get-JiraPriority" { } # Generic catch-all. This will throw an exception if we forgot to mock something. - Mock Invoke-JiraMethod -ModuleName JiraPS { + Mock Invoke-JiraMethod { ShowMockInfo 'Invoke-JiraMethod' 'Method', 'Uri' throw "Unidentified call to Invoke-JiraMethod" } @@ -81,21 +85,15 @@ Describe "Get-JiraPriority" { ############# It "Gets all available priorities if called with no parameters" { - $getResult = Get-JiraPriority -Credential $testCred + $getResult = Get-JiraPriority $getResult | Should Not BeNullOrEmpty $getResult.Count | Should Be 5 } It "Gets one priority if the ID parameter is supplied" { - $getResult = Get-JiraPriority -Id 1 -Credential $testCred + $getResult = Get-JiraPriority -Id 1 $getResult | Should Not BeNullOrEmpty @($getResult).Count | Should Be 1 } - - It "Converts the output object to type JiraPS.Priority" { - $getResult = Get-JiraPriority -Id 1 -Credential $testCred - $getResult | Should Not BeNullOrEmpty - checkType $getResult "JiraPS.Priority" - } } } diff --git a/Tests/Get-JiraRemoteLink.Tests.ps1 b/Tests/Get-JiraRemoteLink.Tests.ps1 index e2a3633d..bd5ad624 100644 --- a/Tests/Get-JiraRemoteLink.Tests.ps1 +++ b/Tests/Get-JiraRemoteLink.Tests.ps1 @@ -49,7 +49,7 @@ Describe "Get-JiraRemoteLink" { Get-JiraIssue -Key $Issue } - Mock ConvertTo-JiraIssueLinkType -ModuleName JiraPS { + Mock ConvertTo-JiraLink -ModuleName JiraPS { $InputObject } @@ -73,14 +73,45 @@ Describe "Get-JiraRemoteLink" { $getResult = Get-JiraRemoteLink -Issue $issueKey $getResult | Should Not BeNullOrEmpty - Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It -ParameterFilter {$Method -eq "Get" -and $Uri -like "$jiraServer/rest/api/*/issue/12345/remotelink"} + $assertMockCalledSplat = @{ + CommandName = 'Invoke-JiraMethod' + ModuleName = 'JiraPS' + ParameterFilter = { + $Method -eq "Get" -and + $Uri -like "$jiraServer/rest/api/*/issue/12345/remotelink" + } + Exactly = $true + Times = 1 + Scope = 'It' + } + Assert-MockCalled @assertMockCalledSplat + + $assertMockCalledSplat = @{ + CommandName = 'ConvertTo-JiraLink' + ModuleName = 'JiraPS' + Exactly = $true + Times = 1 + Scope = 'It' + } + Assert-MockCalled @assertMockCalledSplat } It "Gets information of all remote link from a Jira issue" { $getResult = Get-JiraRemoteLink -Issue $issueKey -LinkId 10000 $getResult | Should Not BeNullOrEmpty - Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It -ParameterFilter {$Method -eq "Get" -and $Uri -like "$jiraServer/rest/api/*/issue/12345/remotelink/10000"} + $assertMockCalledSplat = @{ + CommandName = 'Invoke-JiraMethod' + ModuleName = 'JiraPS' + ParameterFilter = { + $Method -eq "Get" -and + $Uri -like "$jiraServer/rest/api/*/issue/12345/remotelink/10000" + } + Exactly = $true + Times = 1 + Scope = 'It' + } + Assert-MockCalled @assertMockCalledSplat } } } diff --git a/Tests/Get-JiraServerInformation.Tests.ps1 b/Tests/Get-JiraServerInformation.Tests.ps1 index cddd5c1d..55bdfe0c 100644 --- a/Tests/Get-JiraServerInformation.Tests.ps1 +++ b/Tests/Get-JiraServerInformation.Tests.ps1 @@ -49,7 +49,7 @@ Describe "Get-JiraServerInformation" { It "Answers to the alias 'Get-JiraServerInfo'" { $thisAlias = (Get-Alias -Name "Get-JiraServerInfo") $thisAlias.ResolvedCommandName | Should Be "Get-JiraServerInformation" - $thisAlias.Source | Should Be "JiraPS" + $thisAlias.ModuleName | Should Be "JiraPS" } } } diff --git a/Tests/Get-JiraUser.Tests.ps1 b/Tests/Get-JiraUser.Tests.ps1 index 2128e992..203ea66a 100644 --- a/Tests/Get-JiraUser.Tests.ps1 +++ b/Tests/Get-JiraUser.Tests.ps1 @@ -68,7 +68,11 @@ Describe "Get-JiraUser" { } # Searching for a user. - Mock Invoke-JiraMethod -ModuleName JiraPS -ParameterFilter {$Method -eq 'Get' -and $URI -like "$jiraServer/rest/api/*/user/search?username=$testUsername"} { + Mock Invoke-JiraMethod -ModuleName JiraPS -ParameterFilter {$Method -eq 'Get' -and $URI -like "$jiraServer/rest/api/*/user/search?*username=$testUsername*"} { + ShowMockInfo 'Invoke-JiraMethod' 'Method', 'Uri' + ConvertFrom-Json -InputObject $restResult + } + Mock Invoke-JiraMethod -ModuleName JiraPS -ParameterFilter {$Method -eq 'Get' -and $URI -like "$jiraServer/rest/api/*/user/search?*username=%25*"} { ShowMockInfo 'Invoke-JiraMethod' 'Method', 'Uri' ConvertFrom-Json -InputObject $restResult } @@ -128,6 +132,30 @@ Describe "Get-JiraUser" { Assert-MockCalled -CommandName Invoke-JiraMethod -Exactly 2 -Scope It -ParameterFilter {$URI -like "$jiraServer/rest/api/*/user?username=$testUsername&expand=groups"} } + It "Allow it search for multiple users" { + Get-JiraUser -UserName "%" + + Assert-MockCalled -CommandName Invoke-JiraMethod -Exactly 1 -Scope It -ParameterFilter { + $URI -like "$jiraServer/rest/api/*/user/search?*username=%25*" + } + } + + It "Allows to change the max number of users to be returned" { + Get-JiraUser -UserName "%" -MaxResults 100 + + Assert-MockCalled -CommandName Invoke-JiraMethod -Exactly 1 -Scope It -ParameterFilter { + $URI -like "$jiraServer/rest/api/*/user/search?*maxResults=100*" + } + } + + It "Can skip a certain amount of results" { + Get-JiraUser -UserName "%" -Skip 10 + + Assert-MockCalled -CommandName Invoke-JiraMethod -Exactly 1 -Scope It -ParameterFilter { + $URI -like "$jiraServer/rest/api/*/user/search?*startAt=10*" + } + } + It "Provides information about the user's group membership in Jira" { $getResult = Get-JiraUser -UserName $testUsername diff --git a/Tests/Get-JiraVersion.Tests.ps1 b/Tests/Get-JiraVersion.Tests.ps1 index 6816dc56..2af2a5e1 100644 --- a/Tests/Get-JiraVersion.Tests.ps1 +++ b/Tests/Get-JiraVersion.Tests.ps1 @@ -1,15 +1,19 @@ Describe "Get-JiraVersion" { - Import-Module "$PSScriptRoot/../JiraPS" -Force -ErrorAction Stop + BeforeAll { + Remove-Module JiraPS -ErrorAction SilentlyContinue + Import-Module "$PSScriptRoot/../JiraPS" -Force -ErrorAction Stop + } InModuleScope JiraPS { . "$PSScriptRoot/Shared.ps1" + #region Definitions $jiraServer = 'http://jiraserver.example.com' - $versionName1 = '1.0.0.0' - $versionName2 = '2.0.0.0' - $versionName3 = '3.0.0.0' + $versionName1 = 'v1.0' + $versionName2 = 'v2.0' + $versionName3 = 'v3.0' $versionID1 = 16740 $versionID2 = 16840 $versionID3 = 16940 @@ -73,16 +77,17 @@ Describe "Get-JiraVersion" { { "self" : "$jiraServer/rest/api/latest/version/$versionID3", "id" : $versionID3, - "description" : "$versionName2", - "name" : "$versionName2", + "description" : "$versionName3", + "name" : "$versionName3", "archived" : "False", "released" : "False", "projectId" : "$projectId" } -} +] "@ + #endregion Definitions - #region Mock + #region Mocks Mock Get-JiraConfigServer -ModuleName JiraPS { Write-Output $jiraServer } @@ -109,19 +114,19 @@ Describe "Get-JiraVersion" { ConvertFrom-Json $testJson1 } - Mock Invoke-JiraMethod -ModuleName JiraPS -ParameterFilter { $Method -eq 'Get' -and $URI -like "$jiraServer/rest/api/latest/version/$versionId2" } { + Mock Invoke-JiraMethod -ModuleName JiraPS -ParameterFilter { $Method -eq 'Get' -and $URI -like "$jiraServer/rest/api/*/version/$versionId2" } { ShowMockInfo 'Invoke-JiraMethod' 'Method', 'Uri' ConvertFrom-Json $testJson2 } - Mock Invoke-JiraMethod -ModuleName JiraPS -ParameterFilter { $Method -eq 'Get' -and $URI -like "$jiraServer/rest/api/latest/version" } { + Mock Invoke-JiraMethod -ModuleName JiraPS -ParameterFilter { $Method -eq 'Get' -and $URI -like "$jiraServer/rest/api/*/version" } { ShowMockInfo 'Invoke-JiraMethod' 'Method', 'Uri' ConvertFrom-Json $testJsonAll } - Mock Invoke-JiraMethod -ModuleName JiraPS -ParameterFilter { $Method -eq 'Get' -and $URI -like "$jiraServer/rest/api/latest/project/*/versions" } { + Mock Invoke-JiraMethod -ModuleName JiraPS -ParameterFilter { $Method -eq 'Get' -and $URI -like "$jiraServer/rest/api/*/project/*/version" } { ShowMockInfo 'Invoke-JiraMethod' 'Method', 'Uri' - ConvertFrom-Json $testJson1 + ConvertFrom-Json $testJsonAll } # Generic catch-all. This will throw an exception if we forgot to mock something. @@ -129,7 +134,7 @@ Describe "Get-JiraVersion" { ShowMockInfo 'Invoke-JiraMethod' 'Method', 'Uri' throw "Unidentified call to Invoke-JiraMethod" } - #endregion Mock + #endregion Mocks Context "Sanity checking" { $command = Get-Command -Name Get-JiraVersion @@ -141,64 +146,338 @@ Describe "Get-JiraVersion" { } Context "Behavior checking" { + It "gets a Version using Id Parameter Set" { $results = Get-JiraVersion -Id $versionID1 - $results | Should Not BeNullOrEmpty - Assert-MockCalled 'Invoke-JiraMethod' -Times 1 -Scope It -ModuleName JiraPS -Exactly -ParameterFilter { $Method -eq 'Get' -and $URI -like "$jiraServer/rest/api/latest/version/$versionID1" } - Assert-MockCalled 'ConvertTo-JiraVersion' -Times 1 -Scope It -ModuleName JiraPS -Exactly + + $results | Should -Not -BeNullOrEmpty + + $assertMockCalledSplat = @{ + CommandName = 'Invoke-JiraMethod' + ModuleName = 'JiraPS' + ParameterFilter = { + $Method -eq 'Get' -and + $URI -like "*/rest/api/*/version/$versionID1" + } + Scope = 'It' + Exactly = $true + Times = 1 + } + Assert-MockCalled @assertMockCalledSplat + + $assertMockCalledSplat = @{ + CommandName = 'ConvertTo-JiraVersion' + ModuleName = 'JiraPS' + Scope = 'It' + Exactly = $true + Times = 1 + } + Assert-MockCalled @assertMockCalledSplat } + It "gets a Version using multiple IDs" { $results = Get-JiraVersion -Id $versionID1, $versionID2 - $results | Should Not BeNullOrEmpty - Assert-MockCalled 'Invoke-JiraMethod' -Times 1 -Scope It -ModuleName JiraPS -Exactly -ParameterFilter { $Method -eq 'Get' -and $URI -like "$jiraServer/rest/api/latest/version/$versionID1" } - Assert-MockCalled 'Invoke-JiraMethod' -Times 1 -Scope It -ModuleName JiraPS -Exactly -ParameterFilter { $Method -eq 'Get' -and $URI -like "$jiraServer/rest/api/latest/version/$versionID2" } - Assert-MockCalled 'ConvertTo-JiraVersion' -Times 2 -Scope It -ModuleName JiraPS -Exactly + + $results | Should -Not -BeNullOrEmpty + + $assertMockCalledSplat = @{ + CommandName = 'Invoke-JiraMethod' + ModuleName = 'JiraPS' + ParameterFilter = { + $Method -eq 'Get' -and + $URI -like "*/rest/api/*/version/$versionID1" + } + Scope = 'It' + Exactly = $true + Times = 1 + } + Assert-MockCalled @assertMockCalledSplat + + $assertMockCalledSplat = @{ + CommandName = 'Invoke-JiraMethod' + ModuleName = 'JiraPS' + ParameterFilter = { + $Method -eq 'Get' -and + $URI -like "*/rest/api/*/version/$versionID2" + } + Scope = 'It' + Exactly = $true + Times = 1 + } + Assert-MockCalled @assertMockCalledSplat + + $assertMockCalledSplat = @{ + CommandName = 'ConvertTo-JiraVersion' + ModuleName = 'JiraPS' + Scope = 'It' + Exactly = $true + Times = 2 + } + Assert-MockCalled @assertMockCalledSplat } + It "gets a Version using the pipeline from another Version" { $version1 = ConvertTo-JiraVersion ([PSCustomObject]@{Id = [int]($versionID2)}) $version2 = ConvertTo-JiraVersion ([PSCustomObject]@{Id = [int]($versionID2); project = "lorem"}) + $results1 = ($version1 | Get-JiraVersion) - $results2 = ($version1 | Get-JiraVersion) - $results1 | Should Not BeNullOrEmpty - $results2 | Should Not BeNullOrEmpty - Assert-MockCalled 'Invoke-JiraMethod' -Times 2 -Scope It -ModuleName JiraPS -Exactly -ParameterFilter { $Method -eq 'Get' -and $URI -like "$jiraServer/rest/api/latest/version/$versionID2" } - Assert-MockCalled 'ConvertTo-JiraVersion' -Times 4 -Scope It -ModuleName JiraPS -Exactly + $results2 = ($version2 | Get-JiraVersion) + $results1 | Should -Not -BeNullOrEmpty + $results2 | Should -Not -BeNullOrEmpty + + $assertMockCalledSplat = @{ + CommandName = 'Invoke-JiraMethod' + ModuleName = 'JiraPS' + ParameterFilter = { + $Method -eq 'Get' -and + $URI -like "*/rest/api/*/version/$versionID2" + } + Scope = 'It' + Exactly = $true + Times = 2 + } + Assert-MockCalled @assertMockCalledSplat + + $assertMockCalledSplat = @{ + CommandName = 'ConvertTo-JiraVersion' + ModuleName = 'JiraPS' + Scope = 'It' + Exactly = $true + Times = 4 + } + Assert-MockCalled @assertMockCalledSplat } + It "gets all Versions using Project Parameter Set" { $results = Get-JiraVersion -Project $projectKey - $results | Should Not BeNullOrEmpty - Assert-MockCalled 'Invoke-JiraMethod' -Times 1 -Scope It -ModuleName JiraPS -Exactly -ParameterFilter { $Method -eq 'Get' -and $URI -like "$jiraServer/rest/api/latest/project/$projectKey/versions" } - Assert-MockCalled 'Get-JiraProject' -Times 1 -Scope It -ModuleName JiraPS - Assert-MockCalled 'ConvertTo-JiraVersion' -Times 1 -Scope It -ModuleName JiraPS -Exactly + + $results | Should -Not -BeNullOrEmpty + + $assertMockCalledSplat = @{ + CommandName = 'Invoke-JiraMethod' + ModuleName = 'JiraPS' + ParameterFilter = { + $Method -eq 'Get' -and + $URI -like "*/rest/api/*/project/$projectKey/version" -and + $Paging -eq $true + } + Scope = 'It' + Exactly = $true + Times = 1 + } + Assert-MockCalled @assertMockCalledSplat + + $assertMockCalledSplat = @{ + CommandName = 'Get-JiraProject' + ModuleName = 'JiraPS' + Scope = 'It' + Exactly = $true + Times = 1 + } + Assert-MockCalled @assertMockCalledSplat + + $assertMockCalledSplat = @{ + CommandName = 'ConvertTo-JiraVersion' + ModuleName = 'JiraPS' + Scope = 'It' + Exactly = $true + Times = 0 + } + Assert-MockCalled @assertMockCalledSplat } + It "gets all Versions using Project as pipe input" { $results = Get-JiraProject -Project $projectKey | Get-JiraVersion - $results | Should Not BeNullOrEmpty - Assert-MockCalled 'Invoke-JiraMethod' -Times 1 -Scope It -ModuleName JiraPS -Exactly -ParameterFilter { $Method -eq 'Get' -and $URI -like "$jiraServer/rest/api/latest/project/$projectKey/versions" } - Assert-MockCalled 'Get-JiraProject' -Times 1 -Scope It -ModuleName JiraPS - Assert-MockCalled 'ConvertTo-JiraVersion' -Times 1 -Scope It -ModuleName JiraPS -Exactly + + $results | Should -Not -BeNullOrEmpty + + $assertMockCalledSplat = @{ + CommandName = 'Invoke-JiraMethod' + ModuleName = 'JiraPS' + ParameterFilter = { + $Method -eq 'Get' -and + $URI -like "*/rest/api/*/project/$projectKey/version" -and + $Paging -eq $true + } + Scope = 'It' + Exactly = $true + Times = 1 + } + Assert-MockCalled @assertMockCalledSplat + + # Get-JiraProject is called once in the It block + # and once in the `Get-JiraVersion` + $assertMockCalledSplat = @{ + CommandName = 'Get-JiraProject' + ModuleName = 'JiraPS' + Scope = 'It' + Exactly = $true + Times = 2 + } + Assert-MockCalled @assertMockCalledSplat + + $assertMockCalledSplat = @{ + CommandName = 'ConvertTo-JiraVersion' + ModuleName = 'JiraPS' + Scope = 'It' + Exactly = $true + Times = 0 + } + Assert-MockCalled @assertMockCalledSplat } + It "gets all Versions from multiple Projects" { $results = Get-JiraVersion -Project $projectKey, "foo" - $results | Should Not BeNullOrEmpty - Assert-MockCalled 'Invoke-JiraMethod' -Times 2 -Scope It -ModuleName JiraPS -Exactly -ParameterFilter { $Method -eq 'Get' -and $URI -like "$jiraServer/rest/api/latest/project/*/versions" } - Assert-MockCalled 'Get-JiraProject' -Times 2 -Scope It -ModuleName JiraPS - Assert-MockCalled 'ConvertTo-JiraVersion' -Times 2 -Scope It -ModuleName JiraPS -Exactly + + $results | Should -Not -BeNullOrEmpty + + $assertMockCalledSplat = @{ + CommandName = 'Invoke-JiraMethod' + ModuleName = 'JiraPS' + ParameterFilter = { + $Method -eq 'Get' -and + $URI -like "*/rest/api/*/project/*/version" -and + $Paging -eq $true + } + Scope = 'It' + Exactly = $true + Times = 2 + } + Assert-MockCalled @assertMockCalledSplat + + $assertMockCalledSplat = @{ + CommandName = 'Get-JiraProject' + ModuleName = 'JiraPS' + Scope = 'It' + Exactly = $true + Times = 2 + } + Assert-MockCalled @assertMockCalledSplat + + $assertMockCalledSplat = @{ + CommandName = 'ConvertTo-JiraVersion' + ModuleName = 'JiraPS' + Scope = 'It' + Exactly = $true + Times = 0 + } + Assert-MockCalled @assertMockCalledSplat } + It "filters the Versions from a Project by Name" { $results = Get-JiraVersion -Project $projectKey -Name $versionName1 - $results | Should Not BeNullOrEmpty - Assert-MockCalled 'Invoke-JiraMethod' -Times 1 -Scope It -ModuleName JiraPS -Exactly -ParameterFilter { $Method -eq 'Get' -and $URI -like "$jiraServer/rest/api/latest/project/$projectKey/versions" } - Assert-MockCalled 'Get-JiraProject' -Times 1 -Scope It -ModuleName JiraPS - Assert-MockCalled 'ConvertTo-JiraVersion' -Times 1 -Scope It -ModuleName JiraPS -Exactly + + $results | Should -Not -BeNullOrEmpty + + $assertMockCalledSplat = @{ + CommandName = 'Invoke-JiraMethod' + ModuleName = 'JiraPS' + ParameterFilter = { + $Method -eq 'Get' -and + $URI -like "*/rest/api/*/project/*/version" -and + $Paging -eq $true + } + Scope = 'It' + Exactly = $true + Times = 1 + } + Assert-MockCalled @assertMockCalledSplat + + $assertMockCalledSplat = @{ + CommandName = 'Get-JiraProject' + ModuleName = 'JiraPS' + Scope = 'It' + Exactly = $true + Times = 1 + } + Assert-MockCalled @assertMockCalledSplat + + $assertMockCalledSplat = @{ + CommandName = 'ConvertTo-JiraVersion' + ModuleName = 'JiraPS' + Scope = 'It' + Exactly = $true + Times = 0 + } + Assert-MockCalled @assertMockCalledSplat } + It "filters the Versions from a Project by multiple Names" { $results = Get-JiraVersion -Project $projectKey -Name $versionName1, $versionName2 - $results | Should Not BeNullOrEmpty - Assert-MockCalled 'Invoke-JiraMethod' -Times 1 -Scope It -ModuleName JiraPS -Exactly -ParameterFilter { $Method -eq 'Get' -and $URI -like "$jiraServer/rest/api/latest/project/$projectKey/versions" } - Assert-MockCalled 'Get-JiraProject' -Times 1 -Scope It -ModuleName JiraPS - Assert-MockCalled 'ConvertTo-JiraVersion' -Times 1 -Scope It -ModuleName JiraPS -Exactly + + $results | Should -Not -BeNullOrEmpty + + $assertMockCalledSplat = @{ + CommandName = 'Invoke-JiraMethod' + ModuleName = 'JiraPS' + ParameterFilter = { + $Method -eq 'Get' -and + $URI -like "*/rest/api/*/project/*/version" -and + $Paging -eq $true + } + Scope = 'It' + Exactly = $true + Times = 1 + } + Assert-MockCalled @assertMockCalledSplat + + $assertMockCalledSplat = @{ + CommandName = 'Get-JiraProject' + ModuleName = 'JiraPS' + Scope = 'It' + Exactly = $true + Times = 1 + } + Assert-MockCalled @assertMockCalledSplat + + $assertMockCalledSplat = @{ + CommandName = 'ConvertTo-JiraVersion' + ModuleName = 'JiraPS' + Scope = 'It' + Exactly = $true + Times = 0 + } + Assert-MockCalled @assertMockCalledSplat } + + It "Supports the -Skip parameters to page through search results" { + { Get-JiraVersion -Project $projectKey -Skip 10 } | Should -Not -Throw + + $assertMockCalledSplat = @{ + CommandName = 'Invoke-JiraMethod' + ModuleName = 'JiraPS' + ParameterFilter = { + $Method -eq 'Get' -and + $URI -like '*/rest/api/*/project/*/version' -and + $Paging -eq $true -and + $PSCmdlet.PagingParameters.Skip -eq 10 + } + Scope = 'It' + Exactly = $true + Times = 1 + } + Assert-MockCalled @assertMockCalledSplat + } + + It "Supports the -First parameters to page through search results" { + { Get-JiraVersion -Project $projectKey -First 50 } | Should -Not -Throw + + $assertMockCalledSplat = @{ + CommandName = 'Invoke-JiraMethod' + ModuleName = 'JiraPS' + ParameterFilter = { + $Method -eq 'Get' -and + $URI -like '*/rest/api/*/project/*/version' -and + $Paging -eq $true -and + $PSCmdlet.PagingParameters.First -eq 50 + } + Scope = 'It' + Exactly = $true + Times = 1 + } + Assert-MockCalled @assertMockCalledSplat + } + It "assert VerifiableMock" { Assert-VerifiableMock } diff --git a/Tests/Invoke-JiraMethod.Tests.ps1 b/Tests/Invoke-JiraMethod.Tests.ps1 index d04bb5f9..a72639a9 100644 --- a/Tests/Invoke-JiraMethod.Tests.ps1 +++ b/Tests/Invoke-JiraMethod.Tests.ps1 @@ -1,12 +1,90 @@ -Describe "Invoke-JiraMethod" { +[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingConvertToSecureStringWithPlainText", "")] +param() - Import-Module "$PSScriptRoot/../JiraPS" -Force -ErrorAction Stop +Describe "Invoke-JiraMethod" { + BeforeAll { + Remove-Module JiraPS -ErrorAction SilentlyContinue + Import-Module "$PSScriptRoot/../JiraPS" -Force -ErrorAction Stop + } + AfterEach { + # $script:ShowMockData = $false + } InModuleScope JiraPS { . "$PSScriptRoot/Shared.ps1" - $validMethods = @('GET', 'POST', 'PUT', 'DELETE') + #region Definitions + + $utf8String = "Lorem مرحبا Здравствуйте 😁" + $testUsername = 'testUsername' + $testPassword = ConvertTo-SecureString -AsPlainText -Force 'password123' + $testCred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $testUsername, $testPassword + $pagedResponse1 = @" +{ + "startAt" : 0, + "maxResults" : 5, + "total": 7, + "issues": [ + { "id": 1 }, + { "id": 2 }, + { "id": 3 }, + { "id": 4 }, + { "id": 5 } + ] +} +"@ + $pagedResponse2 = @" +{ + "startAt" : 5, + "maxResults" : 5, + "total": 7, + "issues": [ + { "id": 6 }, + { "id": 7 } + ] +} +"@ + $pagedResponse3 = "{}" + $supportedTypes = @("JiraComment", "JiraIssue", "JiraUser", "JiraVersion") + #endregion Definitions + + #region Mocks + Mock Resolve-DefaultParameterValue -ModuleName JiraPS { @{ } } + Mock Join-Hashtable -ModuleName JiraPS { @{ } } + Mock Set-TlsLevel -ModuleName JiraPS { } + Mock Resolve-ErrorWebResponse -ModuleName JiraPS { } + Mock Get-JiraSession -ModuleName JiraPS { + [PSCustomObject]@{ + WebSession = New-Object -TypeName Microsoft.PowerShell.Commands.WebRequestSession + } + } + Mock Test-ServerResponse -Module JiraPS { } + Mock ConvertTo-JiraSession -ModuleName JiraPS { } + foreach ($type in $supportedTypes) { + Mock -CommandName "ConvertTo-$type" -ModuleName JiraPS { } + } + Mock Invoke-WebRequest -ModuleName JiraPS { + ShowMockInfo 'Invoke-WebRequest' -Params 'Uri', 'Method', 'Body', 'Headers', 'ContentType', 'SessionVariable', 'WebSession' + $InvokeWebRequestSplat = @{ + Uri = $Uri + Method = $Method + Body = $Body + Headers = $Headers + WebSession = $WebSession + ContentType = $ContentType + } + if ($SessionVariable) { + $InvokeWebRequestSplat["SessionVariable"] = $SessionVariable + } + + Microsoft.PowerShell.Utility\Invoke-WebRequest @InvokeWebRequestSplat + + if ($SessionVariable) { + Set-Variable -Name $SessionVariable -Value (Get-Variable $SessionVariable).Value -Scope 3 # Pester adds 2 levels of nesting + } + } + #endregion Mocks Context "Sanity checking" { $command = Get-Command -Name Invoke-JiraMethod @@ -16,447 +94,538 @@ Describe "Invoke-JiraMethod" { defParam $command 'Body' defParam $command 'RawBody' defParam $command 'Headers' + defParam $command 'GetParameter' + defParam $command 'Paging' defParam $command 'InFile' defParam $command 'OutFile' + defParam $command 'StoreSession' + defParam $command 'OutputType' defParam $command 'Credential' defParam $command 'CmdLet' It "Restricts the METHODs to WebRequestMethod" { $methodType = $command.Parameters.Method.ParameterType - $methodType.FullName | Should Be "Microsoft.PowerShell.Commands.WebRequestMethod" + $methodType.FullName | Should -Be "Microsoft.PowerShell.Commands.WebRequestMethod" } } Context "Behavior testing" { - [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingConvertToSecureStringWithPlainText", "")] - - $testUri = 'http://example.com' - $testUsername = 'testUsername' - $testPassword = 'password123' - $testCred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $testUsername, (ConvertTo-SecureString -AsPlainText -Force $testPassword) + It "uses Invoke-WebMethod under the hood" { + Invoke-JiraMethod -URI "https://postman-echo.com/get?test=123" -ErrorAction Stop - Mock Invoke-WebRequest { - ShowMockInfo 'Invoke-WebRequest' -Params 'Uri', 'Method' + $assertMockCalledSplat = @{ + CommandName = 'Invoke-WebRequest' + ModuleName = 'JiraPS' + Exactly = $true + Times = 1 + Scope = 'It' + } + Assert-MockCalled @assertMockCalledSplat } - It "Correctly performs all necessary HTTP method requests [$($validMethods -join ',')] to a provided URI" { - foreach ($method in $validMethods) { - { Invoke-JiraMethod -Method $method -URI $testUri } | Should Not Throw + It "parses a JSON response" { + $response = Invoke-JiraMethod -URI "https://postman-echo.com/get?test=123" -ErrorAction Stop - Assert-MockCalled -CommandName Invoke-WebRequest -ParameterFilter {$Method -eq $method -and $Uri -eq $testUri} -Scope It - } + $response | Should -BeOfType [PSCustomObject] } - It "Uses the -ContentType parameter of Invoke-WebRequest to specify application/json and UTF-8" { - { Invoke-JiraMethod -Method Get -URI $testUri } | Should Not Throw - Assert-MockCalled -CommandName Invoke-WebRequest -ParameterFilter {$ContentType -eq 'application/json; charset=utf-8'} -Scope It + It "resolves errors" { + Invoke-JiraMethod -URI "https://postman-echo.com/status/400" -ErrorAction Stop + + Assert-MockCalled -CommandName Resolve-ErrorWebResponse -ModuleName JiraPS -Exactly -Times 1 -Scope It } - It "Uses the -UseBasicParsing switch for Invoke-WebRequest" { - { Invoke-JiraMethod -Method Get -URI $testUri } | Should Not Throw - Assert-MockCalled -CommandName Invoke-WebRequest -ParameterFilter {$UseBasicParsing -eq $true} -Scope It + It "supports TLS1.2 connections" { + Invoke-JiraMethod -URI "https://postman-echo.com/get?test=123" -ErrorAction Stop + + Assert-MockCalled -CommandName Set-TlsLevel -ModuleName JiraPS -Exactly -Times 2 -Scope It + Assert-MockCalled -CommandName Set-TlsLevel -ModuleName JiraPS -ParameterFilter {$Tls12 -eq $true} -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Set-TlsLevel -ModuleName JiraPS -ParameterFilter {$Revert -eq $true} -Exactly -Times 1 -Scope It } + It "uses global default values for parameters" { + Invoke-JiraMethod -URI "https://postman-echo.com/get?test=123" -ErrorAction Stop + + Assert-MockCalled -CommandName Resolve-DefaultParameterValue -ModuleName JiraPS -Exactly -Times 1 -Scope It + } } - $validTestUri = 'https://jira.atlassian.com/rest/api/latest/issue/303853' + Context "Input testing" { + It "parses a string to URi" { + [Uri]$Uri = "https://postman-echo.com/get?test=123" + $Uri | Should -BeOfType [Uri] - # This is a real REST result from Atlassian's public-facing JIRA instance, trimmed and cleaned - # up just a bit for fields we don't care about. + { Invoke-JiraMethod -URI "https://postman-echo.com/get?test=123" -ErrorAction Stop } | Should -Not -Throw + { Invoke-JiraMethod -URI $Uri -ErrorAction Stop } | Should -Not -Throw - # You can obtain this data with a single PowerShell line: - # Invoke-WebRequest -Method Get -Uri https://jira.atlassian.com/rest/api/latest/issue/303853 - $validRestResult = @' -{ - "expand": "renderedFields,names,schema,transitions,operations,editmeta,changelog,versionedRepresentations", - "id": "303853", - "self": "https://jira.atlassian.com/rest/api/latest/issue/303853", - "key": "DEMO-2719", - "fields": { - "issuetype": { - "self": "https://jira.atlassian.com/rest/api/2/issuetype/2", - "id": "2", - "description": "A new feature of the product, which has yet to be developed.", - "iconUrl": "https://jira.atlassian.com/images/icons/issuetypes/newfeature.png", - "name": "New Feature", - "subtask": false - }, - "timespent": null, - "project": { - "self": "https://jira.atlassian.com/rest/api/2/project/10820", - "id": "10820", - "key": "DEMO", - "name": "Demo", - "avatarUrls": { - "48x48": "https://jira.atlassian.com/secure/projectavatar?avatarId=10011", - "24x24": "https://jira.atlassian.com/secure/projectavatar?size=small&avatarId=10011", - "16x16": "https://jira.atlassian.com/secure/projectavatar?size=xsmall&avatarId=10011", - "32x32": "https://jira.atlassian.com/secure/projectavatar?size=medium&avatarId=10011" - } - }, - "fixVersions": [], - "aggregatetimespent": null, - "resolution": null, - "resolutiondate": null, - "workratio": -1, - "lastViewed": null, - "watches": { - "self": "https://jira.atlassian.com/rest/api/2/issue/DEMO-2719/watchers", - "watchCount": 1, - "isWatching": false - }, - "created": "2013-10-26T20:06:23.853+0000", - "priority": { - "self": "https://jira.atlassian.com/rest/api/2/priority/4", - "iconUrl": "https://jira.atlassian.com/images/icons/priorities/minor.png", - "name": "Minor", - "id": "4" - }, - "labels": [], - "aggregatetimeoriginalestimate": null, - "timeestimate": null, - "versions": [], - "issuelinks": [ - { - "id": "115932", - "self": "https://jira.atlassian.com/rest/api/2/issueLink/115932", - "type": { - "id": "10080", - "name": "Detail", - "inward": "is detailed by", - "outward": "details", - "self": "https://jira.atlassian.com/rest/api/2/issueLinkType/10080" - }, - "outwardIssue": { - "id": "303848", - "key": "DEMO-2717", - "self": "https://jira.atlassian.com/rest/api/2/issue/303848", - "fields": { - "summary": "New Feature Test Task", - "status": { - "self": "https://jira.atlassian.com/rest/api/2/status/1", - "description": "Issue is open and has not yet been accepted by Atlassian.", - "iconUrl": "https://jira.atlassian.com/images/icons/statuses/open.png", - "name": "Open", - "id": "1", - "statusCategory": { - "self": "https://jira.atlassian.com/rest/api/2/statuscategory/2", - "id": 2, - "key": "new", - "colorName": "blue-gray", - "name": "To Do" - } - }, - "priority": { - "self": "https://jira.atlassian.com/rest/api/2/priority/4", - "iconUrl": "https://jira.atlassian.com/images/icons/priorities/minor.png", - "name": "Minor", - "id": "4" - }, - "issuetype": { - "self": "https://jira.atlassian.com/rest/api/2/issuetype/2", - "id": "2", - "description": "A new feature of the product, which has yet to be developed.", - "iconUrl": "https://jira.atlassian.com/images/icons/issuetypes/newfeature.png", - "name": "New Feature", - "subtask": false + { Invoke-JiraMethod -URI "hello" -ErrorAction Stop } | Should -Throw + } + + foreach ($method in @('GET', 'POST', 'PUT', 'DELETE')) { + It "accepts [$method] as HTTP method" { + Invoke-JiraMethod -Method $method -URI "https://postman-echo.com/$method" + + $assertMockCalledSplat = @{ + CommandName = 'Invoke-WebRequest' + ModuleName = 'JiraPS' + ParameterFilter = { + $Method -eq $method } + Exactly = $true + Times = 1 + Scope = 'It' } + Assert-MockCalled @assertMockCalledSplat } - }, - { - "id": "119483", - "self": "https://jira.atlassian.com/rest/api/2/issueLink/119483", - "type": { - "id": "10000", - "name": "Reference", - "inward": "is related to", - "outward": "relates to", - "self": "https://jira.atlassian.com/rest/api/2/issueLinkType/10000" - }, - "outwardIssue": { - "id": "304302", - "key": "DEMO-2722", - "self": "https://jira.atlassian.com/rest/api/2/issue/304302", - "fields": { - "summary": "My summary", - "status": { - "self": "https://jira.atlassian.com/rest/api/2/status/1", - "description": "Issue is open and has not yet been accepted by Atlassian.", - "iconUrl": "https://jira.atlassian.com/images/icons/statuses/open.png", - "name": "Open", - "id": "1", - "statusCategory": { - "self": "https://jira.atlassian.com/rest/api/2/statuscategory/2", - "id": 2, - "key": "new", - "colorName": "blue-gray", - "name": "To Do" - } - }, - "priority": { - "self": "https://jira.atlassian.com/rest/api/2/priority/4", - "iconUrl": "https://jira.atlassian.com/images/icons/priorities/minor.png", - "name": "Minor", - "id": "4" - }, - "issuetype": { - "self": "https://jira.atlassian.com/rest/api/2/issuetype/1", - "id": "1", - "description": "A problem which impairs or prevents the functions of the product.", - "iconUrl": "https://jira.atlassian.com/images/icons/issuetypes/bug.png", - "name": "Bug", - "subtask": false - } + } + + It "encodes the Body with UTF-8 to support special chars" { + $invokeJiraMethodSplat = @{ + Method = 'Post' + URI = "https://postman-echo.com/post" + Body = $utf8String + } + Invoke-JiraMethod @invokeJiraMethodSplat + + $assertMockCalledSplat = @{ + CommandName = 'Invoke-WebRequest' + ModuleName = 'JiraPS' + ParameterFilter = { + $Body -is [Byte[]] -and + (($Body -join " ") -eq "76 111 114 101 109 32 195 153 226 128 166 195 152 194 177 195 152 194 173 195 152 194 168 195 152 194 167 32 195 144 226 128 148 195 144 194 180 195 145 226 130 172 195 144 194 176 195 144 194 178 195 145 194 129 195 145 226 128 154 195 144 194 178 195 145 198 146 195 144 194 185 195 145 226 128 154 195 144 194 181 32 195 176 197 184 203 156 194 129" -or + ($Body -join " ") -eq "76 111 114 101 109 32 217 133 216 177 216 173 216 168 216 167 32 208 151 208 180 209 128 208 176 208 178 209 129 209 130 208 178 209 131 208 185 209 130 208 181 32 240 159 152 129") + } + Exactly = $true + Times = 1 + Scope = 'It' + } + Assert-MockCalled @assertMockCalledSplat + } + + It "allows for skipping the UTF-8 encoding of the Body" { + $invokeJiraMethodSplat = @{ + Method = 'Post' + URI = "https://postman-echo.com/post" + Body = $utf8String + RawBody = $true + } + Invoke-JiraMethod @invokeJiraMethodSplat + + $assertMockCalledSplat = @{ + CommandName = 'Invoke-WebRequest' + ModuleName = 'JiraPS' + ParameterFilter = { + $Body -is [String] -and + $Body -eq $utf8String + } + Exactly = $true + Times = 1 + Scope = 'It' + } + Assert-MockCalled @assertMockCalledSplat + } + + It "overwrites module default headers with global PSDefaultParameterValues" {} + + It "overwrites global PSDefaultParameterValues with -Headers" {} + + It "overwrites module default headers with -Headers" {} + + It "overwrites get parameters in the URI with -GetParameter values" {} + + It "passes the -InFile to Invoke-WebRequest" { + $invokeJiraMethodSplat = @{ + Method = 'Post' + URI = "https://postman-echo.com/post" + InFile = "./file-does-not-exist.txt" + } + Invoke-JiraMethod @invokeJiraMethodSplat + + $assertMockCalledSplat = @{ + CommandName = 'Invoke-WebRequest' + ModuleName = 'JiraPS' + ParameterFilter = { + $inFile -eq "./file-does-not-exist.txt" + } + Exactly = $true + Times = 1 + Scope = 'It' + } + Assert-MockCalled @assertMockCalledSplat + } + + It "passes the -OutFile to Invoke-WebRequest" { + $invokeJiraMethodSplat = @{ + Method = 'Post' + URI = "https://postman-echo.com/post" + OutFile = "./file-does-not-exist.txt" + } + Invoke-JiraMethod @invokeJiraMethodSplat + + $assertMockCalledSplat = @{ + CommandName = 'Invoke-WebRequest' + ModuleName = 'JiraPS' + ParameterFilter = { + $OutFile -eq "./file-does-not-exist.txt" } + Exactly = $true + Times = 1 + Scope = 'It' + } + Assert-MockCalled @assertMockCalledSplat + } + + It "uses ConvertTo-JiraSession to store the Session" { + $invokeJiraMethodSplat = @{ + Method = 'Get' + URI = "https://postman-echo.com/get" + StoreSession = $true + ErrorAction = "Stop" + } + Invoke-JiraMethod @invokeJiraMethodSplat + + $assertMockCalledSplat = @{ + CommandName = "Invoke-WebRequest" + ModuleName = 'JiraPS' + ParameterFilter = {$SessionVariable -eq "newSessionVar"} + Exactly = $true + Times = 1 + Scope = 'It' + } + Assert-MockCalled @assertMockCalledSplat + Assert-MockCalled -CommandName ConvertTo-JiraSession -ModuleName JiraPS -Exactly -Times 1 -Scope It + } + + foreach ($type in $supportedTypes) { + It "uses ConvertTo-$type to transform the results" { + Invoke-JiraMethod -Method get -URI "https://postman-echo.com/get" -OutputType $type -Paging -ErrorAction Stop + + Assert-MockCalled -CommandName "ConvertTo-$type" -ModuleName JiraPS -Exactly -Times 1 -Scope It + } + + It "only uses -OutputType with -Paging [$type]" { + Invoke-JiraMethod -Method get -URI "https://postman-echo.com/get" -OutputType $type -ErrorAction Stop + + Assert-MockCalled -CommandName "ConvertTo-$type" -ModuleName JiraPS -Exactly -Times 0 -Scope It + } + } + + It "uses session if no -Credential are passed" { + $invokeJiraMethodSplat = @{ + URI = "https://postman-echo.com/get" + Method = 'get' + ErrorAction = "Stop" } - }, - { - "id": "115931", - "self": "https://jira.atlassian.com/rest/api/2/issueLink/115931", - "type": { - "id": "10000", - "name": "Reference", - "inward": "is related to", - "outward": "relates to", - "self": "https://jira.atlassian.com/rest/api/2/issueLinkType/10000" - }, - "inwardIssue": { - "id": "303852", - "key": "DEMO-2718", - "self": "https://jira.atlassian.com/rest/api/2/issue/303852", - "fields": { - "summary": "REST ye merry gentlemen.", - "status": { - "self": "https://jira.atlassian.com/rest/api/2/status/1", - "description": "Issue is open and has not yet been accepted by Atlassian.", - "iconUrl": "https://jira.atlassian.com/images/icons/statuses/open.png", - "name": "Open", - "id": "1", - "statusCategory": { - "self": "https://jira.atlassian.com/rest/api/2/statuscategory/2", - "id": 2, - "key": "new", - "colorName": "blue-gray", - "name": "To Do" - } - }, - "priority": { - "self": "https://jira.atlassian.com/rest/api/2/priority/4", - "iconUrl": "https://jira.atlassian.com/images/icons/priorities/minor.png", - "name": "Minor", - "id": "4" - }, - "issuetype": { - "self": "https://jira.atlassian.com/rest/api/2/issuetype/2", - "id": "2", - "description": "A new feature of the product, which has yet to be developed.", - "iconUrl": "https://jira.atlassian.com/images/icons/issuetypes/newfeature.png", - "name": "New Feature", - "subtask": false + Invoke-JiraMethod @invokeJiraMethodSplat + + $assertMockCalledSplat = @{ + CommandName = 'Invoke-WebRequest' + ModuleName = 'JiraPS' + ParameterFilter = { + $WebSession -is [Microsoft.PowerShell.Commands.WebRequestSession] -and + $Credential -eq $null + } + Exactly = $true + Times = 1 + Scope = 'It' + } + Assert-MockCalled @assertMockCalledSplat + Assert-MockCalled -CommandName Get-JiraSession -ModuleName JiraPS -Exactly -Times 1 -Scope It + } + + It "uses -Credential even if session is present" { + Mock Get-JiraSession -ModuleName JiraPS { + [PSCustomObject]@{ + WebSession = New-Object -TypeName Microsoft.PowerShell.Commands.WebRequestSession + } + } + + $invokeJiraMethodSplat = @{ + URI = "https://postman-echo.com/get" + Method = 'get' + Credential = $testCred + ErrorAction = "Stop" + } + Invoke-JiraMethod @invokeJiraMethodSplat + + $assertMockCalledSplat = @{ + CommandName = 'Invoke-WebRequest' + ModuleName = 'JiraPS' + ParameterFilter = { + $SessionVariable -eq $null -and + $Credential -ne $null + } + Exactly = $true + Times = 1 + Scope = 'It' + } + Assert-MockCalled @assertMockCalledSplat + Assert-MockCalled -CommandName Get-JiraSession -ModuleName JiraPS -Exactly -Times 0 -Scope It + } + + It "uses -Headers for the call" { + Mock Join-Hashtable -ModuleName JiraPS { + $table = @{ } + foreach ($item in $Hashtable) { + foreach ($key in $item.Keys) { + $table[$key] = $item[$key] } } + $table + } + + $invokeJiraMethodSplat = @{ + Method = 'Get' + URI = "https://postman-echo.com/headers" + Headers = @{ + "X-Fake" = "lorem ipsum" + } + ErrorAction = "Stop" + } + $defaultResponse = Invoke-JiraMethod @invokeJiraMethodSplat + + $invokeJiraMethodSplat["Headers"] = @{ + "X-Fake" = "dolor sum" } + $changedResponse = Invoke-JiraMethod @invokeJiraMethodSplat + + $defaultResponse.headers."x-fake" | Should -Be "lorem ipsum" + $changedResponse.headers."x-fake" | Should -Be "dolor sum" + + $assertMockCalledSplat = @{ + CommandName = "Invoke-WebRequest" + ModuleName = 'JiraPS' + Exactly = $true + Times = 2 + Scope = 'It' + } + Assert-MockCalled @assertMockCalledSplat } - ], - "assignee": { - "self": "https://jira.atlassian.com/rest/api/2/user?username=ben%40atlassian.com", - "name": "ben@atlassian.com", - "key": "ben@atlassian.com", - "emailAddress": "ben at atlassian dot com", - "avatarUrls": { - "48x48": "https://jira.atlassian.com/secure/useravatar?ownerId=ben%40atlassian.com&avatarId=72204", - "24x24": "https://jira.atlassian.com/secure/useravatar?size=small&ownerId=ben%40atlassian.com&avatarId=72204", - "16x16": "https://jira.atlassian.com/secure/useravatar?size=xsmall&ownerId=ben%40atlassian.com&avatarId=72204", - "32x32": "https://jira.atlassian.com/secure/useravatar?size=medium&ownerId=ben%40atlassian.com&avatarId=72204" - }, - "displayName": "Benjamin Naftzger [Atlassian]", - "active": true, - "timeZone": "Europe/Berlin" - }, - "updated": "2013-12-08T11:00:43.133+0000", - "status": { - "self": "https://jira.atlassian.com/rest/api/2/status/1", - "description": "Issue is open and has not yet been accepted by Atlassian.", - "iconUrl": "https://jira.atlassian.com/images/icons/statuses/open.png", - "name": "Open", - "id": "1", - "statusCategory": { - "self": "https://jira.atlassian.com/rest/api/2/statuscategory/2", - "id": 2, - "key": "new", - "colorName": "blue-gray", - "name": "To Do" - } - }, - "components": [], - "timeoriginalestimate": null, - "description": "Creating of an issue using project keys and issue type names using the REST API", - "timetracking": {}, - "attachment": [], - "aggregatetimeestimate": null, - "summary": "REST ye merry gentlemen.", - "creator": { - "self": "https://jira.atlassian.com/rest/api/2/user?username=gokhant", - "name": "gokhant", - "key": "gokhant", - "emailAddress": "gokhant at gmail dot com", - "avatarUrls": { - "48x48": "https://jira.atlassian.com/secure/useravatar?ownerId=gokhant&avatarId=73000", - "24x24": "https://jira.atlassian.com/secure/useravatar?size=small&ownerId=gokhant&avatarId=73000", - "16x16": "https://jira.atlassian.com/secure/useravatar?size=xsmall&ownerId=gokhant&avatarId=73000", - "32x32": "https://jira.atlassian.com/secure/useravatar?size=medium&ownerId=gokhant&avatarId=73000" - }, - "displayName": "Gokhan Tuna", - "active": true, - "timeZone": "Etc/UTC" - }, - "subtasks": [], - "reporter": { - "self": "https://jira.atlassian.com/rest/api/2/user?username=gokhant", - "name": "gokhant", - "key": "gokhant", - "emailAddress": "gokhant at gmail dot com", - "avatarUrls": { - "48x48": "https://jira.atlassian.com/secure/useravatar?ownerId=gokhant&avatarId=73000", - "24x24": "https://jira.atlassian.com/secure/useravatar?size=small&ownerId=gokhant&avatarId=73000", - "16x16": "https://jira.atlassian.com/secure/useravatar?size=xsmall&ownerId=gokhant&avatarId=73000", - "32x32": "https://jira.atlassian.com/secure/useravatar?size=medium&ownerId=gokhant&avatarId=73000" - }, - "displayName": "Gokhan Tuna", - "active": true, - "timeZone": "Etc/UTC" - }, - "aggregateprogress": { - "progress": 0, - "total": 0 - }, - "environment": null, - "duedate": null, - "progress": { - "progress": 0, - "total": 0 - }, - "comment": { - "startAt": 0, - "maxResults": 1, - "total": 1, - "comments": [ - { - "self": "https://jira.atlassian.com/rest/api/2/issue/303853/comment/534625", - "id": "534625", - "author": { - "self": "https://jira.atlassian.com/rest/api/2/user?username=gokhant", - "name": "gokhant", - "key": "gokhant", - "emailAddress": "gokhant at gmail dot com", - "avatarUrls": { - "48x48": "https://jira.atlassian.com/secure/useravatar?ownerId=gokhant&avatarId=73000", - "24x24": "https://jira.atlassian.com/secure/useravatar?size=small&ownerId=gokhant&avatarId=73000", - "16x16": "https://jira.atlassian.com/secure/useravatar?size=xsmall&ownerId=gokhant&avatarId=73000", - "32x32": "https://jira.atlassian.com/secure/useravatar?size=medium&ownerId=gokhant&avatarId=73000" - }, - "displayName": "Gokhan Tuna", - "active": true, - "timeZone": "Etc/UTC" - }, - "body": "test comment", - "updateAuthor": { - "self": "https://jira.atlassian.com/rest/api/2/user?username=gokhant", - "name": "gokhant", - "key": "gokhant", - "emailAddress": "gokhant at gmail dot com", - "avatarUrls": { - "48x48": "https://jira.atlassian.com/secure/useravatar?ownerId=gokhant&avatarId=73000", - "24x24": "https://jira.atlassian.com/secure/useravatar?size=small&ownerId=gokhant&avatarId=73000", - "16x16": "https://jira.atlassian.com/secure/useravatar?size=xsmall&ownerId=gokhant&avatarId=73000", - "32x32": "https://jira.atlassian.com/secure/useravatar?size=medium&ownerId=gokhant&avatarId=73000" - }, - "displayName": "Gokhan Tuna", - "active": true, - "timeZone": "Etc/UTC" - }, - "created": "2013-11-05T02:50:09.991+0000", - "updated": "2013-11-05T02:50:09.991+0000" - } - ] - }, - "votes": { - "self": "https://jira.atlassian.com/rest/api/2/issue/DEMO-2719/votes", - "votes": 0, - "hasVoted": false - }, - "worklog": { - "startAt": 0, - "maxResults": 20, - "total": 0, - "worklogs": [] - } - } -} -'@ - $validObjResult = ConvertFrom-Json -InputObject $validRestResult + It "uses authenticates as anonymous when no -Credential is provided and no session exists" -pending { + Mock Get-JiraSession -ModuleName JiraPS { + $null + } - Context "Output handling - valid object returned (HTTP 200)" { + $invokeJiraMethodSplat = @{ + Method = 'Get' + URI = "https://postman-echo.com/headers" + ErrorAction = "Stop" + } + Invoke-JiraMethod @invokeJiraMethodSplat - It "Outputs an object representation of JSON returned from JIRA" { + $assertMockCalledSplat = @{ + CommandName = "Invoke-WebRequest" + ModuleName = 'JiraPS' + ParameterFilter = { + $Credential -eq $null -and + $WebSession -eq $null + } + Exactly = $true + Times = 2 + Scope = 'It' + } + Assert-MockCalled @assertMockCalledSplat + } - Mock Invoke-WebRequest -ParameterFilter {$Method -eq 'Get' -and $Uri -eq $validTestUri} { - ShowMockInfo 'Invoke-WebRequest' -Params 'Uri', 'Method' - Write-Output [PSCustomObject] @{ - 'Content' = $validRestResult + It "removes and content-type from headers and uses Invoke-WebRequest's -ContentType" { + Mock Join-Hashtable -ModuleName JiraPS { + $table = @{ } + foreach ($item in $Hashtable) { + foreach ($key in $item.Keys) { + $table[$key] = $item[$key] + } } + $table } - $result = Invoke-JiraMethod -Method Get -URI $validTestUri - $result | Should Not BeNullOrEmpty + $invokeJiraMethodSplat = @{ + Method = 'Get' + URI = "https://postman-echo.com/headers" + ErrorAction = "Stop" + } + $defaultResponse = Invoke-JiraMethod @invokeJiraMethodSplat + + $invokeJiraMethodSplat["Headers"] = @{ + "Content-Type" = "text/plain" + } + $changedResponse = Invoke-JiraMethod @invokeJiraMethodSplat - # Compare each property in the result returned to the expected result - foreach ($property in (Get-Member -InputObject $result | Where-Object {$_.MemberType -eq 'NoteProperty'})) { - $result.$property | Should Be $validObjResult.$property + $assertMockCalledSplat = @{ + CommandName = "Invoke-WebRequest" + ModuleName = 'JiraPS' + ParameterFilter = { + $Uri -notlike "*contentType*" -and + $Uri -notlike "*content-Type*" -and + $ContentType -eq "application/json; charset=utf-8" + } + Exactly = $true + Times = 1 + Scope = 'It' } + Assert-MockCalled @assertMockCalledSplat + + $assertMockCalledSplat["ParameterFilter"] = { + $Uri -notlike "*contentType*" -and + $Uri -notlike "*content-Type*" -and + $ContentType -eq "text/plain" + } + Assert-MockCalled @assertMockCalledSplat + } + + It "can handle UTF-8 chars in the response" { + $invokeJiraMethodSplat = @{ + Method = 'Post' + URI = "https://postman-echo.com/post" + Body = $utf8String + ErrorAction = "Stop" + } + $response = Invoke-JiraMethod @invokeJiraMethodSplat + + $response.data | Should -Be $utf8String } } - Context "Output handling - no content returned (HTTP 204)" { - Mock Invoke-WebRequest { - ShowMockInfo 'Invoke-WebRequest' -Params 'Uri', 'Method' + Context "Paged restuls" { + Mock Invoke-WebRequest -ModuleName JiraPS { + ShowMockInfo 'Invoke-WebRequest' -Params 'Uri', 'Method', 'Body' + + $response = "" + if ($Uri -match "startAt\=(\d+)") { + switch ($matches[1]) { + 5 {$response = $pagedResponse2; break } + 7 { $response = $pagedResponse3; break } + } + } + if (-not $response) { + $response = $pagedResponse1 + } - Write-Output [PSCustomObject] @{ - 'StatusCode' = 204 - 'Content' = $null + $InvokeWebRequestSplat = @{ + Uri = "https://postman-echo.com/post" + Method = "Post" + Body = $response } + $result = Microsoft.PowerShell.Utility\Invoke-WebRequest @InvokeWebRequestSplat + + $scriptBlock = "`$response = @`"`n$response`n`"@;Write-Output ([System.Text.Encoding]::UTF8.GetBytes(`$response))" + $result.RawContentStream | Add-Member -MemberType ScriptMethod -Name "ToArray" -Force -Value ([Scriptblock]::Create($scriptBlock)) + $result } - Mock ConvertFrom-Json { - ShowMockInfo 'ConvertFrom-Json' + Mock Join-Hashtable -ModuleName JiraPS { + $table = @{ } + foreach ($item in $Hashtable) { + foreach ($key in $item.Keys) { + $table[$key] = $item[$key] + } + } + $table } - It "Correctly handles HTTP response codes that do not provide a return body" { - { Invoke-JiraMethod -Method Get -URI $validTestUri } | Should Not Throw - Assert-MockCalled -CommandName ConvertFrom-Json -Exactly -Times 0 -Scope It + It "requests each page of the results" { + { + $invokeJiraMethodSplat = @{ + Method = 'Get' + URI = "https://postman-echo.com/Get" + Paging = $true + ErrorAction = "Stop" + } + Invoke-JiraMethod @invokeJiraMethodSplat + } | Should -Not -Throw + + $assertMockCalledSplat = @{ + CommandName = 'Invoke-WebRequest' + ModuleName = 'JiraPS' + Exactly = $true + Times = 3 + Scope = 'It' + } + Assert-MockCalled @assertMockCalledSplat } - } - Context "Output handling - JIRA error returned (HTTP 400 and up)" { - $invalidTestUri = 'https://jira.atlassian.com/rest/api/latest/issue/1' - $invalidRestResult = '{"errorMessages":["Issue Does Not Exist"],"errors":{}}' + It "expands the data container" { + $invokeJiraMethodSplat = @{ + Method = 'Get' + URI = "https://postman-echo.com/Get" + Paging = $true + ErrorAction = "Stop" + } + $result = Invoke-JiraMethod @invokeJiraMethodSplat + + $result.Count | Should -Be 7 - Mock Invoke-WebRequest { - ShowMockInfo 'Invoke-WebRequest' -Params 'Uri', 'Method' - Write-Output [PSCustomObject] @{ - 'StatusCode' = 400 - 'Content' = $invalidRestResult + { $result | Get-Member -Name Id } | Should -Not -Throw + { $result | Get-Member -Name Id } | Should -Not -BeNullOrEmpty + } + + It "fetches only the necessary amount of pages when -First is used" { + $invokeJiraMethodSplat = @{ + Method = 'Get' + URI = "https://postman-echo.com/Get" + Paging = $true + First = 4 + ErrorAction = "Stop" } + $result = Invoke-JiraMethod @invokeJiraMethodSplat + + $result.Count | Should -Be 4 + + $assertMockCalledSplat = @{ + CommandName = 'Invoke-WebRequest' + ModuleName = 'JiraPS' + Exactly = $true + Times = 1 + Scope = 'It' + } + Assert-MockCalled @assertMockCalledSplat } - Mock Resolve-JiraError { - ShowMockInfo 'Resolve-JiraError' -params 'InputObject' + It "limits the number of results when -First is used" { + $invokeJiraMethodSplat = @{ + Method = 'Get' + URI = "https://postman-echo.com/Get" + Paging = $true + First = 6 + ErrorAction = "Stop" + } + $result = Invoke-JiraMethod @invokeJiraMethodSplat + + $result.Count | Should -Be 6 + + $assertMockCalledSplat = @{ + CommandName = 'Invoke-WebRequest' + ModuleName = 'JiraPS' + Exactly = $true + Times = 2 + Scope = 'It' + } + Assert-MockCalled @assertMockCalledSplat + } + + It "starts looking for results with an offset when -Skip is provided" { + $invokeJiraMethodSplat = @{ + Method = 'Get' + URI = "https://postman-echo.com/Get" + Paging = $true + Skip = 5 + ErrorAction = "Stop" + } + $result = Invoke-JiraMethod @invokeJiraMethodSplat + + $result.Count | Should -Be 2 + + $assertMockCalledSplat = @{ + CommandName = 'Invoke-WebRequest' + ModuleName = 'JiraPS' + Exactly = $true + Times = 2 + Scope = 'It' + } + Assert-MockCalled @assertMockCalledSplat } - It "Uses Resolve-JiraError to parse any JIRA error messages returned" { - { Invoke-JiraMethod -Method Get -URI $invalidTestUri } | Should Not Throw - Assert-MockCalled -CommandName Resolve-JiraError -Exactly -Times 1 -Scope It + It "-totalcount" { + # Don't know how to test this } } } diff --git a/Tests/JiraPS.Help.Tests.ps1 b/Tests/JiraPS.Help.Tests.ps1 index 097d9ca1..449b9d3c 100644 --- a/Tests/JiraPS.Help.Tests.ps1 +++ b/Tests/JiraPS.Help.Tests.ps1 @@ -1,200 +1,266 @@ -<# - .NOTES - =========================================================================== - Created with: SAPIEN Technologies, Inc., PowerShell Studio 2016 v5.2.119 - Created on: 4/12/2016 1:11 PM - Created by: June Blender - Organization: SAPIEN Technologies, Inc - Filename: *.Help.Tests.ps1 - =========================================================================== - .DESCRIPTION - To test help for the commands in a module, place this file in the module folder. - To test any module from any path, use https://github.com/juneb/PesterTDD/Module.Help.Tests.ps1 -#> - -<# -.SYNOPSIS -Gets command parameters; one per name. Prefers default parameter set. - -.DESCRIPTION -Gets one CommandParameterInfo object for each parameter in the specified -command. If a command has more than one parameter with the same name, this -function gets the parameters in the default parameter set, if one is specified. - -For example, if a command has two parameter sets: - Name, ID (default) - Name, Path -This function returns: - Name (default), ID Path - -This function is used to get parameters for help and for help testing. - -.PARAMETER Command -Enter a CommandInfo object, such as the object that Get-Command returns. You -can also pipe a CommandInfo object to the function. - -This parameter takes a CommandInfo object, instead of a command name, so -you can use the parameters of Get-Command to specify the module and version -of the command. - -.EXAMPLE -PS C:\> Get-ParametersDefaultFirst -Command (Get-Command New-Guid) -This command uses the Command parameter to specify the command to -Get-ParametersDefaultFirst - -.EXAMPLE -PS C:\> Get-Command New-Guid | Get-ParametersDefaultFirst -You can also pipe a CommandInfo object to Get-ParametersDefaultFirst - -.EXAMPLE -PS C:\> Get-ParametersDefaultFirst -Command (Get-Command BetterCredentials\Get-Credential) -You can use the Command parameter to specify the CommandInfo object. This -command runs Get-Command module-qualified name value. - -.EXAMPLE -PS C:\> $ModuleSpec = @{ModuleName='BetterCredentials';RequiredVersion=4.3} -PS C:\> Get-Command -FullyQualifiedName $ModuleSpec | Get-ParametersDefaultFirst -This command uses a Microsoft.PowerShell.Commands.ModuleSpecification object to -specify the module and version. You can also use it to specify the module GUID. -Then, it pipes the CommandInfo object to Get-ParametersDefaultFirst. -#> -function Get-ParametersDefaultFirst { - param - ( - [Parameter(Mandatory = $true, - ValueFromPipeline = $true)] - [System.Management.Automation.CommandInfo] - $Command - ) +#requires -modules BuildHelpers +#requires -modules Pester - BEGIN { - $Common = 'Debug', 'ErrorAction', 'ErrorVariable', 'InformationAction', 'InformationVariable', 'OutBuffer', 'OutVariable', 'PipelineVariable', 'Verbose', 'WarningAction', 'WarningVariable' - $parameters = @() - } - PROCESS { - if ($defaultPSetName = $Command.DefaultParameterSet) { - $defaultParameters = ($Command.ParameterSets | Where-Object Name -eq $defaultPSetName).parameters | Where-Object Name -NotIn $common - $otherParameters = ($Command.ParameterSets | Where-Object Name -ne $defaultPSetName).parameters | Where-Object Name -NotIn $common - - $parameters += $defaultParameters - if ($parameters -and $otherParameters) { - $otherParameters | ForEach-Object { - if ($_.Name -notin $parameters.Name) { - $parameters += $_ - } - } - $parameters = $parameters | Sort-Object Name - } +Describe "Help tests" -Tag Documentation { + + BeforeAll { + Import-Module BuildHelpers + Remove-Item -Path Env:\BH* + + $projectRoot = (Resolve-Path "$PSScriptRoot/..").Path + if ($projectRoot -like "*Release") { + $projectRoot = (Resolve-Path "$projectRoot/..").Path } - else { - $parameters = $Command.ParameterSets.Parameters | Where-Object Name -NotIn $common | Sort-Object Name -Unique + Set-BuildEnvironment -BuildOutput '$ProjectPath/Release' -Path $projectRoot -ErrorAction SilentlyContinue + $env:BHManifestToTest = $env:BHPSModuleManifest + $isBuild = $PSScriptRoot -like "$env:BHBuildOutput*" + if ($isBuild) { + $Pattern = [regex]::Escape($env:BHProjectPath) + + $env:BHBuildModuleManifest = $env:BHPSModuleManifest -replace $Pattern, $env:BHBuildOutput + $env:BHManifestToTest = $env:BHBuildModuleManifest } - return $parameters + Remove-Module $env:BHProjectName -ErrorAction SilentlyContinue + Import-Module $env:BHManifestToTest + } + AfterAll { + Remove-Module $env:BHProjectName -ErrorAction SilentlyContinue + Remove-Module BuildHelpers -ErrorAction SilentlyContinue + Remove-Item -Path Env:\BH* } - END { } -} -$ModuleBase = "$PSScriptRoot\..\JiraPS" + $DefaultParams = @( + 'Verbose' + 'Debug' + 'ErrorAction' + 'WarningAction' + 'InformationAction' + 'ErrorVariable' + 'WarningVariable' + 'InformationVariable' + 'OutVariable' + 'OutBuffer' + 'PipelineVariable' + 'WhatIf' + 'Confirm' + ) -# Handles modules in version directories -$leaf = Split-Path $ModuleBase -Leaf -$parent = Split-Path $ModuleBase -Parent -$parsedVersion = $null -if ([System.Version]::TryParse($leaf, [ref]$parsedVersion)) { - $ModuleName = Split-Path $parent -Leaf -} -else { - $ModuleName = $leaf -} + $module = Get-Module $env:BHProjectName + $commands = Get-Command -Module $module -CommandType Cmdlet, Function, Workflow # Not alias + $classes = Get-ChildItem "$env:BHProjectPath/docs/en-US/classes/*" -ErrorAction SilentlyContinue + $enums = Get-ChildItem "$env:BHProjectPath/docs/en-US/enumerations/*" -ErrorAction SilentlyContinue -# Removes all versions of the module from the session before importing -Get-Module $moduleName | Remove-Module + #region Public Functions + foreach ($command in $commands) { + $commandName = $command.Name -replace $module.Prefix, '' + $markdownFile = Resolve-Path "$env:BHProjectPath/docs/en-US/commands/$commandName.md" -$Module = Import-Module $ModuleBase\$ModuleName.psd1 -PassThru -ErrorAction Stop -$commands = Get-Command -Module $module -CommandType Cmdlet, Function, Workflow # Not alias + # The module-qualified command fails on Microsoft.PowerShell.Archive cmdlets + $help = Get-Help $command.Name -ErrorAction Stop -## When testing help, remember that help is cached at the beginning of each session. -## To test, restart session. + Context "Function $commandName's Help" { -foreach ($command in $commands) { - $commandName = $command.Name + #region PlatyPS external Help + It "is described in a markdown file" { + $markdownFile | Should -Not -BeNullOrEmpty + Test-Path $markdownFile | Should -Be $true + } - # The module-qualified command fails on Microsoft.PowerShell.Archive cmdlets - $Help = Get-Help $commandName -ErrorAction Stop + It "links the function to the external help" { + # required for PowerShell v3 + $Pattern = [regex]::Escape("# .ExternalHelp ..\JiraPS-help.xml") + $command.Definition -match $Pattern + } - Describe "Test help for $commandName" -Tag "CommandHelp" { + It "does not have Comment-Based Help" { + # We use .EXAMPLE, as we test this extensivly and it is never auto-generated + $command.Definition | Should -Not -BeNullOrEmpty + $Pattern = [regex]::Escape(".EXAMPLE") - # If help is not found, synopsis in auto-generated help is the syntax diagram - It "should not be auto-generated" { - $Help.Synopsis | Should Not BeLike '*`[``]*' - } + $command.Definition | Should -Not -Match "^\s*$Pattern" + } - # Should be a synopsis for every function - It "gets synopsis for $commandName" { - $Help.Synopsis | Should Not beNullOrEmpty - } + It "has no platyPS template artifacts" { + $markdownFile | Should -Not -BeNullOrEmpty + $markdownFile | Should -Not -FileContentMatch '{{.*}}' + } - # Should be a description for every function - It "gets description for $commandName" { - $Help.Description | Should Not BeNullOrEmpty - } + It "has a link to the 'Online Version'" { + [Uri]$onlineLink = ($help.relatedLinks.navigationLink | Where-Object linkText -eq "Online Version:").Uri - # Should be at least one example - It "gets example code from $commandName" { - ($Help.Examples.Example | Select-Object -First 1).Code | Should Not BeNullOrEmpty - } + $onlineLink.Authority | Should -Be "atlassianps.org" + $onlineLink.Scheme | Should -Be "https" + $onlineLink.PathAndQuery | Should -Be "/docs/$env:BHProjectName/commands/$commandName/" + } - # Should be at least one example description - It "gets example help from $commandName" { - ($Help.Examples.Example.Remarks | Select-Object -First 1).Text | Should Not BeNullOrEmpty - } + it "has a valid HelpUri" { + $command.HelpUri | Should -Not -BeNullOrEmpty + $Pattern = [regex]::Escape("https://atlassianps.org/docs/$env:BHProjectName/commands/$commandName") - It "has at least as many examples as ParameterSets" { - ($Help.Examples.Example | Measure-Object).Count | Should Not BeLessThan $command.ParameterSets.Count - } + $command.HelpUri | Should -Match $Pattern + } + + It "defines the frontmatter for the homepage" { + $markdownFile | Should -Not -BeNullOrEmpty + $markdownFile | Should -FileContentMatch "Module Name: $env:BHProjectName" + $markdownFile | Should -FileContentMatchExactly "layout: documentation" + $markdownFile | Should -FileContentMatch "permalink: /docs/$env:BHProjectName/commands/$commandName/" + } + #endregion PlatyPS external Help + + #region Help Content + It "has a synopsis" { + $help.Synopsis | Should -Not -BeNullOrEmpty + } + + It "has a description" { + $help.Description.Text -join '' | Should -Not -BeNullOrEmpty + } + + It "has examples" { + ($help.Examples.Example | Select-Object -First 1).Code | Should -Not -BeNullOrEmpty + } + + It "has desciptions for all examples" { + foreach ($example in ($help.Examples.Example)) { + $example.remarks.Text | Should -Not -BeNullOrEmpty + } + } + + It "has at least as many examples as ParameterSets" { + ($help.Examples.Example | Measure-Object).Count | Should -BeGreaterOrEqual $command.ParameterSets.Count + } + #endregion Help Content + + #region Consistency with Code + # It "does not define parameter position for functions with only one ParameterSet" { + # if ($command.ParameterSets.Count -eq 1) { + # $command.Parameters.Keys | Foreach-Object { + # $command.Parameters[$_].ParameterSets.Values.Position | Should -BeLessThan 0 + # } + # } + # } + + It "has all ParameterSets in the Help" { + # @($command.ParameterSets).Count | Should -Be @($help.Syntax.SyntaxItem).Count + } + + #region Parameters + foreach ($parameterName in $command.Parameters.Keys) { + $parameterCode = $command.Parameters[$parameterName] + + if ($help.Parameters | Get-Member -Name Parameter) { + $parameterHelp = $help.Parameters.Parameter | Where-Object Name -EQ $parameterName - Context "Test parameter help for $commandName" { - # Get parameters. When >1 parameter with same name, - # get parameter from the default parameter set, if any. - if ($parameters = Get-ParametersDefaultFirst -Command $command) { - $parameterNames = $parameters.Name - $HelpParameterNames = $Help.Parameters.Parameter.Name | Sort-Object -Unique - - foreach ($parameter in $parameters) { - $parameterName = $parameter.Name - $parameterHelp = $Help.parameters.parameter | Where-Object Name -EQ $parameterName - - # Should be a description for every parameter - if ($parameterName -notmatch 'Confirm|WhatIf') { - It "gets help for parameter: $parameterName : in $commandName" { - $parameterHelp.Description.Text | Should Not BeNullOrEmpty + if ($parameterName -notin $DefaultParams) { + It "has a description for parameter [-$parameterName] in $commandName" { + $parameterHelp.Description.Text | Should -Not -BeNullOrEmpty } - } - # Required value in Help should match IsMandatory property of parameter - It "help for $parameterName parameter in $commandName has correct Mandatory value" { - $codeMandatory = $parameter.IsMandatory.toString() - $parameterHelp.Required | Should Be $codeMandatory - } + It "has a mandatory flag for parameter [-$parameterName] in $commandName" { + $isMandatory = $parameterCode.ParameterSets.Values.IsMandatory -contains "True" - # Parameter type in Help should match code - It "help for $commandName has correct parameter type for $parameterName" { - $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 + $parameterHelp.Required | Should -BeLike $isMandatory.ToString() + } + + It "matches the type of the parameter [-$parameterName] in code and help of $commandName" { + $codeType = $parameterCode.ParameterType.Name + if ($codeType -eq "Object") { + if (($parameterCode.Attributes) -and ($parameterCode.Attributes | Get-Member -Name PSTypeName)) { + $codeType = $parameterCode.Attributes[0].PSTypeName + } + } + # 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 + } } } - foreach ($helpParm in $HelpParameterNames) { - # Shouldn't find extra parameters in help. - It "finds help parameter in code: $helpParm" { - $helpParm -in $parameterNames | Should Be $true + It "does not have parameters that are not in the code" { + $parameter = @() + if ($help.Parameters | Get-Member -Name Parameter) { + $parameter = $help.Parameters.Parameter.Name | Sort-Object -Unique } + foreach ($helpParm in $parameter) { + $command.Parameters.Keys | Should -Contain $helpParm + } + } + } + #endregion Parameters + #endregion Consistency with Code + } + } + #endregion Public Functions + + #region Classes + if ($classes) { + foreach ($class in $classes) { + Context "Classes $($class.BaseName) Help" { + + It "is described in a markdown file" { + $class.FullName | Should -Not -BeNullOrEmpty + Test-Path $class.FullName | Should -Be $true + } + + It "has no platyPS template artifacts" { + $class.FullName | Should -Not -BeNullOrEmpty + $class.FullName | Should -Not -FileContentMatch '{{.*}}' + } + + It "defines the frontmatter for the homepage" { + $class.FullName | Should -Not -BeNullOrEmpty + $class.FullName | Should -FileContentMatch "Module Name: $env:BHProjectName" + $class.FullName | Should -FileContentMatchExactly "layout: documentation" + $class.FullName | Should -FileContentMatch "permalink: /docs/$env:BHProjectName/classes/$commandName/" + } + } + } + + Context "Missing classes" { + It "has a documentation file for every class" { + foreach ($class in ([AtlassianPS.ServerData].Assembly.GetTypes() | Where-Object IsClass)) { + $classes.BaseName | Should -Contain $class.FullName + } + } + } + } + #endregion Classes + + #region Enumerations + if ($enums) { + foreach ($enum in $enums) { + Context "Enumeration $($enum.BaseName) Help" { + + It "is described in a markdown file" { + $enum.FullName | Should -Not -BeNullOrEmpty + Test-Path $enum.FullName | Should -Be $true + } + + It "has no platyPS template artifacts" { + $enum.FullName | Should -Not -BeNullOrEmpty + $enum.FullName | Should -Not -FileContentMatch '{{.*}}' + } + + It "defines the frontmatter for the homepage" { + $enum.FullName | Should -Not -BeNullOrEmpty + $enum.FullName | Should -FileContentMatch "Module Name: $env:BHProjectName" + $enum.FullName | Should -FileContentMatchExactly "layout: documentation" + $enum.FullName | Should -FileContentMatch "permalink: /docs/$env:BHProjectName/enumerations/$commandName/" + } + } + } + + Context "Missing classes" { + It "has a documentation file for every class" { + foreach ($enum in ([AtlassianPS.ServerData].Assembly.GetTypes() | Where-Object IsEnum)) { + $enums.BaseName | Should -Contain $enum.FullName } } } } + #endregion Enumerations } diff --git a/Tests/JiraPS.Integration.Tests.ps1 b/Tests/JiraPS.Integration.Tests.ps1 index 70e5efe0..0a9fafd1 100644 --- a/Tests/JiraPS.Integration.Tests.ps1 +++ b/Tests/JiraPS.Integration.Tests.ps1 @@ -61,12 +61,14 @@ InModuleScope JiraPS { } Describe 'Handling of Versions' { + $projectKey = "TV" + + $versionName1 = "TESTv1" + $versionName2 = "TESTv2" + $versionName3 = "TESTv3" + Context 'New-JiraVersion' { # ARRANGE - $projectKey = "TV" - $versionName1 = "TESTv1" - $versionName2 = "TESTv2" - $versionName3 = "TESTv3" $versionObject = [PSCustomObject]@{ Name = $versionName1 Description = "My Description" @@ -108,10 +110,6 @@ InModuleScope JiraPS { Context 'Get-JiraVersion' { # ARRANGE - $projectKey = "TV" - $versionName1 = "TESTv1" - $versionName2 = "TESTv2" - $versionName3 = "TESTv3" $versionObject = [PSCustomObject]@{ Name = $versionName1 } @@ -153,14 +151,10 @@ InModuleScope JiraPS { Context 'Set-JiraVersion' { # ARRANGE - $projectKey = "TV" $now = (Get-Date) - $versionName1 = "TESTv1" $oldVersion1 = Get-JiraVersion -Project $projectKey -Name $versionName1 $versionNewName1 = "TESTv1.1" - $versionName2 = "TESTv2" $oldVersion2 = Get-JiraVersion -Project $projectKey -Name $versionName2 - $versionName3 = "TESTv3" $oldVersion3 = Get-JiraVersion -Project $projectKey -Name $versionName3 # ACT @@ -193,15 +187,12 @@ InModuleScope JiraPS { Context 'Remove-JiraVersion' { # ARRANGE - $projectKey = "TV" $versionName1 = "TESTv1.1" - $versionId1 = Get-JiraVersion -Project $projectKey -Name $versionName1 - $versionName2 = "TESTv2" - $versionName3 = "TESTv3" + $versionObject = Get-JiraVersion -Project $projectKey -Name $versionName2 # ACT - Remove-JiraVersion -Version $versionId1 -Force -ErrorAction Stop - Get-JiraVersion -Project $projectKey -Name $versionName2, $versionName3 | Remove-JiraVersion -Force -ErrorAction Stop + Remove-JiraVersion -Version $versionObject -Force -ErrorAction Stop + Get-JiraVersion -Project $projectKey -Name $versionName1, $versionName3 | Remove-JiraVersion -Force -ErrorAction Stop # ASSERT It 'removes the versions' { diff --git a/Tests/JiraPS.Tests.ps1 b/Tests/JiraPS.Tests.ps1 index 4a8ed5bd..3512d171 100644 --- a/Tests/JiraPS.Tests.ps1 +++ b/Tests/JiraPS.Tests.ps1 @@ -183,16 +183,24 @@ Describe "JiraPS" { } Context 'PSScriptAnalyzer Rules' { - Import-Module $manifestFile -Force -ErrorAction Stop - - $analysis = Invoke-ScriptAnalyzer -Path "$moduleRoot" -Recurse -Settings "$projectRoot/PSScriptAnalyzerSettings.psd1" - $scriptAnalyzerRules = Get-ScriptAnalyzerRule - - forEach ($rule in $scriptAnalyzerRules) { - It "Should pass $rule" { - if (($analysis) -and ($analysis.RuleName -contains $rule)) { - $analysis | Where-Object RuleName -eq $rule -OutVariable failures | Out-Default - $failures.Count | Should Be 0 + if ($PSVersionTable.PSVersion.Major -gt 3) { + Import-Module $manifestFile -Force -ErrorAction Stop + + $invokeScriptAnalyzerSplat = @{ + Path = "$moduleRoot" + Settings = "$projectRoot/PSScriptAnalyzerSettings.psd1" + Recurse = $true + ErrorAction = 'SilentlyContinue' + } + $analysis = Invoke-ScriptAnalyzer @invokeScriptAnalyzerSplat + $scriptAnalyzerRules = Get-ScriptAnalyzerRule + + forEach ($rule in $scriptAnalyzerRules) { + It "Should pass $rule" { + if (($analysis) -and ($analysis.RuleName -contains $rule)) { + $analysis | Where-Object RuleName -eq $rule -OutVariable failures | Out-Default + $failures.Count | Should Be 0 + } } } } diff --git a/Tests/New-JiraFilter.Tests.ps1 b/Tests/New-JiraFilter.Tests.ps1 new file mode 100644 index 00000000..d198f004 --- /dev/null +++ b/Tests/New-JiraFilter.Tests.ps1 @@ -0,0 +1,129 @@ +Describe 'New-JiraFilter' { + BeforeAll { + Remove-Module JiraPS -ErrorAction SilentlyContinue + Import-Module "$PSScriptRoot/../JiraPS" -Force -ErrorAction Stop + } + + InModuleScope JiraPS { + + . "$PSScriptRoot/Shared.ps1" + + #region Definitions + $jiraServer = "https://jira.example.com" + + $responseFilter = @" +{ + "self": "$jiraServer/rest/api/latest/filter/12844", + "id": "12844", + "name": "{0}", + "jql": "{1}", + "favourite": false +} +"@ + #endregion Definitions + + #region Mocks + Mock Get-JiraConfigServer -ModuleName JiraPS { + $jiraServer + } + + Mock ConvertTo-JiraFilter -ModuleName JiraPS { + $i = (ConvertFrom-Json $responseFilter) + $i.PSObject.TypeNames.Insert(0, 'JiraPS.Filter') + $i | Add-Member -MemberType AliasProperty -Name 'RestURL' -Value 'self' + $i + } + + Mock Get-JiraFilter -ModuleName JiraPS { + ConvertTo-JiraFilter + } + + Mock Invoke-JiraMethod -ModuleName JiraPS -ParameterFilter {$Method -eq 'Post' -and $URI -like "$jiraServer/rest/api/*/filter"} { + ShowMockInfo 'Invoke-JiraMethod' 'Method', 'Uri', 'Body' + ConvertFrom-Json $responseFilter + } + + Mock Invoke-JiraMethod -ModuleName JiraPS { + ShowMockInfo 'Invoke-JiraMethod' 'Method', 'Uri' + throw "Unidentified call to Invoke-JiraMethod" + } + #endregion Mocks + + Context "Sanity checking" { + $command = Get-Command -Name New-JiraFilter + + defParam $command 'Name' + defParam $command 'Description' + defParam $command 'JQL' + defParam $command 'Favorite' + defParam $command 'Credential' + + defAlias $command 'Favourite' 'Favorite' + } + + Context "Behavior testing" { + It "Invokes the Jira API to create a filter" { + { + $newData = @{ + Name = "myName" + Description = "myDescription" + JQL = "myJQL" + Favorite = $true + } + New-JiraFilter @newData + } | Should Not Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It -ParameterFilter { + $Method -eq 'Post' -and + $URI -like '*/rest/api/*/filter' -and + $Body -match "`"name`":\s*`"myName`"" -and + $Body -match "`"description`":\s*`"myDescription`"" -and + $Body -match "`"jql`":\s*`"myJQL`"" -and + $Body -match "`"favourite`":\s*true" + } + } + } + + Context "Input testing" { + It "-Name and -JQL" { + { + $parameter = @{ + Name = "newName" + JQL = "newJQL" + } + New-JiraFilter @parameter + } | Should Not Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It + } + It "-Name and -Description and -JQL" { + { + $parameter = @{ + Name = "newName" + Description = "newDescription" + JQL = "newJQL" + } + New-JiraFilter @parameter + } | Should Not Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It + } + It "-Name and -Description and -JQL and -Favorite" { + { + $parameter = @{ + Name = "newName" + Description = "newDescription" + JQL = "newJQL" + Favorite = $true + } + New-JiraFilter @parameter + } | Should Not Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It + } + It "maps the properties of an object to the parameters" { + { Get-JiraFilter "12345" | New-JiraFilter } | Should Not Throw + } + } + } +} diff --git a/Tests/New-JiraIssue.Tests.ps1 b/Tests/New-JiraIssue.Tests.ps1 index e5f1aa43..93acf18f 100644 --- a/Tests/New-JiraIssue.Tests.ps1 +++ b/Tests/New-JiraIssue.Tests.ps1 @@ -20,6 +20,10 @@ @{ Key = "TEST-01"} } + Mock Get-JiraIssue { + [PSCustomObject] @{ Key = "TEST-01"} + } + Mock Get-JiraProject { $object = [PSCustomObject] @{ 'ID' = $Project diff --git a/Tests/New-JiraSession.Tests.ps1 b/Tests/New-JiraSession.Tests.ps1 index 1526d4ce..3166f732 100644 --- a/Tests/New-JiraSession.Tests.ps1 +++ b/Tests/New-JiraSession.Tests.ps1 @@ -1,66 +1,92 @@ -[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingConvertToSecureStringWithPlainText", "")] -param() - Describe "New-JiraSession" { - -Import-Module "$PSScriptRoot/../JiraPS" -Force -ErrorAction Stop + BeforeAll { + Remove-Module JiraPS -ErrorAction SilentlyContinue + Import-Module "$PSScriptRoot/../JiraPS" -Force -ErrorAction Stop + } + AfterEach { + try { + (Get-Module JiraPS).PrivateData.Remove("Session") + } + catch { $null = 0 } + } InModuleScope JiraPS { . "$PSScriptRoot/Shared.ps1" + #region Definitions $jiraServer = 'http://jiraserver.example.com' - $authUri = "$jiraServer/rest/api/*/mypermissions" - - $testUsername = 'powershell-test' - $testPassword = ConvertTo-SecureString -String 'test123' -AsPlainText -Force - $testCredential = New-Object -TypeName PSCredential -ArgumentList $testUsername, $testPassword - $testJson = "{}" - $global:newSessionVar = @{} + $testCredential = [System.Management.Automation.PSCredential]::Empty + #endregion Definitions + #region Mocks Mock Get-JiraConfigServer -ModuleName JiraPS { Write-Output $jiraServer } - Mock Invoke-WebRequest -Verifiable -ParameterFilter {$Method -eq 'Get' -and $Uri -like $authUri} { + Mock ConvertTo-JiraSession -ModuleName JiraPS { } + + Mock Invoke-JiraMethod -ModuleName JiraPS -ParameterFilter {$Method -eq 'Get' -and $Uri -like "*/rest/api/*/mypermissions"} { ShowMockInfo 'Invoke-JiraMethod' 'Method', 'Uri' - Write-Output $testJson + New-Object -TypeName Microsoft.PowerShell.Commands.WebRequestSession } - Mock Invoke-WebRequest { + Mock Invoke-JiraMethod -ModuleName JiraPS { ShowMockInfo 'Invoke-JiraMethod' 'Method', 'Uri' throw "Unidentified call to Invoke-JiraMethod" } + #endregion Mocks - It "Invokes a REST method directly to the JIRA server" { - New-JiraSession -Credential $testCredential - Assert-MockCalled -CommandName Invoke-WebRequest -Exactly -Times 1 -Scope It - } + Context "Sanity checking" { + $command = Get-Command -Name New-JiraSession - It "Uses the -UseBasicParsing switch for Invoke-WebRequest" { - { New-JiraSession -Credential $testCredential } | Should Not Throw - Assert-MockCalled -CommandName Invoke-WebRequest -ParameterFilter {$UseBasicParsing -eq $true} -Scope It + defParam $command 'Credential' + defParam $command 'Headers' } - It "Provides the JSessionID of the session in Jira" { - $s = New-JiraSession -Credential $testCredential - $s.JSessionID | Should Be $jSessionId - } + Context "Behavior testing" { + It "uses Basic Authentication to generate a session" { + { New-JiraSession -Credential $testCredential } | Should -Not -Throw + + $assertMockCalledSplat = @{ + CommandName = 'Invoke-JiraMethod' + ModuleName = 'JiraPS' + ParameterFilter = { + $Credential -eq $testCredential + } + Exactly = $true + Times = 1 + Scope = 'It' + } + Assert-MockCalled @assertMockCalledSplat + } - It "Stores the session variable in the module's PrivateData" { - $s = New-JiraSession -Credential $testCredential - $s2 = Get-JiraSession - $s2 | Should Be $s - } + It "can influence the Headers used in the request" { + { New-JiraSession -Credential $testCredential -Headers @{ "X-Header" = $true } } | Should -Not -Throw + + $assertMockCalledSplat = @{ + CommandName = 'Invoke-JiraMethod' + ModuleName = 'JiraPS' + ParameterFilter = { + $Headers.ContainsKey("X-Header") + } + Exactly = $true + Times = 1 + Scope = 'It' + } + Assert-MockCalled @assertMockCalledSplat + } + + It "stores the session variable in the module's PrivateData" { + (Get-Module JiraPS).PrivateData.Session | Should -BeNullOrEmpty - Context "Output checking" { - Mock ConvertTo-JiraSession {} - New-JiraSession -Credential $testCredential + New-JiraSession -Credential $testCredential - It "Uses ConvertTo-JiraSession to beautify output" { - Assert-MockCalled 'ConvertTo-JiraSession' + (Get-Module JiraPS).PrivateData.Session | Should -Not -BeNullOrEmpty } } + + Context "Input testing" { } } } diff --git a/Tests/Remove-JiraFilter.Tests.ps1 b/Tests/Remove-JiraFilter.Tests.ps1 new file mode 100644 index 00000000..71dc5ac2 --- /dev/null +++ b/Tests/Remove-JiraFilter.Tests.ps1 @@ -0,0 +1,166 @@ +Describe 'Remove-JiraFilter' { + BeforeAll { + Remove-Module JiraPS -ErrorAction SilentlyContinue + Import-Module "$PSScriptRoot/../JiraPS" -Force -ErrorAction Stop + } + + InModuleScope JiraPS { + + . "$PSScriptRoot/Shared.ps1" + + #region Definitions + $jiraServer = "https://jira.example.com" + + $responseFilter = @" +{ + "self": "$jiraServer/rest/api/latest/filter/12844", + "id": "12844", + "name": "All JIRA Bugs", + "owner": { + "self": "$jiraServer/rest/api/2/user?username=scott@atlassian.com", + "key": "scott@atlassian.com", + "name": "scott@atlassian.com", + "avatarUrls": { + "16x16": "$jiraServer/secure/useravatar?size=xsmall&avatarId=10612", + "24x24": "$jiraServer/secure/useravatar?size=small&avatarId=10612", + "32x32": "$jiraServer/secure/useravatar?size=medium&avatarId=10612", + "48x48": "$jiraServer/secure/useravatar?avatarId=10612" + }, + "displayName": "Scott Farquhar [Atlassian]", + "active": true + }, + "jql": "project = 10240 AND issuetype = 1 ORDER BY key DESC", + "viewUrl": "$jiraServer/secure/IssueNavigator.jspa?mode=hide&requestId=12844", + "searchUrl": "$jiraServer/rest/api/latest/search?jql=project+%3D+10240+AND+issuetype+%3D+1+ORDER+BY+key+DESC", + "favourite": false, + "sharePermissions": [ + { + "id": 10049, + "type": "global" + } + ], + "sharedUsers": { + "size": 0, + "items": [], + "max-results": 1000, + "start-index": 0, + "end-index": 0 + }, + "subscriptions": { + "size": 0, + "items": [], + "max-results": 1000, + "start-index": 0, + "end-index": 0 + } +} +"@ + #endregion Definitions + + #region Mocks + Mock Get-JiraConfigServer -ModuleName JiraPS { + $jiraServer + } + + Mock ConvertTo-JiraFilter -ModuleName JiraPS { + foreach ($i in $InputObject) { + $i.PSObject.TypeNames.Insert(0, 'JiraPS.Filter') + $i | Add-Member -MemberType AliasProperty -Name 'RestURL' -Value 'self' + $i + } + } + + Mock Get-JiraFilter -ModuleName JiraPS { + foreach ($i in $Id) { + ConvertTo-JiraFilter (ConvertFrom-Json $responseFilter) + } + } + + Mock Invoke-JiraMethod -ModuleName JiraPS -ParameterFilter {$Method -eq 'Delete' -and $URI -like "$jiraServer/rest/api/*/filter/*"} { + ShowMockInfo 'Invoke-JiraMethod' 'Method', 'Uri' + ConvertFrom-Json $responseFilter + } + + Mock Invoke-JiraMethod -ModuleName JiraPS { + ShowMockInfo 'Invoke-JiraMethod' 'Method', 'Uri' + throw "Unidentified call to Invoke-JiraMethod" + } + #endregion Mocks + + Context "Sanity checking" { + $command = Get-Command -Name Remove-JiraFilter + + defParam $command 'InputObject' + defParam $command 'Credential' + } + + Context "Behavior testing" { + Get-JiraFilter -Id 12844 + It "deletes a filter based on one or more InputObjects" { + { Get-JiraFilter -Id 12844 | Remove-JiraFilter } | Should Not Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It -ParameterFilter {$Method -eq 'Delete' -and $URI -like '*/rest/api/*/filter/12844'} + } + + It "deletes a filter based on one ore more filter ids" { + { Remove-JiraFilter -Id 12844 } | Should Not Throw + + Assert-MockCalled -CommandName Get-JiraFilter -ModuleName JiraPS -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It -ParameterFilter {$Method -eq 'Delete' -and $URI -like '*/rest/api/*/filter/12844'} + } + } + + Context "Input testing" { + It "Accepts a filter object for the -InputObject parameter" { + { Remove-JiraFilter -InputObject (Get-JiraFilter "12345") } | Should Not Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It + } + + It "Accepts a filter object without the -InputObject parameter" { + { Remove-JiraFilter (Get-JiraFilter "12345") } | Should Not Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It + } + + It "Accepts multiple filter objects to the -Filter parameter" { + { Remove-JiraFilter -InputObject (Get-JiraFilter 12345, 12345) } | Should Not Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 2 -Scope It + } + + It "Accepts a JiraPS.Filter object via pipeline" { + { Get-JiraFilter 12345, 12345 | Remove-JiraFilter } | Should Not Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 2 -Scope It + } + + It "Accepts an ID of a filter" { + { Remove-JiraFilter -Id 12345 } | Should Not Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It + } + + It "Accepts multiple IDs of filters" { + { Remove-JiraFilter -Id 12345, 12345 } | Should Not Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 2 -Scope It + } + + It "Accepts multiple IDs of filters over the pipeline" { + { 12345, 12345 | Remove-JiraFilter } | Should Not Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 2 -Scope It + } + + It "fails if a negative number is passed as ID" { + { Remove-JiraFilter -Id -1 } | Should Throw + } + + It "fails if something other than [JiraPS.Filter] is provided" { + { Get-Date | Remove-JiraFilter -ErrorAction Stop } | Should Throw + { Remove-JiraFilter "12345" -ErrorAction Stop} | Should Throw + } + } + } +} diff --git a/Tests/Remove-JiraFilterPermission.Tests.ps1 b/Tests/Remove-JiraFilterPermission.Tests.ps1 new file mode 100644 index 00000000..8d34ed47 --- /dev/null +++ b/Tests/Remove-JiraFilterPermission.Tests.ps1 @@ -0,0 +1,151 @@ +Describe 'Remove-JiraFilterPermission' { + BeforeAll { + Remove-Module JiraPS -ErrorAction SilentlyContinue + Import-Module "$PSScriptRoot/../JiraPS" -Force -ErrorAction Stop + } + + InModuleScope JiraPS { + + . "$PSScriptRoot/Shared.ps1" + + #region Definitions + $jiraServer = "https://jira.example.com" + + $filterPermission1 = New-Object -TypeName PSCustomObject -Property @{ Id = 1111 } + $filterPermission1.PSObject.TypeNames.Insert(0, 'JiraPS.FilterPermission') + $filterPermission2 = New-Object -TypeName PSCustomObject -Property @{ Id = 2222 } + $filterPermission2.Id = 2222 + $fullFilter = New-Object -TypeName PSCustomObject -Property @{ + Id = 12345 + RestUrl = "$jiraServer/rest/api/2/filter/12345" + FilterPermissions = @( + $filterPermission1 + $filterPermission2 + ) + } + $fullFilter.PSObject.TypeNames.Insert(0, 'JiraPS.Filter') + $basicFilter = New-Object -TypeName PSCustomObject -Property @{ + Id = 23456 + RestUrl = "$jiraServer/rest/api/2/filter/23456" + } + $basicFilter.PSObject.TypeNames.Insert(0, 'JiraPS.Filter') + + #endregion Definitions + + #region Mocks + Mock Get-JiraConfigServer -ModuleName JiraPS { + $jiraServer + } + + Mock Get-JiraFilter -ModuleName JiraPS { + $basicFilter + } + + Mock Get-JiraFilterPermission -ModuleName JiraPS { + $fullFilter + } + + Mock Invoke-JiraMethod -ModuleName JiraPS -ParameterFilter {$Method -eq 'Delete' -and $URI -like "$jiraServer/rest/api/*/filter/*/permission*"} { + ShowMockInfo 'Invoke-JiraMethod' 'Method', 'Uri' + } + + Mock Invoke-JiraMethod -ModuleName JiraPS { + ShowMockInfo 'Invoke-JiraMethod' 'Method', 'Uri' + throw "Unidentified call to Invoke-JiraMethod" + } + #endregion Mocks + + Context "Sanity checking" { + $command = Get-Command -Name Remove-JiraFilterPermission + + defParam $command 'Filter' + defParam $command 'FilterId' + defParam $command 'PermissionId' + defParam $command 'Credential' + } + + Context "Behavior testing" { + It "Deletes Permission from Filter Object" { + { + Get-JiraFilterPermission -Id 1 | Remove-JiraFilterPermission + } | Should Not Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It -ParameterFilter { + $Method -eq 'Delete' -and + $URI -like '*/rest/api/*/filter/12345/permission/1111' + } + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It -ParameterFilter { + $Method -eq 'Delete' -and + $URI -like '*/rest/api/*/filter/12345/permission/2222' + } + } + + It "Deletes Permission from FilterId + PermissionId" { + { + Remove-JiraFilterPermission -FilterId 1 -PermissionId 3333, 4444 + } | Should Not Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It -ParameterFilter { + $Method -eq 'Delete' -and + $URI -like '*/rest/api/*/filter/23456/permission/3333' + } + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It -ParameterFilter { + $Method -eq 'Delete' -and + $URI -like '*/rest/api/*/filter/23456/permission/4444' + } + } + } + + Context "Input testing" { + It "validates the -Filter to ensure FilterPermissions" { + { Remove-JiraFilterPermission -Filter (Get-JiraFilter -Id 1) } | Should -Throw + { Remove-JiraFilterPermission -Filter (Get-JiraFilterPermission -Id 1) } | Should -Not -Throw + } + + It "finds the filter by FilterId" { + { Remove-JiraFilterPermission -FilterId 1 -PermissionId 1111 } | Should -Not -Throw + + Assert-MockCalled -CommandName Get-JiraFilter -ModuleName JiraPS -Exactly -Times 1 -Scope It + } + + It "does not accept negative FilterIds" { + { Remove-JiraFilterPermission -FilterId -1 -PermissionId 1111 } | Should -Throw + } + + It "does not accept negative PermissionIds" { + { Remove-JiraFilterPermission -FilterId 1 -PermissionId -1111 } | Should -Throw + } + + It "can only process one FilterId" { + { Remove-JiraFilterPermission -FilterId 1, 2 -PermissionId 1111 } | Should -Throw + } + + It "can process multiple PermissionIds" { + { Remove-JiraFilterPermission -FilterId 1 -PermissionId 1111, 2222 } | Should -Not -Throw + } + + It "allows for the filter to be passed over the pipeline" { + { Get-JiraFilterPermission -Id 1 | Remove-JiraFilterPermission } | Should -Not -Throw + } + + It "can ony process one Filter objects" { + $filter = @() + $filter += Get-JiraFilterPermission -Id 1 + $filter += Get-JiraFilterPermission -Id 1 + + { Remove-JiraFilterPermission -Filter $filter } | Should -Throw + } + + It "resolves positional parameters" { + { Remove-JiraFilterPermission 12345 1111 } | Should -Not -Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It + + $filter = Get-JiraFilterPermission -Id 1 + { Remove-JiraFilterPermission $filter } | Should -Not -Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 3 -Scope It + } + } + } +} diff --git a/Tests/Remove-JiraIssue.Tests.ps1 b/Tests/Remove-JiraIssue.Tests.ps1 new file mode 100644 index 00000000..b17143a3 --- /dev/null +++ b/Tests/Remove-JiraIssue.Tests.ps1 @@ -0,0 +1,290 @@ +Describe "Remove-JiraIssue" { + Import-Module "$PSScriptRoot/../JiraPS" -Force -ErrorAction Stop + + InModuleScope JiraPS { + + . "$PSScriptRoot/Shared.ps1" + + $jiraServer = 'http://jiraserver.example.com' + + $TestIssueJSONs = @{ + + # basic issue + 'TEST-1' = @' + { + "expand": "renderedFields,names,schema,operations,editmeta,changelog,versionedRepresentations", + "id": "58159", + "self": "https://jiraserver.example.com/rest/api/2/issue/58159", + "key": "TEST-1", + "fields": { + "subtasks": [], + "project": { + "self": "https://jiraserver.example.com/rest/api/2/project/14801", + "id": "14801", + "key": "TEST", + "name": "TEST - Service Desk", + "avatarUrls": { + "48x48": "https://jiraserver.example.com/secure/projectavatar?avatarId=12003", + "24x24": "https://jiraserver.example.com/secure/projectavatar?size=small&avatarId=12003", + "16x16": "https://jiraserver.example.com/secure/projectavatar?size=xsmall&avatarId=12003", + "32x32": "https://jiraserver.example.com/secure/projectavatar?size=medium&avatarId=12003" + } + }, + "aggregatetimespent": null, + "resolutiondate": null, + "workratio": -1, + "description": "Test issue.", + "summary": "Test Issue", + "comment": { + "comments": [], + "maxResults": 0, + "total": 0, + "startAt": 0 + } + } + } +'@ + # issue w/ subtasks + 'TEST-2' = @' + { + "expand": "renderedFields,names,schema,operations,editmeta,changelog,versionedRepresentations", + "id": "58160", + "self": "https://jiraserver.example.com/rest/api/2/issue/58160", + "key": "TEST-2", + "fields": { + "subtasks": [ + { + "id": "58161", + "key": "TEST-3", + "self": "https://jiraserver.example.com/rest/api/2/issue/58161", + "fields": { + "summary": "Test Sub-Task", + "status": { + "self": "https://jiraserver.example.com/rest/api/2/status/11202", + "description": "This was auto-generated by JIRA Service Desk during workflow import", + "iconUrl": "https://jiraserver.example.com/images/icons/status_generic.gif", + "name": "Open", + "id": "11202", + "statusCategory": { + "self": "https://jiraserver.example.com/rest/api/2/statuscategory/2", + "id": 2, + "key": "new", + "colorName": "blue-gray", + "name": "To Do" + } + }, + "priority": { + "self": "https://jiraserver.example.com/rest/api/2/priority/4", + "iconUrl": "https://jiraserver.example.com/images/icons/priorities/minor.svg", + "name": "Medium", + "id": "4" + }, + "issuetype": { + "self": "https://jiraserver.example.com/rest/api/2/issuetype/5", + "id": "5", + "description": "The sub-task of the issue", + "iconUrl": "https://jiraserver.example.com/secure/viewavatar?size=xsmall&avatarId=11016&avatarType=issuetype", + "name": "Sub-task", + "subtask": true, + "avatarId": 11016 + } + } + } + ], + "project": { + "self": "https://jiraserver.example.com/rest/api/2/project/14801", + "id": "14801", + "key": "TEST", + "name": "TEST - Service Desk", + "avatarUrls": { + "48x48": "https://jiraserver.example.com/secure/projectavatar?avatarId=12003", + "24x24": "https://jiraserver.example.com/secure/projectavatar?size=small&avatarId=12003", + "16x16": "https://jiraserver.example.com/secure/projectavatar?size=xsmall&avatarId=12003", + "32x32": "https://jiraserver.example.com/secure/projectavatar?size=medium&avatarId=12003" + } + }, + "description": "Test issue with a sub-task attached.", + "summary": "Test Parent-Task Issue", + "comment": { + "comments": [], + "maxResults": 0, + "total": 0, + "startAt": 0 + } + } + } +'@ + # the sub-task itself + 'TEST-3' = @' + { + "expand": "renderedFields,names,schema,operations,editmeta,changelog,versionedRepresentations", + "id": "58161", + "self": "https://jiraserver.example.com/rest/api/2/issue/58161", + "key": "TEST-3", + "fields": { + "parent": { + "id": "58160", + "key": "TEST-2", + "self": "https://jiraserver.example.com/rest/api/2/issue/58160", + "fields": { + "summary": "Test Parent-Task Issue", + "status": { + "self": "https://jiraserver.example.com/rest/api/2/status/1", + "description": "The issue is new and has not been looked at yet.", + "iconUrl": "https://jiraserver.example.com/images/icons/statuses/open.png", + "name": "OPENED", + "id": "1", + "statusCategory": { + "self": "https://jiraserver.example.com/rest/api/2/statuscategory/2", + "id": 2, + "key": "new", + "colorName": "blue-gray", + "name": "To Do" + } + }, + "priority": { + "self": "https://jiraserver.example.com/rest/api/2/priority/4", + "iconUrl": "https://jiraserver.example.com/images/icons/priorities/minor.svg", + "name": "Medium", + "id": "4" + }, + "issuetype": { + "self": "https://jiraserver.example.com/rest/api/2/issuetype/3", + "id": "3", + "description": "A task that needs to be done.", + "iconUrl": "https://jiraserver.example.com/secure/viewavatar?size=xsmall&avatarId=11018&avatarType=issuetype", + "name": "Task", + "subtask": false, + "avatarId": 11018 + } + } + }, + "subtasks": [], + "project": { + "self": "https://jiraserver.example.com/rest/api/2/project/14801", + "id": "14801", + "key": "TEST", + "name": "TEST - Service Desk", + "avatarUrls": { + "48x48": "https://jiraserver.example.com/secure/projectavatar?avatarId=12003", + "24x24": "https://jiraserver.example.com/secure/projectavatar?size=small&avatarId=12003", + "16x16": "https://jiraserver.example.com/secure/projectavatar?size=xsmall&avatarId=12003", + "32x32": "https://jiraserver.example.com/secure/projectavatar?size=medium&avatarId=12003" + } + }, + "description": "Test sub-task.", + "summary": "Test Sub-Task", + "comment": { + "comments": [], + "maxResults": 0, + "total": 0, + "startAt": 0 + } + } + } +'@ + + } + + Mock Get-JiraConfigServer -ModuleName JiraPS { + Write-Output $jiraServer + } + + Mock Get-JiraIssue { + $obj = $TestIssueJSONs[$Key] | ConvertFrom-Json + + $obj.PSObject.TypeNames.Insert(0, 'JiraPS.Issue') + + $obj | Add-Member -MemberType ScriptMethod -Name ToString -Value {return ""} -Force + return $obj + } + + Mock Invoke-JiraMethod -ModuleName JiraPS -ParameterFilter {$URI -like "$jiraServer/rest/api/*/issue/TEST-1?*" -and $Method -eq "Delete"} { + return $null + } + + Mock Invoke-JiraMethod -ModuleName JiraPS -ParameterFilter {$URI -like "$jiraServer/rest/api/*/issue/TEST-2?deleteSubTasks=False" -and $Method -eq "Delete"} { + + Write-Error -Exception -ErrorId + $MockedResponse = @" + { + "errorMessages": [ + "The issue 'TEST-2' has subtasks. You must specify the 'deleteSubtasks' parameter to delete this issue and all its subtasks." + ], + "errors": {} + } +"@ | ConvertFrom-Json + + + $Exception = ([System.ArgumentException]"Server responded with Error") + $errorId = "ServerResponse" + $errorCategory = 'NotSpecified' + $errorTarget = $MockedResponse + + $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord -ArgumentList $Exception,$errorId,$errorCategory,$errorTarget + $errorItem.ErrorDetails = "Jira encountered an error: [The issue 'TEST-2' has subtasks. You must specify the 'deleteSubtasks' parameter to delete this issue and all its subtasks.]" + + $PSCmdlet.WriteError($errorItem) + } + + Mock Invoke-JiraMethod -ModuleName JiraPS -ParameterFilter {$URI -like "$jiraServer/rest/api/*/issue/TEST-2?deleteSubTasks=True" -and $Method -eq "Delete"} { + return $null + } + + # Generic catch-all. This will throw an exception if we forgot to mock something. + Mock Invoke-JiraMethod -ModuleName JiraPS { + ShowMockInfo 'Invoke-JiraMethod' 'Method', 'Uri' + throw "Unidentified call to Invoke-JiraMethod" + } + + ############# + # Tests + ############# + + Context "Sanity checking" { + $command = Get-Command -Name Remove-JiraIssue + + defParam $command 'IssueId' + defParam $command 'InputObject' + defParam $command 'IncludeSubTasks' + defParam $command 'Credential' + } + + Context "Functionality" { + + It "Accepts generic object with the correct properties" { + { + $issue = Get-JiraIssue -Key TEST-1 + Remove-JiraIssue -Issue $issue -Force + } | Should Not Throw + Assert-MockCalled -CommandName Invoke-JiraMethod -Exactly -Times 1 -Scope It + } + + It "Accepts string-based input as a non-pipelined parameter" { + {Remove-JiraIssue -IssueId TEST-1 -Force} | Should Not Throw + Assert-MockCalled -CommandName Invoke-JiraMethod -Exactly -Times 1 -Scope It + } + + It "Accepts a JiraPS.Issue object over the pipeline" { + { Get-JiraIssue -Key TEST-1 | Remove-JiraIssue -Force} | Should Not Throw + Assert-MockCalled -CommandName Invoke-JiraMethod -Exactly -Times 1 -Scope It + } + + It "Writes an error on issues with subtasks" { + # Pester is not capable of (easily) asserting non-terminating errors, + # so the error is upgraded to a terminating one in this situation. + { Get-JiraIssue -Key TEST-2 | Remove-JiraIssue -Force -ErrorAction Stop} | Should Throw + Assert-MockCalled -CommandName Invoke-JiraMethod -Exactly -Times 1 -Scope It + } + + It "Passes on issues with subtasks and -DeleteSubTasks" { + { Get-JiraIssue -Key TEST-2 | Remove-JiraIssue -IncludeSubTasks -Force} | Should Not Throw + Assert-MockCalled -CommandName Invoke-JiraMethod -Exactly -Times 1 -Scope It + } + + It "Validates pipeline input" { + { @{id = 1} | Remove-JiraIssue -ErrorAction Stop} | Should Throw + } + } + } +} diff --git a/Tests/Remove-JiraIssueAttachment.Tests.ps1 b/Tests/Remove-JiraIssueAttachment.Tests.ps1 index 74dc8316..c1a13c4d 100644 --- a/Tests/Remove-JiraIssueAttachment.Tests.ps1 +++ b/Tests/Remove-JiraIssueAttachment.Tests.ps1 @@ -109,7 +109,7 @@ Describe "Remove-JiraIssueAttachment" { { Remove-JiraIssueAttachment -AttachmentId $attachmentId1, $attachmentId2 -Force } | Should Not Throw { Remove-JiraIssueAttachment -Issue (Get-JiraIssue $issueKey) -Force } | Should Not Throw { Remove-JiraIssueAttachment -Issue $issueKey -FileName $attachmentFile1 -Force } | Should Not Throw - { Remove-JiraIssueAttachment -Issue $issueKey -FileName $attachmentFile1, $attachmentFile2 -Credential $Cred -Force } | Should Not Throw + { Remove-JiraIssueAttachment -Issue $issueKey -FileName $attachmentFile1, $attachmentFile2 -Force } | Should Not Throw # ensure the calls under the hood Assert-MockCalled 'Get-JiraIssue' -ModuleName JiraPS -Exactly -Times 4 -Scope It diff --git a/Tests/Remove-JiraSession.Tests.ps1 b/Tests/Remove-JiraSession.Tests.ps1 index b758e5bc..a194ecb9 100644 --- a/Tests/Remove-JiraSession.Tests.ps1 +++ b/Tests/Remove-JiraSession.Tests.ps1 @@ -1,73 +1,31 @@ -[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingConvertToSecureStringWithPlainText", "")] -param() - Describe "Remove-JiraSession" { + BeforeAll { + Remove-Module JiraPS -ErrorAction SilentlyContinue + Import-Module "$PSScriptRoot/../JiraPS" -Force -ErrorAction Stop + } - Import-Module "$PSScriptRoot/../JiraPS" -Force -ErrorAction Stop - - InModuleScope JiraPS { - - . "$PSScriptRoot/Shared.ps1" - - $jiraServer = 'http://jiraserver.example.com' - $authUri = "$jiraServer/rest/api/*/mypermissions" - $jSessionId = '76449957D8C863BE8D4F6F5507E980E8' - - $testUsername = 'powershell-test' - $testPassword = ConvertTo-SecureString -String 'test123' -AsPlainText -Force - $testCredential = New-Object -TypeName PSCredential -ArgumentList $testUsername, $testPassword - - $testJson = @" -{ -} -"@ + . "$PSScriptRoot/Shared.ps1" - Mock Get-JiraConfigServer -ModuleName JiraPS { - Write-Output $jiraServer - } - - Mock Invoke-WebRequest -Verifiable -ParameterFilter {$Method -eq 'GET' -and $Uri -like $authUri} { - ShowMockInfo 'Invoke-JiraMethod' 'Method', 'Uri' - $global:newSessionVar = @{} - Write-Output $testJson - } - - Mock Invoke-WebRequest { - ShowMockInfo 'Invoke-JiraMethod' 'Method', 'Uri' - throw "Unidentified call to Invoke-JiraMethod" - } + #region Mocks + Mock Get-JiraSession -ModuleName JiraPS { + (Get-Module JiraPS).PrivateData.Session + } + #endregion Mocks - It "Closes a saved JiraPS.Session object from module PrivateData" { + Context "Sanity checking" { + $command = Get-Command -Name Remove-JiraSession - # This probably isn't the best test for this, but it's about all I can come up with at the moment. - # New-JiraSession has some slightly more elaborate testing, which includes a test for Get-JiraSession, - # so if both of those pass, they should work as expected here. + defParam $command 'Session' + } - New-JiraSession -Credential $testCredential - Get-JiraSession | Should Not BeNullOrEmpty + Context "Behavior testing" { + It "Closes a removes the JiraPS.Session data from module PrivateData" { + (Get-Module JiraPS).PrivateData = @{ Session = $true } + (Get-Module JiraPS).PrivateData.Session | Should -Not -BeNullOrEmpty Remove-JiraSession - Get-JiraSession | Should BeNullOrEmpty - } - - It "Correctly handles sessions from a variable" { - $Session = New-JiraSession -Credential $testCredential - $Session | Should Not BeNullOrEmpty - Get-JiraSession | Should Not BeNullOrEmpty - - Remove-JiraSession $Session - Get-JiraSession | Should BeNullOrEmpty - } - - It "Correctly handles pipeline input from New-JiraSession" { - { New-JiraSession -Credential $testCredential | Remove-JiraSession } | Should Not Throw - Get-JiraSession | Should BeNullOrEmpty - } - It "Correctly handles pipeline input from Get-JiraSession" { - New-JiraSession -Credential $testCredential - { Get-JiraSession | Remove-JiraSession } | Should Not Throw - Get-JiraSession | Should BeNullOrEmpty + (Get-Module JiraPS).PrivateData.Session | Should -BeNullOrEmpty } } } diff --git a/Tests/Set-JiraFilter.Tests.ps1 b/Tests/Set-JiraFilter.Tests.ps1 new file mode 100644 index 00000000..7f550f76 --- /dev/null +++ b/Tests/Set-JiraFilter.Tests.ps1 @@ -0,0 +1,307 @@ +Describe 'Set-JiraFilter' { + BeforeAll { + Remove-Module JiraPS -ErrorAction SilentlyContinue + Import-Module "$PSScriptRoot/../JiraPS" -Force -ErrorAction Stop + } + + InModuleScope JiraPS { + + . "$PSScriptRoot/Shared.ps1" + + #region Definitions + $jiraServer = "https://jira.example.com" + + $responseFilter = @" +{ + "self": "$jiraServer/rest/api/latest/filter/12844", + "id": "12844", + "name": "All JIRA Bugs", + "owner": { + "self": "$jiraServer/rest/api/2/user?username=scott@atlassian.com", + "key": "scott@atlassian.com", + "name": "scott@atlassian.com", + "avatarUrls": { + "16x16": "$jiraServer/secure/useravatar?size=xsmall&avatarId=10612", + "24x24": "$jiraServer/secure/useravatar?size=small&avatarId=10612", + "32x32": "$jiraServer/secure/useravatar?size=medium&avatarId=10612", + "48x48": "$jiraServer/secure/useravatar?avatarId=10612" + }, + "displayName": "Scott Farquhar [Atlassian]", + "active": true + }, + "jql": "project = 10240 AND issuetype = 1 ORDER BY key DESC", + "viewUrl": "$jiraServer/secure/IssueNavigator.jspa?mode=hide&requestId=12844", + "searchUrl": "$jiraServer/rest/api/latest/search?jql=project+%3D+10240+AND+issuetype+%3D+1+ORDER+BY+key+DESC", + "favourite": false, + "sharePermissions": [ + { + "id": 10049, + "type": "global" + } + ], + "sharedUsers": { + "size": 0, + "items": [], + "max-results": 1000, + "start-index": 0, + "end-index": 0 + }, + "subscriptions": { + "size": 0, + "items": [], + "max-results": 1000, + "start-index": 0, + "end-index": 0 + } +} +"@ + #endregion Definitions + + #region Mocks + Mock Get-JiraConfigServer -ModuleName JiraPS { + $jiraServer + } + + Mock ConvertTo-JiraFilter -ModuleName JiraPS { + foreach ($i in $InputObject) { + $i.PSObject.TypeNames.Insert(0, 'JiraPS.Filter') + $i | Add-Member -MemberType AliasProperty -Name 'RestURL' -Value 'self' + $i + } + } + + Mock Get-JiraFilter -ModuleName JiraPS { + foreach ($i in $Id) { + ConvertTo-JiraFilter (ConvertFrom-Json $responseFilter) + } + } + + Mock Invoke-JiraMethod -ModuleName JiraPS -ParameterFilter {$Method -eq 'Put' -and $URI -like "$jiraServer/rest/api/*/filter/*"} { + ShowMockInfo 'Invoke-JiraMethod' 'Method', 'Uri', 'Body' + ConvertFrom-Json $responseFilter + } + + Mock Invoke-JiraMethod -ModuleName JiraPS { + ShowMockInfo 'Invoke-JiraMethod' 'Method', 'Uri' + throw "Unidentified call to Invoke-JiraMethod" + } + #endregion Mocks + + Context "Sanity checking" { + $command = Get-Command -Name Set-JiraFilter + + defParam $command 'InputObject' + defParam $command 'Name' + defParam $command 'Description' + defParam $command 'JQL' + defParam $command 'Favorite' + defParam $command 'Credential' + + defAlias $command 'Favourite' 'Favorite' + } + + Context "Behavior testing" { + It "Invokes the Jira API to update a filter" { + { + $newData = @{ + Name = "newName" + Description = "newDescription" + JQL = "newJQL" + Favorite = $true + } + Get-JiraFilter -Id 12844 | Set-JiraFilter @newData + } | Should Not Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It -ParameterFilter { + $Method -eq 'Put' -and + $URI -like '*/rest/api/*/filter/12844' -and + $Body -match "`"name`":\s*`"newName`"" -and + $Body -match "`"description`":\s*`"newDescription`"" -and + $Body -match "`"jql`":\s*`"newJQL`"" -and + $Body -match "`"favourite`":\s*true" + } + } + + It "Can set the Description to Empty" { + { + $newData = @{ + Description = "" + } + Get-JiraFilter -Id 12844 | Set-JiraFilter @newData + } | Should Not Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It -ParameterFilter { + $Method -eq 'Put' -and + $URI -like '*/rest/api/*/filter/12844' -and + $Body -match "`"description`":\s*`"`"" + } + } + + It "Skips the filter if no value was changed" { + { Get-JiraFilter -Id 12844 | Set-JiraFilter } | Should Not Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 0 -Scope It + } + } + + Context "Input testing" { + It "accepts a filter object for the -InputObject parameter" { + { Set-JiraFilter -InputObject (Get-JiraFilter "12345") -Name "test" } | Should Not Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It + } + + It "accepts a filter object without the -InputObject parameter" { + { Set-JiraFilter (Get-JiraFilter "12345") -Name "test" } | Should Not Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It + } + + It "fails with multiple filter objects to the -Filter parameter" { + { Set-JiraFilter -InputObject (Get-JiraFilter 12345, 12345) -Name "test" } | Should Throw + } + + It "accepts a JiraPS.Filter object via pipeline" { + { Get-JiraFilter 12345, 12345 | Set-JiraFilter -Name "test" } | Should Not Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 2 -Scope It + } + + It "fails if something other than [JiraPS.Filter] is provided to InputObject" { + { "12345" | Set-JiraFilter -ErrorAction Stop } | Should Throw + { Set-JiraFilter "12345" -ErrorAction Stop} | Should Throw + } + + It "accepts -InputObject" { + { + $parameter = @{ + InputObject = Get-JiraFilter "12345" + } + Set-JiraFilter @parameter + } | Should Not Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 0 -Scope It + } + It "accepts -InputObject and -Name" { + { + $parameter = @{ + InputObject = Get-JiraFilter "12345" + Name = "newName" + } + Set-JiraFilter @parameter + } | Should Not Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It + } + It "accepts -InputObject and -Description" { + { + $parameter = @{ + InputObject = Get-JiraFilter "12345" + Description = "newDescription" + } + Set-JiraFilter @parameter + } | Should Not Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It + } + It "accepts -InputObject and -JQL" { + { + $parameter = @{ + InputObject = Get-JiraFilter "12345" + JQL = "newJQL" + } + Set-JiraFilter @parameter + } | Should Not Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It + } + It "accepts -InputObject and -Favorite" { + { + $parameter = @{ + InputObject = Get-JiraFilter "12345" + Favorite = $true + } + Set-JiraFilter @parameter + } | Should Not Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It + } + It "accepts -InputObject and -Name and -Description" { + { + $parameter = @{ + InputObject = Get-JiraFilter "12345" + Name = "newName" + Description = "newDescription" + } + Set-JiraFilter @parameter + } | Should Not Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It + } + It "accepts -InputObject and -Name and -JQL" { + { + $parameter = @{ + InputObject = Get-JiraFilter "12345" + Name = "newName" + JQL = "newJQL" + } + Set-JiraFilter @parameter + } | Should Not Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It + } + It "accepts -InputObject and -Name and -Favorite" { + { + $parameter = @{ + InputObject = Get-JiraFilter "12345" + Name = "newName" + Favorite = $true + } + Set-JiraFilter @parameter + } | Should Not Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It + } + It "accepts -InputObject and -Name and -Description and -JQL" { + { + $parameter = @{ + InputObject = Get-JiraFilter "12345" + Name = "newName" + Description = "newDescription" + JQL = "newJQL" + } + Set-JiraFilter @parameter + } | Should Not Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It + } + It "accepts -InputObject and -Name and -Description and -Favorite" { + { + $parameter = @{ + InputObject = Get-JiraFilter "12345" + Name = "newName" + Description = "newDescription" + Favorite = $true + } + Set-JiraFilter @parameter + } | Should Not Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It + } + It "accepts -InputObject and -Name and -Description and -JQL and -Favorite" { + { + $parameter = @{ + InputObject = Get-JiraFilter "12345" + Name = "newName" + Description = "newDescription" + JQL = "newJQL" + Favorite = $true + } + Set-JiraFilter @parameter + } | Should Not Throw + + Assert-MockCalled -CommandName Invoke-JiraMethod -ModuleName JiraPS -Exactly -Times 1 -Scope It + } + } + } +} diff --git a/Tools/build.requirements.psd1 b/Tools/build.requirements.psd1 index 2c0e1b26..48501742 100644 --- a/Tools/build.requirements.psd1 +++ b/Tools/build.requirements.psd1 @@ -14,8 +14,13 @@ Parameters = @{ SkipPublisherCheck = $true } - Version = "4.1.1" + Version = "4.3.1" } platyPS = "latest" - PSScriptAnalyzer = "latest" + PSScriptAnalyzer = @{ + Parameters = @{ + SkipPublisherCheck = $true + } + Version = "latest" + } } diff --git a/_posts/2018-06-28-JiraPS-v2.8.md b/_posts/2018-06-28-JiraPS-v2.8.md new file mode 100644 index 00000000..c52df538 --- /dev/null +++ b/_posts/2018-06-28-JiraPS-v2.8.md @@ -0,0 +1,111 @@ +--- +layout: article +permalink: /article/:categories/:title +title: JiraPS v2.8 +date: 2018-06-28 12:00:00 +categories: Announcement +thumbnail: +author: lipkau +tags: + - JiraPS + - Release +--- + +We have just uploaded a new version of the **JiraPS** module to the [Gallery](https://www.powershellgallery.com/packages/JiraPS/2.8.0) and to [GitHub](https://github.com/AtlassianPS/JiraPS/tree/v2.8.0). + + +## Description + +This release brings new functionality, improvements and bug fixes that has been submitted by our contributors. + +## CHANGELOG + +### IMPROVEMENTS + +- Added support for paginated response from API server by means of `-Paging` (#291, [@lipkau[]]) + - moved default values to `JiraPS.psm1` so that they could be changed from outside of the module + _in preparation for using `Configuration`_ + - added logic for `Command parameters` > `Global defaults` > `module defaults` + - huge improvement to Unit Tests for `Invoke-JiraMethod` + - improved how HTTP responses > 399 are handled (`Resolve-ErrorWebResponse`) + - added `.EXTERNALHELP` to all functions (needed for Powershell v3) + - updated all functions which can use paging so that they use the new `-Paging` functionality + - `Get-JiraGroupMember` + - `Get-JiraIssue -Query` + - `Get-JiraIssue -Filter` + - `Get-JiraIssueComment` + - `Get-JiraVersion` + - added `-Sort` to influence the sorting of the results + - added default `PageSize` + - marked parameters which duplicate `[SupportsPaging]` parameter as **deprecated**. These are: + - `Get-JiraGroupMember -StartIndex` + - `Get-JiraGroupMember -MaxResults` + - `Get-JiraIssue -StartIndex` + - `Get-JiraIssue -MaxResults` + - added `-Headers` logic to `New-JiraSession` +- Updated `Pester` to version `4.3.1` (#289, [@lipkau[]]) +- Added full set of functions to manage Filter Permissions (#289, [@lipkau[]]) + - introduced a new Object _`[JiraPS.FilterPermission]`_ + - added property `FilterPermissions` to _`[JiraPS.Filter]`_ which is the list of _`[JiraPS.FilterPermission]`_ + of this Filter + - introduced a new Object _`[JiraPS.ProjectRole]`_ + - added `Add-JiraFilterPermission` + - added `Get-JiraFilterPermission` + - added `Remove-JiraFilterPermission` +- Added `-Id` parameter to `Remove-JiraFilter` (#288, [@lipkau[]]) + - by adding `-Id` parameter (which accepts a value by pipeline), this function can now + be used in scenarios like `Get-Content "listOfFilter.txt | Remover-JiraFilter` +- Changed logic of `Get-JiraUser` to return multiple results for a search (#272, [@lipkau[]]) + - `Get-JiraUser` was hardcoded to return only one result, while the API can return multiple results + - added parameter (`-MaxResults`, `-Skip`) to influence the results of the API + - _API has a limitation of 1000 items in response_ +- Added posts for homepage to the module's repository (#268, [@lipkau[]]) + - by maintaining the posts of the homepage in the module's repository, the new version of the + module can be deployed without manual changes to the homepage (thanks to #259 mentioned bellow) +- Improved handling of _Credentials_ (#271, [@lipkau[]]) + - added an empty _`[Credential]`_ object as default value + - `-Credentail "Username"` now uses the _`[String]`_ as value for the Username in the Credentials dialog + - this change is important for Powershell v3 compatibility +- Added missing interactions with _Filters_ (#266, [@lipkau[]]) + - added `-Favorite` to `Get-JiraFilter`, which lists all Filters marked as favorite by the user + - added `New-JiraFilter` + - added `Remove-JiraFilter` + - added `Set-JiraFilter` +- Added `Remove-JiraIssue` (#265, [@hmmwhatsthisdo[]]) +- Improved Build script (to deploy changes to the homepage) (#259, [@lipkau[]]) + +### BUG FIXES + +- Reverted `Add-JiraIssueAttachament` as JiraPS v2.7 broke it (#287, [@lipkau[]]) + - `JiraPS v2.7` tried to move the logic for generating the `multipart/form-data` to `Invoke-WebRequest` + - this is still desired, but as it broke the functionality of `Add-JiraIssueAttachament`, + this was rolled-back + - shall be fixed/re-implemented with [#284](https://github.com/AtlassianPS/JiraPS/issues/284) +- Fixed resolving of Remote Link (#286, [@lipkau[]]) + - function was using the wrong private function for converting the response to custom object +- Improved error handling for ErrorDetails and non-JSON/HTML responses (#277, [@hmmwhatsthisdo[]]) +- Fully support Powershell v3 (#273, [@lipkau[]]) +- Fixed parameter used in documentation but not in code (#263, [@lipkau[]]) + - added alias `-Issue` to `Get-JiraIssue -Key` as the documentation used it + - updated documentation to use the `-Key` parameter + +_Full list of issues can be found in [Milestone v2.8](https://github.com/AtlassianPS/JiraPS/milestone/11)._ + + + [@alexsuslin]: https://github.com/alexsuslin + [@axxelG]: https://github.com/axxelG + [@beaudryj]: https://github.com/beaudryj + [@brianbunke]: https://github.com/brianbunke + [@Clijsters]: https://github.com/Clijsters + [@colhal]: https://github.com/colhal + [@Dejulia489]: https://github.com/Dejulia489 + [@ebekker]: https://github.com/ebekker + [@hmmwhatsthisdo]: https://github.com/hmmwhatsthisdo + [@jkknorr]: https://github.com/jkknorr + [@kittholland]: https://github.com/kittholland + [@LiamLeane]: https://github.com/LiamLeane + [@lipkau]: https://github.com/lipkau + [@lukhase]: https://github.com/lukhase + [@padgers]: https://github.com/padgers + [@ThePSAdmin]: https://github.com/ThePSAdmin + [@WindowsAdmin92]: https://github.com/WindowsAdmin92 diff --git a/appveyor.yml b/appveyor.yml index b3835125..46d23e84 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -16,7 +16,7 @@ environment: matrix: fast_finish: true -version: 2.7.{build} +version: 2.8.{build} # Don't rebuild when I tag a release on GitHub skip_tags: true @@ -25,13 +25,6 @@ skip_tags: true # Or if I include "skip-tests" in the commit message skip_commits: message: /skip\-tests/ - files: - - .github/ - - .vscode/ - - README.md - - .gitattributes - - .gitignore - - .env* # PRs, by definition, don't change anything and therefore should not increment the version # To be fair, this is not important, and is really just AppVeyor enabling my pedantry diff --git a/docs/en-US/commands/Add-JiraFilterPermission.md b/docs/en-US/commands/Add-JiraFilterPermission.md new file mode 100644 index 00000000..ddfa6661 --- /dev/null +++ b/docs/en-US/commands/Add-JiraFilterPermission.md @@ -0,0 +1,231 @@ +--- +external help file: JiraPS-help.xml +Module Name: JiraPS +online version: https://atlassianps.org/docs/JiraPS/commands/Add-JiraFilterPermission/ +locale: en-US +schema: 2.0.0 +layout: documentation +permalink: /docs/JiraPS/commands/Add-JiraFilterPermission/ +--- +# Add-JiraFilterPermission + +## SYNOPSIS + +Share a Filter with other users. + +## SYNTAX + +### ByInputObject (Default) + +```powershell +Add-JiraFilterPermission [-Filter] [-Type] + [[-Value] ] [[-Credential] ] [-WhatIf] [-Confirm] + [] +``` + +### ById + +```powershell +Add-JiraFilterPermission [-Id] [-Type] [[-Value] ] + [[-Credential] ] [-WhatIf] [-Confirm] [] +``` + +## DESCRIPTION + +Share a Filter with other users, such as "Group", "Project", "ProjectRole", +"Authenticated" or "Global". + +## EXAMPLES + +### Example 1 + +```powershell +Add-JiraFilterPermission -Filter (Get-JiraFilter 12345) -Type "Global" +#------- +Add-JiraFilterPermission -Id 12345 -Type "Global" +``` + +Two methods of sharing Filter 12345 with everyone. + +### Example 2 + +```powershell +12345 | Add-JiraFilterPermission -Type "Authenticated" +``` + +Share Filter 12345 with authenticated users. + +_The Id could be read from a file._ + +### Example 3 + +```powershell +Get-JiraFilter 12345 | Add-JiraFilterPermission -Type "Group" -Value "administrators" +``` + +Share Filter 12345 only with users in the administrators groups. + +## PARAMETERS + +### -Filter + +Filter object to which the permission should be applied + +```yaml +Type: JiraPS.Filter +Parameter Sets: ByInputObject +Aliases: + +Required: True +Position: 0 +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -Id + +Id of the Filter to which the permission should be applied + +_Id can be passed over the pipeline when reading from a file._ + +```yaml +Type: UInt32[] +Parameter Sets: ById +Aliases: + +Required: True +Position: 0 +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -Type + +Type of the permission to add + +```yaml +Type: String +Parameter Sets: (All) +Aliases: +Accepted values: Group, Project, ProjectRole, Authenticated, Global + +Required: True +Position: 1 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Value + +Value for the Type of the permission. + +The Value differs per Type of the permission. + +Here is a table to know what Value to provide: +|Type |Value |Source | +|-------------|---------------------|----------------------------------------------------| +|Group |Name of the Group |Can be retrieved with `(Get-JiraGroup ...).Name` | +|Project |Id of the Project |Can be retrieved with `(Get-JiraProject ...).Id` | +|ProjectRole |Id of the ProjectRole|Can be retrieved with `(Get-JiraProjectRole ...).Id`| +|Authenticated| **must be null** | | +|Global | **must be null** | | + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: 2 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Credential + +Credentials to use to connect to JIRA. +If not specified, this function will use anonymous access. + +```yaml +Type: PSCredential +Parameter Sets: (All) +Aliases: + +Required: False +Position: 3 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WhatIf + +Shows what would happen if the cmdlet runs. +The cmdlet is not run. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: wi + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Confirm + +Prompts you for confirmation before running the cmdlet. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: cf + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters + +This cmdlet supports the common parameters: -Debug, -ErrorAction, +-ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, +-OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. +For more information, see about_CommonParameters +(). + +## INPUTS + +### [JiraPS.Filter] + +## OUTPUTS + +### [JiraPS.Filter] + +## NOTES + +This functions does not validate the input for `-Value`. +In case the value is invalid, unexpected or missing, the API will response with +an error. + +This function requires either the `-Credential` parameter to be passed or +a persistent JIRA session. +See `New-JiraSession` for more details. +If neither are supplied, this function will run with anonymous access to JIRA. + +## RELATED LINKS + +[Get-JiraFilter](../Get-JiraFilter/) + +[Get-JiraFilterPermission](../Get-JiraFilterPermission/) + +[Remove-JiraFilterPermission](../Remove-JiraFilterPermission/) diff --git a/docs/en-US/commands/Get-JiraFilter.md b/docs/en-US/commands/Get-JiraFilter.md index 15d72777..47d95c19 100644 --- a/docs/en-US/commands/Get-JiraFilter.md +++ b/docs/en-US/commands/Get-JiraFilter.md @@ -24,18 +24,26 @@ Get-JiraFilter [-Id] [-Credential ] [ ### ByInputObject ```powershell -Get-JiraFilter -InputObject [-Credential ] [] +Get-JiraFilter -InputObject [-Credential ] + [] +``` + +### MyFavorite + +```powershell +Get-JiraFilter -Favorite [-Credential ] [] ``` ## DESCRIPTION -This function returns information about a filter in JIRA, including the JQL syntax of the filter, its owner, and sharing status. +This function returns information about a filter in JIRA, including the JQL +syntax of the filter, its owner, and sharing status. This function is only capable of returning filters by their Filter ID. This is a limitation of JIRA's REST API. -The easiest way to obtain the ID of a filter is to load the filter in the "regular" Web view of JIRA, -then copy the ID from the URL of the page. +The easiest way to obtain the ID of a filter is to load the filter in the +"regular" Web view of JIRA, then copy the ID from the URL of the page. ## EXAMPLES @@ -55,6 +63,15 @@ $filterObject | Get-JiraFilter Gets the information of a filter by providing a filter object + +### EXAMPLE 3 + +```powershell +Get-JiraFilter -Favorite +``` + +Gets all filters makes as "favorite" by the user + ## PARAMETERS ### -Id @@ -89,6 +106,22 @@ Accept pipeline input: True (ByValue) Accept wildcard characters: False ``` +### -Favorite + +Fetch all filters marked as favorite by the user + +```yaml +Type: SwitchParameter +Parameter Sets: MyFavorite +Aliases: Favourite + +Required: True +Position: Named +Default value: False +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -Credential Credentials to use to connect to JIRA. @@ -108,12 +141,15 @@ Accept wildcard characters: False ### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. -For more information, see about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216). +This cmdlet supports the common parameters: -Debug, -ErrorAction, +-ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, +-OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. +For more information, see about_CommonParameters +(). ## INPUTS -### [JiraPS.Filter[]] / [String[]] +### [JiraPS.Filter] / [String] The filter to look up in JIRA. This can be a String (filter ID) or a JiraPS.Filter object. @@ -123,10 +159,21 @@ The filter to look up in JIRA. This can be a String (filter ID) or a JiraPS.Filt ## NOTES -This function requires either the `-Credential` parameter to be passed or a persistent JIRA session. +This function requires either the `-Credential` parameter to be passed or +a persistent JIRA session. See `New-JiraSession` for more details. If neither are supplied, this function will run with anonymous access to JIRA. -Remaining operations for `filter` have not yet been implemented in the module. - ## RELATED LINKS + +[New-JiraFilter](../New-JiraFilter/) + +[Set-JiraFilter](../Set-JiraFilter/) + +[Remove-JiraFilter](../Remove-JiraFilter/) + +[Add-JiraFilterPermission](../Add-JiraFilterPermission/) + +[Get-JiraFilterPermission](../Get-JiraFilterPermission/) + +[Remove-JiraFilterPermission](../Remove-JiraFilterPermission/) diff --git a/docs/en-US/commands/Get-JiraFilterPermission.md b/docs/en-US/commands/Get-JiraFilterPermission.md new file mode 100644 index 00000000..b9878f71 --- /dev/null +++ b/docs/en-US/commands/Get-JiraFilterPermission.md @@ -0,0 +1,140 @@ +--- +external help file: JiraPS-help.xml +Module Name: JiraPS +online version: https://atlassianps.org/docs/JiraPS/commands/Get-JiraFilterPermission/ +locale: en-US +schema: 2.0.0 +layout: documentation +permalink: /docs/JiraPS/commands/Get-JiraFilterPermission/ +--- +# Get-JiraFilterPermission + +## SYNOPSIS + +Fetch the permissions of a specific Filter. + +## SYNTAX + +### ByInputObject (Default) + +```powershell +Get-JiraFilterPermission [-Filter] [[-Credential] ] + [] +``` + +### ById + +```powershell +Get-JiraFilterPermission [-Id] [[-Credential] ] + [] +``` + +## DESCRIPTION + +This allows the user to retrieve all the sharing permissions set for a Filter. + +## EXAMPLES + +### Example 1 + +```powershell +Get-JiraFilterPermission -Filter (Get-JiraFilter 12345) +#------- +Get-JiraFilterPermission -Id 12345 +``` + +Two methods for retrieving the permissions set for Filter 12345. + +### Example 2 + +```powershell +12345 | Get-JiraFilterPermission +#------- +Get-JiraFilter 12345 | Add-JiraFilterPermission +``` + +Two methods for retrieve the permissions set for Filter 12345 by using the pipeline. + +_The Id could be read from a file._ + +## PARAMETERS + +### -Filter + +Filter object from which to retrieve the permissions + +```yaml +Type: JiraPS.Filter +Parameter Sets: ByInputObject +Aliases: + +Required: True +Position: 0 +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -Id + +Id of the Filter from which to retrieve the permissions + +```yaml +Type: UInt32[] +Parameter Sets: ById +Aliases: + +Required: True +Position: 0 +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -Credential + +Credentials to use to connect to JIRA. +If not specified, this function will use anonymous access. + +```yaml +Type: PSCredential +Parameter Sets: (All) +Aliases: + +Required: False +Position: 1 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters + +This cmdlet supports the common parameters: -Debug, -ErrorAction, +-ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, +-OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. +For more information, see about_CommonParameters +(). + +## INPUTS + +### [JiraPS.Filter] + +## OUTPUTS + +### [JiraPS.Filter] + +## NOTES + +This function requires either the `-Credential` parameter to be passed or +a persistent JIRA session. +See `New-JiraSession` for more details. +If neither are supplied, this function will run with anonymous access to JIRA. + +## RELATED LINKS + +[Get-JiraFilter](../Get-JiraFilter/) + +[Add-JiraFilterPermission](../Add-JiraFilterPermission/) + +[Remove-JiraFilterPermission](../Remove-JiraFilterPermission/) diff --git a/docs/en-US/commands/Get-JiraGroupMember.md b/docs/en-US/commands/Get-JiraGroupMember.md index 2606fac4..b618d8d9 100644 --- a/docs/en-US/commands/Get-JiraGroupMember.md +++ b/docs/en-US/commands/Get-JiraGroupMember.md @@ -16,8 +16,8 @@ Returns members of a given group in JIRA ## SYNTAX ```powershell -Get-JiraGroupMember [-Group] [[-StartIndex] ] [[-MaxResults] ] - [[-Credential] ] [] +Get-JiraGroupMember [-Group] [[-IncludeInactive] ] [[-StartIndex] ] [[-MaxResults] ] + [[PageSize] ] [-IncludeTotalCount] [-Skip ] [-First ] [[-Credential] ] [] ``` ## DESCRIPTION @@ -61,14 +61,55 @@ Accept pipeline input: True (ByPropertyName, ByValue) Accept wildcard characters: False ``` +### -IncludeInactive + +Include inactive users in the results. + +By default they are not shown. + +```yaml +Type: Switch +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: False +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -PageSize + +Maximum number of results to fetch per call. + +This setting can be tuned to get better performance according to the load on the server. + +> Warning: too high of a PageSize can cause a timeout on the request. + +```yaml +Type: UInt32 +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: 25 +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -StartIndex +> NOTE: This parameter has been marked as deprecated and will be removed with the next major release. +> Use `-Skip` instead. + Index of the first user to return. This can be used to "page" through users in a large group or a slow connection. ```yaml -Type: Int32 +Type: UInt32 Parameter Sets: (All) Aliases: @@ -81,12 +122,15 @@ Accept wildcard characters: False ### -MaxResults +> NOTE: This parameter has been marked as deprecated and will be removed with the next major release. +> Use `-First` instead. + Maximum number of results to return. By default, all users will be returned. ```yaml -Type: Int32 +Type: UInt32 Parameter Sets: (All) Aliases: @@ -97,6 +141,58 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -IncludeTotalCount + +Causes an extra output of the total count at the beginning. + +Note this is actually a uInt64, but with a custom string representation. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Skip + +Controls how many things will be skipped before starting output. + +Defaults to 0. + +```yaml +Type: UInt64 +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: 0 +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -First + +Indicates how many items to return. + +```yaml +Type: UInt64 +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: 18446744073709551615 +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -Credential Credentials to use to connect to JIRA. diff --git a/docs/en-US/commands/Get-JiraIssue.md b/docs/en-US/commands/Get-JiraIssue.md index b6e0e0ec..5a01c7c9 100644 --- a/docs/en-US/commands/Get-JiraIssue.md +++ b/docs/en-US/commands/Get-JiraIssue.md @@ -18,27 +18,29 @@ Returns information about an issue in JIRA. ### ByIssueKey (Default) ```powershell -Get-JiraIssue [-Key] [-Credential ] [] +Get-JiraIssue [-Key] [-IncludeTotalCount] [-Skip ] [-First ] + [-Credential ] [] ``` ### ByInputObject ```powershell -Get-JiraIssue [-InputObject] [-Credential ] [] +Get-JiraIssue [-InputObject] [-IncludeTotalCount] [-Skip ] [-First ] + [-Credential ] [] ``` ### ByJQL ```powershell -Get-JiraIssue -Query [-StartIndex ] [-MaxResults ] [-PageSize ] - [-Credential ] [] +Get-JiraIssue -Query [-StartIndex ] [-MaxResults ] [[PageSize] ] + [-IncludeTotalCount] [-Skip ] [-First ] [-Credential ] [] ``` ### ByFilter ```powershell -Get-JiraIssue -Filter [-StartIndex ] [-MaxResults ] [-PageSize ] - [-Credential ] [] +Get-JiraIssue -Filter [-StartIndex ] [-MaxResults ] [[PageSize] ] + [-IncludeTotalCount] [-Skip ] [-First ] [-Credential ] [] ``` ## DESCRIPTION @@ -173,12 +175,15 @@ Accept wildcard characters: False ### -StartIndex +> NOTE: This parameter has been marked as deprecated and will be removed with the next major release. +> Use `-Skip` instead. + Index of the first issue to return. This can be used to "page" through issues in a large collection or a slow connection. ```yaml -Type: Int32 +Type: UInt32 Parameter Sets: ByJQL, ByFilter Aliases: @@ -191,12 +196,15 @@ Accept wildcard characters: False ### -MaxResults +> NOTE: This parameter has been marked as deprecated and will be removed with the next major release. +> Use `-First` instead. + Maximum number of results to return. By default, all issues will be returned. ```yaml -Type: Int32 +Type: UInt32 Parameter Sets: ByJQL, ByFilter Aliases: @@ -218,13 +226,65 @@ but if the REST calls take a long time, try playing with different values here. ```yaml -Type: Int32 +Type: UInt32 Parameter Sets: ByJQL, ByFilter Aliases: Required: False Position: Named -Default value: 50 +Default value: 25 +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -IncludeTotalCount + +Causes an extra output of the total count at the beginning. + +Note this is actually a uInt64, but with a custom string representation. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Skip + +Controls how many things will be skipped before starting output. + +Defaults to 0. + +```yaml +Type: UInt64 +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: 0 +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -First + +Indicates how many items to return. + +```yaml +Type: UInt64 +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: 18446744073709551615 Accept pipeline input: False Accept wildcard characters: False ``` diff --git a/docs/en-US/commands/Get-JiraUser.md b/docs/en-US/commands/Get-JiraUser.md index 7c04aa99..76d9d06c 100644 --- a/docs/en-US/commands/Get-JiraUser.md +++ b/docs/en-US/commands/Get-JiraUser.md @@ -24,7 +24,7 @@ Get-JiraUser [-Credential ] [] ### ByUserName ```powershell -Get-JiraUser [-UserName] [-IncludeInactive] [-Credential ] [] +Get-JiraUser [-UserName] [-IncludeInactive] [[-MaxResults] ] [[-Skip] ] [-Credential ] [] ``` ### ByInputObject @@ -113,6 +113,42 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -MaxResults + +Maximum number of user to be returned. + +> The API does not allow for any value higher than 1000. + +```yaml +Type: UInt32 +Parameter Sets: ByUserName +Aliases: + +Required: False +Position: Named +Default value: 50 +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Skip + +Controls how many objects will be skipped before starting output. + +Defaults to 0. + +```yaml +Type: UInt64 +Parameter Sets: ByUserName +Aliases: + +Required: False +Position: Named +Default value: 0 +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -Credential Credentials to use to connect to JIRA. diff --git a/docs/en-US/commands/Get-JiraVersion.md b/docs/en-US/commands/Get-JiraVersion.md index 5c282363..2450d0e1 100644 --- a/docs/en-US/commands/Get-JiraVersion.md +++ b/docs/en-US/commands/Get-JiraVersion.md @@ -18,25 +18,31 @@ This function returns information about a JIRA Project's Version ### byId (Default) ```powershell -Get-JiraVersion -Id [-Credential ] [] +Get-JiraVersion -Id [-PageSize ] [-IncludeTotalCount] [-Skip ] + [-First ] [-Credential ] [] ``` ### byInputVersion ```powershell -Get-JiraVersion [-InputVersion] [-Credential ] [] +Get-JiraVersion [-InputVersion] [-PageSize ] [-IncludeTotalCount] + [-Skip ] [-First ] [-Credential ] [] ``` ### byProject ```powershell -Get-JiraVersion [-Project] [-Name ] [-Credential ] [] +Get-JiraVersion [-Project] [-Name ] [[-Sort] ] + [-PageSize ] [-IncludeTotalCount] [-Skip ] [-First ] + [-Credential ] [] ``` ### byInputProject ```powershell -Get-JiraVersion [-InputProject] [-Name ] [-Credential ] [] +Get-JiraVersion [-InputProject] [-Name ] [[-Sort] ] + [-PageSize ] [-IncludeTotalCount] [-Skip ] [-First ] + [-Credential ] [] ``` ## DESCRIPTION @@ -101,7 +107,7 @@ Accept wildcard characters: False A Version object to search for ```yaml -Type: Object +Type: JiraPS.Version Parameter Sets: byInputVersion Aliases: @@ -133,7 +139,7 @@ Accept wildcard characters: False A Project Object to search ```yaml -Type: Object +Type: JiraPS.Project Parameter Sets: byInputProject Aliases: @@ -160,6 +166,101 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### Sort + +Define the order in which the versions should be sorted before returning. + +Possible values are: + +* sequence +* name +* startDate +* releaseDate + +```yaml +Type: String +Parameter Sets: byProject, byInputProject +Aliases: Versions + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -PageSize + +Maximum number of results to fetch per call. + +This setting can be tuned to get better performance according to the load on the server. + +> Warning: too high of a PageSize can cause a timeout on the request. + +```yaml +Type: UInt32 +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: 25 +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -IncludeTotalCount + +Causes an extra output of the total count at the beginning. + +Note this is actually a uInt64, but with a custom string representation. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Skip + +Controls how many things will be skipped before starting output. + +Defaults to 0. + +```yaml +Type: UInt64 +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: 0 +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -First + +Indicates how many items to return. + +```yaml +Type: UInt64 +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: 18446744073709551615 +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -Credential Credentials to use to connect to JIRA. diff --git a/docs/en-US/commands/Invoke-JiraMethod.md b/docs/en-US/commands/Invoke-JiraMethod.md index 47b5e548..b1f4662b 100644 --- a/docs/en-US/commands/Invoke-JiraMethod.md +++ b/docs/en-US/commands/Invoke-JiraMethod.md @@ -17,8 +17,9 @@ Invoke a specific call to a Jira REST Api endpoint ```powershell Invoke-JiraMethod [-URI] [[-Method] ] [[-Body] ] [-RawBody] - [[-Headers] ] [[-InFile] ] [[-OutFile] ] [-StoreSession] - [[-Credential] ] [[-Cmdlet] ] [] + [[-Headers] ] [[-GetParameter] ] [[-Paging] ] [[-InFile] ] + [[-OutFile] ] [-StoreSession] [[-OutputType] ] [[-Credential] ] + [[-Cmdlet] ] [] ``` ## DESCRIPTION @@ -240,6 +241,40 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -GetParameter + +Key-Value pair of the Headers to be used. + +```yaml +Type: Hashtable +Parameter Sets: (All) +Aliases: + +Required: False +Position: 4 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Paging + +Use paging on the results. + +More about paging: + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: 4 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -InFile Path to a file that will be uploaded with a multipart/form-data request. @@ -252,7 +287,7 @@ Parameter Sets: (All) Aliases: Required: False -Position: 4 +Position: 5 Default value: None Accept pipeline input: False Accept wildcard characters: False @@ -270,7 +305,7 @@ Parameter Sets: (All) Aliases: Required: False -Position: 5 +Position: 6 Default value: None Accept pipeline input: False Accept wildcard characters: False @@ -286,7 +321,25 @@ Parameter Sets: (All) Aliases: Required: False -Position: Named +Position: 7 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -OutputType + +Name of the data type that is expected to be returned. + +Currently only used in combination with `-Paging` + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: 8 Default value: None Accept pipeline input: False Accept wildcard characters: False @@ -305,7 +358,7 @@ Parameter Sets: (All) Aliases: Required: False -Position: 6 +Position: 9 Default value: None Accept pipeline input: False Accept wildcard characters: False @@ -321,13 +374,66 @@ Parameter Sets: (All) Aliases: Required: False -Position: 7 +Position: 10 Default value: None Accept pipeline input: False Accept wildcard characters: False ``` +### -IncludeTotalCount + +Causes an extra output of the total count at the beginning. + +Note this is actually a uInt64, but with a custom string representation. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Skip + +Controls how many things will be skipped before starting output. + +Defaults to 0. + +```yaml +Type: UInt64 +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: 0 +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -First + +Indicates how many items to return. + +```yaml +Type: UInt64 +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: 18446744073709551615 +Accept pipeline input: False +Accept wildcard characters: False +``` + ### CommonParameters + This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216). diff --git a/docs/en-US/commands/New-JiraFilter.md b/docs/en-US/commands/New-JiraFilter.md new file mode 100644 index 00000000..d36c5445 --- /dev/null +++ b/docs/en-US/commands/New-JiraFilter.md @@ -0,0 +1,202 @@ +--- +external help file: JiraPS-help.xml +Module Name: JiraPS +online version: https://atlassianps.org/docs/JiraPS/commands/New-JiraFilter/ +locale: en-US +schema: 2.0.0 +layout: documentation +permalink: /docs/JiraPS/commands/New-JiraFilter/ +--- +# New-JiraFilter + +## SYNOPSIS + +Create a new Jira filter. + +## SYNTAX + +```powershell +New-JiraFilter -Name [-Description ] -JQL [-Favorite] [-Credential ] + [-WhatIf] [-Confirm] [] +``` + +## DESCRIPTION + +Create a new Jira filter. + +## EXAMPLES + +### Example 1 + +```powershell +New-JiraFilter -Name "My Bugs" -JQL "type = Bug and assignee = currentuser()" +``` + +Creates a new filter named "My Bugs" + +### Example 2 + +```powershell +New-JiraFilter -Name "My Bugs" -JQL "type = Bug and assignee = currentuser()" -Favorite +``` + +Creates a new filter named "My Bugs" and stores it as favorite + +### Example 3 + +```powershell +$splatNewFilter = @{ + Name = "My Bugs" + Description = "collections of bugs assigned to me" + JQL = "type = Bug and assignee = currentuser()" + Favorite = $true +} +New-JiraFilter @splatNewFilter +``` + +Creates a new filter named "My Bugs" using splatting + +## PARAMETERS + +### -Name + +Name of the filter. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -Description + +Description for the filter. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -JQL + +JQL string which the filter uses for matching issues. + +More about JQL at + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -Favorite + +Make this new filter a favorite of the user. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: Favourite + +Required: False +Position: Named +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + +### -Credential + +Credentials to use to connect to JIRA. +If not specified, this function will use anonymous access. + +```yaml +Type: PSCredential +Parameter Sets: (All) +Aliases: + +Required: False +Position: 2 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WhatIf + +Shows what would happen if the cmdlet runs. +The cmdlet is not run. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: wi + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Confirm + +Prompts you for confirmation before running the cmdlet. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: cf + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters + +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. +For more information, see about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### [System.String] + +## OUTPUTS + +### [JiraPS.Filter] + +## NOTES + +This function requires either the `-Credential` parameter to be passed or a persistent JIRA session. +See `New-JiraSession` for more details. +If neither are supplied, this function will run with anonymous access to JIRA. + +## RELATED LINKS + +[Get-JiraFilter](../Get-JiraFilter/) + +[Set-JiraFilter](../Set-JiraFilter/) + +[Remove-JiraFilter](../Remove-JiraFilter/) diff --git a/docs/en-US/commands/Remove-JiraFilter.md b/docs/en-US/commands/Remove-JiraFilter.md new file mode 100644 index 00000000..66dd3b5c --- /dev/null +++ b/docs/en-US/commands/Remove-JiraFilter.md @@ -0,0 +1,198 @@ +--- +external help file: JiraPS-help.xml +Module Name: JiraPS +online version: https://atlassianps.org/docs/JiraPS/commands/Remove-JiraFilter/ +locale: en-US +schema: 2.0.0 +layout: documentation +permalink: /docs/JiraPS/commands/Remove-JiraFilter/ +--- +# Remove-JiraFilter + +## SYNOPSIS + +Removes an existing filter. + +## SYNTAX + +### byInputObject (Default) + +```powershell +Remove-JiraFilter [-InputObject] [-WhatIf] [-Confirm] + [] +``` + +### byId (Default) + +```powershell +Remove-JiraFilter [-Id] [-WhatIf] [-Confirm] [] +``` + +## DESCRIPTION + +This function will remove a filter from Jira. +Deleting a filter removed is permanently from Jira. + +## EXAMPLES + +### Example 1 + +```powershell +Remove-JiraFilter -InputObject (Get-JiraFilter "12345") +``` + +Removes the filter `12345` from Jira. + +### Example 2 + +```powershell +$filter = Get-JiraFilter "12345", "98765" +Remove-JiraFilter -InputObject $filter +``` + +Removes two filters (`12345` and `98765`) from Jira. + +### Example 3 + +```powershell +Get-JiraFilter "12345", "98765" | Remove-JiraFilter +``` + +Removes two filters (`12345` and `98765`) from Jira. + +### Example 4 + +```powershell +Get-JiraFilter -Favorite | Remove-JiraFilter -Confirm +``` + +Asks for each favorite filter confirmation to delete it. + +### Example 5 + +```powershell +$listOfFilters = 1,2,3,4 +$listOfFilters | Remove-JiraFilter +``` + +Remove filters with id "1", "2", "3" and "4". + +This input allows for the ID of the filters to be stored in an array and passed +to the command. (eg: `Get-Content` from a file with the ids) + +## PARAMETERS + +### -InputObject + +Filter object to be deleted. + +Object can be retrieved with `Get-JiraFilter` + +```yaml +Type: JiraPS.Filter +Parameter Sets: ByInputObject +Aliases: + +Required: True +Position: 0 +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -Id + +Id of the filter to be deleted. + +Accepts integers over the pipeline. + +```yaml +Type: UInt32[] +Parameter Sets: ById +Aliases: + +Required: True +Position: 0 +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -Credential + +Credentials to use to connect to JIRA. +If not specified, this function will use anonymous access. + +```yaml +Type: PSCredential +Parameter Sets: (All) +Aliases: + +Required: False +Position: 2 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WhatIf + +Shows what would happen if the cmdlet runs. +The cmdlet is not run. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: wi + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Confirm + +Prompts you for confirmation before running the cmdlet. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: cf + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters + +This cmdlet supports the common parameters: -Debug, -ErrorAction, +-ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, +-OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. +For more information, see about_CommonParameters +(). + +## INPUTS + +### [JiraPS.Filter] + +## OUTPUTS + +## NOTES + +This function requires either the `-Credential` parameter to be passed or +a persistent JIRA session. +See `New-JiraSession` for more details. +If neither are supplied, this function will run with anonymous access to JIRA. + +## RELATED LINKS + +[Get-JiraFilter](../Get-JiraFilter/) + +[New-JiraFilter](../New-JiraFilter/) + +[Set-JiraFilter](../Set-JiraFilter/) diff --git a/docs/en-US/commands/Remove-JiraFilterPermission.md b/docs/en-US/commands/Remove-JiraFilterPermission.md new file mode 100644 index 00000000..f60384b7 --- /dev/null +++ b/docs/en-US/commands/Remove-JiraFilterPermission.md @@ -0,0 +1,183 @@ +--- +external help file: JiraPS-help.xml +Module Name: JiraPS +online version: https://atlassianps.org/docs/JiraPS/commands/Remove-JiraFilterPermission/ +locale: en-US +schema: 2.0.0 +layout: documentation +permalink: /docs/JiraPS/commands/Remove-JiraFilterPermission/ +--- +# Remove-JiraFilterPermission + +## SYNOPSIS + +Remove a permission of a Filter + +## SYNTAX + +### ByFilterId (Default) + +```powershell +Remove-JiraFilterPermission [-Filter] [[-Credential] ] + [-WhatIf] [-Confirm] [] +``` + +### ByFilterObject + +```powershell +Remove-JiraFilterPermission [-FilterId] [-PermissionId] + [[-Credential] ] [-WhatIf] [-Confirm] [] +``` + +## DESCRIPTION + +Remove a sharing permission of a Filter. + +## EXAMPLES + +### Example 1 + +```powershell +Remove-JiraFilterPermission -FilterId 11822 -PermissionId 1111, 2222 +``` + +Remove two share permissions of Filter with ID '11822' + +### Example 1 + +```powershell +Get-JiraFilter 11822 | Get-JiraFilterPermission | Remove-JiraFilterPermission +``` + +Remove all permissions of Filter 11822 + +## PARAMETERS + +### -Filter + +Object of the Filter from which to remove a permission. + +```yaml +Type: JiraPS.Filter +Parameter Sets: ByFilterObject +Aliases: + +Required: True +Position: 0 +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -FilterId + +Id of the Filter from which to remove a permission. + +```yaml +Type: UInt32 +Parameter Sets: ByFilterId +Aliases: + +Required: True +Position: 0 +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -PermissionId + +List of id's of the permissions to remove. + +```yaml +Type: UInt32[] +Parameter Sets: ByFilterId +Aliases: + +Required: True +Position: 1 +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -Credential + +Credentials to use to connect to JIRA. +If not specified, this function will use anonymous access. + +```yaml +Type: PSCredential +Parameter Sets: (All) +Aliases: + +Required: False +Position: 1 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WhatIf + +Shows what would happen if the cmdlet runs. +The cmdlet is not run. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: wi + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Confirm + +Prompts you for confirmation before running the cmdlet. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: cf + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters + +This cmdlet supports the common parameters: -Debug, -ErrorAction, +-ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, +-OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. +For more information, see about_CommonParameters +(). + +## INPUTS + +### System.Object + +## OUTPUTS + +### System.Object + +## NOTES + +This function requires either the `-Credential` parameter to be passed or +a persistent JIRA session. +See `New-JiraSession` for more details. +If neither are supplied, this function will run with anonymous access to JIRA. + +## RELATED LINKS + +[Get-JiraFilter](../Get-JiraFilter/) + +[Add-JiraFilterPermission](../Add-JiraFilterPermission/) + +[Get-JiraFilterPermission](../Get-JiraFilterPermission/) diff --git a/docs/en-US/commands/Remove-JiraIssue.md b/docs/en-US/commands/Remove-JiraIssue.md new file mode 100644 index 00000000..f4e3c64e --- /dev/null +++ b/docs/en-US/commands/Remove-JiraIssue.md @@ -0,0 +1,208 @@ +--- +external help file: JiraPS-help.xml +Module Name: JiraPS +online version: https://atlassianps.org/docs/JiraPS/commands/Remove-JiraIssue/ +locale: en-US +schema: 2.0.0 +layout: documentation +permalink: /docs/JiraPS/commands/Remove-JiraIssue/ +--- + +# Remove-JiraIssue + +## SYNOPSIS + +Removes an existing issue from JIRA. + +## SYNTAX + +### ByInputObject (Default) + +```powershell +Remove-JiraIssue [-InputObject] [-IncludeSubTasks] [[-Credential] ] [-Force] [-WhatIf] + [-Confirm] [] +``` + +### ByIssueId + +```powershell +Remove-JiraIssue [-IssueId] [-IncludeSubTasks] [[-Credential] ] [-Force] [-WhatIf] + [-Confirm] [] +``` + +## DESCRIPTION + +This function will remove an issue from Jira. +Deleting an issue removes it permanently from JIRA, including all of its comments and attachments. + +If you have completed an issue, it should usually be resolved or closed - not deleted. + +If an issue includes sub-tasks, these are deleted as well. + +## EXAMPLES + +### EXAMPLE 1 + +```powershell +Remove-JiraIssue -IssueId ABC-123 +``` + +Removes issue \[ABC-123\] from JIRA. + +### EXAMPLE 2 + +```powershell +Remove-JiraIssue -IssueId ABC-124 -IncludeSubTasks +``` + +Removes issue \[ABC-124\] from JIRA, including any subtasks therein. + +### EXAMPLE 3 + +```powershell +Get-JiraIssue -Query "Project = ABC AND label = NeedsDeletion" | Remove-JiraIssue -IncludeSubTasks +``` + +Removes all issues from project ABC (including their subtasks) that have the label "NeedsDeletion". + +## PARAMETERS + +### -InputObject + +One or more issues to delete, specified as `JiraPS.Issue` objects (e.g. from `Get-JiraIssue`) + +```yaml +Type: Object[] +Parameter Sets: ByInputObject +Aliases: + +Required: True +Position: 1 +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -IssueId + +One or more issues to delete, either: + +* Issue Keys (e.g. "TEST-1234") +* Numerical issue IDs + +```yaml +Type: String[] +Parameter Sets: ByInputObject +Aliases: + +Required: True +Position: 1 +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -IncludeSubTasks + +Removes any subtasks associated with the issue(s) to be deleted. + +If the issue has no subtasks, this parameter is ignored. If the issue has subtasks and this parameter is missing, then the issue will not be deleted and an error will be returned. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: False +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Credential + +Credentials to use to connect to JIRA. + +```yaml +Type: PSCredential +Parameter Sets: (All) +Aliases: + +Required: False +Position: 2 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Force + +Suppress user confirmation. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: False +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WhatIf + +Shows what would happen if the cmdlet runs. +The cmdlet is not run. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: wi + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Confirm + +Prompts you for confirmation before running the cmdlet. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: cf + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters + +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. +For more information, see about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### [JiraPS.Issue] / [String] + +## OUTPUTS + +### Output (if any) + +## NOTES + +If the issue has subtasks you must include the parameter IncludeSubTasks to delete the issue. You cannot delete an issue without its subtasks also being deleted. + +This function requires either the \`-Credential\` parameter to be passed or a persistent JIRA session. +See \`New-JiraSession\` for more details. + +## RELATED LINKS diff --git a/docs/en-US/commands/Set-JiraFilter.md b/docs/en-US/commands/Set-JiraFilter.md new file mode 100644 index 00000000..1ceb28cc --- /dev/null +++ b/docs/en-US/commands/Set-JiraFilter.md @@ -0,0 +1,226 @@ +--- +external help file: JiraPS-help.xml +Module Name: JiraPS +online version: https://atlassianps.org/docs/JiraPS/commands/Set-JiraFilter/ +locale: en-US +schema: 2.0.0 +layout: documentation +permalink: /docs/JiraPS/commands/Set-JiraFilter/ +--- +# Set-JiraFilter + +## SYNOPSIS + +Make changes to an existing Filter. + +## SYNTAX + +```powershell +Set-JiraFilter [-InputObject] [[-Name] ] [[-Description] ] [[-JQL] ] + [-Favorite] [[-Credential] ] [-WhatIf] [-Confirm] [] +``` + +## DESCRIPTION + +Make changes to an existing Filter. + +If no changing parameter is provided, no action will be performed. + +## EXAMPLES + +### Example 1 + +```powershell +Set-JiraFilter -InputObject (Get-JiraFilter "12345") -Name "NewName" +``` + +Changes the name of filter "12345" to "NewName" + +### Example 2 + +```powershell +$filterData = @{ + InputObject = Get-JiraFilter "12345" + Description = "A new description" + JQL = "project = TV AND type = Bug" + Favorite = $true +} +Set-JiraFilter @filterData +``` + +Changes the description and JQL of filter "12345" and make it a favorite + +### Example 3 + +```powershell +Get-JiraFilter -Favorite | + Where name -notlike "My*" | + Set-JiraFilter -Favorite $false +``` + +Remove all favorite filters where the name does not start with "My" + +## PARAMETERS + +### -InputObject + +Filter object to be changed. + +Object can be retrieved with `Get-JiraFilter` + +```yaml +Type: JiraPS.Filter +Parameter Sets: (All) +Aliases: + +Required: True +Position: 0 +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -Name + +New value for the filter's Name. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: 1 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Description + +New value for the filter's Description. + +Can be an empty string. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: 2 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -JQL + +New value for the filter's JQL string which the filter uses for matching issues. + +More about JQL at + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: 3 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Favorite + +Boolean flag if the filter should be marked as favorite for the user. + +```yaml +Type: Boolean +Parameter Sets: (All) +Aliases: Favourite + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Credential + +Credentials to use to connect to JIRA. +If not specified, this function will use anonymous access. + +```yaml +Type: PSCredential +Parameter Sets: (All) +Aliases: + +Required: False +Position: 2 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WhatIf + +Shows what would happen if the cmdlet runs. +The cmdlet is not run. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: wi + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Confirm + +Prompts you for confirmation before running the cmdlet. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: cf + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters + +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. +For more information, see about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### [JiraPS.Filter] / [String] + +## OUTPUTS + +### [JiraPS.Filter] + +## NOTES + +This function requires either the `-Credential` parameter to be passed or a persistent JIRA session. +See `New-JiraSession` for more details. +If neither are supplied, this function will run with anonymous access to JIRA. + +## RELATED LINKS + +[Get-JiraFilter](../Get-JiraFilter/) + +[New-JiraFilter](../New-JiraFilter/) + +[Remove-JiraFilter](../Remove-JiraFilter/)