11 min read

(For more resources related to this topic, see here.)

Creating an issue from a plugin

Here, we will see how to programmatically create an issue from a plugin. Prior to Version 4.1, JIRA used IssueManager to create an issue. From JIRA 4.1, there is this IssueService class that drives the issue operations. Since IssueService is recommended over IssueManager.

How to do it…

The main advantage of using IssueService over the IssueManager class is that it takes care of the validation and error handling. The following are the steps to create an issue using the IssueService class:

  1. Create an instance of the IssueService class. You can either inject it in the constructor or get it from the ComponentAccessor class, as shown:

    IssueService issueService = ComponentAccessor.getIssueService();

  2. Create the issue input parameters. In this step, we will set all the values that are required to create the issue using the IssueInputParameters class.
    1. Create an instance of the IssueInputParameters class:

      IssueInputParameters issueInputParameters = new
      IssueInputParametersImpl();

    2. Populate the IssueInputParameters class with the values required to create the issue as shown in the next few lines of code:

      issueInputParameters.setProjectId(10000L).
      setIssueTypeId("5").setSummary("Test Summary").
      setReporterId("admin").setAssigneeId("admin").
      setDescription("Test Description").setStatusId("1").
      setPriorityId("2").setFixVersionIds(10000L, 12121L);

    3. Make sure all the required values such as project, issue type, summary, and other mandatory values required when the issue is created using the user interface, are set on the IssueInputParameters class.
    4. Here, we have used test values, but make sure to replace them with appropriate values. For example, the project, issue type ID, priority ID, Fix version IDs, reporter, and assignee should have appropriate values.
  3. Validate the input parameters using IssueService:

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

    Here, the user is the one creating the issue. The validation is done based on the user permissions and the createValidationResult variable will have errors if the validation fails due to permission issues or due to invalid input parameters!

  4. If the createValidationResult variable is valid, create the issue using IssueService:

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

    Here, we use the createValidationResult object to create the issue, as it already has the processed input parameters. If the result is not valid, handle the errors as shown in the following code:

    if (!createValidationResult.isValid()) {
    Collection<String> errorMessages = createValidationResult.
    getErrorCollection().getErrorMessages();
    for (String errorMessage : errorMessages) {
    System.out.println(errorMessage);
    }
    Map<String, String> errors = createValidationResult.
    getErrorCollection().getErrors();
    Set<String> errorKeys = errors.keySet();
    for (String errorKey : errorKeys) {
    System.out.println(errors.get(errorKey));
    }
    }

    Here, we just print the error to the console if the result is invalid. The errorMessages object will have all non-field-specific errors such as permission issue-related errors, and so on, but any field-specific errors, such as input validation errors, will appear in the errors map where the key will be the field name. We should handle both the error types as appropriate.

  5. After the creation of an issue, check whether the createResult object is valid or not. If not, handle it appropriately. The createResult object will have errors only if there is a severe problem with JIRA (for example, if you can’t communicate with the database, the workflow has changed since you invoked validate, and so on).

    if (!createResult.isValid()) {
    Collection<String> errorMessages = createResult.
    getErrorCollection().getErrorMessages();
    for (String errorMessage : errorMessages) {
    System.out.println(errorMessage);
    }
    }

    Here again, we just print the error to the console.

  6. If createResult is valid, then the issue is created successfully and you can retrieve it as:

    MutableIssue issue = createResult.getIssue();

How it works…

By using IssueService, JIRA now validates the inputs we give it using the rules we have set up in JIRA via the user interfaces, such as the mandatory fields, permission checks, individual field validations, and so on. Behind the scenes, it still uses the IssueManager class.

There’s more…

As mentioned before, prior to JIRA 4.1, we needed to use the IssueManager class to create the issues. It can still be used in JIRA 4.1 and higher, but this is not recommended as it overrides all the validations.

But then again, what if you want to override those validations due to some reason? For example, to skip permission checks or field screen validations inside the plugin? In such cases, we might still need IssueManager.

Using IssueManager to create the issue

Follow these steps to create an issue with the IssueManager class:

  1. Initialize an issue object using the IssueFactory class:

    MutableIssue issue = ComponentAccessor.getIssueFactory().
    getIssue();

  2. Set all the fields required on the issue object:

    issue.setProjectId(10000L);
    issue.setIssueTypeId("5");
    issue.setAssigneeId("admin");

  3. Create the issue using IssueManager:

    Issue createdIssue = ComponentAccessor.getIssueManager().
    createIssueObject(user, issue);

  4. Handle CreateException to catch any errors.

Creating subtasks on an issue

Here, we will demonstrate how to create a subtask from a JIRA plugin. It is very similar to the issue creation, but there are some notable differences.

Subtasks are useful for splitting up a parent issue into a number of tasks, which can be assigned and tracked separately. The progress on an issue is generally a sum of the progress on all its subtasks, although people use it for a lot of other purposes too.

How to do it…

There are two steps to creating a subtask:

  1. Create an issue object. A subtask object is nothing but an issue object in the backend. The only difference is that it has a parent issue associated with it. So, when we create a subtask issue object, we will have to define the parent issue in addition to what we normally do while creating a normal issue.
  2. Link the newly created subtask issue to the parent issue.

Let’s see these steps in more detail:

  1. Create the subtask issue object. Here, the IssueInputParameters interface is constructed (after changing the methods such as setIssueTypeId() appropriately).

    For this issue, we will use the validateSubTaskCreate method—which takes an extra parameter named parentId— instead of validateCreate:

    CreateValidationResult createValidationResult = issueService.
    validateSubTaskCreate(user, parent.getId(), issueInputParameters);

    Here, parent is the issue object on which we are creating the subtask.

  2. Create an issue after checking for errors, as we have seen previously:

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

  3. Create a link between the newly created subtask issue and the parent issue:
    1. Get an instance of SubTaskManager. You can either inject it in the constructor or get it from ComponentAccessor as follows:

      SubTaskManager subTaskManager = ComponentAccessor.
      getSubTaskManager();

    2. Create the subtask link:

      subTaskManager.createSubTaskIssueLink(parent, createResult.
      getIssue(), user);

  4. The subtask should now be created with a link back to the original parent issue.

Updating an issue

Here, let’s look at editing an existing issue. Users can edit the issue to update one or more fields, and there are screen schemes or field configurations to define what a user can see while editing an issue. Moreover, there is the edit project permission option to limit editing to selected users, groups, or roles.

Programmatically editing an issue also takes these things into account.

How to do it…

Let’s assume that we have an existing issue object. We will just modify the summary to a new summary. Following are the steps to do this:

  1. Create the IssueInputParameters object with the input fields that need to be modified:

    IssueInputParameters issueInputParameters = new
    IssueInputParametersImpl();
    issueInputParameters.setSummary("Modified Summary");

    If you do not want to retain the existing values and just want the summary on the issue to be updated, you can set the retainExistingValuesWhenParameterNotProvided flag as shown:

    issueInputParameters.setRetainExistingValuesWhenParameterNotProvid
    ed(false);

  2. Validate the input parameters using IssueService:

    UpdateValidationResult updateValidationResult = issueService.
    validateUpdate(user, issue.getId(), issueInputParameters);

    Here, issue is the existing issue object.

  3. If updateValidationResult is valid, update the issue:

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

    If it is not valid, handle the errors as we did when creating the issue.

  4. Validate updateResult and handle the error, if any. If it is not valid, the updated issue object can be retrieved as:

    MutableIssue updatedIssue = updateResult.getIssue();

Deleting an issue

Here, let us look at deleting an issue programmatically.

How to do it…

Let us assume that we have an existing issue object. For deletion as well, we will use the IssueService class. Following are the steps to do this:

  1. Validate the delete operation on the issue using IssueService:

    DeleteValidationResult deleteValidationResult = issueService.
    validateDelete(user, issue.getId());

    Here, issue is the existing issue object that needs to be deleted.

  2. If deleteValidationResult is valid, invoke the delete operation:

    if (deleteValidationResult.isValid()) {
    ErrorCollection deleteErrors = issueService.delete(user,
    deleteValidationResult);
    }

  3. If deleteValidationResult is invalid, handle the errors appropriately.
  4. Confirm whether the deletion was successful by checking the deleteErrors collection:

    if (deleteErrors.hasAnyErrors()){
    Collection<String> errorMessages = deleteErrors.
    getErrorMessages();
    for (String errorMessage : errorMessages) {
    System.out.println(errorMessage);
    }
    } else {
    System.out.println("Deleted Succesfully!");
    }

Adding new issue operations

Here, we will look at adding new operations to an issue. The existing issue operations include Edit Issue, Clone Issue, and so on, but most of the time, people tend to look for similar operations with variations or entirely new operations that they can perform on an issue.

Prior to JIRA 4.1, the issue operations were added using the Issue Operations Plugin Module (http://confluence.atlassian.com/display/JIRADEV/Issue+Operations+Plugin+Module). But since JIRA 4.1, new issue operations are added using the Web Item Plugin Module (http://confluence.atlassian.com/display/JIRADEV/Web+Item+Plugin+Module).

A Web Item Plugin module is a generic module that is used to define links in various application menus. One such menu is the issue operations menu. Here, we will only concentrate on using the web-item module to create issue operations.

Getting ready

Create a skeleton plugin using Atlassian Plugin SDK.

How to do it…

Creating a web item is pretty easy! All we need to do is to place it in the appropriate section. There are already defined web sections in JIRA, and we can add more sections using the Web Section module if needed.

Let us create a new operation that lets us administer the project of an issue when we are on the View Issue page. All we need here is to add an operation that takes us to the Administer Project page. Following are the steps to create the new operation:

  1. Identify the web section where the new operation should be placed.

    For issue operations, JIRA already has multiple web sections defined. We can add our new operation on any one of these sections. The following is a diagram from the Atlassian documentation detailing with each of the available web sections for the issue operations:

  2. If we want to add a new operation along with Move, Link, and so on, we need to add the new web item under the operations-operations section. If you are rather hoping to add it right at the top, along with Edit, Assign, and Comment, the section must be operations-top-level. We can reorder the operation using the weight attribute.
  3. Define the web item module in the plugin descriptor with the section identified in the previous step! For our example, the module definition in atlassian-plugin.xml will look like the following:

    <web-item key="manage-project" name="Manage Project"
    section="operations-operations" weight="100">
    <label>Manage Project</label>
    <tooltip>Manages the Project in which the issue belongs </
    tooltip>
    <link linkId="manage-project-link">/plugins/servlet/projectconfig/${
    issue.project.key}</link>
    </web-item>

    As you can see, this has a unique key and a human-readable name. The section here is operations-operations. The weight attribute is used to reorder the operations as we saw earlier, and here we use weight as 100 to put it at the bottom of the list.

    label is the name of the operation that will appear to the user. We can add a tooltip as well, which can have a friendly description of the operation. The next part, that is, the link attribute, is the most important one, as it links us to the operation that we want to perform. Essentially it is just a link, and hence you can use it to redirect to anywhere; the Atlassian site, for example.

    In our example, we need to take the user to the administer project area. Luckily, in this case, we know the servlet to be invoked, as it is an existing servlet in JIRA. All we need to do is to invoke the project-config servlet by passing the project key as well. The issue object is available on the view issue page as $issue, and hence we can retrieve the project ID on the link as ${issue.project.key}.

    In cases where we need to do new things, we will have to create an action or servlet by ourselves and point the link to that action/servlet.

  4. Package the plugin and deploy it.

How it works…

At runtime, you will see a new operation on the View Issue page on the More Actions drop-down menu, as shown in the next screenshot:

After clicking on the link, the Administer Project screen will appear as expected. As you might have noticed, the URL is populated with the correct key from the expression ${issue.project.key}.

Also, just change the section or weight and see how the operation appears at various places on the screen!

There’s more…

Prior to JIRA 4.1, the Issue Operations module was used in the creation of new issue operations. You can find details about it in the Atlassian documentation at http://confluence.atlassian.com/display/JIRADEV/Issue+Operations+Plugin+Module.

LEAVE A REPLY

Please enter your comment!
Please enter your name here