UniBPM Connector
Назначение
unibpm-external-task-unibpm — это коннектор UniBPM для выполнения внутренних действий платформы через механизм External Task.
Коннектор предназначен для использования в unibpm-integration-runtime и позволяет процессам вызывать бизнес-функции UniBPM без прямого обращения к REST API из BPMN-модели.
Процессная модель оперирует бизнес-действиями (action), а детали HTTP/Feign, URL и credentials остаются на уровне runtime-конфигурации.
Подход соответствует общей архитектуре External Task в UniBPM:
unibpm-engineпубликует заданиеunibpm-integration-runtimeпринимает его и находит подходящий handler- handler использует
unibpm-api-client unibpm-appисполняет бизнес-логику и возвращает результат
Архитектурный принцип
Коннектор unibpm является бизнесовым, а не техническим.
Это означает, что в BPMN задаются:
uni.connectoruni.actionuni.version
и бизнесовые входы/выходы
В BPMN не должны задаваться:
- HTTP method
- URL
- path
- query parameters
- credentials
Пример правильного подхода:
uni.connector = unibpm
uni.action = comments.add
uni.version = v1
Пример неправильного подхода:
uni.connector = unibpm
uni.action = rest.invoke
unibpm.method = POST
unibpm.path = /api/...
Включение коннектора
unibpm:
integration:
connectors:
unibpm:
enabled: true
Конфигурация API-клиента
Handlers коннектора используют unibpm-api-client.
Credentials и URL определяются не в handler-ах, а на уровне конкретного инстанса unibpm-integration-runtime.
Пример:
unibpm:
client:
url: http://unibpm-app:8099
Способ авторизации зависит от конфигурации unibpm-api-client / FeignClient.
Важно:
External Task handler не должен знать credentials.
Это принципиальная архитектурная договорённость.
Общий контракт External Task
Базовые extension properties
uni.connector = unibpm
uni.action = <business-action>
uni.version = v1
Входные параметры
unibpm.in.<name> = <value or expression>
Выходные параметры
unibpm.out.<name> = <processVariableName>
Подстановка переменных процесса
Поддерживаются выражения вида:
unibpm.in.ticketId = ${ticketId}
Разрешение значений выполняется через ExtensionPropertyResolver.
Проекция результата (ticket.select)
Для операций, возвращающих объект заявки (ticket.get, ticket.duplicate, ticket.move, ticket.merge), поддерживается свойство:
ticket.select = id,number,name
Свойство работает как whitelist-проекция полей ответа UniBPM API.
Поддерживаются:
- простые поля (
id,number,name,priority,impact,dueDate) - вложенные поля через dot notation (
owner.id,owner.name,type.id,type.name,state.id,state.name)
Если ticket.select не задано, по умолчанию возвращаются только поля:
id,number,name
Это сделано для того, чтобы не сохранять в переменные процесса полный JSON заявки, который может быть слишком большим для Camunda (например, из-за body, вложенных объектов и списков).
Результат возвращается в одну переменную процесса:
unibpm.out.result
Пример:
uni.connector = unibpm
uni.action = ticket.get
uni.version = v1
unibpm.in.ticketId = ${ticketId}
ticket.select = id,number,name,owner.id,owner.name,type.id,type.name,priority
Результат:
{
"id": "5f758930-f1bb-48be-b687-d2518a94466a",
"number": 6567,
"name": "Баги. Ошибки при скачивании pdf-файла в filepicker",
"owner": {
"id": "v.ivanova@acme.com",
"name": "Victoria Ivanova"
},
"type": {
"id": "72a23e56-85c6-410b-865c-dbd9b04a6aac",
"name": "Issue"
},
"priority": "VERY_HIGH"
}
Если в ticket.select указано несуществующее поле, в результате для него будет возвращено null.
Базовый абстрактный handler
Все handlers коннектора наследуются от AbstractUnibpmHandler.
Ответственность AbstractUnibpmHandler
- фиксирует
connector = "unibpm" - фиксирует
version = "v1" - даёт helper-методы для чтения extension properties
- централизует использование
ExtensionPropertyResolver
Реализованные operations
На текущем этапе в коннекторе реализованы следующие бизнес-операции:
comments.addapprovals.results.getapprovals.aggregatemessage.sendticket.assign-ownerticket.assign-responsibleticket.change-bodyticket.change-due-dateticket.change-impactticket.change-nameticket.change-priorityticket.change-typeticket.getticket.duplicateticket.moveticket.merge
1. Operation comments.add
Назначение
Добавляет внутренний комментарий к заявке UniBPM.
Комментарий создаётся как INTERNAL.
Action
uni.action = comments.add
Входы
Обязательные
unibpm.in.ticketId
unibpm.in.body
Значения
unibpm.in.ticketId— UUID заявкиunibpm.in.body— текст/HTML комментария
Выходы
Опциональные
unibpm.out.commentId
unibpm.out.createdAt
unibpm.out.commentId— имя process variable, куда будет положен ID созданного комментарияunibpm.out.createdAt— имя process variable, куда будет положено время создания комментария
Пример
uni.connector = unibpm
uni.action = comments.add
uni.version = v1
unibpm.in.ticketId = ${ticketId}
unibpm.in.body = <p>Комментарий, добавленный процессом</p>
unibpm.out.commentId = createdCommentId
unibpm.out.createdAt = createdCommentCreatedAt
Результат
При успешном выполнении handler возвращает ExternalTaskResult со статусом COMPLETED.
Если заданы output-mapping свойства, в процесс будут записаны:
- ID комментария
- время создания комментария
Особенности
- комментарий всегда создаётся как
CommentType.INTERNAL - credentials определяются Feign client configuration
- в BPMN не используются детали REST API
2. Operation approvals.results.get
Назначение
Получает по заявке список сохранённых результатов согласования (ApprovalDecisionDto).
Используется в случаях, когда процессу нужен сырой список решений согласующих.
Операция возвращает все сохранённые результаты согласования по заявке без дополнительной фильтрации по попытке или этапу согласования.
Action
uni.action = approvals.results.get
Входы
Обязательные
unibpm.in.ticketId
Выходы
Опциональные
unibpm.out.results
unibpm.out.count
unibpm.out.results— имя process variable для спискаApprovalDecisionDtounibpm.out.count— имя process variable для количества решений
Пример
uni.connector = unibpm
uni.action = approvals.results.get
uni.version = v1
unibpm.in.ticketId = ${ticketId}
unibpm.out.results = approvalResults
unibpm.out.count = approvalResultsCount
Результат
В процесс могут быть записаны:
approvalResults— список объектовApprovalDecisionDtoapprovalResultsCount— число решений
Если в процессе требуется отбор результатов только по конкретной попытке согласования и этапу, рекомендуется использовать operation approvals.aggregate.
3. Operation approvals.aggregate
Назначение
Получает результаты согласования по заявке и сразу возвращает их в агрегированном бизнес-виде.
Это рекомендуемый вариант для использования в процессе, если требуется сразу принять решение:
- согласовано
- вернуть на доработку
Action
uni.action = approvals.aggregate
Бизнес-логика агрегации
Handler получает из UniBPM результаты согласования по:
- заявке
- попытке согласования
- этапу согласования
После этого применяется правило агрегации, явно заданное в extension properties.
В текущей версии поддерживается правило:
ANY_REJECT_REWORK
Смысл правила:
- если есть хотя бы один результат, равный значению
notApprovedValue, итог =REVISION_REQUIRED - если результатов
notApprovedValueнет, итог =APPROVED
Дополнительно handler:
- считает количество approve
- считает количество reject
- собирает комментарии отказавших
- может вернуть сырой список решений
Входы
Обязательные
unibpm.in.ticketId
unibpm.in.approval.attempt
unibpm.in.approval.stage
unibpm.in.approval.rule
Опциональные
unibpm.in.approval.approvedValue
unibpm.in.approval.notApprovedValue
Значения
unibpm.in.ticketId— UUID заявкиunibpm.in.approval.attempt— номер попытки согласованияunibpm.in.approval.stage— имя этапа согласованияunibpm.in.approval.rule— правило агрегации результатов согласованияunibpm.in.approval.approvedValue— значение, которое трактуется как положительное решение (по умолчаниюAPPROVED)unibpm.in.approval.notApprovedValue— значение, которое трактуется как отрицательное решение (по умолчаниюNOT_APPROVED)
Выходы
Опциональные
unibpm.out.result
unibpm.out.approveCount
unibpm.out.rejectCount
unibpm.out.comments
unibpm.out.rawResults
Значения
unibpm.out.result— итоговое бизнес-решениеunibpm.out.approveCount— количество решений, равныхapprovedValueunibpm.out.rejectCount— количество решений, равныхnotApprovedValueunibpm.out.comments— список комментариев отказавших согласующихunibpm.out.rawResults— сырой списокApprovalDecisionDto
Пример
uni.connector = unibpm
uni.action = approvals.aggregate
uni.version = v1
unibpm.in.ticketId = ${ticketId}
unibpm.in.approval.attempt = ${approvalAttempt}
unibpm.in.approval.stage = ${approvalStage}
unibpm.in.approval.rule = ANY_REJECT_REWORK
unibpm.in.approval.approvedValue = APPROVED
unibpm.in.approval.notApprovedValue = NOT_APPROVED
unibpm.out.result = approvalResult
unibpm.out.approveCount = approvalApproveCount
unibpm.out.rejectCount = approvalRejectCount
unibpm.out.comments = approvalRejectComments
unibpm.out.rawResults = approvalResults
Пример результата
{
"approvalResult": "REVISION_REQUIRED",
"approvalApproveCount": 2,
"approvalRejectCount": 1,
"approvalRejectComments": [
"ne ok"
]
}
Рекомендуемое использование в BPMN
Gateway
${approvalResult == "APPROVED"}
и
${approvalResult == "REVISION_REQUIRED"}
Использование комментариев
approvalRejectComments можно использовать:
- в задаче на доработку
- в комментарии к заявке
- в уведомлениях
- в UI
Рекомендуемый BPMN-паттерн
Для кейса согласования рекомендуется использовать именно approvals.aggregate, а не сырые ApprovalDecisionDto, если процессу нужно принять решение.
Рекомендуемый вариант
Start
->
ServiceTask approvals.aggregate
->
ExclusiveGateway
├─ APPROVED
└─ REVISION_REQUIRED
Почему так лучше
- BPMN остаётся бизнесовой
- gateway использует простую переменную
- не нужно писать сложные FEEL/EL выражения по массиву DTO
- легче сопровождать модель
Обработка ошибок
При технических ошибках handlers выбрасывают ExternalTaskFailureException.
Типовые причины:
- отсутствует обязательная extension property
- не удалось разрешить
${variable} - ticketId имеет некорректный формат UUID
unibpm-appнедоступен- API вернул ошибку
- задано неподдерживаемое правило агрегации в ‘unibpm.in.approval.rule’
Типовой retry timeout:
DEFAULT_RETRY_DURATION_MS = 30_000L
4. Operation message.send
Назначение
Создаёт исходящее сообщение в UniBPM через SenderMessageApi.
Операция используется для постановки сообщения в исходящую очередь UniBPM. Дальнейшая фактическая доставка выполняется внутренними механизмами платформы.
Action
uni.action = message.send
Бизнес-логика
Handler:
- создаёт
SenderMessage - устанавливает
direction = OUTBOX - устанавливает
completed = false - устанавливает
retries = 3 - вызывает
SenderMessageApiClient.createSenderMessage(...) - возвращает в процесс идентификатор созданного сообщения и признак его обработки
Входы
Обязательные
unibpm.in.sender
unibpm.in.recipients
unibpm.in.subject
Опциональные
unibpm.in.copy
unibpm.in.body
unibpm.in.channelName
unibpm.in.inReplyTo
unibpm.in.attachments
Значения
unibpm.in.sender— отправитель сообщенияunibpm.in.recipients— список получателей через запятуюunibpm.in.copy— список получателей копии через запятуюunibpm.in.subject— тема сообщенияunibpm.in.body— тело сообщенияunibpm.in.channelName— имя канала доставкиunibpm.in.inReplyTo— идентификатор родительского сообщения / значение для reply-сценарияunibpm.in.attachments— список UUID вложений через запятую
Выходы
Handler всегда возвращает в process variables:
messageId— UUID созданного сообщенияmessageCompleted— признак того, что сообщение уже обработано системой доставки.
На момент создания сообщения значение обычно равно false,
так как доставка выполняется асинхронно внутренними механизмами UniBPM.
Пример
uni.connector = unibpm
uni.action = message.send
uni.version = v1
unibpm.in.sender = info@reunico.com
unibpm.in.recipients = employee@company.com
unibpm.in.copy = hr@company.com
unibpm.in.subject = Заявление на отпуск согласовано
unibpm.in.body = Добрый день!
Заявление на отпуск согласовано. В настоящее время формируются необходимые документы. После подготовки вам будет направлено уведомление для их подписания.
unibpm.in.channelName = EMAIL
Пример с подстановкой переменных
uni.connector = unibpm
uni.action = message.send
uni.version = v1
unibpm.in.sender = info@reunico.com
unibpm.in.recipients = ${employeeEmail}
unibpm.in.subject = Заявление на отпуск не согласовано
unibpm.in.body = Добрый день!
Заявление на отпуск не согласовано.
Замечания по заявлению: ${approvalRejectComments}
unibpm.in.channelName = EMAIL
unibpm.in.attachments = ${attachmentIds}
Результат
При успешном выполнении handler возвращает ExternalTaskResult со статусом COMPLETED и process variables:
messageIdmessageCompleted
Особенности
- action не отправляет сообщение напрямую во внешний канал, а создаёт запись
SenderMessageв UniBPM unibpm.in.recipients,unibpm.in.copyиunibpm.in.attachmentsпередаются как CSV-строкиunibpm.in.bodyможет быть многострочнымunibpm.in.channelNameявляется опциональным и может использоваться для маршрутизации по каналам доставки- retries исходящего автоматически устанавливается в
3
Рекомендуемое использование в BPMN
Рекомендуется использовать action для пользовательских и системных уведомлений процесса:
- уведомление о согласовании
- уведомление о возврате на доработку
- сервисные сообщения участникам процесса
Пример
Start
->
ServiceTask message.send
->
End
5. Operation ticket.assign-owner
Назначение
Назначает владельца заявки UniBPM или снимает владельца.
Action
uni.action = ticket.assign-owner
Входы
Обязательные
unibpm.in.ticketId
Опциональные
unibpm.in.ownerId
Значения
unibpm.in.ticketId— UUID заявкиunibpm.in.ownerId— идентификатор пользователя, назначаемого владельцем
Если unibpm.in.ownerId не задан, равен null или содержит только пробелы, владелец снимается.
Результат
При успешном выполнении handler возвращает ExternalTaskResult со статусом COMPLETED.
Пример
uni.connector = unibpm
uni.action = ticket.assign-owner
uni.version = v1
unibpm.in.ticketId = ${ticketId}
unibpm.in.ownerId = ${employeeKey}
6. Operation ticket.assign-responsible
Назначение
Назначает ответственного по заявке UniBPM или снимает ответственного.
Action
uni.action = ticket.assign-responsible
Входы
Обязательные
unibpm.in.ticketId
Опциональные
unibpm.in.responsibleId
Значения
unibpm.in.ticketId— UUID заявкиunibpm.in.responsibleId— идентификатор пользователя, назначаемого ответственным
Если unibpm.in.responsibleId не задан, равен null или содержит только пробелы, ответственный снимается.
Результат
При успешном выполнении handler возвращает ExternalTaskResult со статусом COMPLETED.
Пример
uni.connector = unibpm
uni.action = ticket.assign-responsible
uni.version = v1
unibpm.in.ticketId = ${ticketId}
unibpm.in.responsibleId = ${employeeKey}
7. Operation ticket.change-body
Назначение
Изменяет тело заявки UniBPM.
Action
uni.action = ticket.change-body
Входы
Обязательные
unibpm.in.ticketId
Опциональные
unibpm.in.body
Значения
unibpm.in.ticketId— UUID заявкиunibpm.in.body— новое тело заявки
Результат
При успешном выполнении handler возвращает ExternalTaskResult со статусом COMPLETED.
Пример
uni.connector = unibpm
uni.action = ticket.change-body
uni.version = v1
unibpm.in.ticketId = ${ticketId}
unibpm.in.body = <p>Обновлённое описание заявки</p>
8. Operation ticket.change-due-date
Назначение
Изменяет срок исполнения заявки UniBPM.
Action
uni.action = ticket.change-due-date
Входы
Обязательные
unibpm.in.ticketId
Опциональные
unibpm.in.dueDate
Значения
unibpm.in.ticketId— UUID заявкиunibpm.in.dueDate— новая дата срока исполнения в форматеyyyy-MM-dd
Если unibpm.in.dueDate не задан, равен null или содержит только пробелы, срок исполнения снимается.
Результат
При успешном выполнении handler возвращает ExternalTaskResult со статусом COMPLETED.
Пример
uni.connector = unibpm
uni.action = ticket.change-due-date
uni.version = v1
unibpm.in.ticketId = ${ticketId}
unibpm.in.dueDate = 2026-05-01
9. Operation ticket.change-impact
Назначение
Изменяет impact заявки UniBPM.
Action
uni.action = ticket.change-impact
Входы
Обязательные
unibpm.in.ticketId
Опциональные
unibpm.in.impact
Значения
unibpm.in.ticketId— UUID заявкиunibpm.in.impact— новое значениеImpact
Допустимые значения Impact:
VERY_LOWLOWMEDIUMHIGHVERY_HIGH
Если unibpm.in.impact не задан, равен null или содержит только пробелы, значение Impact снимается.
Результат
При успешном выполнении handler возвращает ExternalTaskResult со статусом COMPLETED.
Пример
uni.connector = unibpm
uni.action = ticket.change-impact
uni.version = v1
unibpm.in.ticketId = ${ticketId}
unibpm.in.impact = HIGH
10. Operation ticket.change-name
Назначение
Изменяет наименование заявки UniBPM.
Action
uni.action = ticket.change-name
Входы
Обязательные
unibpm.in.ticketId
Опциональные
unibpm.in.name
Значения
unibpm.in.ticketId— UUID заявкиunibpm.in.name— новое наименование заявки
Результат
При успешном выполнении handler возвращает ExternalTaskResult со статусом COMPLETED.
Пример
uni.connector = unibpm
uni.action = ticket.change-name
uni.version = v1
unibpm.in.ticketId = ${ticketId}
unibpm.in.name = Обновлённое наименование заявки
11. Operation ticket.change-priority
Назначение
Изменяет приоритет заявки UniBPM.
Action
uni.action = ticket.change-priority
Входы
Обязательные
unibpm.in.ticketId
Опциональные
unibpm.in.priority
Значения
unibpm.in.ticketId— UUID заявкиunibpm.in.priority— новое значениеTicketPriority
Допустимые значения TicketPriority:
VERY_LOW— очень низкий: косметика,nice-to-haveLOW— низкий: несущественная проблема / вопросMEDIUM— средний: обычные инциденты / запросы, есть обходные путиHIGH— высокий: работа пользователей блокирована, проблема встречается частоVERY_HIGH— очень высокий: критичный блокер, массовое влияние, бизнес-процессы остановлены
Если unibpm.in.priority не задан, равен null или содержит только пробелы, приоритет снимается.
Результат
При успешном выполнении handler возвращает ExternalTaskResult со статусом COMPLETED.
Пример
uni.connector = unibpm
uni.action = ticket.change-priority
uni.version = v1
unibpm.in.ticketId = ${ticketId}
unibpm.in.priority = VERY_HIGH
12. Operation ticket.change-type
Назначение
Изменяет тип заявки UniBPM.
Action
uni.action = ticket.change-type
Входы
Обязательные
unibpm.in.ticketId
Опциональные
unibpm.in.typeId
Значения
unibpm.in.ticketId— UUID заявкиunibpm.in.typeId— UUID нового типа заявки (TicketType)
Если unibpm.in.typeId не задан, равен null или содержит только пробелы, тип снимается.
Результат
При успешном выполнении handler возвращает ExternalTaskResult со статусом COMPLETED.
Пример
uni.connector = unibpm
uni.action = ticket.change-type
uni.version = v1
unibpm.in.ticketId = ${ticketId}
unibpm.in.typeId = ${ticketTypeId}
13. Operation ticket.get
Назначение
Получает заявку UniBPM и возвращает в процесс её проекцию.
Action
uni.action = ticket.get
Входы
Обязательные
unibpm.in.ticketId
Опциональные
ticket.select
Значения
unibpm.in.ticketId— UUID заявкиticket.select— whitelist-список полей результата через запятую
Если ticket.select не задано, по умолчанию возвращаются только поля id, number, name.
Выходы
Handler возвращает одну process variable:
unibpm.out.result
Пример
uni.connector = unibpm
uni.action = ticket.get
uni.version = v1
unibpm.in.ticketId = ${ticketId}
ticket.select = id,number,name,owner.id,owner.name,state.id,state.name
14. Operation ticket.duplicate
Назначение
Создаёт дубликат заявки UniBPM и возвращает в процесс проекцию созданной заявки.
Action
uni.action = ticket.duplicate
Входы
Обязательные
unibpm.in.ticketId
Опциональные
ticket.select
Значения
unibpm.in.ticketId— UUID исходной заявкиticket.select— whitelist-список полей результата через запятую
Если ticket.select не задано, по умолчанию возвращаются только поля id, number, name.
Выходы
Handler возвращает одну process variable:
unibpm.out.result
Пример
uni.connector = unibpm
uni.action = ticket.duplicate
uni.version = v1
unibpm.in.ticketId = ${ticketId}
ticket.select = id,number,name,workflow.id,workflow.name,type.id,type.name
15. Operation ticket.move
Назначение
Перемещает заявку UniBPM в другое пространство (workflow) и при необходимости меняет её тип.
Action
uni.action = ticket.move
Входы
Обязательные
unibpm.in.ticketId
unibpm.in.workflowId
Опциональные
unibpm.in.ticketTypeId
ticket.select
Значения
unibpm.in.ticketId— UUID заявкиunibpm.in.workflowId— UUID целевого workflowunibpm.in.ticketTypeId— UUID целевого типа заявкиticket.select— whitelist-список полей результата через запятую
Если unibpm.in.ticketTypeId не задан, равен null или содержит только пробелы, тип заявки при перемещении не изменяется.
Если ticket.select не задано, по умолчанию возвращаются только поля id, number, name.
Выходы
Handler возвращает одну process variable:
unibpm.out.result
Пример
uni.connector = unibpm
uni.action = ticket.move
uni.version = v1
unibpm.in.ticketId = ${ticketId}
unibpm.in.workflowId = ${targetWorkflowId}
unibpm.in.ticketTypeId = ${targetTicketTypeId}
ticket.select = id,number,name,workflow.id,workflow.name,type.id,type.name
16. Operation ticket.merge
Назначение
Объединяет две заявки UniBPM и возвращает в процесс проекцию целевой заявки после объединения.
Action
uni.action = ticket.merge
Входы
Обязательные
unibpm.in.sourceTicketId
unibpm.in.targetTicketId
Опциональные
ticket.select
Значения
unibpm.in.sourceTicketId— UUID исходной заявки, которая будет объединенаunibpm.in.targetTicketId— UUID целевой заявки, в которую выполняется объединениеticket.select— whitelist-список полей результата через запятую
Если ticket.select не задано, по умолчанию возвращаются только поля id, number, name.
Выходы
Handler возвращает одну process variable:
unibpm.out.result
Пример
uni.connector = unibpm
uni.action = ticket.merge
uni.version = v1
unibpm.in.sourceTicketId = ${sourceTicketId}
unibpm.in.targetTicketId = ${targetTicketId}
ticket.select = id,number,name,state.id,state.name,workflow.id,workflow.name
План дальнейшего развития
Потенциальные следующие business actions:
tickets.searchcomments.listattachments.signattachments.verifyworkflows.startpermissions.check
Итог
unibpm-external-task-unibpm — это системный бизнес-коннектор UniBPM для вызова внутренних возможностей платформы через External Task.
Его ключевые свойства:
- бизнесовый контракт
- отсутствие transport-деталей в BPMN
- использование
unibpm-api-client - отсутствие knowledge о credentials внутри handler-ов
- поддержка ticket-operations для работы с заявками UniBPM
- удобство расширения новыми
action