Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mdemyanov/ffd79c349a1e4691fdbc20473fdbccf0 to your computer and use it in GitHub Desktop.
Save mdemyanov/ffd79c349a1e4691fdbc20473fdbccf0 to your computer and use it in GitHub Desktop.
[Naumen, ITSM 365] Модуль интеграции с Jira

Базовый модуль для интеграции Naumen SMP / ITSM 365 с JIRA

Базовые настройки

1 настройка модуля

  1. Переопределите глобальные перемнные
    1. JIRA_URL - адрес вашего сервера JIRA
    2. LOGIN - логин пользователя с правами на: создать/изменить/получить issue, а также добавлять в них комментарии и файлы
    3. PASS - пароль пользователя
  2. Добавьте модуль в соответствующий каталог инструкция
    1. Код модуля: jiraRest
    2. Версия модуля: 1

2 настройка объектной модели

Добавте новые атрибуты (вы можете связать любые объекты с системой JIRA, в этом примере мы рассмотрим только заявки) Откройте настройки класса "Заявки" и добавьте следующие строковые атрибуты

  1. Название Ключ jira, Код jiraKey, Редактируемый: нет
  2. Название Ссылка jira, Код jiraSelf, Редактируемый: нет
  3. Название Идентификатор jira, Код jiraId, Редактируемый: нет

3 настройка сценариев интеграции

Настройте типовой пример

  1. Добавьте новое действие по событию
  2. Названия и код действия, а также прилагаемого скрипта заполняйте по своему усмотрению, главное, чтобы вы и другие технологи могли быстро разобраться, что это за действие и зачем оно нужно.
  3. Объекты: выберите "Заявка" или "Запрос" из выпадающего списка
  4. Проставьте галочку напростив пункта "Взаимодействие с внешней системой"
  5. Добавьте новый скрипт из примера 2. Создать issue в jira при создании заявки.groovy
  6. Нажмите "Сохранить" затем "Включить"

Методы интеграции: подробности в исходнике

Объект JIRA

  • createIssue - Функция для создания issue
    • @param fields - ассоциативный массив полей для заполнения
    • @return объект Issue
  • editIssue - Функция для редактирования issue
    • @param id - идентификатор issue в Jira
    • @param fields - ассоциативный массив полей для заполнения
    • @return объект Issue
  • getIssue - Функция для получения issue.
  • addCommentToIssue - Функция для добавления комментария к заявке в Jira.
    • @param id - идентификатор issue в Jira
    • @param text - текст комментария
    • @return массив с ответом сервера
  • getCommentsFromIssue - Функция для получения комментариев из заявки в Jira.
    • @param id - идентификатор issue в Jira
    • @return массив с ответом сервера
  • addFileToIssue - Функция для добавления файла к заявке в Jira.
    • @param id - идентификатор issue в Jira
    • @param file - файл в системе NSMP
    • @return Функция ничего не возвращает.
  • addFilesToIssue - * Функция для добавления файлов к заявке в Jira.
    • @param id - идентификатор issue в Jira
    • @param file - коллекция файлов в системе NSMP
    • @return Функция ничего не возвращает.

Объект ISSUE

  • editIssue - Функция для редактирования issue
    • @param fields - ассоциативный массив полей для заполнения
    • @return объект Issue
  • getIssue - Функция для получения issue.
  • addCommentToIssue - Функция для добавления комментария к заявке в Jira.
    • @param text - текст комментария
    • @return массив с ответом сервера
  • getCommentsFromIssue - Функция для получения комментариев из заявки в Jira.
    • @return массив с ответом сервера
  • addFileToIssue - Функция для добавления файла к заявке в Jira.
    • @param file - файл в системе NSMP
    • @return Функция ничего не возвращает.
  • addFilesToIssue - * Функция для добавления файлов к заявке в Jira.
    • @param file - коллекция файлов в системе NSMP
    • @return Функция ничего не возвращает.

Как пользоваться

Как пользоваться модулем в скриптах

В любом скрипте системы вы можете вызвать modules.jiraRest.jira - эта контрукция венет экземпляр объекта Jira, используя методы которого вы можете создавать или получать информацию по issue. Если вы хотите выполнить действия в JIRA от произвольного пользователя - используйте метод modules.jiraRest.getJira(login, pass).

/*! UTF8 */
//Автор: mdemyanov
//Дата создания: 2019-04-08
//Код:
//Код модуля: jiraRest
//Назначение:
/**
* Базовые методы для интеграции с JIRA
* @param JIRA_URL адрес сервера
* @param API версия API
* @param LOGIN логин пользователя
* @param TOKEN пароль пользователя
*/
//Версия: 4.8.*
//Категория:
import groovy.transform.Field
import groovyx.net.http.HTTPBuilder
import groovyx.net.http.ContentType
import org.apache.http.entity.mime.HttpMultipartMode
import org.apache.http.entity.mime.MultipartEntity
import org.apache.http.entity.mime.content.ByteArrayBody
import org.apache.commons.io.FileUtils
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import static groovyx.net.http.ContentType.JSON
import static groovyx.net.http.Method.POST
import static groovyx.net.http.Method.PUT
//Параметры------------------------------------------------------
@Field String JIRA_URL = 'https://company.atlassian.net/'
@Field String API = 'rest/api/2/'
@Field String LOGIN = 'user@email.ru'
@Field String TOKEN = 'tokenFromAtlassianId'
class Jira {
static final Logger LOGGER = LoggerFactory.getLogger(Jira.class)
static final String ISSUE_PATH = 'issue/'
static final String OBJ_COMMENT_PATH = '/comment'
static final String OBJ_FILE_PATH = "/attachments"
static final String LOGGER_PRE_MSG = '[Модуль интеграции с JIRA] '
static final String CREATE_ERROR = 'ошибка создания объекта'
static final String EDIT_ERROR = 'ошибка редактирования объекта'
static final String GET_ERROR = 'ошибка получения объекта'
static final String ADD_FILE_ERROR = 'ошибка добавления файла'
static final String FILE_ERROR = 'ошибка скачивания файла'
static final List SUCCESS_CODES = [200, 201, 204]
HTTPBuilder client
String authString
Jira(String baseUrl, String login, String pass) {
client = new HTTPBuilder(baseUrl)
authString = "${login}:${pass}".getBytes().encodeBase64().toString()
client.setHeaders([Authorization: "Basic $authString"])
client.ignoreSSLIssues()
}
/**
* Функция для создания issue.
* @param fields - ассоциативный массив полей для заполнения
* @return объект Issue
*/
Issue createIssue(Map fields) {
Map body = [
fields: fields
]
Issue issue
try {
client.post(path: ISSUE_PATH,
requestContentType: JSON,
body: body
) { response, reader ->
if (SUCCESS_CODES.contains(response.statusLine.statusCode)) {
issue = new Issue(this, reader)
} else {
LOGGER.error("$LOGGER_PRE_MSG$CREATE_ERROR (сервер ответил: ${response.statusLine})")
}
}
} catch (Exception e) {
LOGGER.error(LOGGER_PRE_MSG + CREATE_ERROR, e)
}
return issue
}
/**
* Функция для редактирования issue.
* @param id - идентификатор issue в Jira
* @param fields - ассоциативный массив полей для заполнения
* @return объект Issue
*/
Issue editIssue(String id, Map fields) {
Map bodies = [
fields: fields
]
Issue issue
try {
client.request(PUT) { request ->
uri.path = ISSUE_PATH + id
requestContentType = JSON
body = bodies
response.success =
{
resp ->
if (resp.statusLine.statusCode == 204) {
LOGGER.info(LOGGER_PRE_MSG + 'Отредактирована заявка' + resp.getEntity()?.content.text)
}
}
response.failure = {
resp ->
LOGGER.error(LOGGER_PRE_MSG + EDIT_ERROR + resp.statusLine)
}
}
}
catch (Exception e) {
LOGGER.error("$LOGGER_PRE_MSG ошибка редактирования issue: ${id}", e)
}
return issue
}
/**
* Функция для получения issue.
* @param id - идентификатор issue в Jira
* @param query - необязательный параметр с опциями поиска
* (https://docs.atlassian.com/software/jira/docs/api/REST/8.1.0/?_ga=2.89757272.2131659664.1554716400-1621280212.1547636494#api/2/issue-getIssue)
* @return объект Issue
*/
Issue getIssue(String id, Map query = [:]) {
Issue issue
try {
client.get(path: ISSUE_PATH + id, query: query) { response, reader ->
issue = new Issue(this, reader)
}
} catch (Exception e) {
LOGGER.error(LOGGER_PRE_MSG + GET_ERROR, e)
}
return issue
}
/**
* Функция для добавления комментария к заявке в Jira.
* @param id - идентификатор issue в Jira
* @param text - текст комментария
* @return массив с ответом сервера
*/
Map addCommentToIssue(String id, String text) {
def result
try {
result = client.post(
path: ISSUE_PATH + id + OBJ_COMMENT_PATH,
requestContentType: JSON,
body: [
body: text
]
)
}
catch (Exception e) {
LOGGER.error("$LOGGER_PRE_MSG ошибка добавления комментария к issue: ${id}", e)
}
return result
}
def getTransition(String from, String to){
def status = utils.findFirst('reference$status', ['title': to])
if(status==null){
LOGGER.error("Переход в статус ${to} не описан в справочнике.")
return -1
}
return status.jira
}
def getFile(String content){
File file = File.createTempFile("dlup", ".dat")
def res
try {
client.get(uri: content, contentType: ContentType.BINARY) { response, reader ->
FileUtils.copyInputStreamToFile(reader, file)
res = FileUtils.readFileToByteArray(file)
}
} catch (Exception e) {
LOGGER.error(LOGGER_PRE_MSG + FILE_ERROR, e)
}
return res
}
/**
* Функция для получения комментариев из заявки в Jira.
* @param id - идентификатор issue в Jira
* @return массив с ответом сервера
*/
Map getCommentsFromIssue(String id) {
def result
try {
result = client.get(
path: ISSUE_PATH + id + OBJ_COMMENT_PATH,
requestContentType: JSON
)
}
catch (Exception e) {
LOGGER.error("$LOGGER_PRE_MSG ошибка получения комментариев issue: ${id}", e)
}
return result
}
/**
* Функция для добавления файла к заявке в Jira.
* @param id - идентификатор issue в Jira
* @param file - файл в системе NSMP
* @return Функция ничего не возвращает.
*/
void addFileToIssue(String id, def file, def bytefile) {
try {
def content = new ByteArrayBody(bytefile, file.mimeType, file.title)
client.request(POST) { request ->
uri.path = ISSUE_PATH + id + OBJ_FILE_PATH
headers.'X-Atlassian-Token' = 'nocheck'
requestContentType: "multipart/form-data"
MultipartEntity multiPartContent = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE)
multiPartContent.addPart("file", content)
request.setEntity(multiPartContent)
response.success =
{
resp ->
if (resp.statusLine.statusCode == 200) {
LOGGER.info(LOGGER_PRE_MSG + 'добавлен файл' + resp.getEntity()?.content.text)
}
}
response.failure = {
resp ->
LOGGER.error(LOGGER_PRE_MSG + ADD_FILE_ERROR + resp.statusLine)
}
}
}
catch (Exception e) {
LOGGER.error("$LOGGER_PRE_MSG ошибка добавления файла к issue: ${id}", e)
}
}
/**
* Функция для добавления файлов к заявке в Jira.
* @param id - идентификатор issue в Jira
* @param file - коллекция файлов в системе NSMP
* @return Функция ничего не возвращает.
*/
void addFilesToIssue(String id, List files) {
files.each { file -> addFileToIssue(id, file) }
}
}
class Issue {
Jira jira
Map data
Issue(Jira jiraObject, Map issueData) {
jira = jiraObject
data = issueData
}
String getId() { return data.id }
String getKey() { return data.key }
String getSelf() { return data.self }
/**
* Функция для получения issue.
* @param query - необязательный параметр с опциями поиска
* (https://docs.atlassian.com/software/jira/docs/api/REST/8.1.0/?_ga=2.89757272.2131659664.1554716400-1621280212.1547636494#api/2/issue-getIssue)
* @return объект Issue
*/
Issue getIssue() {
return jira.getIssue(id)
}
/**
* Функция для редактирования issue.
* @param fields - ассоциативный массив полей для заполнения
* @return объект Issue
*/
Issue editIssue(Map fields) {
return jira.editIssue(id, fields)
}
/**
* Функция для добавления комментария к заявке в Jira.
* @param text - текст комментария
* @return массив с ответом сервера
*/
Map addCommentToIssue(String text) {
return jira.addCommentToIssue(id, text)
}
/**
* Функция для получения комментариев из заявки в Jira.
* @return массив с ответом сервера
*/
Map getCommentsFromIssue() {
return jira.getCommentsFromIssue(id)
}
/**
* Функция для добавления файла к заявке в Jira.
* @param file - файл в системе NSMP
* @return Функция ничего не возвращает.
*/
void addFileToIssue(def file) {
jira.addFileToIssue(file)
}
/**
* Функция для добавления файлов к заявке в Jira.
* @param file - коллекция файлов в системе NSMP
* @return Функция ничего не возвращает.
*/
void addFilesToIssue(List files) {
jira.addFilesToIssue(files)
}
}
//Функции--------------------------------------------------------
/**
* Получить экземпляр объекта для работы с JIRA
* @param baseUrl - адрес инсталляции Jira + версия API, например, 'https://testerj.atlassian.net/'
* @param login - логин
* @param pass - пароль
* @return Jira объект с базовыми методами взаимодействия с сервером
* Примеры вызова метода:
* - modules.jiraRest.jira - вернет объект с параметрами по умолчанию, зашитими в модуль (JIRA_URL, LOGIN и тд)
* - modules.jiraRest.getJira(login, pass) - вернет объект для заданного логина и пароля
*/
Jira getJira() {
return new Jira(JIRA_URL + API, LOGIN, TOKEN)
}
Jira getJira(String login, String pass) {
return new Jira(JIRA_URL + API, login, pass)
}
/*! UTF8 */
//Автор: mdemyanov
//Дата создания: 2019-04-09
//Код:
//Назначение:
/**
* Создать issue в jira при создании заявки
*/
//Версия: 4.8.*
//Категория:
//Параметры------------------------------------------------------
// Код проекта в JIRA
def PROJECT = ['key': 'TP']
// Наименование типа issue
def ISSUE_TYPE = ['name': 'Bug']
// Идентификатор приоритета в JIRA
def PRIORITY = ['id': '3']
// Справочник парамтров для копирования из Naumen SMP в JIRA
def SCALL_TO_JIRA = [
summary : 'shortDescr',
description: 'descriptionRTF'
]
// Справочник парамтров для копирования из JIRA в Naumen SMP
def JIRA_TO_SCALL = [
jiraKey: 'key',
jiraSelf: 'self',
jiraId: 'id'
]
//Функции--------------------------------------------------------
//Основной блок -------------------------------------------------
def fields = [
project : PROJECT,
issuetype: ISSUE_TYPE,
priority : PRIORITY
]
fields += SCALL_TO_JIRA.collectEntries { jiraField, attr -> [(jiraField): subject[attr]] }
// Получаем экзепляр JIRA с реквизитами из модуля и вызываем метод для создания issue
def issue = modules.jiraRest.jira.createIssue(fields)
if (issue == null) {
logger.error("Возникла ошибка при попытке создать issue из заявки ${subject.UUID}")
} else {
def attrs = JIRA_TO_SCALL.collectEntries {attr, jiraField-> [(attr): issue.data[jiraField]]}
utils.edit(subject, attrs)
}
@AlexKing90210
Copy link

Добрый день!
Хотел уточнить в описании модуля (Скрипт №1) в строке 161 значение reference$status это элемент системы Naumen SD ? И что это за элемент?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment