Guide - Performing issue operations

Available:

JIRA 4.1 and later.

This page introduces concepts and techniques for working with JIRA issues in a plugin. It covers these topics:

IssueService

JIRA 4.1 has 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's. This services methods will make sure that when dealing with Issues that 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.

Plugin developers wanting 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 will ensure that you do not put corrupted data into JIRA.

The general format of the service is that there are two methods per operation. One method, the validation method, generates a result object that is used as the parameter to the next "do" method. If validation does not pass then there will be internationalized error messages in the result object that explain what was wrong and you will be unable to invoke the "do" method with this parameter.

The "do" methods also return a result object which will contain the new state of the issue if the operation was successful and errors if something went wrong during the action.

Getting an instance of the IssueService

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

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

Retrieving an issue

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

final IssueService.IssueResult issueResult = issueService.getIssue(remoteUser, 10000L);
final MutableIssue mutableIssue = issueResult.getIssue();
//OR
final IssueService.IssueResult issueResult = issueService.getIssue(null, "JRA-1234");
final MutableIssue mutableIssue = issueResult.getIssue();

IssueInputParameters - a builder that specifies issue values

To perform an operation on an issue you will 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:

        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);

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. See the javadoc for full details.

IssueInputParameters - custom fields

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

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

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

Once you have setup the issue builder, described above, in the way you would like, then you need to get an instance of the issue service, as described above.

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

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, etc). The create result will only have errors if there is a severe problem with JIRA (e.g. can't communicate with the DB, the workflow has changed since you invoked validate, etc.). See the javadoc for full details.

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

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 will use the same com.atlassian.jira.issue.IssueInputParameters as a builder that lets you tell JIRA what you want to change on 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 which you can provide to the update method.

If there are any errors (e.g. insufficient permissions, missing required fields, referencing values that do not exist, etc) then there will be i18n'ed messages in the returned result object. See the javadoc on the method for full details.

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, etc). The update result will only have errors if there is a severe problem with JIRA (e.g. can't communicate with the DB, the workflow has changed since you invoked validate, etc.). See the javadoc for full details.

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

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 will 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. See the javadoc for full details.

If there are any errors (e.g. insufficient permissions, missing required fields, referencing values that do not exist, etc) then there will be i18n'ed messages in the returned result object. See the javadoc on the method for full details.

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, etc). The transition result will only have errors if there is a severe problem with JIRA (e.g. can't communicate with the DB, the workflow has changed since you invoked validate, etc.). See the javadoc for full details.

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

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

Deleting an issue is quite easy. You just 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 (e.g. insufficient permissions, the issue no longer exists, etc) then there will be i18n'ed messages in the returned result object. See the javadoc on the method for full details.

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, etc.). The delete result will only have errors if there is a severe problem with JIRA. See the javadoc for full details.

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

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 you for some reason 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 are creating 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.

// A list of GenericValues representing issues
List issues = ...
for (Iterator iterator = issues.iterator(); iterator.hasNext();)
{
    GenericValue issue = (GenericValue) iterator.next();
    // Retrieve a collection of all linked issues and their link types
    LinkCollection linkCollection = getIssueLinkManager().getLinkCollection(issue, authenticationContext.getUser());
    Set linkTypes = linkCollection.getLinkTypes();
    if (linkTypes != null)
    {
        // For each link type
        for (Iterator iterator1 = linkTypes.iterator(); iterator1.hasNext();)
        {
            IssueLinkType linkType = (IssueLinkType) iterator1.next();
            // Get the outward linked issues
            List outwardIssues = linkCollection.getOutwardIssues(linkType.getName());
            if (outwardIssues != null)
            {
                for (Iterator iterator2 = outwardIssues.iterator(); iterator2.hasNext();)
                {
                    GenericValue outwardIssue = (GenericValue) iterator2.next();
                    System.out.println("outwardIssue = " + outwardIssue);
                }
            }
            // And the inward linked issues
            List inwardIssues = linkCollection.getInwardIssues(linkType.getName());
            if (inwardIssues != null)
            {
                for (Iterator iterator2 = inwardIssues.iterator(); iterator2.hasNext();)
                {
                    GenericValue inwardIssue = (GenericValue) iterator2.next();
                    System.out.println("inwardIssue = " + inwardIssue);
                }
            }
        }
    }
}

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

Please note that the code above uses JiraAuthenticationContext to retrieve the remote 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.

Was this page helpful?

Have a question about this article?

See questions about this article

Powered by Confluence and Scroll Viewport