Представим ситуацию – вам надо сделать небольшой отчет на основе данных из другой системы. Звучит обыденно и вы сразу в голове представляете, что надо будет делать: узнать какие будут входные данные и в каком виде они к нам поступят, какая будет логика отчета (что на что надо умножить и т.д.) и в каком виде отчет должен быть представлен (график, диаграмма, таблица и т.д.), реализовать.
Но что, если я вам скажу, что это должна быть не какая-то новая система, а плагин для Jira. Все вышесказанное никуда не уходит, но добавляются нюансы. Вот о таких нюансах (и как не вылететь из-за них из всех разумных сроков) эта статья.
Начало положено
У нас в группе компаний НЛМК для отслеживания задач используется Jira. И естественно в процессе использования она была сильно адаптирована под наши рабочие процессы (много кастомных полей, несколько workflow под разные типы задач и т.д.). И вот назрела необходимость сделать небольшой отчет, который будет агрегировать информацию по задачам на разработку (в терминологии НЛМК – задание на изменение или просто ЗНИ) в разрезе этих кастомных полей. Как уже было написано выше, задача довольно таки тривиальная и многие сейчас скажут – “Да я подобные задачи делаю по 2 каждый день. Не вижу проблемы”. Мы тоже сначала не видели.
К отчету были приведены следующие требования:
возможность задать параметры отчета, которые будут выступать фильтрами для ЗНИ, которые должны попасть в отчет;
сам отчет будет представлен в виде таблицы;
должна быть возможность выгрузить отчет в виде excel-файла.
Если говорят, что надо делать, значит сделаем. После небольшого мозгового штурма были 2 основных варианта:
реализовать функционал в качестве плагина для JIRA;
сделать свою систему, которая будет использовать Jira в качестве master-системы.
Со вторым подходом все понятно: все свое кастомное (front, back, экспорт в excel), стучимся в Jira по REST и запрашиваем необходимую информацию. Займет, предположительно, больше времени, но на выходе получим расширяемую систему с претензиями на рост.
Начали копать про плагины для Jira. Сначала все выглядело не очень позитивно. Но потом выяснилось, что Atlassian SDK позволяет добавить в плагин модуль report. Читаем первые 2 абзаца документации (приведу тезисно):
модуль позволяет реализовать отчет на основе данных из Jira (неудивительно);
позволяет сделать как html-представление, так и в виде excel-файла;
позволяет задать пользователю параметры перед формирование отчета.
Выглядит словно под нас делали, но мы не стали спешить радоваться и решили сначала посмотреть, как это все предлагают реализовывать.
Начальные параметры указываются непосредственно
в atlassian-plugin.xml:
<properties>
<property>
<key>startDate</key>
<name>report.startdate</name>
<description>report.startdate.description</description>
<type>date</type>
</property>
<property>
<key>endDate</key>
<name>report.issuecreation.enddate</name>
<description>report.enddate.description</description>
<type>date</type></property>
</property>
<property>
<key>projectid</key>
<name>report.issuecreation.projectid.name</name>
<description>report.projectid.description</description>
<type>multiselect</type>
<values class="com.company.report.valuesgenerator.ProjectsGenerator"/>
</property>
</properties>
Из этого описания сама Jira сгенерирует страницу с параметрами. При нажатии кнопки будет вызван основной класс отчета, в который придут выбранные параметры, на основании которых будет реализована логика формирования отчета. Далее сгенерированный отчет объединяется с шаблоном и результат выводится на экран пользователю.
А что по экспорту в excel? В основном классе отчета можно переопределить метод isExcelViewSupported класса AbstractReport, чтобы он возвращал true и тогда в углу отчета появляется магическая кнопка для экспорта в excel-файл.
Что из этого всего вытекает:
не надо самим делать полностью front, лишь непосредственно страницу отчета;
не надо самим делать экспорт в excel;
отчет будет в существующей системе, которой уже пользуются пользователи.
На основе всего вышесказанного было принято решение реализовывать отчет в виде плагина для Jira.
Первый блин - не комом, но могло быть и лучше
На основе всего изученного выше начали работу. Делали все так сказать по фэн-шую с небольшими вкраплениями собственных идей. Но как оказалось не все так гладко в датском королевстве.
Начнем с экрана с начальными параметрами. Jira правда сгенерировала этот экран на основе указанного нами xml, но вот красота сего произведения оставляла желать лучшего. И если с полями для выбора дат все было вполне приемлемо, то вот с multiselect все оказалось хуже.
На выходе получался стандартный select с атрибутом multiple. С одной стороны, все работает – значения успешно прокидываются в select, пользователь может выбрать несколько значений, который потом успешно попадают в основной класс отчета. С другой стороны, пользоваться этим было откровенно неудобно, особенно если у параметра было много допустимых значений (больше 5).
Далее возникла проблема экспортом отчета в excel. Опять же, при нажатии кнопки экспорта, пользователю предлагалось загрузить на компьютер файл с форматом .xls, который можно было открыть и увидеть ту же информацию. Однако как оказалось это не настоящий excel, а просто html-файл с форматом .xls. Excel достаточно умный чтобы открыть такой файл, но вот дальнейшая работа с ним проблематична – если надо дополнительно что-то посчитать или не дай бог построить сводный отчет, то беда.
С этой проблемой нам удалось справиться. Jira позволяет в плагине добавить свой REST-endpoint, который будет казаться частью самой Jira, что мы и сделали. Отвечал этот endpoint за генерацию «настоящего» excel-файла по параметрам с первого экрана. Чтобы его вызвать была добавлена соответствующая кнопка на экран с самим отчетом.
Следующая проблема, с которой нам пришлось бороться это проблема прав. В Jira все отчеты, реализованные через модуль report попадают во вкладку "Отчеты" экрана проекта. Получается, что все пользователи могут этот отчет вызвать, что нас категорически не устраивало. После некоторых раздумий пришли к такому решению: проверять права на этапе генерирования отчета и в случае если у пользователя нет прав на данный отчет, после экрана с параметрами выдавать другую страницу с соответствующим сообщением.
В итоге было принято решение что текущая реализация подойдет в качестве первой версии отчета, но есть над чем работать.
Надо еще подумать
Начали думать, как нам быть и что делать. Посмотрим, что происходит под капотом.
Jira из xml генерирует начальную страницу для выбора параметров отчета. Для каждого select в плагине должен присутствовать класс реализующий интерфейс ValuesGenerator. В качестве примера можно привести генератор для проектов:
public class ProjectsGenerator implements ValuesGenerator {
@Override
public Map getValues(Map map) {
final ProjectManager projectManager = ComponentAccessor.getProjectManager();
return projectManager.getProjects().stream()
.collect(Collectors.toMap(Project::getKey, Project::getName));
}
}
После того как будут получены значения для всех select интерфейс выбора параметров будет предоставлен пользователю. Вот на этом моменте уже появляются 2 проблемы:
не можем вклиниться в процесс генерации этого экрана и сделать его более приятным глазу;
на данном этапе нет возможности провалидировать параметры, выбранные пользователем из-за чего валидация в основном классе отчета.
Проблема с правами возникает из-за того, что создатели Jira предполагают, что все отчеты, реализованные через модуль report должны быть доступны через соответствующую вкладку в проекте. В рамках нашей задачи такой подход нас не устраивает.
После тщательного изучения документации нашли что есть такой модуль как webwork. Суть его в том, что в рамках него можно объявлять несколько связок url-шаблон-класс с переходами от одной связки к другой. Кроме того, в этом же модуле можно сразу указать какие роли требуются для перехода по данному url. В atlassian-plugin.xml это выглядит следующим образом (пример взят из документации):
<webwork1 key="reference-actions" name="Reference WebWork Action" class="java.lang.Object" roles-required="use">
<actions>
<action name="com.atlassian.jira.dev.reference.plugin.actions.PreparedReferenceAction" alias="PreparedReferenceAction" roles-required="sysadmin">
<view name="success">templates/actions/prepared-reference-action.vm</view>
</action>
<action name="com.atlassian.jira.dev.reference.plugin.actions.ReferenceAction" alias="ReferenceAction">
<view name="success">templates/actions/reference-action.vm</view> </action>
</actions>
</webwork1>
Работа плагина с учетом нового подхода будет выглядеть следующим образом:
Можно сделать так сказать двойную проверку прав, которая будет происходить средствами самой Jira:
на уровне видимости кнопки – реализуется с помощью condition для web-item;
на уровне action в модуле webwork (на случай если у пользователя осталась ссылка на отчет, а права у него забрали).
Теперь касательно интерфейса. Если мы не можем вклиниться в его генерацию мы просто реализуем свой. В таком случае будет возможность и навести красоту, и вынести валидацию параметров из основного класса отчета в класс для страницы с параметрами.
Соответственно в нашем случае описание модуля будет выглядеть следующим образом:
<webwork1 key="report" name="report-webwork" i18n-name-key="report.name" class="java.lang.Object" roles-required="report-role" >
<description key="report.description">Report-webwork Plugin</description>
<actions>
<action name="com.report.webwork.ReportConfigurationAction" alias="ReportConfiguration">
<view name="report-configuration">/templates/configuration/report-configuration.vm</view>
</action>
<action name="com.report.webwork.WebReportAction" alias="WebReport">
<view name="view">/templates/view/html-view.vm</view>
</action>
</actions>
</webwork1>
Данный подход был предложен заказчику, и он согласился что надо пробовать.
Второй блин
Решили идти итерациями:
перевод текущего функционала на webwork;
добавление дополнительных «хотелок» (подсветка строк в отчете, добавление новых столбцов и т.д.);
решение проблемы с правами.
Основным челенджом первой итерации было написать собственно страницы с параметрами отчета. Тут уж мы обратились за помощью к коллегам из отдела frontend-разработки – обрисовали им ситуацию и что хотим получить на выходе. После некоторого молчания коллеги нам выкатили UI, в котором элементы выглядят уже гораздо приятнее:
От нас оставалось только преобразовать это все в velocity-шаблон и привязать к определенному action в webwork. Конфигурация модуля выглядит следующим образом:
<webwork1 key="report" name="report-webwork" i18n-name-key="report.name" class="java.lang.Object" roles-required="report-role" >
<description key="report.description">Report-webwork Plugin</description>
<actions>
<action name="com.report.webwork.ReportConfigurationAction" alias="ReportConfiguration">
<view name="report-configuration">/templates/configuration/report-configuration.vm</view>
</action>
<action name="com.report.webwork.WebReportAction" alias="WebReport">
<view name="view">/templates/view/html-view.vm</view>
</action>
</actions>
</webwork1>
Классы, которые предоставляли выдающим спискам значения подверглись минимальной доработке, после чего у нас появился красивый экран с параметрами отчета.
Был добавлен web-item, который представлял собой ссылку на action с конфигурацией отчета. В результате в верхней панели появилась кнопка для вызова отчета.
REST-endpoint для экспорта в excel в итоге переехал без изменений.
На выходе получился отчет с приятным интерфейсом, той же функциональностью (за мелкими улучшениями) и реализованный более гибким способом.