Часто вижу вопросы о том, как сделать плагин с использованием функциональности из Jira Software. В интернете найти информацию сложно, поэтому я решил сделать статью, в которой расскажу, как подключить Jira Software функциональность к плагину для Jira.

Часто возникает необходимость получить все задачи, которые связаны с эпиком, или добавить задачу к эпику. Это обычно делают через связь типа «Epic Link», но давайте попробуем это сделать «правильно» через сервисы, которые предоставляет Jira Software.

Итак, в этой статье разработаем плагин, который выводит информацию обо всех задачах в эпике и добавляет задачи в эпик через созданный нами REST вызов.

Создаем плагин


Открываем терминал и выполняем команду:

atlas-create-jira-plugin

Терминал будет задавать вопросы. Ниже привожу вопросы и ответы на них:

Define value for groupId: : ru.matveev.alexey.sw.tutorial
Define value for artifactId: : sw-tutorial
Define value for version:  1.0.0-SNAPSHOT: :
Define value for package:  ru.matveev.alexey.sw.tutorial: :
Confirm properties configuration:
groupId: ru.matveev.alexey.sw.tutorial
artifactId: sw-tutorial
version: 1.0.0-SNAPSHOT
package: ru.matveev.alexey.sw.tutorial
Y: : Y

Вносим изменения в pom.xml


Меняем свойство jira.version на 7.9.0:

<jira.version>7.9.0</jira.version>

Добавляем в maven-jira-plugin в тэг configuration:

<applications>
    <application>
        <applicationKey>jira-software</applicationKey>
        <version>${jira.software.application.version}</version>
    </application>
</applications>

Добавляем свойство jira.software.application.version:

<jira.software.application.version>7.9.0</jira.software.application.version>

Строки выше позволят нам запустить Jira Software с помощью команды atlas-run версии 7.9.0.

Так же необходимо увеличить размер JVM, так как в моем окружении Jira Software не запустилась с ошибкой о нехватке памяти:

<jvmArgs>-Xms512M -Xmx1g</jvmArgs> 

Добавляем REST модуль


В терминале запускаем следующую команду:

atlas-create-jira-plugin-module

Ниже привожу вопросы и ответы на них:

Enter New Classname MyRestResource: :
Choose a number (1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/31/32/33/34): 14
Enter Package Name ru.matveev.alexey.sw.tutorial.rest: :
Enter REST Path /myrestresource: :
Enter Version 1.0: :
Show Advanced Setup? (Y/y/N/n) N: : N
Add Another Plugin Module? (Y/y/N/n) N: : N

Добавляем функциональность Jira Software


Запускаем в терминале:

atlas-run

После того, как Jira запустится, в папке плагина появится диреректория target. Нужно перейти в директорию target/jira/home/plugins/installed-plugins и поискать jar файл, который начинается с jira-greenhopper-plugin. В моем случае файл выглядит вот так:

jira-greenhopper-plugin-7.9.0-DAILY20180326142825.jar

Такое название файла означает, что версия jira-greenhopper-plugin плагина 7.9.0-DAILY20180326142825. Поэтому добавляем зависимость от плагина jira-greenhopper-plugin в файл pom.xml следующим образом:

<dependency>
    <groupId>com.atlassian.jira.plugins</groupId>
    <artifactId>jira-greenhopper-plugin</artifactId>
    <version>7.9.0-DAILY20180326142825</version>
    <scope>provided</scope>
</dependency>

После того, как мы добавили плагин в зависимости, мы можем использовать функциональность Jira Software. Но теперь нам нужно понять, какие сервисы Jira Software мы можем использовать.
Jira использует OSGI, что означает, что плагин должен экспортировать сервисы, которые могут быть использованы другими плагинами. Давайте попробуем найти сервисы, которые экспортирует Jira Software.

В браузере переходим по ссылке:

http://localhost:2990/jira/plugins/servlet/upm#osgi 

Мой экран выглядит вот так:



Найдем Atlassian Greenhopper плагин и откроем Registered Services меню:



Среди всех сервисов будут и необходимые нам сервисы:

Service 1477 com.atlassian.greenhopper.service.issue.RapidViewIssueService
Service 1476 com.atlassian.greenhopper.service.issuelink.EpicService

Напишем просмотр задач в эпике и добавление задачи в эпик


Удалим файл sw-tutorial/src/test/java/ut/ru/matveev/alexey/sw/tutorial/rest/MyRestResourceTest.java.

Изменим содержимое файла sw-tutorial/src/main/java/ru/matveev/alexey/sw/tutorial/rest/MyRestResource.java на:

package ru.matveev.alexey.sw.tutorial.rest;

import com.atlassian.greenhopper.model.Epic;
import com.atlassian.greenhopper.service.Page;
import com.atlassian.greenhopper.service.PageRequests;
import com.atlassian.greenhopper.service.ServiceOutcome;
import com.atlassian.greenhopper.service.issue.RapidViewIssue;
import com.atlassian.greenhopper.service.issue.RapidViewIssueService;
import com.atlassian.greenhopper.service.issuelink.EpicService;
import com.atlassian.jira.issue.Issue;
import com.atlassian.jira.issue.IssueManager;
import com.atlassian.jira.jql.builder.JqlQueryBuilder;
import com.atlassian.jira.security.JiraAuthenticationContext;
import com.atlassian.jira.user.ApplicationUser;
import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport;
import com.atlassian.plugins.rest.common.security.AnonymousAllowed;
import com.atlassian.query.Query;
import javax.inject.Inject;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Path("/message")
public class MyRestResource {

    private final EpicService epicService;
    private final IssueManager issueManager;
    private final JiraAuthenticationContext jiraAuthenticationContext;
    private final RapidViewIssueService rapidViewIssueService;

    @Inject
    public MyRestResource(@ComponentImport EpicService epicService,
                          @ComponentImport IssueManager issueManager,
                          @ComponentImport JiraAuthenticationContext jiraAuthenticationContext,
                          @ComponentImport RapidViewIssueService rapidViewIssueService) {
        this.epicService = epicService;
        this.issueManager = issueManager;
        this.jiraAuthenticationContext = jiraAuthenticationContext;
        this.rapidViewIssueService = rapidViewIssueService;
    }

    @Path("/hello")
    @GET
    @AnonymousAllowed
    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
    public Response getMessage()
    {
       return Response.ok(new MyRestResourceModel("Hello World")).build();
    }

    @Path("/epictasks")
    @GET
    @AnonymousAllowed
    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
    public Response getEpicTasks(@QueryParam("epic key") String epicKey)
    {
        ApplicationUser user = jiraAuthenticationContext.getLoggedInUser();
        ServiceOutcome<Epic> epic = epicService.getEpic(user, epicKey);
        Query query = JqlQueryBuilder.newBuilder().where().buildQuery();
        ServiceOutcome<Page<RapidViewIssue>> issues = rapidViewIssueService.getIssuesForEpic(user, epic.getValue(), PageRequests.request(0L, 100), query);
        List<String> issueList = issues.getValue().getValues().stream().map(el -> el.getIssue().getSummary()).collect(Collectors.toList());
        return Response.ok(new MyRestResourceModel(issueList.toString())).build();
    }

    @Path("/epictasks")
    @POST
    @AnonymousAllowed
    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
    public Response addTaskToEpic(@QueryParam("epic key") String epicKey,
                                  @QueryParam("issue key") String issueKey)
    {
        ApplicationUser user = jiraAuthenticationContext.getLoggedInUser();
        ServiceOutcome<Epic> epic = epicService.getEpic(user, epicKey);
        Set<Issue> issueSet = new HashSet<>();
        issueSet.add(issueManager.getIssueByCurrentKey(issueKey));
        epicService.addIssuesToEpic(user, epic.getValue(), issueSet);
        return Response.ok(new MyRestResourceModel(issueKey + " added to " + epicKey)).build();
    }
}

Код выше сырой и был создан лишь для того, чтобы показать, как можно работать с Jira Software. Специально для упрощения кода были опущены все необходимые проверки, поэтому код может вызывать NullPointerException при передачи неверных параметров.

В коде создается метод epictasks. Если к epictasks обратиться через метод GET и передать ключ эпика, то будут выведены все задачи в переданном эпике. Если к epictasks обратиться через метод POST и передать ключ эпика и ключ задачи, то задача будет добавлена в эпик.

Допустим у нас есть эпик с ключом SW-1 и задача с ключом SW-5. Сначала давайте обратимся в epictasks через GET:



Из скриншота выше мы видим, что у эпика SW-1 не задач.

Теперь обратимся к epictasks через метод POST и передадим в него епик SW-1 и задачу SW-5:



Мы видим, что задача SW-5 была добавлена в эпик SW-1.

Давайте еще раз попробуем вызвать epictasks через метод GET:



Мы видим, что у эпика SW-1 появилась задача SW-5.

Таким образом мы выполнили цель данной статьи: мы добавили задачу в эпик и получили информцию о задачах в эпике с помощью функциональности Jira Software.

Исходный код плагина можно взять здесь.

Комментарии (1)


  1. staticlab
    27.04.2018 17:14
    -1

    Atlassian Jira Software функциональность
    Найдем Atlassian Greenhopper плагин и откроем Registered Services меню

    Велик могучим русский языка