Jira REST API. Интеграция Jira с Битрикс КП

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.
Как-то у нас исторически сложилось, что Менеджеры сидят в Битрикс КП, а Разработчики в Jira. Менеджеры привыкли ставить и решать задачи через КП, Разработчики — через Джиру. Отсюда пришла мне крайне узкоспециализированная, но крайне интересная задача по частичной синхронизации Джиры и Битрикс КП.

966967c238053bfb5801de52071c1b2c.jpg

Мне не разрешили выкладывать готовый код, но я могу описать здесь все проблемы, с которыми столкнулась.
Ахтунг! Букавмнога, очень многа!

Что требовалось:
  1. При создании задачи в КП, если отмечена галочка синхронизации, - создавать задачу и в Джире.
  2. Опять же, если отмечена галочка синхронизации, то стягивать потраченное время из задач Джиры в задачи КП.
  3. Для уже существующих задач в КП иметь возможность выставить задачу из Джиры ручками, чтобы синхронизовать время.
  4. Стягивание времени осуществляется по соответствующему агенту (у нас они естественно на кроне).
  5. Для уже существующих задач КП иметь возможность создавать задачу в Джире.

Терминология

Джира вообще достаточно оригинальна в использовании непривычных терминов. Поэтому укажу соответствия.
  • issue - запрос. Далее по статье я буду называть issue - задачами Джиры (в противоположность задачам КП).
  • summary - имя задачи, TITLE, заголовок.
  • reporter - кем создана задача, автор задачи, CREATED_BY
  • assignee - на кого задача, ответственный, RESPONSIBLE.
  • project - проект, в рамках которого создаётся задача. наиболее близкое понятие в КП - группа задачи, но соответствие конечно натянутое.
  • worklog - единица отмеченного времени. Для КП это единица отмеченного затраченного времени, или в терминах битрикс апи - elapsed time. Мне нравится термин ворклог, далее в статье я буду его использовать.
Продумываем реализацию

1. добавляем свойство для задачи КП, в котором будем хранить номер задачи в Джире, и свойство для галочки, синхронизовать время или нет. Возможно также создание свойства для хранения типа создаваемой задачи Джиры (Баг, Задание, Новая функция и т.п.)
2. для создания задачи в Джире требуется указать проект (ключ проекта), в рамках которого создаётся эта задача. В нашем КП это свойство группы, в которой создаётся задача. Если свойство "Ключ проекта" в группе не прописано, то задачу в Джире не создаём. (ну или создаём в дефолтовом проекте например)
3. пользователей выясняем по емейлам, можно сделать свойство соответствия в КП - пользователь в Джире. Остальные соответствия достаточно прозрачны.
4. синхронизируем задачи с отмеченной галочкой и с определёнными статусами. вешаем на агента (у нас они естественно на кроне)

Хелло, ворлд

Собственно, на первый разговор с Джирой было потрачено некоторое количество нервов. Но по накоплению опыта, удачного и не очень, всё было сделано.
Во-первых, смотрим на номер версии вашей Джиры и выбираем соответствующий REST API вот отсюда. В основном они одинаковые, но есть нюансы конечно. Надо обращать внимание на метод отправки запроса, от этого зависит действие Джиры. GET - это чаще всего запрос на получение данных, POST - создание, PUT - обновление, DELETE - удаление. Есть исключения из этого правила, лучше всегда смотреть в документацию.

Прежде чем изобретать велосипед были перепробованы несколько проектов с гитхаба.
Потом я плюнула и попробовала просто кодом закурлить список проектов с нашей Джиры.
$objCurl = curl_init();
 
 curl_setopt_array($objCurl, array(
  CURLOPT_URL => JIRA_URL.'/rest/api/latest/project',
  CURLOPT_USERPWD => JIRA_USERNAME . ':' . JIRA_PASSWORD,
  CURLOPT_HTTPHEADER => array('Content-type: application/json'),
  CURLOPT_RETURNTRANSFER => true
 ));
 $result = curl_exec($objCurl);
 curl_close($objCurl);

Радости были полные штаны =)

Примеры простых GET адресов для запросов Джиры
  • /rest/api/latest/project - получить список проектов
  • /rest/api/latest/project/SUPPORT - получить информацию о проекте с ключом SUPPORT
  • /rest/api/latest/issue/SUPPORT-13 - получить информацию о задаче SUPPORT-13
  • /rest/api/latest/priority - список возможных приоритетов для задач
  • /rest/api/latest/issue/SUPPORT-13/worklog - список ворклогов для задачи с ключом SUPPORT-13
createmeta

Запрос данных у createmeta осуществляется методом GET, но возможно указание дополнительных параметров. Для чего он нужен?
Для каждого проекта в Джире могут быть ограничения как типы создаваемых задач, так и поля этих задач. Так вот createmeta позволяет узнавать данные, необходимые для создания задачи.
Имеется возможность фильтрации по:
  • проекту - указываются ключи projectKeys или айдишки projectIds проекта
  • типу задач - указываются айдишки issuetypeIds или названия issutypeNamesтипов задач
Дополнительно можно расширить запрос указанием expand, например поля задач можно получить только при указании expand=projects.issuetypes.fields
Например, получим свойства полей задач, для проектов SUPPORT и TEST, задач типа Баг (айди 1).
/rest/api/latest/issue/createmeta?projectKeys=SUPPORT,TEST&issuetypeIds=1&expand=projects.issuetypes.fields

Тестовое создание задачи в Джире

Создание осуществляется POST запросом по адресу /rest/api/latest/issue. Посмотрим в документацию:
Запрос создаёт задачу или подзадачу из массива, представленного в формате JSON.
Поля (а также параметры полей), которые могут быть заполнены при создании, вы можете выяснить у createmeta.

Все задачи у нас в Джире от проекта к проекту не отличаются структурой и типами, поэтому я один раз спросила список полей на заполнение и сформировала массив, который нужен для создания задачи в Джире (чем быстрее, тем лучше; если всё делать правильно, то закопаемся).

$arFields = array(
 'fields'=>array(
  'summary' => 'Test create task',
  'description' => 'test description task',
  'issuetype' => array(
   'id' => 1
  ),
  'assignee' => array(
   'name' => 'c_piper'
  ), 
  'reporter' => array(
   'name' => 'c_piper'
  ),
  'project' => array(
   'key' => 'SUPPORT'
  ),
  'duedate' => '2016-01-16',
  'priority' => array(
   'name' => 'Низкий'
  )
 )
);

Этот массив надо json_encode и отправить POST-запросом Джире. Как? Читаем дальше.

Методы отправки запросов

Все методы регулируются опциями curl, которые соответственно добавляем в зависимости от того, чего хотим добиться, например для метода POST добавляем

if ($request_type == 'POST')
 {
  curl_setopt($objCurl, CURLOPT_POST, true);
  curl_setopt($objCurl, CURLOPT_POSTFIELDS, $jdata);
 }

Где jdata - это json закодированный массив данных (например, на добавление задачи Джиры)

Итак, чтобы добавить задачу Джиры:
  1. формируем массив с полями, необходимыми для создания задачи
  2. делаем json_encode этих полей
  3. отправляем POST запрос Джире
Пользовательские свойства задачи в КП

Пользовательское свойство задачи создаётся через админку Настройки -- Настройки продукта -- Пользовательские поля -- Добавить. Пользовательское поле задачи отличается тем, что при создании в пункте Объект надо прописать TASKS_TASK, а Код поля принято начинать с префикса UF_

7c12bbc2bbf01456ee2f033efd0e9c81.png


Соответствия полей задач КП и полей задач Джиры

После добавления задачи в Битрикс КП происходит событие onTaskAdd, на него и вешается создание задачи в Джире.
Приоритетов к задачах КП всего три, прописываем соответствия для Джиры
  • CTasks::PRIORITY_LOW = Низкий
  • CTasks::PRIORITY_AVERAGE = Средний
  • CTasks::PRIORITY_HIGH = Высокий
Соответствия пользователей из КП и из Джиры мы составляем используя значения пользовательского поля типа список для пользователя КП. Если что, значения пользовательского поля типа список можно получить вот так:

$hJU = CUserFieldEnum::GetList(array(), array('USER_FIELD_ID' => $uf_id));
while($row = $hJU->Fetch())
{
    echo $row['ID'].' = '.$row['VALUE'].'
';
}

Секунды задачи из КП нужно преобразовать в вид Джиры, это справитесь сами.

Итак, для создания задачи в Джире после создания задачи в Битрикс КП:
  • заводим обработчик события onTaskAdd
  • в обработчике осуществляем перевод нужных полей из Битрикс задачи в соответствующие значения Джира задачи
  • переводим этот массив в json
  • отправляем POST-запрос Джире
  • узнаём по ответу ключ задачи и прописываем его через CTasks::Update
Также, чтобы иметь возможность создания задач в Джире для уже существующих задач в КП, надо:
  • заводим обработчик OnBeforeTaskUpdate
  • если до этого у задачи КП была проставлена галочка синхронизации или заполнено поле ключа задачи в Джире, то ничего не делаем, иначе - продолжаем
  • в обработчике осуществляем перевод нужных полей из Битрикс задачи в соответствующие значения Джира задачи
  • переводим массив в json
  • отправляем POST-запрос джире на создание задачи
  • по ответу от Джиры узнаём ключ созданной задачи и записываем его в массив$arFields, который поступает в обработчик события
Синхронизация ворклогов

Так как разработчики отмечают время в Джире, то надо переносить это время в КП. Продумаем реализацию.
  • создаём агента, внутри которого и будет происходить вся обработка
  • спрашиваем у КП задачи (CTasks::GetList) определённого статуса, у которых есть галочка синхронизации и заполнено поле Ключа задачи в Джире
  • для этих задач в КП спрашиваем уже существующие ворклоги (CTaskElapsedTime::GetList)
  • для найденных ключей задач в Джире спрашиваем ворклоги
  • сравниваем уже существующие в КП с теми, которые пришли из Джиры
  • прописываем если надо в КП новые ворклоги (CTaskElapsedTime::Add)
Получить ворклоги от Джиры можно просто сделав GET-запрос /rest/api/latest/issue/SUPPORT-13/worklog
где SUPPORT-13 -- это ключ задачи в Джире

Проблема только в том, что нельзя получить у Джиры все ворклоги сразу для массива ключей задач Джиры, поэтому, сами понимаете, приходится получать их в цикле.
Чтобы опознать ворклоги, добавленные из Джиры, в начале комментария к ворклогу добавляется метка, содержащая ключ задачи Джиры, айди ворклога и подпись что ворклог импортирован из Джиры.

f5ea04433117bfe73ac2783ede24b603.png

При добавлении ворклога в КП есть одна особенность: если отдельно не прописать пользователя, создавшего ворклог, то в КП автором ворклога будет считаться Админ с id=1, хотя время и пропишется для пользователя-ответственного.
//формируем массив на добавление
$arWFields = array(
 //кому время добавляется
 'USER_ID' => $arUser[$worklog->author->name], 
 'TASK_ID' => $cp_task_id,
 'MINUTES' => floor($worklog->timeSpentSeconds/60),
 'COMMENT_TEXT' => $worklog->comment
);
//кто создатель времени
$arParams = array('USER_ID' => $arUser[$worklog->author->name]);
//добавление ворклога
$objCTET = new CTaskElapsedTime();
$objCTET->Add($arWFields, $arParams);

Источник: https://www.infospice.ru/blog/bitrix_inside/jira-rest-api-i-bitriks-kp-uchimsya-razgovarivat-s-jira-sinkhronizatsi/


Интересные статьи

Интересные статьи

Продолжаю публикацию решений отправленных на дорешивание машин с площадки HackTheBox. Надеюсь, что это поможет хоть кому-то развиваться в области ИБ. В данной статье повозимся с docker regisrty...
Сравнивать CRM системы – дело неблагодарное. Очень уж сильно они отличаются в целях создания, реализации, в деталях.
Привет, Хабр! Меня зовут Роман, и я хочу рассказать сегодня о том, как мы в университете Иннополис разрабатывали тестовый стенд и сервис для системы Acronis Active Restore, которая скоро должна с...
Дисклеймер Я — разработчик. Я пишу код, с базой данных взаимодействую лишь как пользователь. Я ни в коем случае не претендую на должность системного администратора и, тем более, dba. Но… Та...
Cтатья будет полезна тем, кто думает какую выбрать CMS для интернет-магазина, сравнивает различные движки, ищет в них плюсы и минусы важные для себя.