Atlassian Jira — довольно популярный багтрекер/система управления проектами. Одним из ее плюсов является то, что она хорошо поддается разного рода доработкам.
Цель статьи — поверхностное знакомство с тем, как можно доработать эту систему под свои нужды, показать, что она позволяет с собой делать.
Есть несколько других хороших трекеров вроде Redmine и YouTrack, но сравнивать я их не стану.
Предположим, систему вы уже выбрали и/или она уже используется, и у вас есть программист, знающий Java.
Я буду описывать весь процесс для Jira начиная с версии 5. Для четвертой все сложнее, для третьей разработка плагина больше похожа на ад.
Также для примеров я буду использовать IDE Intellij IDEA CE. Все описанное можно адаптировать и для других IDE.
Типы модулей
Atlassian предоставляет api для написания разнообразных модулей. Их можно писать и к другим ихним продуктам, вроде Confluence, мы же сосредоточимся на Jira.
Модули представляют собой чаще всего разного рода виджеты, кнопочки и страницы, при взаимодействии с которыми выполняется ваш код. Они имеют фиксированное место в системе и по-умолчанию изолированы друг от друга.
С помощью модуля, например, можно добавить дополнительный таб в окно просмотра задачи с выводом какой-либо дополнительной информации, при желании интерактивный, добавить кнопочку в меню действий над задачей, или же заставить выполняться свой код во время одного из переходов по workflow.
Уровень выше — настраиваемый интерактивный виджет для дашборда, который общается с системой через RESTful сервис.
Или, например, собственная обработка почтовых сообщений.
Один плагин может содержать сколько угодно модулей, как правило, объединенных общей идеей и назначением.
Программное взаимодействие с системой
У Jira есть довольно богатый api. Он позволяет делать всякие вещи с задачами, проектами и пользователями без ручного обращения к бд и прочего безобразия. Реализуется он через компоненты с названиями вроде UserManager, IssueManager, etc, попадающие в класс вашей реализации модуля с помощью dependency injection во время первой загрузки плагина (иногда во время первого обращения к коду). Удобней всего засовывать их через конструктор, таким образом, ваш код может выглядеть вот так:
public class ConfigureGradesAction extends JiraWebActionSupport {
private final JiraAuthenticationContext authenticationContext;
private final WebResourceManager webResourceManager;
private final ActiveObjects ao;
private final VelocityManager velocityManager;
private final ProjectRoleManager projectRoleManager;
ConfigureGradesAction(
JiraAuthenticationContext authenticationContext,
WebResourceManager webResourceManager,
ActiveObjects ao,
VelocityManager velocityManager,
ProjectRoleManager projectRoleManager
) {
this.authenticationContext = authenticationContext;
this.webResourceManager = webResourceManager;
this.ao = ao;
this.velocityManager = velocityManager;
this.projectRoleManager = projectRoleManager;
}
// ...
}
Довольно монструозно, а что поделать. В Intellij IDEA есть удобное автозаполнение конструктора парой нажатий. Может быть, в eclipse и netbeans тоже.
Все эти менеджеры можно подгружать и в рантайме через статические методы класса ComponentAccessor. В нем не представлены все api, которые возможно заинжектить, но большинство основных там есть. Классный хинт: набираешь ComponentAccessor, точку, и получаешь 80% api джиры. Это очень удобно, особенно в первое время.
Давайте уже что-нибудь напишем
В качестве примера мы напишем простейший плагин, добавляющий в задачу кнопку, при нажатии на которую текст в задаче переворачивается задом наперед. Этого функционала вам как раз и не хватало.
Настройка среды разработки
Для начала необходимо установить и настроить atlassian sdk. Это — большой шаг.
Он очень облегчает работу. Представляет собой набор maven-целей в собственных обертках с собственным окружением.
Процесс установки хорошо описан в официальной документации. Вообще, она покрывает, наверное, 90% всего, что можно сделать с Jira.
Сейчас у них появились установочные пакеты под MacOS, Debian/Ubuntu, CentOS/Fedora и Windows. Раньше не было.
Поэтому установка сводится к двум шагам:
1) Установка jdk
2) Установка atlassian sdk из готового пакета
Если вашей системы нет в списке, значит вы достаточно крутой, чтобы разобраться самостоятельно.
Теперь у вас должен появиться набор консольных утилит, начинающихся с atlas-.
Чтобы создать скелет для плагина, нужно набрать atlas-create-jira-plugin и ответить на пару вопросов.
По окончании в папке с именем, указанном в artifactId, создается maven-артифакт вашего плагина.
- Версия — Конечно же, Shiny new JIRA 5
- groupId — Можно относиться как к неймспейсу для ваших плагинов. Для вашего домена, например, com.mycompany.jira.plugins
- artifactId — Уникальное название плагина. Например, reverse-jira-issue
- version — Я обычно оставяю дефолтное значение
- package — Это java package, в неймспейсе которого будет ваш код. По-умолчанию предлагается то же, что было введено в groupId, но я предпочитаю писать в формате groupId.artifactId.
На имя com.mycompany.jira.plugins.reverse-jira-issue будет ругаться компилятор, поэтому в нашем случае напишем com.mycompany.jira.plugins.issue.reverse
Добавление модуля
В atlassian sdk есть команда, создающая scaffold для модулей в плагине — atlas-create-jira-plugin-module. Почему бы не использовать ее. Она спросит тип модуля — выбираем Web Item
Имя — Reverse Issue Web Item; Section — operations-top-level (ссылки на все типы секшенов внизу этой страницы)
Спросит так же ссылку, которая должна будет открываться по кнопке. Наберем туда путь к нашему будущему действию — /secure/ReverseIssueAction.jspa
После всех ответов скрипт добавит в src/main/resources/atlassian-plugin.xml следующую запись:
<web-item name="Reverse Issue Web Item" i18n-name-key="reverse-issue-web-item.name" key="reverse-issue-web-item" section="operations-top-level" weight="1000">
<description key="reverse-issue-web-item.description">The Reverse Issue Web Item Plugin</description>
<label key="reverse-issue-web-item.label"></label>
<link linkId="reverse-issue-web-item-link">/secure/ReverseIssueAction.jspa</link>
</web-item>
Никаких java-классов не создастся, данный модуль обходится без кода.
Попробуем запустить и посмотреть, что получилось. Делается это командой atlas-run.
Она собирает инстанс джиры в подпапке target/jira и вытаскивает его на адрес 127.0.0.1:2990/jira/.
Первоначальная сборка и загрузка всех зависимостей займет длительное время.
Теперь заходим на указанный адрес и в логин-пароль вбиваем admin/admin. Создаем проект с произвольным названием через administration -> Create your first project, после чего выходим на главную страницу,
кликает Create Issue, пишем какое-нибудь название и переходим в созданную задачу.
В действиях над задачей появится кнопка Reverse Issue Web Item. Пока что она ведет на несуществующий линк. Исправим это, добавив свой action.
Создание действия
Action — это код, выполняющийся при обращении к url вида /secure/ActionName!doSomething.jspa
Джира здесь использует свой фреймворк Webwork, который при обращении по url создает объект вашего класса, назначает объявленным полям объекта переданные в post и get параметры, дергает в нем метод, в примере выше public string doSomething(), а без doSomething в конце имени — public string doDefault(), и дальше либо редиректит куда-то, либо передает этот инстанс в шаблон velocity, который вернет ответ пользователю.
Для простоты мы обойдемся без шаблона, а сделаем редирект обратно в задачу.
Сначала воспользуемся scaffolding'ом для создания экшена.
atlas-create-jira-plugin-module -> Webwork Plugin -> Reverse Issue Webwork Module
name: Reverse Issue
В atlassian-plugin.xml добавится нечто подобное:
<webwork1 key="reverse-issue" name="Reverse Issue" i18n-name-key="reverse-issue.name">
<description key="reverse-issue.description">The Reverse Issue Plugin</description>
<actions>
<action name="com.mycompany.jira.plugins.jira.webwork.ReverseIssueAction" alias="ReverseIssueAction">
<view name="success">/templates/reverse-issue/success.vm</view>
<view name="input">/templates/reverse-issue/input.vm</view>
<view name="error">/templates/reverse-issue/error.vm</view>
</action>
</actions>
</webwork1>
Здесь action alias должен по имени совпадать с link'ом в web-item, например
ReverseIssueAction и /secure/ReverseIssueAction.jspa
Если не совпадает, то или другое нужно поправить.
Перезагрузка кода
Теперь можно применить сделанные изменения на локальном инстансе.
Удобнее всего сделать это не перезагружая сам инстанс с помощью тулзы atlas-cli.
В соседней консоли откроем папку плагина и напишем этот самый atlas-cli.
После подрузки введем команду pi — сокращенно от plugin install.
Теперь по нажатию нашей созданной в самом начале кнопки будет открываться страница с дефолтным шаблоном success.vm.
Редирект
Нам же нужен редирект обратно, для чего, для начала, следует передать в get-е ссылку на задачу.
Для этого поправим link в atlassian-plugin.xml на /secure/ReverseIssueAction.jspa?issue=$issue.id
На самом деле, эта запись прогоняется через velocity как шаблон, и в умолчальном контексте здесь есть объект $issue, на котором можно вызывать всякие методы. Это описано где-то в дебрях документации.
Снова pi в нашем atlas-cli, чтобы проверить, что параметр issue действительно передается.
Ссылка по кнопке в этот раз откроется такая: 127.0.0.1:2990/jira/secure/ReverseIssueAction.jspa?issue=10000
Чтобы экшен подцепил параметр, нужно объявить в нем сеттер setIssue(long issue) и одноименное(а можно и не очень) свойство issue.
public class ReverseIssueAction extends JiraWebActionSupport
{
private static final Logger log = LoggerFactory.getLogger(ReverseIssueAction.class);
private long issue;
@Override
public String execute() throws Exception {
log.warn(Long.toString(issue));
return super.execute(); //returns SUCCESS
}
public void setIssue(long issue) {
this.issue = issue;
}
}
Добавим так же в метод execute строчку log.warn(Long.toString(issue));
Она распечатает id задачи в консоль, в которой запущен сервер.
Снова делаем pi и проверяем, что все работает. Должно работать, почему бы и нет.
Чтобы сделать редирект, нам недостаточно id задачи, нужен ее ключ. На самом деле, ключ мы могли передать на первом шаге, но я хочу показать, как базово использовать апи джиры. Объявим компонент IssueManager в конструкторе и достанем из него запрос по id, из которого достанем ключ.
Сразу же сделаем редирект.
public class ReverseIssueAction extends JiraWebActionSupport
{
private static final Logger log = LoggerFactory.getLogger(ReverseIssueAction.class);
private long issue;
private final IssueManager issueManager;
public ReverseIssueAction(IssueManager issueManager) {
this.issueManager = issueManager;
}
@Override
public String execute() throws Exception {
log.warn(Long.toString(issue));
String key = issueManager.getIssueObject(issue).getKey();
return getRedirect("/browse/"+key);
}
public void setIssue(long issue) {
this.issue = issue;
}
}
Основной функционал
Теперь осталось перевернуть текст в задаче.
public class ReverseIssueAction extends JiraWebActionSupport
{
private static final Logger log = LoggerFactory.getLogger(ReverseIssueAction.class);
private long issue;
private final IssueManager issueManager;
public ReverseIssueAction(IssueManager issueManager) {
this.issueManager = issueManager;
}
@Override
public String execute() throws Exception {
log.warn(Long.toString(issue));
MutableIssue issueObject = issueManager.getIssueObject(issue);
String key = issueObject.getKey();
String newSummary = reverse(issueObject.getSummary());
issueObject.setSummary(newSummary);
String newDescription = reverse(issueObject.getDescription());
issueObject.setDescription(newDescription);
boolean sendNotification = true;
issueManager.updateIssue(getLoggedInUser(), issueObject, EventDispatchOption.ISSUE_UPDATED, sendNotification);
return getRedirect("/browse/"+key);
}
private static String reverse(String s) {
return new StringBuilder(s).reverse().toString();
}
public void setIssue(long issue) {
this.issue = issue;
}
}
Снова делаем pi в консоли утилиты atlas-cli и проверяем кнопочку.
Автор: Firfi
Огромное Вам спасибо за статью, очень помогла во многом разобраться