Всем привет!
В этой статье продолжим разговор про разработку собственных функций для SIL. Предыдущую статью можно почитать вот здесь.
Одним из главных преимуществ SIL является то, что код, написанный на SIL, будет работать на всех версиях Jira, поэтому нам нужно сделать так, чтобы наши кастомные функции работали бы также.
Клонируем архетип:
Устанавливем архетип в локальный репозиторий и создаем плагин из этого архетипа. Подробнее, как это сделать, можно почитать вот тут.
Для того, чтобы сделать нашу функцию переносимой между разными версиями Jira, мы будет использовать сервисы, которые предоставляет SIL. Т.е. мы будет проксировать наши вызовы к Jira Java API через SIL Java Api.
Но как узнать, какие сервисы предоставляет SIL?
Устанавливаем SIL и переходит по следующему адресу:
localhost:2990/jira/plugins/servlet/upm/osgi
Вы увидите информацию OSGI по все плагинам в Jira:
Найдем в списке KATL Commons плагин и раскроем Registered services:
JMUserServices, JMProjectServices это сервисы, которые экспортирует SIL.
Теперь мы знаем, какие SIL сервисы доступны, и по названию сервиса мы можем догадаться о его предназначении.
Теперь мы знаем какие сервисы нам доступны. Давайте используем их в нашем плагине.
Этот класс содержит все сервисы, которые мы будем использовать для наших функций. Я создаю отдельный такой класс для того, чтобы не дублировать обращение к сервисам в каждой функции.
Давайте посмотрим вот на эти строчки:
Getter это Lombok аннотация, которая создает get метод для свойства kIssueService.
Мы создаем свойства класса для каждого используемого сервиса. Далее мы получаем эти сервисы через конструктор.
Готово.
Добавляем бин BeanService в класс ESLauncher и передаем этот бин в наши функции:
Теперь мы можем использовать BeanService в наших функциях.
В SayHello.java мы сделаем функцию, которая будет принимать электронный адрес пользователя, проверять, что пользователь, который выполняет скрипт администратор, удалять все тикеты, которые были созданы пользователем, с переданным электронным адресом.
Конечно, мы можем сделать эту логику и без написания нашей собственной функции. Код на SIL будет выглядеть вот так:
Но я хотел сделать небольшой пример, который бы показал основные принципы работы.
Вот текст класса:
Мы реализуем логику в методе runRoutine:
Я сделал комментарии по коду.
Хотел бы отдельно рассказать про этот сервис. В SIL во все методы, где требуется передать кастомное поле, мы можем передавать либо имя поля, либо идентификатор поля в формате customfield_NNNNN, либо алиас поля.
Если бы мы не использовали сервисы SIL, то нам пришлось бы реализовывать эту логику самостоятельно. Но используя сервис KIssueFieldsService мы задействуем эту функциональность одной строчкой:
Теперь Вы знаете, как создавать переносимы между версиями Jira функции SIL.
В этой статье продолжим разговор про разработку собственных функций для SIL. Предыдущую статью можно почитать вот здесь.
Одним из главных преимуществ SIL является то, что код, написанный на SIL, будет работать на всех версиях Jira, поэтому нам нужно сделать так, чтобы наши кастомные функции работали бы также.
Создаем плагин из sil-extension-archetype
Клонируем архетип:
git clone https://alex1mmm@bitbucket.org/alex1mmm/sil-extension-archetype.git --branch v1 --single-branch
Устанавливем архетип в локальный репозиторий и создаем плагин из этого архетипа. Подробнее, как это сделать, можно почитать вот тут.
Добавляем сервисы SIL
Для того, чтобы сделать нашу функцию переносимой между разными версиями Jira, мы будет использовать сервисы, которые предоставляет SIL. Т.е. мы будет проксировать наши вызовы к Jira Java API через SIL Java Api.
Но как узнать, какие сервисы предоставляет SIL?
Устанавливаем SIL и переходит по следующему адресу:
localhost:2990/jira/plugins/servlet/upm/osgi
Вы увидите информацию OSGI по все плагинам в Jira:
Найдем в списке KATL Commons плагин и раскроем Registered services:
JMUserServices, JMProjectServices это сервисы, которые экспортирует SIL.
Теперь мы знаем, какие SIL сервисы доступны, и по названию сервиса мы можем догадаться о его предназначении.
Посмотрим код в созданном плагине
Теперь мы знаем какие сервисы нам доступны. Давайте используем их в нашем плагине.
BeanService.java
@Named
public class BeanService {
@Getter
private final KIssueService kIssueService;
@Getter
private final ClassLoaderService classLoaderService;
@Getter
private final CurrentUserHelper currentUserHelper;
@Getter
private final JMIssueSearchServices jmIssueSearchServices;
@Getter
private final UserHelper userHelper;
@Getter
private final JMIssueServices jmIssueServices;
@Inject
public BeanService(@ComponentImport KIssueService kIssueService,
@ComponentImport CurrentUserHelper currentUserHelper,
@ComponentImport JMIssueSearchServices jmIssueSearchServices,
@ComponentImport UserHelper userHelper,
@ComponentImport JMIssueServices jmIssueServices,
ClassLoaderService classLoaderService) {
this.kIssueService = kIssueService;
this.classLoaderService = classLoaderService;
this.currentUserHelper = currentUserHelper;
this.jmIssueSearchServices = jmIssueSearchServices;
this.userHelper = userHelper;
this.jmIssueServices = jmIssueServices;
}
}
Этот класс содержит все сервисы, которые мы будем использовать для наших функций. Я создаю отдельный такой класс для того, чтобы не дублировать обращение к сервисам в каждой функции.
Давайте посмотрим вот на эти строчки:
@Getter
private final KIssueService kIssueService;
Getter это Lombok аннотация, которая создает get метод для свойства kIssueService.
Мы создаем свойства класса для каждого используемого сервиса. Далее мы получаем эти сервисы через конструктор.
public BeanService(@ComponentImport KIssueService kIssueService,
@ComponentImport CurrentUserHelper currentUserHelper,
@ComponentImport JMIssueSearchServices jmIssueSearchServices,
@ComponentImport UserHelper userHelper,
@ComponentImport JMIssueServices jmIssueServices,
ClassLoaderService classLoaderService) {
this.kIssueService = kIssueService;
this.classLoaderService = classLoaderService;
this.currentUserHelper = currentUserHelper;
this.jmIssueSearchServices = jmIssueSearchServices;
this.userHelper = userHelper;
this.jmIssueServices = jmIssueServices;
}
Готово.
Добавляем бин BeanService в класс ESLauncher и передаем этот бин в наши функции:
/* Создаем свойство BeanService */
private final BeanService beanService;
@Inject
public ESLauncher(@ComponentImport EventPublisher eventPublisher,
PluginInfoService pluginInfoService,
@ComponentImport CommonPluginConfigurationService commonPluginConfigurationService,
@ComponentImport HostConfigurationProvider hostConfigurationProvider,
@ClasspathComponent PluginConfigurationServiceImpl pluginConfigurationService,
/* Инжектим BeanService */
BeanService beanService)
{
super(eventPublisher, pluginInfoService, hostConfigurationProvider, pluginConfigurationService);
log.error("eslauncher constructor");
this.beanService = beanService;
}
@Override
public void doAtLaunch() {
super.doAtLaunch();
log.error("eslauncher doatlaunch");
RoutineRegistry.register(new SayHello( beanService,"SayHello"));
RoutineRegistry.register(new SayHello2( beanService,"SayHello2"));
RoutineRegistry.register(new SayHello3( beanService,"SayHello3"));
}
Теперь мы можем использовать BeanService в наших функциях.
SayHello.java
В SayHello.java мы сделаем функцию, которая будет принимать электронный адрес пользователя, проверять, что пользователь, который выполняет скрипт администратор, удалять все тикеты, которые были созданы пользователем, с переданным электронным адресом.
Конечно, мы можем сделать эту логику и без написания нашей собственной функции. Код на SIL будет выглядеть вот так:
function deleteIssueForUser(string userEmail) {
if (isUserInGroup("jira-administrators", currentUserKey())) {
string[] issues = selectIssues("reporter = " + currentUserKey());
for (string issue in issues) {
deleteIssue(issue);
}
}
}
deleteIssueForUser("user@email.com");
Но я хотел сделать небольшой пример, который бы показал основные принципы работы.
Вот текст класса:
@Slf4j
public class SayHello extends AbstractSILRoutine<MutableString> {
private static final SILType[][] types = {{ TypeInst.STRING }};
private final BeanService beanService;
public SayHello(BeanService beanService, String name) {
super(beanService.getClassLoaderService().getPluginClassLoader(), name, types);
this.beanService = beanService;
}
@Override
public SILType<MutableString> getReturnType() {
return TypeInst.STRING;
}
@Override
protected SILValue<MutableString> runRoutine(SILContext silContext, List<SILValue<?>> list) {
SILValue param = list.get(0);
String userEmail = param.toStringValue();
KIssueService kIssueService = beanService.getKIssueService();
CurrentUserHelper currentUserHelper = beanService.getCurrentUserHelper();
JMIssueSearchServices jmIssueSearchServices = beanService.getJmIssueSearchServices();
UserHelper userHelper = beanService.getUserHelper();
JMIssueServices jmIssueServices = beanService.getJmIssueServices();
ApplicationUser requestedUser = userHelper.getUserByEmail(userEmail);
if (currentUserHelper.isUserAdministrator()) {
SearchService.ParseResult parseResult = jmIssueSearchServices.getSearchService().parseQuery(requestedUser, "reporter = " + requestedUser.getKey());
List<Issue> issues = (List<Issue>) kIssueService.searchIssues(requestedUser, parseResult.getQuery());
issues.stream().forEach(issue -> kIssueService.deindexIssue(jmIssueServices.getIssueManager().getIssueByCurrentKey(issue.getKey())));
}
return SILValueFactory.string( "issues deleted");
}
@Override
public String getParams() {
return "(userEmail)";
}
}
Мы реализуем логику в методе runRoutine:
@Override
protected SILValue<MutableString> runRoutine(SILContext silContext, List<SILValue<?>> list) {
/* Получаем электронный адрес пользователя */
SILValue param = list.get(0);
String userEmail = param.toStringValue();
/* Создаем локальные переменные для всех сервисов SIL */
KIssueService kIssueService = beanService.getKIssueService();
CurrentUserHelper currentUserHelper = beanService.getCurrentUserHelper();
JMIssueSearchServices jmIssueSearchServices = beanService.getJmIssueSearchServices();
UserHelper userHelper = beanService.getUserHelper();
JMIssueServices jmIssueServices = beanService.getJmIssueServices();
/* Используем SIL UserHelper, чтобы получить ApplicationUser по электронному адресу */
ApplicationUser requestedUser = userHelper.getUserByEmail(userEmail);
/* Используем SIL CurrentUserHelper, чтобы проверить, что текущий пользователь администратор Jira */
if (currentUserHelper.isUserAdministrator()) {
/* Используем SIL сервисы для получения тикетов по JQL запросу */
SearchService.ParseResult parseResult = jmIssueSearchServices.getSearchService().parseQuery(requestedUser, "reporter = " + requestedUser.getKey());
List<Issue> issues = (List<Issue>) kIssueService.searchIssues(requestedUser, parseResult.getQuery());
/* Удаляем полученные тикеты */
issues.stream().forEach(issue -> kIssueService.deindexIssue(jmIssueServices.getIssueManager().getIssueByCurrentKey(issue.getKey())));
}
return SILValueFactory.string( "issues deleted");
}
Я сделал комментарии по коду.
Сервис KIssueFieldsService
Хотел бы отдельно рассказать про этот сервис. В SIL во все методы, где требуется передать кастомное поле, мы можем передавать либо имя поля, либо идентификатор поля в формате customfield_NNNNN, либо алиас поля.
Если бы мы не использовали сервисы SIL, то нам пришлось бы реализовывать эту логику самостоятельно. Но используя сервис KIssueFieldsService мы задействуем эту функциональность одной строчкой:
CustomField customField= this.kIssueFieldsService.getCustomField(customFieldName);
Теперь Вы знаете, как создавать переносимы между версиями Jira функции SIL.