Issues¶
The SDK provides several types for working with Jira issues.
Issue Types¶
rest.Issues¶
A slice of jira.Issue from the go-jira library:
issues, err := client.IssueAPI.SearchIssues("project = FOO", false)
// issues is rest.Issues ([]jira.Issue)
for _, issue := range issues {
fmt.Printf("%s: %s\n", issue.Key, issue.Fields.Summary)
}
rest.IssueMore¶
A wrapper around jira.Issue with convenience methods:
import "github.com/grokify/gojira/rest"
issue := issues[0]
im := rest.NewIssueMore(&issue)
// Convenience accessors
key := im.Key()
summary := im.Summary()
status := im.StatusName()
typeName := im.TypeName()
assignee := im.AssigneeName()
resolution := im.Resolution()
rest.IssuesSet¶
A structured set of issues with aggregation and filtering:
// Create from Issues
issuesSet, err := issues.IssuesSet(nil)
if err != nil {
log.Fatal(err)
}
// Get counts
countsByStatus := issuesSet.CountsByStatus()
countsByType := issuesSet.CountsByType(true, true)
countsByProject := issuesSet.CountsByProject()
// Filter
filtered := issuesSet.FilterByStatus("Open")
Searching Issues¶
Basic Search¶
issues, err := client.IssueAPI.SearchIssues(
"project = FOO AND status = Open", // JQL query
false, // Retrieve all (paginate)
)
Search with Pagination¶
// Retrieve all matching issues (handles pagination internally)
issues, err := client.IssueAPI.SearchIssues(jql, true)
Search to IssuesSet¶
Getting Issues¶
Single Issue¶
With Options¶
opts := &rest.GetQueryOptions{
ExpandChangelog: true, // Include issue history
}
issue, err := client.IssueAPI.Issue(ctx, "FOO-123", opts)
Multiple Issues¶
keys := []string{"FOO-123", "FOO-456", "FOO-789"}
issues, err := client.IssueAPI.Issues(ctx, keys, nil)
IssuesSet Operations¶
Aggregations¶
issuesSet, _ := issues.IssuesSet(nil)
// Count by standard fields
byStatus := issuesSet.CountsByStatus() // map[string]uint
byType := issuesSet.CountsByType(true, true)
byProject := issuesSet.CountsByProject()
// Count by custom field
byCF, err := issuesSet.CountsByCustomFieldValues("customfield_10001")
Filtering¶
// Filter by status
open := issuesSet.FilterByStatus("Open")
// Filter by type
bugs := issuesSet.FilterByType("Bug")
// Filter by custom criteria
filtered := issuesSet.Filter(func(im *rest.IssueMore) bool {
return im.Priority() == "High"
})
Export to Table¶
tbl, err := issuesSet.TableDefault(nil, true, "Initiative", []string{})
if err != nil {
log.Fatal(err)
}
// Write to file
err = tbl.WriteXLSX("output.xlsx", "Issues")
Parent Issues¶
Include parent issues (epics, initiatives):
err := client.IssueAPI.IssuesSetAddParents(issuesSet)
if err != nil {
log.Fatal(err)
}
// Access parents
if issuesSet.Parents != nil {
parentKeys := issuesSet.Parents.Keys()
}
Reading from Files¶
From JSON¶
Issue Fields¶
Access fields through the jira.Issue.Fields struct:
issue := issues[0]
// Standard fields
summary := issue.Fields.Summary
description := issue.Fields.Description
status := issue.Fields.Status.Name
issueType := issue.Fields.Type.Name
priority := issue.Fields.Priority.Name
assignee := issue.Fields.Assignee.DisplayName
created := issue.Fields.Created
updated := issue.Fields.Updated
// Custom fields (via Unknowns map)
if val, ok := issue.Fields.Unknowns["customfield_10001"]; ok {
// Handle custom field value
}
Example: Issue Report¶
package main
import (
"fmt"
"log"
"github.com/grokify/gojira/rest"
)
func main() {
client, err := rest.NewClientFromBasicAuth(
"https://your-instance.atlassian.net",
"your-email@example.com",
"your-api-token",
false,
)
if err != nil {
log.Fatal(err)
}
// Search issues
issues, err := client.IssueAPI.SearchIssues("project = FOO", true)
if err != nil {
log.Fatal(err)
}
// Convert to IssuesSet for analysis
issuesSet, err := issues.IssuesSet(nil)
if err != nil {
log.Fatal(err)
}
// Print status breakdown
fmt.Println("Issues by Status:")
for status, count := range issuesSet.CountsByStatus() {
fmt.Printf(" %s: %d\n", status, count)
}
// Print type breakdown
fmt.Println("\nIssues by Type:")
for issueType, count := range issuesSet.CountsByType(true, true) {
fmt.Printf(" %s: %d\n", issueType, count)
}
}