Код по созданию реквеста в JSD не работает под кастомером

Всем привет!

У нас был вопрос в московской группе пользователей Atlassian по поводу того, что есть код, который должен создавать реквест (request) в Jira Service Desk под кастомером (customer), но он работает только под пользователем, который имеет лицензию на JSD (агентом), а под кастомером работать не хочет. Вот этот код:

ServiceDeskCustomerRequestService customerRequestService = ComponentAccessor.getOSGiComponentInstanceOfType(ServiceDeskCustomerRequestService.class);
        ServiceDeskService serviceDeskService = ComponentAccessor.getOSGiComponentInstanceOfType(ServiceDeskService.class);
        RequestTypeService requestTypeService = ComponentAccessor.getOSGiComponentInstanceOfType(RequestTypeService.class);

        ApplicationUser curUser = ComponentAccessor.getUserUtil().getUserByName("admin");
        int portalID = 1;
        ServiceDesk serviced = serviceDeskService.getServiceDeskById(curUser, portalID);


        RequestTypeQuery requestTypeQuery = requestTypeService.newQueryBuilder()
                .serviceDesk(serviced.getId())
                .build();

        PagedResponse<RequestType> requestTypes = requestTypeService.getRequestTypes(curUser, requestTypeQuery);

        RequestType rqType = requestTypes.findFirst().get();
___

CustomerRequestCreateParameters reqBuilder = customerRequestService.newCreateBuilder()
                .serviceDesk(serviced)
                .requestType(rqType)
                .fieldValue(
                        FieldId.withId("summary"),
                        FieldInputValue.withValue(params.getSummary())
                )
                .customerRequestChannelSource(CustomerRequestChannelSource.PORTAL)
                .raiseOnBehalfOf(curUser.getName())  
//                .requestParticipants(user)
                .build();
//
        CustomerRequest customerRequest = customerRequestService.createCustomerRequest(curUser, reqBuilder);

Когда мы запускаем этот код, то получаем ошибку:

ERROR      [feature.customer.request.CustomerRequestManager] Request creation caused a validation error that should have been handled by JSD code instead of JIRA: The issue type selected is invalid.

Ладно, давайте сначала зарефакторим этот код. Вот, есть такая строчка:

ServiceDeskService serviceDeskService = ComponentAccessor.getOSGiComponentInstanceOfType(ServiceDeskService.class);

Здесь вызывается сервис из JSD. Так вызывать сервисы в плагинах Atlassian нельзя. Здесь используется статические методы класса ComponentAccessor и это нам усложнит написание модульных тестов для этого кода. Так вызывают код из груви подобных плагинов в Atlassian, но мы не используем эти плагины в данном случае, поэтому нужно вызывать сервисы правильно. Правильный метод это заинжектить эти сервисы через конструктор или проперти.

Теперь посмотрим вот этот код:

ApplicationUser curUser = ComponentAccessor.getUserUtil().getUserByName("admin");

Кроме ComponentAcessor мы еще используем и депрекейтид метод getUserByName, да еще и хардкодим пользователя. Нам не нужны депрекейтид методы и хардкоды

Вот еще один хардкод:

int portalID = 1;

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

Я избавлюсь от статических классов и депрекейтид методов:

@Named
public class MyServlet extends HttpServlet {
    private static final Logger log = LoggerFactory.getLogger(MyServlet.class);


    final private ServiceDeskCustomerRequestService serviceDeskCustomerRequestService;
    final private ServiceDeskService serviceDeskService;
    final private RequestTypeService requestTypeService;
    final private CustomerContextService customerContextService;
    final private JiraAuthenticationContext jiraAuthenticationContext;
    final private UserManager userManager;

    @Inject
    public MyServlet(@ComponentImport
                             ServiceDeskCustomerRequestService serviceDeskCustomerRequestService,
                     @ComponentImport
                             ServiceDeskService serviceDeskService,
                     @ComponentImport
                             RequestTypeService requestTypeService,
                     @ComponentImport
                             CustomerContextService customerContextService,
                     @ComponentImport
                             JiraAuthenticationContext jiraAuthenticationContext,
                     @ComponentImport
                             UserManager userManager

    ) {

        this.serviceDeskCustomerRequestService = serviceDeskCustomerRequestService;
        this.serviceDeskService = serviceDeskService;
        this.requestTypeService = requestTypeService;
        this.customerContextService = customerContextService;
        this.jiraAuthenticationContext = jiraAuthenticationContext;
        this.userManager = userManager;
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {


            ApplicationUser customerUser = userManager.getUserByName("agrant-sd-demo");

            int portalID = 1;
            ServiceDesk serviced = serviceDeskService.getServiceDeskById(customerUser, portalID);
            RequestTypeQuery requestTypeQuery = requestTypeService.newQueryBuilder()
                    .serviceDesk(serviced.getId())
                    .build();
            PagedResponse<RequestType> requestTypes = requestTypeService.getRequestTypes(customerUser, requestTypeQuery);
            RequestType rqType = requestTypes.findFirst().get();
            CustomerRequestCreateParameters reqBuilder = serviceDeskCustomerRequestService.newCreateBuilder()
                    .serviceDesk(serviced)
                    .requestType(rqType)
                    .fieldValue(
                            FieldId.withId("summary"),
                            FieldInputValue.withValue("my summary")
                    )
                    .customerRequestChannelSource(CustomerRequestChannelSource.PORTAL)
                    .build();
            CustomerRequest customerRequest = serviceDeskCustomerRequestService.createCustomerRequest(customerUser, reqBuilder);

        resp.setContentType("text/html");
        resp.getWriter().write("<html><body>Hello World</body></html>");
    }

}

Я превратил код в сервлет, чтобы было легче тестировать. Заинжектил сервисы через конструктор:

public MyServlet(@ComponentImport
                             ServiceDeskCustomerRequestService serviceDeskCustomerRequestService,
                     @ComponentImport
                             ServiceDeskService serviceDeskService,
                     @ComponentImport
                             RequestTypeService requestTypeService,
                     @ComponentImport
                             CustomerContextService customerContextService,
                     @ComponentImport
                             JiraAuthenticationContext jiraAuthenticationContext,
                     @ComponentImport
                             UserManager userManager

    )

И избавился от депрекейтид метода:

ApplicationUser customerUser = userManager.getUserByName("agrant-sd-demo");

Изменил пользователя с админа на кастомера и запустил код.

Я получил ошибку вот на этой строке:

ServiceDesk serviced = serviceDeskService.getServiceDeskById(customerUser, portalID);

Проблема в том, что кастомер не может выбрать ServiceDesk объект, потому что у него недостаточно разрешений. Я переписал код вот так:

            ApplicationUser adminUser = userManager.getUserByName("admin");            
            ApplicationUser customerUser = userManager.getUserByName("agrant-sd-demo");

            int portalID = 1;
            ServiceDesk serviced = serviceDeskService.getServiceDeskById(adminUser, portalID);
            RequestTypeQuery requestTypeQuery = requestTypeService.newQueryBuilder()
                    .serviceDesk(serviced.getId())
                    .build();
            PagedResponse<RequestType> requestTypes = requestTypeService.getRequestTypes(adminUser, requestTypeQuery);
            RequestType rqType = requestTypes.findFirst().get();
            CustomerRequestCreateParameters reqBuilder = serviceDeskCustomerRequestService.newCreateBuilder()
                    .serviceDesk(serviced)
                    .requestType(rqType)
                    .fieldValue(
                            FieldId.withId("summary"),
                            FieldInputValue.withValue("my summary")
                    )
                    .customerRequestChannelSource(CustomerRequestChannelSource.PORTAL)
                    .raiseOnBehalfOf(customerUser.getName())
                    .build();
            CustomerRequest customerRequest = serviceDeskCustomerRequestService.createCustomerRequest(adminUser, reqBuilder);

Я добавил вот такую строчку:

ApplicationUser adminUser = userManager.getUserByName("admin");    

Заменил по коду customerUser на adminUser и добавил еще вот такую строчку:

.raiseOnBehalfOf(customerUser.getName())

Все эти изменения означают, что я выбираю ServiceDesk объект, RequestType объект и создаю реквест под админом, но от имени кастомера.

И чудо случилось, реквест был создан. Но…..

Давайте посмотрим на реквест. Мы увидим, что репортер у нас кастомер, а вот создатель у нас админ:

Не, это не то что мы хотим! Создатель тоже должен быть кастомер!

Так, мы попрыгали, теперь нужно подумать.

Кто такие кастомеры? Кастомеры это такие специальные пользователи в JSD, которые не имеют никаких полномочий, но могут создавать запросы. Интересно, а как Atlassian сделал так, что в Jira есть какие-то бесправные пользователи, но с правами?

Так, нужно порыться Jira Service Desk Rest Api. Я набрал в гугле вот так:

jira servicedesk api java

И получил вот такой линк:

https://docs.atlassian.com/jira-servicedesk/3.9.1/

Я предположил, что должен быть какой-то сервис, который наделяет правами бесправных кастомеров и этот сервис относится к кастомерам. Значит и искать нужно в кастомерах. Я нашел вот такой пакет:

com.atlassian.servicedesk.api.customer

И открыл его:

Ого! Прям то, что нам нужно. Откроем CustomerContextService и почитаем:

Service Desk customer context позволяет любому пользователю получить доступ к JIRA Service Desk и JIRA Platform services!
Этот сервис позволяет настроить контекст для кастомера и запускать код, к которому будет применены специальные разрешения JIRA Service Desk.

Вообще в Jira только лицензионные пользователи могут входить в Jira и обращаться к сервисам. Но Jira Service Desk исключение из этого правила. В JSD любой пользователь рассматривается как кастомер, когда этот пользователь обращается за помощью. Поэтому этот сервис позволяет вам запускать код от имени этого бесправного пользователя.

Все отлично объяснено. Это такой бэкдор для кастомеров. Давайте им воспользуемся:

customerContextService.runInCustomerContext(() -> {
            ApplicationUser customerUser = userManager.getUserByName("agrant-sd-demo");
            jiraAuthenticationContext.setLoggedInUser(customerUser);
            int portalID = 1;
            ServiceDesk serviced = serviceDeskService.getServiceDeskById(customerUser, portalID);
            RequestTypeQuery requestTypeQuery = requestTypeService.newQueryBuilder()
                    .serviceDesk(serviced.getId())
                    .build();
            PagedResponse<RequestType> requestTypes = requestTypeService.getRequestTypes(customerUser, requestTypeQuery);
            RequestType rqType = requestTypes.findFirst().get();
            CustomerRequestCreateParameters reqBuilder = serviceDeskCustomerRequestService.newCreateBuilder()
                    .serviceDesk(serviced)
                    .requestType(rqType)
                    .fieldValue(
                            FieldId.withId("summary"),
                            FieldInputValue.withValue("my summary")
                    )
                    .customerRequestChannelSource(CustomerRequestChannelSource.PORTAL)
                    .build();
            CustomerRequest customerRequest = serviceDeskCustomerRequestService.createCustomerRequest(customerUser, reqBuilder);
        });

Я выполняю весь код под CustomerContext:

customerContextService.runInCustomerContext(() -> {
....
});

А также внутри этого контекста, я говорю, что я кастомер:

jiraAuthenticationContext.setLoggedInUser(customerUser);

И на этот раз настоящее чудо произошло!

Вот конечный код:

package ru.matveev.alexey.wrongcode.servlet;

import com.atlassian.jira.security.JiraAuthenticationContext;
import com.atlassian.jira.user.ApplicationUser;
import com.atlassian.jira.user.UserUtils;
import com.atlassian.jira.user.util.UserManager;
import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport;
import com.atlassian.servicedesk.api.ServiceDesk;

import com.atlassian.servicedesk.api.ServiceDeskService;
import com.atlassian.servicedesk.api.customer.CustomerContextService;
import com.atlassian.servicedesk.api.field.FieldId;
import com.atlassian.servicedesk.api.field.FieldInputValue;
import com.atlassian.servicedesk.api.request.CustomerRequest;
import com.atlassian.servicedesk.api.request.CustomerRequestChannelSource;
import com.atlassian.servicedesk.api.request.CustomerRequestCreateParameters;
import com.atlassian.servicedesk.api.request.ServiceDeskCustomerRequestService;
import com.atlassian.servicedesk.api.requesttype.RequestType;
import com.atlassian.servicedesk.api.requesttype.RequestTypeQuery;
import com.atlassian.servicedesk.api.requesttype.RequestTypeService;
import com.atlassian.servicedesk.api.util.paging.PagedResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import javax.inject.Named;
import javax.servlet.*;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Named
public class MyServlet extends HttpServlet {
    private static final Logger log = LoggerFactory.getLogger(MyServlet.class);


    final private ServiceDeskCustomerRequestService serviceDeskCustomerRequestService;
    final private ServiceDeskService serviceDeskService;
    final private RequestTypeService requestTypeService;
    final private CustomerContextService customerContextService;
    final private JiraAuthenticationContext jiraAuthenticationContext;
    final private UserManager userManager;

    @Inject
    public MyServlet(@ComponentImport
                             ServiceDeskCustomerRequestService serviceDeskCustomerRequestService,
                     @ComponentImport
                             ServiceDeskService serviceDeskService,
                     @ComponentImport
                             RequestTypeService requestTypeService,
                     @ComponentImport
                             CustomerContextService customerContextService,
                     @ComponentImport
                             JiraAuthenticationContext jiraAuthenticationContext,
                     @ComponentImport
                             UserManager userManager

    ) {

        this.serviceDeskCustomerRequestService = serviceDeskCustomerRequestService;
        this.serviceDeskService = serviceDeskService;
        this.requestTypeService = requestTypeService;
        this.customerContextService = customerContextService;
        this.jiraAuthenticationContext = jiraAuthenticationContext;
        this.userManager = userManager;
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {


        customerContextService.runInCustomerContext(() -> {
            ApplicationUser customerUser = userManager.getUserByName("agrant-sd-demo");
            jiraAuthenticationContext.setLoggedInUser(customerUser);
            int portalID = 1;
            ServiceDesk serviced = serviceDeskService.getServiceDeskById(customerUser, portalID);
            RequestTypeQuery requestTypeQuery = requestTypeService.newQueryBuilder()
                    .serviceDesk(serviced.getId())
                    .build();
            PagedResponse<RequestType> requestTypes = requestTypeService.getRequestTypes(customerUser, requestTypeQuery);
            RequestType rqType = requestTypes.findFirst().get();
            CustomerRequestCreateParameters reqBuilder = serviceDeskCustomerRequestService.newCreateBuilder()
                    .serviceDesk(serviced)
                    .requestType(rqType)
                    .fieldValue(
                            FieldId.withId("summary"),
                            FieldInputValue.withValue("my summary")
                    )
                    .customerRequestChannelSource(CustomerRequestChannelSource.PORTAL)
                    .build();
            CustomerRequest customerRequest = serviceDeskCustomerRequestService.createCustomerRequest(customerUser, reqBuilder);
        });
        resp.setContentType("text/html");
        resp.getWriter().write("<html><body>Hello World</body></html>");
    }

}

If you have found a spelling error, please, notify us by selecting that text and pressing Ctrl+Enter.

Leave a Reply

%d bloggers like this:

Spelling error report

The following text will be sent to our editors: