Учебник членов роли функции JQL

 

Применимость

Jira 7.0.0 и более поздних версий.

Уровень опыта

Расширенный. Вы должны были пройти хотя бы один промежуточный учебник, прежде чем работать с этим учебником. См. Список обучающих программ для разработчиков.

Временная оценка

Для завершения этого урока вам потребуется около 1 часа.

Обзор учебника

Функции JQL - это мощный инструмент, который улучшает функциональность поиска Jira. Разработчики могут создавать свои собственные JQL-функции с использованием модуля плагина JQL-функции.

В этом учебном курсе показана реализация новой функции JQL, называемой roleMembers. Эта функция возвращает пользователей, которые являются членами конкретной роли проекта Jira.

Первый аргумент - это имя роли, члены которой мы пытаемся найти. Это обязательно. Любые другие аргументы называются проектами, роли которых должны быть проверены. Когда проект не указан, запрашиваются все проекты, которые может видеть поисковик. Например, вызов roleMembers (trole, tproj) найдет всех пользователей в роли trole для проекта tproj. С другой стороны, вызов roleMembers ('testrole') возвращает всех пользователей в роли testrole во всех проектах, которые может видеть поисковик.

Об этих инструкциях

Вы можете использовать любую поддерживаемую комбинацию операционной системы и IDE для создания этого приложения. Эти инструкции были написаны с использованием macOS Sierra и IntelliJ IDEA 2017.3. Если вы используете другую комбинацию, вы должны использовать эквивалентные операции для своей конкретной среды.

 

Этот учебник был последний раз проверен с помощью Jira 7.10.0 с использованием Atlassian Plugin SDK 6.3.10

Прежде чем вы начнете

Чтобы завершить этот учебник, вам необходимо знать следующее:

  1. Основы разработки Java: классы, интерфейсы, методы, использование компилятора и т. д.
  2. Ознакомьтесь с инструментами разработки, такими как Maven и IDE.
  3. Как создать проект Atlassian plugin с помощью Atlassian Plugin SDK.
  4. Как настроить параметры системы Jira.

Источник приложения

Мы рекомендуем вам проработать этот учебник. Если вы хотите пропустить или проверить свою работу, когда закончите, вы можете найти исходный код приложения на Atlassian Bitbucket.

Чтобы клонировать репозиторий, выполните следующую команду:


git clone https://bitbucket.org/atlassian_tutorial/role-members-jql-function

Кроме того, вы можете загрузить исходный код в виде ZIP-архива.

 

Шаг 1. Создайте проект приложения

На этом этапе вы будете использовать команду atlas для создания кода-заглушки для вашего приложения. Команды atlas являются частью SDK Atlassian Plugin и автоматизируют большую часть разработки приложений для вас.

  1. Настройте SDK Atlassian Plugin и создайте проект, если вы этого еще не сделали.
  2. Откройте терминал и перейдите в каталог, в котором вы хотите сохранить код приложения.
  3. Чтобы создать скелет приложения, выполните следующую команду:

atlas-create-jira-plugin

  1. Чтобы определить ваше приложение, введите следующую информацию.

group-id

com.example.plugins.tutorial

artifact-id

role-members-jql-function-tutorial

version

1.0-SNAPSHOT

package

com.example.plugins.tutorial

  1. Перейдите в каталог проекта, созданный на предыдущем шаге.

cd role-members-jql-function-tutorial/

  1. Удалите тестовые каталоги.

 

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


rm -rf ./src/test/java
rm -rf ./src/test/resources/

  1. Удалите ненужные файлы классов Java.

rm -rf ./src/main/java/com/example/plugins/tutorial/*

  1. Импортируйте проект в свою избранную среду IDE.

Шаг 2. Измените POM и добавьте зависимости

Рекомендуется ознакомиться с файлом конфигурации проекта, известным как POM (то есть, файл определения объектной модели проекта). POM объявляет зависимости вашего приложения, настройки и метаданные (информация о вашем приложении).

Измените POM следующим образом:

  1. Перейдите в каталог role-members-jql-function-tutorial, созданный SDK.
  2. Откройте файл pom.xml.
  3. Добавьте название организации или организации и URL вашего веб-сайта в элемент organization:

<organization>
    <name>Пример компании</name>
    <url>http://www.example.com/</url>
</organization>

  1. Обновите элемент name до более читаемого:

<name>Role members JQL function</name>

Это имя вашего приложения, которое появится на странице «Управление надстройками» в консоли администрирования Jira.

  1. Обновите элемент description:

<description>Этот плагин демонстрирует, как добавить функцию jql в Atlassian Jira </description>

  1. Сохраните файл pom.xml.

Шаг 3. Добавьте  модуль функции JQL в дескриптор приложения

После ознакомления с ссылкой модуля плагина JQL-функции выполните следующие действия:

  1. Перейдите в src / main / resources / и откройте файл atlassian-plugin.xml.
  2. Добавьте модуль jql-function в качестве дочернего элемента atlassian-plugin.

<jql-function key="role-members" i18n-name-key="rolefunc.name" name="Role Members Function"
          class="com.example.plugins.tutorial.RoleFunction">
    <description key="rolefunc.description">JQL function to return the members of a particular role</description>
    <fname>roleMembers</fname>
    <list>true</list>
</jql-function> 
  • Атрибут class идентифицирует класс реализации обработчика com.example.plugins.tutorial.RoleFunction.
  • Элемент fname указывает имя функции, которую пользователи используют в своих JQL-запросах.
  • Элемент list указывает, что наша функция возвращает список значений.
  1. Сохраните файл.

Шаг 4. Добавьте текст пользовательского интерфейса в файл ресурсов i18n

Когда вы создали приложение, SDK сгенерировал вам файл ресурсов i18n. Здесь вызывается текст пользовательского интерфейса. Добавьте в него текстовую строку пользовательского интерфейса следующим образом:

  1. Перейдите в src / main / resources и откройте файл ресурсов role-members-jql-function-tutorial.properties.
  2. Добавьте следующее свойство:

rolefunc.bad.num.arguments={0} function takes one or more arguments
rolefunc.role.not.exist={0} role does not exist
rolefunc.project.not.exist=Project with id or key {0} does not exist 
rolefunc.name=Role Members function 
  1. Сохраните файл

Шаг 5. Создайте реализацию JqlFunction

Теперь давайте создадим функцию JQL, на которую  была ссылка в дескрипторе приложения. Мы собираемся сделать это простым, чтобы начать с этого, и строим этот класс по мере того, как мы идем.

  1. Перейдите в src / main / java / com / example / plugins / tutorial / и создайте новый файл с именем RoleFunction.java.
  2. Добавьте в файл следующий код:

package com.example.plugins.tutorial;
 
import com.atlassian.jira.JiraDataType;
import com.atlassian.jira.JiraDataTypes;
import com.atlassian.jira.jql.operand.QueryLiteral;
import com.atlassian.jira.jql.query.QueryCreationContext;
import com.atlassian.jira.plugin.jql.function.AbstractJqlFunction;
import com.atlassian.jira.user.ApplicationUser;
import com.atlassian.jira.util.MessageSet;
import com.atlassian.jira.util.MessageSetImpl;
import com.atlassian.plugin.spring.scanner.annotation.component.Scanned;
import com.atlassian.query.clause.TerminalClause;
import com.atlassian.query.operand.FunctionOperand;
 
import javax.annotation.Nonnull;
import java.util.Arrays;
import java.util.List;
 
@Scanned
public class RoleFunction extends AbstractJqlFunction {
 
    @Nonnull
    @Override
    public MessageSet validate(ApplicationUser applicationUser,
                               @Nonnull FunctionOperand functionOperand, @Nonnull TerminalClause terminalClause) { return new MessageSetImpl();     }      
 @Nonnull     @Override     public List<QueryLiteral> getValues(@Nonnull QueryCreationContext queryCreationContext                                        
 @Nonnull FunctionOperand functionOperand,                                         @Nonnull TerminalClause terminalClause) {<         return Collections.emptyList();     }       @Override     public int getMinimumNumberOfExpectedArguments() {         return 1;     }       @Nonnull     @Override     public JiraDataType getDataType() {         return JiraDataTypes.USER;     } } 

Пока что исходный код функции JQL не делает много. Но он создает хорошую основу для построения, и это дает нам возможность задуматься над некоторыми концепциями. Обратите внимание на методы в классе:

  • Функция getMinimumNumberOfExpectedArguments () в основном возвращает наименьшее количество аргументов, которые может принимать функция. Значение, возвращаемое этим методом, должно быть согласованным между вызовами метода. Нам нужно как минимум 1 аргумент, являющийся ролью проекта.
  • Метод getDataType () вызывается для определения типа данных, возвращаемых функцией. Значение говорит Jira, с какими предложениями JQL функция, как можно ожидать, будет работать.  В нашем случае мы хотим вернуть пользователей.
  • Методы validate () и getValues () вызывают для проверки аргументов функции JQL присодиняет полученный результат соответственно. Имейте в виду, что эти функции не могут вернуть значение null.
  1. Сохраните файл.

 

Шаг 6. Внедрите проверку RoleFunction

Перед запуском запроса вы должны быть уверены, что аргументы, переданные пользователем, действительны.На этом шаге, в методе validate, вы проверите, что роль проекта и проекты существуют. Поскольку вы будете использовать ProjectService, проекты, которые не отображаются для пользователя, не будут найдены.

  1. В том же файле RoleFunction.java обновите метод validate() следующим:

@Nonnull
@Override
public MessageSet validate(ApplicationUser applicationUser,
                          @Nonnull FunctionOperand functionOperand,
                          @Nonnull TerminalClause terminalClause) {
   MessageSet messages = new MessageSetImpl();
   final List<String> arguments = functionOperand.getArgs();
 
 
//Make sure we have the correct number of arguments.
   
   //Make sure the role is valid.
   final String requestedRole = arguments.get(0);
   ProjectRole role = projectRoleManager.getProjectRole(requestedRole);
   if (role == null) {
       messages.addErrorMessage(getI18n().getText("rolefunc.role.not.exist", requestedRole));
       return messages;
   }
 
   //Make sure the project arguments are valid if provided.
   if (arguments.size() > 1) {
       for (String project : arguments.subList(1, arguments.size())) {
           ProjectService.GetProjectResult result = getProjectResult(project);
           if (!result.isValid()) {
               result.getErrorCollection().getErrorMessages().forEach(messages::addErrorMessage);
               return messages;           }        }    }      return messages; } 

В приведенном выше фрагменте мы проверяем, что:

  • Функция имеет правильное количество аргументов.
  • Функция имеет роль.
  • Существуют проекты.

Если проверка не выполняется, мы добавляем сообщение об ошибке в MessageSet. Валидация успешна только в том случае, если отсутствует сообщение об ошибке .

  1. Чтобы сделать метод валидации работающим, введите ProjectRoleManager и ProjectService с помощью Atlassian Spring Scanner и реализуйте метод getProjectResult ().

В этом примере показана инъекция(вставка) зависимостей:


@JiraImport
private final ProjectRoleManager projectRoleManager;
@JiraImport
private final ProjectService projectService;
 
public RoleFunction(ProjectRoleManager projectRoleManager, ProjectService projectService) {
    this.projectRoleManager = projectRoleManager;
    this.projectService = projectService;
}

GetProjectResult () должен иметь возможность получать проект по ключу проекта или идентификатору проекта. Вот простая реализация:


private ProjectService.GetProjectResult getProjectResult(String project){
    ProjectService.GetProjectResult result = null;
    try {
        result = projectService.getProjectById(Long.parseLong(project));
    } catch (NumberFormatException e){
        result = projectService.getProjectByKey(project);
    }
    return result;
}

  1. Сохраните файл.

Шаг 7. Внедрите метод getValues

  1. Если функция прошла проверку, будет вызван метод getValues. Замените код заглушки следующим текстом:

@Nonnull
@Override
public List<QueryLiteral> getValues(@Nonnull QueryCreationContext queryCreationContext,
                                   @Nonnull FunctionOperand functionOperand,
                                   @Nonnull TerminalClause terminalClause) {
   final List<String> arguments = functionOperand.getArgs();
   //Can't do anything when no argument is specified. This is an error so return empty list.
   if (arguments.isEmpty()) {
       return Collections.emptyList();
   }
 
   final ProjectRole projectRole = projectRoleManager.getProjectRole(arguments.get(0));
   //Role not in system, then do nothing.
   if (projectRole == null) {
       return Collections.emptyList();
   }
 
   final Set<ApplicationUser> users = new HashSet<>();
   //Projects are specified, then look at those projects.
   if (arguments.size() > 1) {
       for (String project : arguments.subList(1, arguments.size())) {
           ProjectService.GetProjectResult result = getProjectResult(project);
           users.addAll(projectRoleManager.getProjectRoleActors(projectRole, result.getProject()).getApplicationUsers());
       }
   } else {
       ServiceOutcome<List<Project>> result = projectService.getAllProjects(queryCreationContext.getApplicationUser());
       for (Project project: result.getReturnedValue()) {
           users.addAll(projectRoleManager.getProjectRoleActors(projectRole, project).getApplicationUsers());       }    }      //Convert all the users to query literals.   
 final List<QueryLiteral> literals = new ArrayList<>();    for (ApplicationUser user : users)
 {        literals.add(new QueryLiteral(functionOperand, user.getKey()));    
}      return literals; } 

Мы используем ProjectRoleManager для получения ProjectRoleActors для указанных роли проекта и проекта, а затем конвертируем их в ApplicationUser.

  1. Сохраните файл.

Шаг 8. Создание, установка и запуск приложения.

Давайте запустим Jira и посмотрим, что у нас есть.

  1. Откройте терминал и перейдите в корневой каталог приложения, где находится pom.xml.
  2. Запустите следующую команду SDK:

atlas-run

Эта команда загружает и запускает Jira с установленным вами приложением.

  1. Откройте экземпляр Jira в браузере и войдите в систему с помощью admin / admin по умолчанию.
  1. Создайте новый проект, проблемы и роли.
  2. Выполните запрос JQl, т. е. assignee IN roleMembers(Administrators, TEST)

РИСУНОК

  1. Создайте нового пользователя и убедитесь, что у пользователя нет разрешений для просмотра проекта.
  1. Войдите в систему с новым пользователем и попробуйте запустить тот же запрос. В результате вы должны увидеть ошибку. Здесь работает наша функция validate ().
  1. Введите имя для вашего обработчика (это может быть что угодно, потому что мы не сохраним его на этот раз), а затем нажмите «Далее».

Обратите внимание на конфигурационную форму для этого обработчика.

РИСУНОК

Отсюда вы можете продолжать работать, когда вы продолжаете разработку приложения. Чтобы перезагрузить приложение, используйте QuickReload. Когда вы работаете, оно восстанавливает ваше приложение за кулисами.

Чтобы использовать QuickReload, выполните следующие действия:

  1. Откройте новое окно терминала и перейдите в корневую папку приложения.
  2. Чтобы перестроить приложение и запустить QuickReload, запустите команду atlas-package.
  3. Когда сборка завершается успешно, Jira перезагружает приложение.
  4. Вернитесь в свой браузер и проверьте свои изменения (возможно, сначала вам нужно обновить страницу браузера).

Шаг 9. Внедрите дезинфицирующее средство для функции JQL

Чтобы сделать функцию действительно готовой, вашей RoleFunction также необходимо внедрить ClauseSanitisingJqlFunction. Сохраненный JQL-поиск (фильтр) может использоваться совместно с несколькими пользователями. Хотя эта функциональность очень полезна, она также позволяет утечке информации. Например, предположим, что у вас есть фильтр, который содержит assignee in roleMembers(Administrators, Proj), и вы делитесь фильтром с Janice, который не может видеть Proj. Однако поиск не вернет никаких результатов, однако Janice будет знать, что проект Proj существует, хотя у нее нет разрешения на его просмотр. Функция JQL, которая может предоставлять конфиденциальную информацию, также должна реализовывать дополнительный интерфейс ClauseSanitisingJqlFunction.

Функция roleMembers должна проверять, что прошедший пользователь может видеть все аргументы проекта. Если все прошедшие проекты видны, мы можем просто вернуть функцию FunctionOperand как переданную. С другой стороны, если некоторые из проектов не видны, то дезинфицирующее средство должно возвращать новую FunctionOperand, в которой имена заменены идентификаторами проекта. Это не идеальное решение, поскольку мы по-прежнему сталкиваемся с тем фактом, что существует проект, который искатель не видит, однако мы больше не позволяем просачиваться имени проекта.

  1. Для реализации функции ClauseSanitisingJqlFunction используйте следующий код:

public FunctionOperand sanitiseOperand(final ApplicationUser user, @Nonnull final FunctionOperand functionOperand) {
    final List<String> arguments = functionOperand.getArgs();
 
    //We only sanitise projects, so just return the original function if there are no projects.
    if (arguments.size() <= 1) {
        return functionOperand;
    }
 
    boolean argChanged = false;
    final List<String> newArgs = new ArrayList<>(arguments.size());
    newArgs.add(arguments.get(0));
    for (final String argument : arguments.subList(1, arguments.size())) {
        final Project project = projectManager.getProjectObjByKey(argument);
        if (project != null && !permissionManager.hasPermission(ProjectPermissions.BROWSE_PROJECTS, project, user)) {
            newArgs.add(project.getId().toString());
            argChanged = true;
        } else {
            newArgs.add(argument);
        }
    }
 
    if (argChanged) {
        return new FunctionOperand(functionOperand.getName(), newArgs);
    } else {
        return functionOperand;
    }
}

ProjectManager, который мы использовали для получения проектов, не выполняет проверку прав и возвращает все проекты, соответствующие запросу. Однако мы используем PermissionManager, чтобы проверить, имеет ли пользователь разрешение Browse и заменить имя проекта на ID. Использование вставку конструктора для ProjectManager и PermissionManager было показано ранее.

  1. Сохраните файл.

Шаг 10. Проверьте свой дезинфицирующее средство

  1. Вернитесь в браузер и войдите в Jira как админ.
  2. Запустите JQL-запрос, как ранее, сохраните фильтр, а затем поделитесь им с logged-in-users.
  3. Войдите в систему как ваш тестовый пользователь без разрешения.
  4. Найдите фильтр, который вы только что создали, и запустите его.

РИСУНОК

  1. Sanitizer заменяет ключ проекта идентификатором проекта.

Следующие шаги

Функция roleMembers, вероятно, должна выполнять некоторые проверки прав на аргумент роли проекта. Если бы такая проверка была выполнена, то дезинфицирующее средство, вероятно, должно было измениться, чтобы отразить любую логику здесь.

Поздравляю, вот и все!

Имейте удовольствие!

Похожие темы

  • Добавление JQL-функции в Jira.
  • JQL-модуль.

По материалам Atlassian JIRA  Server Developer Role Members JQL function tutorial