Gymterview
senior

Как создать Workflow Post-Function, Condition, Validator?

Расширения workflow позволяют добавлять автоматическую логику при переходах между статусами в Jira: Post-Function выполняет действие после перехода, Condition определяет видимость перехода, Validator проверяет условия перед выполнением.

Post-Function

Код AutoAssignFunction и Factory
<workflow-function key="auto-assign-function"
                   class="com.example.plugin.workflow.AutoAssignFunctionFactory">
    <function-class>com.example.plugin.workflow.AutoAssignFunction</function-class>
    <orderable>true</orderable>
    <unique>false</unique>
    <resource name="view" type="velocity"
              location="templates/workflow/auto-assign-view.vm"/>
    <resource name="input-parameters" type="velocity"
              location="templates/workflow/auto-assign-input.vm"/>
</workflow-function>
public class AutoAssignFunction implements FunctionProvider {

    private static final Logger log = LoggerFactory.getLogger(AutoAssignFunction.class);

    @Override
    public void execute(Map transientVars, Map args, PropertySet ps) {
        MutableIssue issue = (MutableIssue) transientVars.get("issue");
        String groupName = (String) args.get("groupName");

        if (groupName == null || groupName.isEmpty()) {
            log.warn("Имя группы не задано для AutoAssignFunction");
            return;
        }

        GroupManager groupManager = ComponentAccessor.getGroupManager();
        Collection<ApplicationUser> members = groupManager.getUsersInGroup(groupName);

        if (!members.isEmpty()) {
            ApplicationUser assignee = members.iterator().next();
            issue.setAssignee(assignee);
            log.info("Задача {} назначена на {}", issue.getKey(),
                    assignee.getDisplayName());
        }
    }
}

Condition

Пример
public class OnlyReporterCondition extends AbstractJiraCondition {

    @Override
    public boolean passesCondition(Map transientVars, Map args, PropertySet ps)
            throws WorkflowException {
        Issue issue = (Issue) transientVars.get("issue");
        ApplicationUser currentUser = getCallerUser(transientVars, args);

        if (issue == null || currentUser == null) return false;
        return currentUser.equals(issue.getReporter());
    }
}

Validator

Пример
public class CommentRequiredValidator implements Validator {

    @Override
    public void validate(Map transientVars, Map args, PropertySet ps)
            throws InvalidInputException, WorkflowException {

        int minLength = Integer.parseInt(
                (String) args.getOrDefault("minLength", "10"));

        IssueInputParameters inputParams =
                (IssueInputParameters) transientVars.get("issueInputParameters");
        String comment = inputParams != null ? inputParams.getComment() : null;

        if (comment == null || comment.trim().length() < minLength) {
            throw new InvalidInputException(
                    "Комментарий обязателен (минимум " + minLength + " символов)");
        }
    }
}

Частые ошибки

  • Не вызвать issue.store() в Post-Function — изменения не сохраняются
  • Бросать RuntimeException в Condition — нужно возвращать false, а не падать
  • Тяжёлая логика в Condition — вызывается при каждом отображении задачи для определения доступных переходов
  • Порядок Post-Functions важен — встроенные функции Jira (Update Issue, Generate Events) должны идти после кастомных

Как используется в 2026

  • Workflow-расширения — одна из самых востребованных функций DC-плагинов
  • В Cloud аналог — Forge jira:workflowCondition, jira:workflowValidator, jira:workflowPostFunction
  • ScriptRunner покрывает около 80% типичных workflow-сценариев без написания Java-плагина

На собеседовании: чётко разграничьте три типа: Condition (видимость перехода), Validator (проверка перед), Post-Function (действие после). Factory-класс обязателен — он отвечает за UI конфигурации в admin-панели. В Condition нельзя бросать исключения — только возвращать boolean.