Last updatedMay 6, 2019

Performing issue operations

Available:

Jira 4.1 and later.

This page introduces concepts and techniques for working with Jira issues in an app.

IssueService

Jira 4.1 introduced an API level object called the IssueService. This service class is used to perform create, update, delete, and transition operations in Jira with issue. This services methods make sure that when dealing with issues all of Jira's business rules are enforced. This means that permissions and data validation will be checked, proper events will be fired, and notifications will be triggered.

App developers who want to perform any of these operations should use the IssueService as it abstracts the normally complicated issue operations into something a bit simpler and it ensures that you do not put corrupted data into Jira.

The general format of the service is that there are two methods per operation – validation and "do" method. The validation method generates a result object that is used as the parameter to the next "do" method. If validation does not pass, there is internationalized error message in the result object that explains what is wrong and you are not able to invoke the "do" method with this parameter.

The "do" methods also return a result object that contains the new state of the issue if the operation is successful and errors if something goes wrong during the action.

Getting an instance of the IssueService

You can get an IssueService object either by constructor injection using Atlassian Spring Scanner or explicitly via a getIssueService call like:

1
IssueService issueService = ComponentAccessor.getInstance().getIssueService();

Retrieving an issue

Issues can be retrieved using the IssueService either by ID or key:

1
2
3
4
5
IssueService.IssueResult issueResult = issueService.getIssue(applicationUser, 10000L);
MutableIssue mutableIssue = issueResult.getIssue();
//OR
IssueService.IssueResult issueResult = issueService.getIssue(null, "JRA-1234");
MutableIssue mutableIssue = issueResult.getIssue();

IssueInputParameters – a builder that specifies issue values

To perform an operation on an issue, you need to use an instance of com.atlassian.jira.issue.IssueInputParameters as a builder that lets you tell Jira what you want the issue to look like.

Here is an example of how to use the builder object:

1
2
3
4
5
6
7
8
9
10
11
12
13
IssueInputParameters issueInputParameters = issueService.newIssueInputParameters();
issueInputParameters.setProjectId(12345L)
    .setIssueTypeId("2");
    .setSummary("This is a summary");
    .setReporterId("joeuser");
    .setAssigneeId("otheruser");
    .setDescription("I am a description");
    .setEnvironment("I am an environment");
    .setStatusId("2");
    .setPriorityId("2");
    .setResolutionId("2");
    .setSecurityLevelId(10000L);
    .setFixVersionIds(10000L, 10001L);

Sometimes you may need to set a field that is not present on create or update screens. To do so, you need to use following method:

1
issueInputParameters.setSkipScreenCheck(true);

This is used in issue creation, update, and transitions to specify new or updated values.

This builder can be used to add comments (with or without restrictions) to an issue and to set custom field values. For full details, see the javadoc about IssueInputParameters mentioned previously.

IssueInputParameters – custom fields

Custom fields values can be added via the com.atlassian.jira.issue.IssueInputParameters.addCustomFieldValue methods.

Here some content describing how the values strings should be formatted for the various field types.

1
2
3
4
5
6
7
8
9
10
IssueInputParameters issueInputParameters = issueService.newIssueInputParameters();
issueInputParameters
    .addCustomFieldValue(myNumberField.getId(), "3")
    .addCustomFieldValue(myDateField.getId(), "3/Jan/2012")
    .addCustomFieldValue(myDateTime.getId(), "30/Jan/13 10:53 PM")
    .addCustomFieldValue(mySelectList.getId(), "1234")                  // 1234 is option id as string
    .addCustomFieldValue(myMultiSelectList.getId(), "1234, 5678")       // 1234 and 5678 are option id's as strings
    .addCustomFieldValue(myMultiSelectUserList.getId(), "uname1, uname2")
    .addCustomFieldValue(myCascadingSelectList.getId(), <parent option id as string>)
    .addCustomFieldValue(myCascadingSelectList.getId() + ":1", <child option id as string>);

Creating a new issue

After you set up the issue builder in the way you would like, you need to get an instance of the issue service.

To validate that your issue can be created as specified, you must invoke the validateCreate method. If there are any errors (for example, insufficient permissions, missing required fields, referencing values that do not exist, and so on), there will be i18n'ed messages in the returned result object.

Once you have a valid CreateValidationResult, you can pass this object to the issue service create method. This will create the issue and perform all the related tasks (event publication, issue indexing, and so on). The create result will only have errors if there is a severe problem with Jira (for example, can't communicate with the database, the workflow has changed since you invoked validate, and so on).

For full details, see the javadoc.

Here is an example of how to invoke the service to create the issue we setup above:

1
2
3
4
5
6
7
8
9
10
11
12
IssueService issueService = ComponentAccessor.getInstance().getIssueService();

CreateValidationResult createValidationResult = issueService.validateCreate(user, issueInputParameters);

if (createValidationResult.isValid())
{
    IssueResult createResult = issueService.create(user, createValidationResult);
    if (!createResult.isValid())
    {
        // Do something
    }
}

Editing an existing issue

Editing an existing issue is very similar to creating an issue. You need to use the same com.atlassian.jira.issue.IssueInputParameters as a builder that lets you tell Jira what you want to change in the issue.

You must invoke the validateUpdate method with the issue ID you wish to update and the IssueInputParameters that contains the changes that you want to apply. This will produce an UpdateValidationResult that you can provide to the update method.

If there are any errors (for example, insufficient permissions, missing required fields, referencing values that do not exist, and so on), there will be i18n'ed messages in the returned result object.

Once you have a valid UpdateValidationResult, you can pass this object to the issue service update method. This will update the issue and perform all the related tasks (event publication, issue indexing, and so on). The update result will only have errors if there is a severe problem with Jira (for example, can't communicate with the database, the workflow has changed since you invoked validate, etc.).

For full details, see the javadoc.

Here is an example of how to invoke the service to update the summary of an issue with an ID of 12345:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
IssueInputParameters issueInputParameters = issueService.newIssueInputParameters();
issueInputParameters.setSummary("I am a new summary");

IssueService issueService = ComponentAccessor.getInstance().getIssueService();

UpdateValidationResult updateValidationResult = issueService.validateUpdate(user, 12345L, issueInputParameters);

if (updateValidationResult.isValid())
{
    IssueResult updateResult = issueService.update(user, updateValidationResult);
    if (!updateResult.isValid())
    {
        // Do something
    }
}

Transitioning an existing issue

Transitioning an issue is much like editing an issue. You need to specify an additional parameter, the transition action ID, which identifies the transition the issue should make, along with the IssueInputParameters object specifying any values you wish to set while transitioning. You must invoke the validateTransition method on the issue service to generate a TransitionValidationResult.

If there are any errors (for example, insufficient permissions, missing required fields, referencing values that do not exist, and so on) then there will be i18n'ed messages in the returned result object.

Once you have a valid TransitionValidationResult, you can pass this object to the issue service transition method. This will transition the issue and perform all the related tasks (event publication, issue indexing, workflow post functions, and so on). The transition result will only have errors if there is a severe problem with Jira (for example, can't communicate with the database, the workflow has changed since you invoked validate, and so on).

For full details, see the javadoc.

Here is an example of how to invoke the service to transition an issue with an ID of 12345 to an ID of 10000 while also setting the assignee:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
IssueInputParameters issueInputParameters = issueService.newIssueInputParameters();
issueInputParameters.setAssigneeId("newdude");

IssueService issueService = ComponentAccessor.getInstance().getIssueService();

TransitionValidationResult transitionValidationResult = issueService.validateTransition(user, 12345L, 10000L, issueInputParameters);

if (transitionValidationResult.isValid())
{
    IssueResult transitionResult = issueService.transition(user, transitionValidationResult);
    if (!transitionResult.isValid())
    {
        // Do something
    }
}

Delete an existing issue

To delete an issue, you need to provide the issue service with the ID of the issue you wish to delete. You must invoke the validateDelete method and it will generate a DeleteValidationResult. This can be used to invoke the delete method.

If there are any errors (for example, insufficient permissions, the issue no longer exists, and so on), then there will be i18n'ed messages in the returned result object.

Once you have a valid DeleteValidationResult, you can pass this object to the issue service delete method. This will delete the issue and perform all the related tasks (delete associated attachments, comments, worklogs, and so on). The delete result will only have errors if there is a severe problem with Jira.

For full details, see the javadoc.

Here is an example of how to invoke the service to delete an issue with an ID of 12345:

1
2
3
4
5
6
7
8
9
10
11
12
IssueService issueService = ComponentAccessor.getInstance().getIssueService();

DeleteValidationResult deleteValidationResult = issueService.validateDelete(user, 12345L);

if (deleteValidationResult.isValid())
{
    ErrorCollection deleteErrors = issueService.delete(user, deleteValidationResult);
    if (deleteResult.hasAnyErrors())
    {
        // Do something
    }
}

Issue operations without validation

If, for some reason, you do not want to use the IssueService class, then you should look at the javadoc for the IssueManager class for create, delete, and update, and also at the WorkflowManager for the transition.

However, we highly recommend using the IssueService class for these operations since there is a lot of business logic associated with issue operations.

The following sample iterates over a list of issues and for each issue retrieves its linked issues. This code can be useful if you create a custom report that shows linked issues.

Remember that each link has a direction and a type. Therefore the issues in the Link Collection are grouped by link type and direction.

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
// A list of Issues representing issues
List<Issue> issues = ...
for (Issue issue: issues ) {
    // Retrieve a collection of all linked issues and their link types
    LinkCollection linkCollection = issueLinkManager.getLinkCollection(issue, authenticationContext.getLoggedInUser());
    Set<IssueLinkType> linkTypes = linkCollection.getLinkTypes();
    if (linkTypes != null) {
        // For each link type
        for (IssueLinkType linkType : linkTypes) {
            // Get the outward linked issues
            List<Issue> outwardIssues = linkCollection.getOutwardIssues(linkType.getName());
            if (outwardIssues != null) {
                for (Issue outwardIssue : outwardIssues) {
                    System.out.println("outwardIssue = " + outwardIssue);
                }
            }
            // And the inward linked issues
            List<Issue> inwardIssues = linkCollection.getInwardIssues(linkType.getName());
            if (inwardIssues != null) {
                for (Issue inwardIssue : inwardIssues) {
                    System.out.println("inwardIssue = " + inwardIssue);
                }
            }
        }
    }
}

One way to retrieve a list of issues is to make and run a Search Request.

Note that the code above uses JiraAuthenticationContext to retrieve the application user. The easiest way to get access to an instance of the JiraAuthenticationContext is to declare it as a dependency in the constructor of your class.

1
2
3
4
5
6
7
8
9
10
@Named
public class ExampleClass {
    @JiraImport
    private JiraAuthenticationContext authenticationContext;

    @Inject
    public ExampleClass(JiraAuthenticationContext authenticationContext) {
        this.authenticationContext = authenticationContext;
    }
}

Next steps

Also read about Creating a Jira issue CRUD servlet and issue search.