Programming Issues

Exclusive offer: get 50% off this eBook here
JIRA 5.x Development Cookbook

JIRA 5.x Development Cookbook — Save 50%

This book is your one-stop resource for mastering JIRA extensions and customizations with this book and ebook

$35.99    $18.00
by Jobin Kuruvilla | July 2013 | Cookbooks Enterprise Articles

In this article by Jobin Kuruvilla, the author of JIRA 5.x Development Cookbook we will learn about programming issues, that is, creating, editing, or deleting issues, creating new issue operations, managing the various other operations available on issues via JIRA APIs, and so on.

(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.

JIRA 5.x Development Cookbook This book is your one-stop resource for mastering JIRA extensions and customizations with this book and ebook
Published: April 2013
eBook Price: $35.99
Book Price: $59.99
See more
Select your format and quantity:

Conditions on issue operations

When new operations are created, it is often a requirement to hide them or show them based on the permissions or state of the issue or something else. JIRA allows conditions to be added while defining the web items, and when the conditions are not satisfied, the web item won't show up!

Here, we will lock down the new issue operation to Project Administrators exclusively.

Getting ready...

Create the Manage Project issue operation.

How to do it...

Following are the steps to add a new condition to an issue operation's web item:

  1. Create the condition class. The class should implement the com.atlassian.plugin.web.Condition interface, but it is recommended to extend com.atlassian.jira.plugin.webfragment.conditions.AbstractIssueCondition when creating an issue condition.

    While extending AbstractIssueCondition, we will have to implement the shouldDisplay method, as shown here:

    public class AdminCondition extends AbstractIssueCondition { private final PermissionManager permissionManager; public AdminCondition(PermissionManager permissionManager) { this.permissionManager = permissionManager; } @Override public boolean shouldDisplay(User user, Issue issue, JiraHelper jiraHelper) { return this.permissionManager.hasPermission(Permissions. PROJECT_ADMIN, issue.getProjectObject(), user); } }

    Here, a true value is returned if the user has the PROJECT_ADMIN permission on the project. That is all we need for the condition class.

  2. Include the condition class in the web item:

    <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"> /secure/project/ViewProject.jspa?pid=${issue.project.id} </link> <condition class="com.jtricks.conditions.AdminCondition"/> </web-item>

    It is possible to invert a condition by using the invert flag, as shown:

    <condition class="com.jtricks.conditions.AdminCondition" invert="true"/>

    Once inverted, this condition will check if the user is an admin and will return true only for non-admin users.

    Condition elements can also take optional parameters, as shown:

    <condition class="com.atlassian.jira.plugin.webfragment. conditions.JiraGlobalPermissionCondition"> <param name="permission">sysadmin</param> </condition>

    The parameters can be retrieved in the condition class by overriding the init(Map params) method. In this case, params is a map of string key/value pairs that hold these parameters, in which case, the Map will have permission as the key, and the value passed (sysadmin in our example) can be accessed using the key and can then be used in passing or failing the condition.

    For example, the following code in the conditions class will get you the appropriate permission type:

    int permission = Permissions.getType((String) params. get("permission")); // Permissions.SYSTEM_ADMIN in this case

    It is also possible to combine multiple conditions using the conditions element. The conditions element will have multiple condition elements connected through a logical AND (default) or OR condition.

    For example, if we want to make our example operation available to both Project Administrators as well JIRA System Administrators, we can do so using an OR condition, as shown here:

    <conditions type="OR"> <condition class="com.atlassian.jira.plugin.webfragment. conditions.JiraGlobalPermissionCondition"> <param name="permission">sysadmin</param> </condition> <condition class="com.jtricks.conditions.AdminCondition"/> </conditions>

  3. Package the plugin and deploy it.

How it works...

Once the plugin is deployed, we can go and check the operation on the View Issue page. If you are a Project Administrator (or a JIRA system admin, depending on which condition you used), you will see the operation. If the user doesn't have the permissions, the operation won't be shown.

Working with attachments

The attachments feature is a useful feature in JIRA, and it sometimes helps to manage the attachments on an issue through the JIRA APIs. In this article, we will learn how to work with attachments using the JIRA API.

There are three major operations that can be done on attachments, namely Create, Read, and Delete. We will see each of them here.

Getting ready

Make sure the attachments are enabled in your JIRA instance. You can do this from Administration | System | Advanced | Attachments, as mentioned at http://confluence.atlassian.com/display/JIRA/Configuring+File+Attachments.

How to do it...

All the operations on the attachments can be performed using the AttachmentManager API. The AttachmentManager interface can be retrieved either by injecting it in to the constructor or from the ComponentAccessor class, as shown here:

AttachmentManager attachmentManager = ComponentAccessor. getAttachmentManager();

Creating an attachment

An attachment can be created on an issue using the createAttachment method on the AttachmentManager interface, as shown:

ChangeItemBean changeBean = attachmentManager.createAttachment(new File(fileName), newFileName, "text/plain", user, issue);

The following are the arguments:

  • The fileName value here needs to be the full path to the file on the server. You can also create a File object by uploading from the client machine, depending on the requirement.
  • newFileName is the name with which the file will be attached to the issue, and it can be different from the original filename.
  • The third parameter is the contentType of the file. In this case, we are uploading a text file, and hence the content type is text/plain.
  • user is the user who is attaching the file.
  • issue is the issue to which the file will be attached.

If you also want to set a list of properties on an attachment as a key/value pair and create the attachment at a specific time, it can be done using the overloaded method createAttachment, which takes two extra parameters—attachmentProperties, which is a Map containing the key/value properties, and createdTime, which is of the type java.util.Date.

Such attachment properties will be stored in the database using PropertySet.

Reading attachments on an issue

AttachmentManager has a method to retrieve the list of attachments of type com.atlassian.jira.issue.attachment.Attachment available on an issue. The following is how we do it:

List<Attachment> attachments = this.attachmentManager. getAttachments(issue); for (Attachment attachment : attachments) { System.out.println("Attachment: "+attachment.getFilename()+" attached by "+attachment.getAuthor()); }

The object attachment holds all the information of the attachment, including any properties set during the creation of the attachment.

Deleting an attachment

All you need to do here is to retrieve the attachment object that needs to be deleted and invoke the deleteAttachment method on AttachmentManager:

this.attachmentManager.deleteAttachment(attachment);

Here, attachment is an attachment that can be retrieved using the getAttachment(id) method or by iterating on the list of attachments retrieved previously.

There's more...

AttachmentManager also has other useful methods such as attachmentsEnabled, isScreenshotAppletEnabled(), isScreenshotAppletSupportedByOS(), and so on, to check whether the respective functionality is enabled or not.

Check out http://docs.atlassian.com/jira/latest/com/atlassian/jira/issue/AttachmentManager.html for a full list of available methods.

Time tracking and worklog management

Time tracking is one of the biggest pluses for any issue tracking system. JIRA's time tracking is highly configurable and gives you plenty of options to manage the work done and the remaining time.

Even though the time tracking in JIRA can be done using the JIRA UI, many users want to do it from the customized pages or third-party applications or plugins. Here, we will see how to do time tracking using the JIRA APIs.

Before we start, each of the operations on worklogs, namely create, edit, or delete, have different modes. Whenever one of these operations is performed, we can adjust the remaining amount of work to be done in the following ways:

  • Let JIRA adjust the remaining work automatically.

    For example, if the remaining estimate is 2 hours and we log 30 minutes, JIRA will automatically adjust the remaining estimate to 1 hour 30 minutes.

  • Enter a new remaining estimate time while performing the operations.

    For example, if the remaining estimate is 2 hours and we log 30 minutes, we can force JIRA to change the remaining estimate to 1 hour (instead of the automatically calculated 1 hour 30 minutes).

  • Adjust the remaining estimate, or in other words, reduce a specific amount of time from the remaining estimate.

    For example, if the remaining estimate is 2 hours and we log 30 minutes, we can force JIRA to reduce the remaining estimate by 1 hour 30 minutes (instead of automatically reducing the logged 30 minutes). When we do that, the remaining estimate will come out to be 30 minutes.

  • Leave the remaining estimate as it is.

Getting ready...

Make sure time tracking is turned on as explained at http://confluence.atlassian.com/display/JIRA/Configuring+Time+Tracking. It can be enabled from the Administration | System | Issue Features | Time Tracking menu.

How to do it...

Worklogs in JIRA can be managed using the WorklogService class. It does all the major operations, such as creating worklogs, updating them, or deleting them, and it does this in all the four different modes we have seen previously.

We will look at how to create worklogs, or in other words, log work, in the following four modes:

  • Auto adjusting the remaining estimate
  • Logging work and retaining the remaining estimate
  • Logging work with a new remaining estimate
  • Logging work and adjusting the remaining estimate by a value

Auto adjusting the remaining estimate

  1. Create the JIRA Service Context for the user who is logging work:

    JiraServiceContext jiraServiceContext = new JiraServiceContextImpl(user);

  2. Create a WorklogInputParametersImpl.Builder object to create the parameters needed for the worklog creation:

    final WorklogInputParametersImpl.Builder builder = WorklogInputParametersImpl.issue(issue).timeSpent(timeSpent). startDate(new Date()).comment(null).groupLevel(null). roleLevelId(null);

    Here, the issue is the issue on which work is logged and timeSpent is the time that we are going to log in. timeSpent is a String that represents the format in which time is entered in JIRA, that is, *w *d *h *m (representing weeks, days, hours, and minutes, where * can be any number).

    startDate here can be the date from when the work has started. We can also optionally add comments and set the worklog visibility to certain groups or project roles! Set these parameters as null when the worklog is visible to all.

  3. Create the WorklogInputParameters object from the builder and validate it using the WorklogService class:

    WorklogResult result = this.worklogService.validateCreate(jiraServ iceContext, builder.build());

  4. Create the worklog using WorklogService:

    Worklog worklog = this.worklogService.createAndAutoAdjustRemaining Estimate(jiraServiceContext, result, false);

    Here, as you can see, the method invoked is createAndAutoAdjustRemainingEstimate, which will create the worklog and automatically adjust the remaining estimate on the issue.

    The method takes as input the service context we created, the WorklogResult object after validating the input parameters, and a Boolean, which will be used to dispatch an event if needed. When the Boolean value is true, the Work Logged On Issue event is fired.

With this, the work will be logged on the issue.

Logging work and retaining the remaining estimate

Here, the first three steps are similar to what was discussed in the Auto adjusting the remaining estimate section previously. The only difference is that the method invoked on WorklogService is createAndRetainRemainingEstimate instead of createAndAutoAdjustRemainingEstimate.

The full code is as shown here:

JiraServiceContext jiraServiceContext = new JiraServiceContextImpl(user); final WorklogInputParametersImpl.Builder builder = WorklogInputParametersImpl.issue(issue).timeSpent(timeSpent). startDate(new Date()).comment(null).groupLevel(null). roleLevelId(null); WorklogResult result = this.worklogService.validateCreate
(jiraServiceContext, builder.build()); Worklog worklog = this.worklogService.createAndRetainRemainingEstimate (jiraServiceContext, result, false);

Logging work with a new remaining estimate

Here, the first two steps are similar to what was previously discussed in the Auto adjusting the remaining estimate section:

  1. Create the JIRA Service Context for the user who is logging work:

    JiraServiceContext jiraServiceContext = new JiraServiceContextImpl(user);

  2. Create a WorklogInputParametersImpl.Builder object to create the parameters needed for the worklog creation:

    final WorklogInputParametersImpl.Builder builder = WorklogInputParametersImpl.issue(issue).timeSpent(timeSpent) .startDate(new Date()).comment(null).groupLevel(null). roleLevelId(null);

  3. Create the New Estimate Input Parameters from the Builder object:

    final WorklogNewEstimateInputParameters params = builder. newEstimate(newEstimate).buildNewEstimate();

    Here, we specify newEstimate, which is a String representation similar to timeSpent.newEstimate and will be set as the remaining estimate on the issue.

  4. Create the WorklogResult interface from WorklogNewEstimateInputParameters using WorklogService:

    WorklogResult result = this.worklogService.validateUpdateWithNewEs timate(jiraServiceContext, params);

    The result here will be an instance of WorklogNewEstimateResult, which will be used in the next step!

  5. Create the worklog using WorklogService:

    Worklog worklog = this.worklogService.createWithNewRemainingEstima te(jiraServiceContext, (WorklogNewEstimateResult) result, false);

    Here, the method used is createWithNewRemainingEstimate, which sets newEstimate as the remaining estimate on the issue after logging the work using timeSpent! As you can see, the result object is converted to WorklogNewEstimateResult.

Logging work and adjusting the remaining estimate by a value

Here the process is much similar to the preceding method. The only difference is that the adjustmentAmount method is used on Builder instead of newEstimate and validateCreateWithManuallyAdjustedEstimate is used on WorklogService to create the worklog. Also, WorklogResult is an instance of WorklogAdjustmentAmountResult.

The code is as follows:

JiraServiceContext jiraServiceContext = new JiraServiceContextImpl(user); final WorklogInputParametersImpl.Builder builder = WorklogInputParametersImpl.issue(issue).timeSpent(timeSpent). startDate(new Date()).comment(null).groupLevel(null). roleLevelId(null); final WorklogAdjustmentAmountInputParameters params = builder.
adjustmentAmount(estimateToReduce).buildAdjustmentAmount(); WorklogResult result = worklogService.validateCreateWithManuallyAdjust edEstimate(jiraServiceContext, params); Worklog worklog = this.worklogService.createWithManuallyAdjustedEstimate
(jiraServiceContext, (WorklogAdjustmentAmountResult) result, false);

How it works...

Once we create or update the worklogs using the WorklogService API, the changes will be reflected on the issue under the Work Log tab, as shown in the following screenshot:

You can also see that the graphical representation of Time Tracking reflects these changes.

When a worklog is deleted, it appears under the Change History as shown here:

There's more

Similarly, worklogs can be updated using WorklogService as well.

Updating worklogs

Updating worklogs is similar to creating them in many ways. Here, we pass the ID of the Worklog object to be updated instead of the issue we pass when creating a worklog. Also, of course, the methods invoked on WorklogService are different. The following is the code to update a given worklog for the first mode where the remaining estimate is auto adjusted:

JiraServiceContext jiraServiceContext = new JiraServiceContextImpl(user); final WorklogInputParametersImpl.Builder builder = WorklogInputParametersImpl.worklogId(worklog.getId()). timeSpent(timeSpent).startDate(new Date()).comment(null). groupLevel(null).roleLevelId(null); WorklogResult result = this.worklogService.validateUpdate(jiraServiceC ontext, builder.build()); Worklog updatedLog = this.worklogService.updateAndAutoAdjustRemainingE stimate(jiraServiceContext, result, false);

As you can see, a builder is created by passing the worklog ID, which is unique across issues. WorklogResult here is created using the validateUpdate method, and the worklog is finally updated using the updateAndAutoAdjustRemainingEstimate method.

The other modes are also similar to how we created the worklogs. Let us quickly see how to update a worklog with a new remaining estimate:

JiraServiceContext jiraServiceContext = new JiraServiceContextImpl(user); final WorklogInputParametersImpl.Builder builder = WorklogInputParametersImpl.worklogId(worklog.getId()). timeSpent(timeSpent).startDate(new Date()).comment(null). groupLevel(null).roleLevelId(null); final WorklogNewEstimateInputParameters params = builder. newEstimate(newEstimate).buildNewEstimate(); WorklogResult result = this.worklogService.validateUpdateWithNewEstima te(jiraServiceContext, params); Worklog updatedLog = this.worklogService.updateWithNewRemainingEstimat e(jiraServiceContext, (WorklogNewEstimateResult) result, false);

The preceding code looks pretty familiar, doesn't it? It is similar to creating a worklog with a new estimate, except that we call the respective update methods, as discussed before.

We can update a worklog by retaining the estimate and also adjust it by a specified amount of time from the remaining estimate in the same way.

Deleting worklogs

Deleting a worklog is slightly different and maybe easier than creating or updating one, as it doesn't involve building the input parameters.

Auto adjusting the remaining estimate

All we need here is the worklog ID and to create the JIRA Service Context. The code is as shown here:

JiraServiceContext jiraServiceContext = new JiraServiceContextImpl(user); WorklogResult worklogResult = worklogService.validateDelete
(jiraServiceContext, worklog.getId()); worklogService.deleteAndAutoAdjustRemainingEstimate
(jiraServiceContext, worklogResult, false);

Here, the validateDelete method takes the worklog ID as input and creates a WorklogResult, which is then used in the deleteAndAutoAdjustRemainingEstimate method.

Deleting a worklog and retaining the remaining estimate

This is done in almost the same way as mentioned in the previous section, except that the deleteAndRetainRemainingEstimate method is used instead of deleteAndAutoAdjustRemainingEstimate:

JiraServiceContext jiraServiceContext = new JiraServiceContextImpl(user); WorklogResult worklogResult = worklogService.validateDelete(jiraServic eContext, worklog.getId()); worklogService.deleteAndRetainRemainingEstimate(jiraServiceContext, worklogResult, false);

Deleting a worklog with a new remaining estimate

As mentioned before, we don't create the input parameters while deleting worklogs. Instead, while validating, newEstimate is used to create WorklogResult, which is an instance of WorklogNewEstimateResult. The code is as follows:

JiraServiceContext jiraServiceContext = new JiraServiceContextImpl(user); WorklogResult worklogResult = worklogService.validateDeleteWithNewEstimate
(jiraServiceContext, worklog.getId(), newEstimate); worklogService.deleteWithNewRemainingEstimate(jiraServiceContext, (WorklogNewEstimateResult) worklogResult, false);

Deleting a worklog and adjusting the remaining estimate

This is also pretty much the same as mentioned in the previous section, except for the method names:

JiraServiceContext jiraServiceContext = new JiraServiceContextImpl(user); WorklogResult worklogResult = worklogService.validateDeleteWit hManuallyAdjustedEstimate(jiraServiceContext, worklog.getId(), adjustmentAmount); worklogService.deleteWithManuallyAdjustedEstimate(jiraServiceContext, (WorklogAdjustmentAmountResult) worklogResult, false);

Here, adjustmentAmount is the value that is used to increase the remaining estimate on the issue.

Working with comments on issues

Here, we will see how to manage commenting on issues using the JIRA API.

Let us have a look at all of the three major operations—creating, editing, and deleting comments. We will also have a look at how to restrict the comment visibility to a specific group of people or to a project role.

How to do it...

JIRA uses the CommentService class to manage the comments on an issue.

Creating comments on issues

A comment can be added on to an issue as follows:

Comment comment = this.commentService.create(user, issue, commentString, false, new SimpleErrorCollection());

Here, commentString is the comment we are adding, user is the user adding the comment, and issue is the issue on which the comment is added. The fourth argument is a Boolean that determines whether an event should be dispatched or not. If it is true, an Issue Commented event is thrown.

Creating comments on an issue and restricting it to a project role or group

If we need to restrict the visibility of the comments, we need to use the overridden create method on the CommentService class that takes the role ID and group name along with the other attributes. Only one of them should be passed at one time.

In order to restrict the comment visibility to groups, the Comment Visibility property under General Configuration should be set to Groups & Project Roles. The default is to allow restricting comments only for project roles.

For example, the comment can be restricted to a group as follows:

Comment comment = this.commentService.create(user, issue, commentString, group, null, false, new SimpleErrorCollection());

In this group, group is the name of the group, and the fifth parameter (null) is roleId.

Restricting to a role is done as follows:

Comment comment = this.commentService.create(user, issue, commentString, null, roleId, false, new SimpleErrorCollection());

In this case, group is null and roleId is the unique ID of the ProjectRole that we need to restrict the comment to.

The Boolean to dispatch events can be used in both cases.

Updating comments

Following are the steps to update a comment:

  1. Create the MutableComment object from the comment to be updated:

    MutableComment comm = this.commentService.getMutableComment(user, comment.getId(), new SimpleErrorCollection());

  2. Modify the comment with the following statement:

    comm.setBody("New Comment");

    Here, we update the body of the comment, though we can also update other attributes such as the author, group level, role level, and so on.

  3. Update the comment using CommentService:

    this.commentService.update(user, comm, false, new SimpleErrorCollection());

Deleting comments

A comment can be deleted as shown here:

this.commentService.delete(new JiraServiceContextImpl(user), comment, false);

Here, comment is the comment object to be deleted. The last Boolean argument determines whether to dispatch the event or not.

JIRA 5.x Development Cookbook This book is your one-stop resource for mastering JIRA extensions and customizations with this book and ebook
Published: April 2013
eBook Price: $35.99
Book Price: $59.99
See more
Select your format and quantity:

Programming change logs

Tracking changes to an issue is very important. JIRA stores all the changes that are done on an issue as change logs along with the information of who made the change and when. Sometimes, when we do custom development, we will have to update the Change History ourselves when there are some changes on the issue by our plugin.

Change histories are logged as change groups, which are groups of one or more change items made by a user at any one time. Each change item will be a change made on any single field.

In this article, we will see how to add change logs on an issue using the JIRA API.

How to do it...

Each change item in JIRA is created as a ChangeItemBean. A ChangeItemBean can be of two different types—one for system fields, where the field type is ChangeItemBean. STATIC_FIELD, and another for custom fields, where the field type is ChangeItemBean.CUSTOM_FIELD.

The following are the steps to add a Change History:

  1. Create a ChangeItemBean for the change that needs to be recorded for every item that is changed:

    ChangeItemBean changeBean = new ChangeItemBean(ChangeItemBean. STATIC_FIELD, IssueFieldConstants.SUMMARY, "Old Summary", "New Summary");

    Here, the first attribute is fieldType and the second one is the name of the field. For system fields of type ChangeItemBean.STATIC_FIELD, the name can be retrieved from the IssueFieldConstants class. For example, IssueFieldConstants.SUMMARY represents the issue summary.

    The third and fourth arguments are the old value and the new value of the field respectively.

    As we know, some of the JIRA fields have an ID value and a String value. For example, the issue status has the status name and the corresponding status ID. In such cases, we can use an overridden constructor that also takes the old ID and new ID, as shown here:

    ChangeItemBean changeBean = new ChangeItemBean(ChangeItemBean. STATIC_FIELD, IssueFieldConstants.STATUS,"1", "Open", "3", "In Progress");

    For custom fields, we use the field type ChangeItemBean.CUSTOM_FIELD and the custom field name. Everything else is the same:

    ChangeItemBean changeBean = new ChangeItemBean(ChangeItemBean. CUSTOM_FIELD, "My Field", "Some Old Value", "Some New Value");

    It is worth noting that the field name can be manipulated to give any value when the fieldType value is ChangeItemBean.CUSTOM_FIELD. It is probably a useful feature when you want to programmatically add change logs that are not directly related to a field; say, for adding a subtask!

    ChangeItemBean changeBean = new ChangeItemBean(ChangeItemBean. CUSTOM_FIELD, "Some Heading", "Some Old Value", "Some New Value");

  2. Create a change holder and add the change items into it:

    IssueChangeHolder changeHolder = new DefaultIssueChangeHolder(); changeHolder.addChangeItem(changeBean);

  3. Create and store the change log using the items in the changeHolder object using the ChangeLogUtils class:

    GenericValue changeLog = ChangeLogUtils.createChangeGroup(user, issue, issue, changeHolder.getChangeItems(), false);

    Here, user is the user making the change. The second and third arguments are the original issue and the issue after the changes. You can give both as the same if the change items are explicitly created and added to changeHolder.

    But if we are modifying an issue using the setter methods, an easier way might be to pass the original issue object along with the modified issue object (object after setter methods are invoked) and set the last argument as true, which determines whether a list of change items needs to be generated from the before and after objects. In that case, we don't need to explicitly create changeItems, and hence the third argument can be an empty list. We can still pass additional changeItems if needed as the third argument in which case both the passed changeItems and generated changeItems (from issue before and after modification) will be created!

How it works...

Once the change logs are added, they will appear in the issues change log panel, as shown in the following screenshot:

Notice that the highlighted change log is added even though there is no field named Some Heading. Also, see how both the ID and name are shown for the Status field!

Programming issue links

Issue linking is another important feature in JIRA. It helps us to define the relationship between issues. In this article, we will see how to create links between issues and to break them using the JIRA APIs.

Before we start, an issue link type has an inward and an outward description. For every issue link, there will be a source issue and a destination issue. From a source issue, we can look up the destination issues by looking up the outward links. Similarly, from a destination issue, we can look up the source issues by looking up the inward links.

Getting Ready

Make sure the Issue Linking feature is turned ON in JIRA and the valid link types are created. This can be done from Administration | System | Issue Features | Issue Linking, as explained at http://confluence.atlassian.com/display/JIRA/Configuring+Issue+Linking.

How to do it...

Issue links are managed in JIRA with the help of the IssueLinkManager class. The following are the steps to create an issue link between two given issues:

  1. Get the IssueLinkType object for the link type we are going to create. This can be retrieved using the IssueLinkTypeManager class. The IssueLinkTypeManager class can be retrieved from the ComponentAccessor or can be injected in to the constructor:

    IssueLinkTypeManager issueLinkTypeManager = ComponentAccessor.getC omponent(IssueLinkTypeManager.class); IssueLinkType linkType = issueLinkTypeManager.getIssueLinkTypesByN ame("Duplicate").iterator().next();

    Here, we are getting the Duplicate issue link type. Even though the getIssueLinkTypesByName method returns a collection, there will be only one link with the same name.

  2. Create the issue link using the IssueLinkManager class. The IssueLinkManager class can also be retrieved from the ComponentAccessor class or injected in to the constructor:

    IssueLinkManager issueLinkManager = ComponentAccessor. getIssueLinkManager(); issueLinkManager.createIssueLink(sourceIssue.getId(), destIssue. getId(), linkType.getId(), null, user);

    Here, we pass the source and destination issue IDs in the order mentioned along with the link type ID. The fourth parameter is the sequence, which is of type long, and is used to order the links on the user interface. user is the user who is performing the link action.

There's more...

Let's now see how to delete links or just display them.

Deleting issue links

Following are the steps to do this:

  1. Retrieve the IssueLinkType, as we did earlier:

    IssueLinkTypeManager issueLinkTypeManager = ComponentAccessor.getC omponent(IssueLinkTypeManager.class); IssueLinkType linkType = issueLinkTypeManager.getIssueLinkTypesByN ame("Duplicate").iterator().next();

  2. Get the issue link to be deleted using the IssueLinkManager class:

    IssueLink issueLink = issueLinkManager.getIssueLink(sourceIssue. getId(), destIssue.getId(), linkType.getId());

    Here, the sourceIssue and destIssue parameters represent the source and destination issues respectively.

  3. Delete the link using the IssueLinkManager class:

    issueLinkManager.removeIssueLink(issueLink, user);

Retrieving issue links on an issue

We can retrieve the inward or outward links on an issue or all the linked issues using different methods on the IssueLinkManager class.

All inward links can be retrieved as shown:

List<IssueLink> links = issueLinkManager.getInwardLinks(issue. getId()); for (IssueLink issueLink : links) { System.out.println(issueLink.getIssueLinkType().getName()+": Linked from "+issueLink.getSourceObject().getKey());

Here, issue is the destination object and we are getting all the inward issue links and displaying the source issue key.

Similarly, outward links can be retrieved as shown:

links = issueLinkManager.getOutwardLinks(issue.getId()); for (IssueLink issueLink : links) { System.out.println(issueLink.getIssueLinkType().getName()+": Linked to "+issueLink.getDestinationObject().getKey()); }

Here, issue is the source object and we are getting all the outward issue links and displaying the destination issue key.

All the linked issues can be retrieved in a single method as shown:

LinkCollection links = this.issueLinkManager.getLinkCollection(issue, user); Collection<Issue> linkedIssues = links.getAllIssues();

Discarding fields while cloning

Cloning of issues in JIRA is an easy way to replicate an existing issue. While cloning, a new issue is created exactly similar to the original issue with identical values for all its fields, except for a few special ones. The special ones include created date, updated date, issue key, status, and so on.

But, in addition to the special fields JIRA has chosen, we might want to ignore a few other fields while cloning an issue. How about a unique custom field? We surely don't want to replicate that while cloning?

Here is an easy way to discard any such fields while cloning an issue.

Getting ready...

Create a Skeleton plugin using the Atlassian Plugin SDK.

How to do it...

We need to do here is to create a new webwork action that extends the existing JIRA clone action and overrides the required method. In this specific case, we will be overriding the setFields() method to remove the cloning of the specific fields we are interested in!

Let us, for example, say that we want to avoid cloning a unique number field named Test Number. Following are the steps to do this:

  1. Add a new webwork module in the atlassian-plugin.xml file with a new action class and the same alias as JIRA's clone action, CloneIssueDetails. Once we do that, the new action class will be executed while cloning issues:

    <webwork1 key=" jtricks-clone-issue-details" name="JTricks Clone Issue Details" > <actions> <action name="com.jtricks.JTricksCloneIssueDetails" alias="CloneIssueDetails"> <view name="input">/secure/views/cloneissue-start.jsp</view> <view name="error">/secure/views/cloneissue-start.jsp</view> </action> </actions> </webwork1>

  2. Create the new class by extending the existing action class:

    public class JTricksCloneIssueDetails extends CloneIssueDetails{ ... }

  3. Override the setFields() method to set a null value for the fields we do not want to clone:

    @Override protected void setFields() throws FieldLayoutStorageException { super.setFields(); // Set null values for interested fields here }

  4. Add the code to set null values. In our example, we set a null value for the Test Number custom field:

    CustomField customField = customFieldManager.getCustomFieldObjectB yName("Test Number"); getIssueObject().setCustomFieldValue(customField, null);

    Here we get the cloned issue using the getIssueObject method and set the null value for the custom field. Don't forget to use the getCustomFieldObject method by passing the custom field ID if the field name is not unique!

    If we want to set null values for a system field, such as fix for versions, the method is the same:

    getIssueObject().setFixVersions(null);

  5. Package the plugin and deploy it.

    An action can be overridden only once. Care must be taken not to override it again in another plugin (this might be a third-party plugin) as only one will be picked up.

How it works...

Once the clone operation is invoked, the new action we have created will be executed. The clone operation creates a new issue object and copies the values to its fields from the original issue. This is done in the setFields method.

It is only logical to override this method and set null values for fields we do not want to clone. As shown previously, the setFields method from the super class, which is the JIRA's builtin class, is executed first. Once this method is executed, the new issue object, which can be retrieved using the getIssueObject method, has all the values populated. We just reset some of the values by setting them to null.

JavaScript tricks on issue fields

JIRA provides a lot of options to manage the various fields on an issue. Field configuration schemes, screen schemes, and so on, help the JIRA admins to show or hide fields, mark them as mandatory, and so on, differently for different issue types and projects.

Irrespective of how configurable these schemes are, there are still areas where we need to perform some custom development. For example, if we need to show or hide fields based on the values of another field, then JIRA doesn't have any built-in option to do so.

In that case, what is the best way to deal with this? It is always possible to create a new composite custom field that can have multiple fields driven by each other's behavior. But probably an easier way—that doesn't need developing a plugin—is to drive this using JavaScript. To make things better, JIRA offers a jQuery library that can be used to write neat JavaScript code!

However, using JavaScript to handle field behavior can create problems. It limits the behavior to the browser, it is client side, and it is dependent on whether JavaScript is enabled or not. But given its advantages and ease of use, most users prefer to do it. Here, we will see a small example of using JavaScript to show or hide the values of a custom field based on the issue's priority value!

How to do it...

Let us assume that we have a custom field named Why Critical?. The field should be shown only if the priority of the issue is critical.

Following are the simple steps to achieve this using JavaScript:

  1. Write the JavaScript to achieve the functionality.

    In our example, we need to show the Why Critical? field only when the priority is critical. Let us write the JavaScript for these purposes as an example:

    1. Identify the ID value for priority. We can get it by looking at the URL while editing the priority or from the JIRA database by looking at the priority table.
    2. Identify the ID of the custom field. We can get this also in a similar fashion, either by looking at the URL while editing the custom field or from the customfield table.
    3. Write the JavaScript to show or hide the field depending on the priority value. Here, we use JIRA's jQuery library, which has a predefined namespace, AJS; a short name for Atlassian JavaScript!

      <script type="text/javascript"> (function($){ $(document).ready(function(){ var priority = document.getElementById('priority'); hideOrShow(priority.value); priority.onchange=function() { hideOrShow(priority.value); }; }); function hideOrShow(priorityVal){ if (priorityVal == '2'){ AJS.$("#customfield_10170").closest('div.field-group').show(); } else { AJS.$("#customfield_10170").closest('div.field-group').hide(); } } })(AJS.$); </script>

    4. Here, 10170 is the ID of the custom field, and hence customfield_10170 represents the unique custom field ID! Also, 2 is the ID of the priority system field.

      In this example, we created a page load event where the script looks at the priority value and sets the visibility of the div element surrounding the custom field as hidden or shown.

      The following part captures the on load event of the page where the custom field is in edit mode:

      (function($){ $(document).ready(function(){ ... }); })(AJS.$);

      The following code shows the field, if the priority is 2:

      AJS.$("#customfield_10170").closest('div.field-group'). show();

      For every other priority value, the closest div surrounding field is hidden.

  2. Add the preceding JavaScript to the description of the custom field.

The field behavior will be effective on the next reload after the JavaScript is added on to the field description.

How it works...

Whenever the field is rendered under the velocity view in the edit mode, the field description is executed along with all the JavaScript code in there!

Once the script is added in the relevant field configuration screen, the field will not appear for priority values other than critical, as shown in the next screenshot:

Here, the priority is Major (value 3), and hence the Why Critical? field is not available. But the moment the priority is changed to Critical, we can see the field appearing back on the page:

The JavaScript can now be modified to do a lot of other useful stuff! Don't forget to modify the scripts according to your needs, specifically your browser and your version of JIRA.

Creating issues and comments from an e-mail

It is possible to automatically create issues or comments in JIRA based on incoming e-mail messages. This feature is very useful in scenarios such as helpdesks where the users normally send an e-mail to a designated e-mail address and the support team works on issues raised, such as this one!

Once configured correctly, any new e-mail that comes in will create a corresponding issue in JIRA and the replies to the e-mail notifications on that issue will be created as comments on that issue. It is also possible to attach documents on the issue by attaching them on the e-mail-provided attachments that are enabled in JIRA. If external user management is not enabled, it is also possible to create a user account—if they don't already have an account.

Here, we will see how we can configure JIRA to enable this feature.

How to do it...

The following are the steps to enable issue creation from e-mails:

  1. Create an e-mail account on the server—typically, one e-mail account for each JIRA project. This mailbox should be accessible via POP, IMAP, or on the local filesystem. JIRA will periodically scan this mailbox and create issues or comments based on the e-mail.
  2. Navigate to JIRA's Administration | System | Mail | Incoming Mail.
  3. Click on the Add POP / IMAP mail server button.
  4. Enter the details for the POP or IMAP mail server created in Step 1 and click on Add.
  5. Click on the Add incoming mail handler button:
    • Name: The name of the mail handler.
    • Server: Pick one from the servers configured above or select the Local Files option for an external mail service that writes messages to the filesystem.
    • Delay: Choose a delay for the handler to run and scan the mails.
    • Handler: Select one of the handlers from the list. Details of available handlers can be found at https://confluence.atlassian.com/display/JIRA/Creating+Issues+and+Comments+from+Email#CreatingIssuesandCommentsfromEmail-messagehandlers. Let us pick Create a new issue or add a comment to an existing issue handler.
    • Folder Name: For IMAP Server, specify the folder name if it is a folder other than the inbox. For the Local Files option, specify the subdirectory within the import/mail directory in JIRA home.
  6. Click Next to add the handler parameters specific to the handle selected. This is the most important part, as this is where we specify the parameters that will be used while creating the issue. Following is the list of important parameters for the handler we picked:
    • Project: Project where the issue should be created.
    • Issue Type: Type of the issue to be created. For example, if we want the issue to be created as a bug, select Bug.
    • Strip Quotes: If enabled, this strips previous messages from the replies.
    • Catch Email Address: If this is added, JIRA will process only e-mails sent to this address. It is used when there are multiple aliases for the same e-mail inbox.
    • Bulk: Determines how to handle "bulk" e-mails. The possible options are:
      • ignore: Ignore the e-mail and do nothing.
      • forward: Forward the e-mail to the address set in the Forward Email text field.
      • delete: Delete the e-mail permanently.
      • accept: Accept the e-mail for processing.
    • Forward Email: Error notifications and unhandled e-mails (used in conjunction with bulk forward handle parameter) will be forwarded to this address.
    • Create Users: If this is set to true, accounts will be created for new senders. This option is not compatible with the Default Reporter option.
    • Default Reporter: Can be used to create an issue with the specified reporter when the sender does not match with an existing user. This option will not be available if Create Users is checked.
    • Notify Users: Only used if Create Users is checked. It indicates whether users should get a e-mail notification for the new accounts created.
    • CC Assignee: If this is set, the new issue will be assigned to a matching user in the To, Cc, or the Bcc field in the given order depending on where the user is matched.
    • CC Watchers: If this is set, matching users in To, Cc, and Bcc fields are added as watchers on the issue. Even the new users created by the Create Users option can be added as a watcher using this option.
  7. Finish the handler creation.

JIRA is now configured to receive e-mails to the newly added mailbox.

How it works...

The handler we have set up scans the mailbox every n minutes as configured in the delay and picks up the new incoming messages. When a new message is received, JIRA scans through the subject to see if there are any mentions of an already existing issue. If there is one, the e-mail is added as a comment on the mentioned issue with the e-mail body as the comment text. If there is no mention of an issue in the subject, JIRA still checks whether the e-mail is a reply to another e-mail that had already created an issue or not. If so, the e-mail body is again added as a comment on that issue. This is done by checking the in-reply-to header in the e-mail.

If JIRA still couldn't find any matching issues, a new issue is created in the project and configured in the handle parameters. The e-mail subject will become the issue summary and e-mail body will become the description.

Any attachments on an e-mail—new or replies—will be added as attachments on the issue.

More information about the creation of issues and comments from an e-mail and on how the other handlers work can be found at http://confluence.atlassian.com/display/JIRA/Creating+Issues+and+Comments+from+Email.

It is also worth checking the plugin exchange for plugins with extended mail handlers that are capable of adding more details on the issue while creation, such as custom field values. Some of them have far better filtering mechanisms as well.

Summary

In this article, we will looked at the various APIs and methods used for managing issues programmatically. It also covered the CRUD operations, working with attachments, programming the change logs and issue links, time tracking, and much more.

Resources for Article:


Further resources on this subject:


About the Author :


Jobin Kuruvilla

Jobin Kuruvilla is an Atlassian Consultant with experience in customizing JIRA and writing JIRA plugins for various customers. He is currently working with Go2Group as an Atlassian platinum expert, and is involved in managing Atlassian products for big enterprises as well as small starter license installations.

Jobin is the author of JIRA Development Cookbook, Packt Publishing, released in 2011, which is a well-received book in the JIRA community. He also runs a website named J-Tricks (http://www.j-tricks.com), using which he shares numerous tutorials to help the developer community, who he believes have contributed immensely to his personal development. It is indeed those tutorials that sowed the first seeds of JIRA Development Cookbook.

Jobin started his career as a Java/J2EE developer in one of the biggest IT companies in India. After spending his initial years in the SOA world, he got hooked into this amazing product called JIRA, which he came across during the evaluation of a number of third-party tools. Soon, Jobin realized the power of JIRA, and pledged to spread the word. He has been doing it ever since, and he reckons there is a long way to go!

Books From Packt


JIRA Development Cookbook
JIRA Development Cookbook

JIRA 5.2 Essentials
JIRA 5.2 Essentials

JIRA 4 Essentials
JIRA 4 Essentials

RESTful PHP Web Services
RESTful PHP Web Services

RESTful Java Web Services
RESTful Java Web Services

Getting Started With Oracle SOA Suite 11g R1 – A Hands-On Tutorial
Getting Started With Oracle SOA Suite 11g R1 – A Hands-On Tutorial

SOA Made Simple
SOA Made Simple

SOA and WS-BPEL
SOA and WS-BPEL


Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software