Public/Get-JiraIssue.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
function Get-JiraIssue {
    <#
    .SYNOPSIS
        Returns information about an issue in JIRA.
    .DESCRIPTION
        This function obtains references to issues in JIRA.
 
        This function can be used to directly query JIRA for a specific issue key or internal issue ID. It can also be used to query JIRA for issues matching a specific criteria using JQL (Jira Query Language).
 
        For more details on JQL syntax, see this articla from Atlassian: https://confluence.atlassian.com/display/JIRA/Advanced+Searching
 
        Output from this function can be piped to various other functions in this module, including Set-JiraIssue, Add-JiraIssueComment, and Invoke-JiraIssueTransition.
    .EXAMPLE
        Get-JiraIssue -Key TEST-001
        This example returns a reference to JIRA issue TEST-001.
    .EXAMPLE
        Get-JiraIssue "TEST-002" | Add-JiraIssueComment "Test comment from PowerShell"
        This example illustrates pipeline use from Get-JiraIssue to Add-JiraIssueComment.
    .EXAMPLE
        Get-JiraIssue -Query 'project = "TEST" AND created >= -5d'
        This example illustrates using the Query parameter and JQL syntax to query Jira for matching issues.
    .EXAMPLE
        Get-JiraIssue -InputObject $oldIssue
        This example illustrates how to get an update of an issue from an old result of Get-JiraIssue stored in $oldIssue.
    .EXAMPLE
        Get-JiraFilter -Id 12345 | Get-JiraIssue
        This example retrieves all issues that match the criteria in the saved fiilter with id 12345.
    .INPUTS
       This function can accept JiraPS.Issue objects, Strings, or Objects via the pipeline.
 
       * If a JiraPS.Issue object is passed, this function returns a new reference to the same issue.
        * If a String is passed, this function searches for an issue with that issue key or internal ID.
        * If an Object is passed, this function invokes its ToString() method and treats it as a String.
    .OUTPUTS
       This function outputs the JiraPS.Issue object retrieved.
    .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.
    #>

    [CmdletBinding(DefaultParameterSetName = 'ByIssueKey')]
    param(
        # Key of the issue to search for.
        [Parameter( Position = 0, Mandatory, ParameterSetName = 'ByIssueKey' )]
        [ValidateNotNullOrEmpty()]
        [String[]]
        $Key,

        # Object of an issue to search for.
        [Parameter( Position = 0, Mandatory, ParameterSetName = 'ByInputObject' )]
        [ValidateNotNullOrEmpty()]
        [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,
                        $_
                    )
                    $errorItem.ErrorDetails = "Wrong object type provided for Issue. Expected [JiraPS.Issue] or [String], but was $($_.GetType().Name)"
                    $PSCmdlet.ThrowTerminatingError($errorItem)
                }
                else {
                    return $true
                }
            }
        )]
        [Object[]]
        $InputObject,
        <#
          #ToDo:Deprecate
          This is not necessary if $Key uses ValueFromPipelineByPropertyName
          #ToDo:CustomClass
          Once we have custom classes, this check can be done with Type declaration
        #>


        # JQL query for which to search for.
        [Parameter( Mandatory, ParameterSetName = 'ByJQL' )]
        [Alias('JQL')]
        [String]
        $Query,

        # Object of an existing JIRA filter from which the results will be returned.
        [Parameter( Mandatory, ParameterSetName = 'ByFilter' )]
        [ValidateNotNullOrEmpty()]
        [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,
                        $_
                    )
                    $errorItem.ErrorDetails = "Wrong object type provided for Filter. Expected [JiraPS.Filter] or [String], but was $($_.GetType().Name)"
                    $PSCmdlet.ThrowTerminatingError($errorItem)
                    <#
                      #ToDo:CustomClass
                      Once we have custom classes, this check can be done with Type declaration
                    #>

                }
                else {
                    return $true
                }
            }
        )]
        [Object]
        $Filter,

        # Index of the first issue to return. This can be used to "page" through
        # issues in a large collection or a slow connection.
        [Parameter( ParameterSetName = 'ByJQL' )]
        [Parameter( ParameterSetName = 'ByFilter' )]
        [Int]
        $StartIndex = 0,

        # Maximum number of results to return. By default, all issues will be
        # returned.
        [Parameter( ParameterSetName = 'ByJQL' )]
        [Parameter( ParameterSetName = 'ByFilter' )]
        [Int]
        $MaxResults = 0,

        # How many issues should be returned per call to JIRA. This parameter
        # only has effect if $MaxResults is not provided or set to 0. Normally,
        # you should not need to adjust this parameter, but if the REST calls
        # take a long time, try playing with different values here.
        [Parameter( ParameterSetName = 'ByJQL' )]
        [Parameter( ParameterSetName = 'ByFilter' )]
        [Int]
        $PageSize = 50,

        # Credentials to use to connect to JIRA.
        # If not specified, this function will use anonymous access.
        [PSCredential]
        $Credential
    )

    begin {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started"

        $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}"
    }

    process {
        Write-DebugMessage "[$($MyInvocation.MyCommand.Name)] ParameterSetName: $($PsCmdlet.ParameterSetName)"
        Write-DebugMessage "[$($MyInvocation.MyCommand.Name)] PSBoundParameters: $($PSBoundParameters | Out-String)"

        switch ($PSCmdlet.ParameterSetName) {
            'ByIssueKey' {
                foreach ($_key in $Key) {
                    Write-Verbose "[$($MyInvocation.MyCommand.Name)] Processing [$_key]"
                    Write-Debug "[$($MyInvocation.MyCommand.Name)] Processing `$_key [$_key]"

                    $parameter = @{
                        URI        = $resourceURi -f $_key
                        Method     = "GET"
                        Credential = $Credential
                    }
                    Write-Debug "[$($MyInvocation.MyCommand.Name)] Invoking JiraMethod with `$parameter"
                    $result = Invoke-JiraMethod @parameter

                    Write-Output (ConvertTo-JiraIssue -InputObject $result)
                }
            }
            'ByInputObject' {
                # Write-Warning "[$($MyInvocation.MyCommand.Name)] The parameter '-InputObject' has been marked as deprecated."
                foreach ($_issue in $InputObject) {
                    Write-Verbose "[$($MyInvocation.MyCommand.Name)] Processing [$_issue]"
                    Write-Debug "[$($MyInvocation.MyCommand.Name)] Processing `$_issue [$_issue]"

                    Write-Output (Get-JiraIssue -Key $_issue.Key -Credential $Credential)
                }
            }
            'ByJQL' {
                $escapedQuery = ConvertTo-URLEncoded $Query

                $parameter = @{
                    URI        = $searchURi -f $escapedQuery, $StartIndex, $MaxResults
                    Method     = "GET"
                    Credential = $Credential
                }
                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
                    }
                }
            }
            '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)
                }
                else {
                    Write-Output (Get-JiraIssue -Query $jql -Credential $Credential -MaxResults $MaxResults)
                }
            }
        }
    }

    end {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Complete"
    }
}