Ранее я писал как можно настроить OTRS в роли провайдера. Это когда любая система может обратиться к OTRS и запросить данные.
Сейчас же я опишу, как произвести настройку OTRS в роли запрашивающего (requester). Когда в OTRS происходит какое-то событие и после этого идет обращение к внешней системе. А так же проблемы, с которыми столкнулся. Если заинтересовало, то прошу под кат.
![](https://habrastorage.org/r/w1560/web/ee0/373/158/ee0373158a894e1c85f588014c7726e1.png)
Итак, установили мы замечательный OTRS, начали в нем работать. Но захотелось от системы большего. В нашем случае — интеграции с телеграм ботом. Кейс следующий: если приходит тикет от ВИП пользователя, то бот должен сразу проинформировать об этом ответственных лиц.
А для этого надо, чтобы система сама могла с ним общаться и сообщать о приходе таких тикетов.
В самом начале нам надо включить invoker. По умолчанию их всего два, и как сделать больше, я не разбирался. Для задачи и одного вполне хватает.
Для этого заходим в “Конфигурация системы” и далее ищем.
Edit Config Settings in GenericInterface -> GenericInterface::Invoker::ModuleRegistration
![](https://habrastorage.org/r/w1560/web/455/a03/a1e/455a03a1ec004d208cd0af3011cdcfdb.png)
Просто включаем, ничего менять не надо.
Далее переходим в администрирование — > веб-сервисы.
![](https://habrastorage.org/r/w1560/web/9ca/73c/e0b/9ca73ce0bdf14a3295bdc3eb44f4a62a.png)
![](https://habrastorage.org/r/w1560/web/eb9/891/2fd/eb98912fdfa34bc3b9fdaa2a473b64b5.png)
Создаем новый веб сервис.
Вписываем название интерфейса
Выбираем сетевой транспорт HTTP::REST в блоке «OTRS как запрашивающий».
Жмем “Сохранить”.
![](https://habrastorage.org/r/w1560/web/ad2/00f/a62/ad200fa6232e438baf13f026d323a865.png)
После сохранения есть возможность выбрать Invoker и настроить транспорт.
Invoker у нас один, его и выбираем.
![](https://habrastorage.org/r/w1560/web/8da/4e1/feb/8da4e1febba943728e369895dc7b28cc.png)
После добавления invoker вам сразу предложат его настроить.
Имя — тут все понятно
Дальше две мапы параметров. Я не уверен, что они в принципе работают, поэтому просто настроил пробрасывать как есть.
Триггер события — я выбрал по созданию тикета. Важно — не забудьте нажать плюсик. Только после этого ваш триггер добавится. Я минут 5 разбирался, почему после перезахода в настройки не видел своего триггера.
Триггер может быть синхронным и асинхронным. Если синхронный, то пока OTRS не получит ответ, вы не сможете дальше работать с тикетом. Асинхронный — OTRS отправит запрос в фоне.
Пример проблемы синхронных вызовов:
Настроили триггер на смену сервиса в тикете.
Открываем в тикете окно смены сервиса, меняем сервис.
Пока внешняя система не даст ответ, окно не закроется. Хотя должно закрываться сразу.
![](https://habrastorage.org/r/w1560/web/f72/382/771/f72382771e6e417990b8c72736954649.png)
Далее настраиваем транспорт
![](https://habrastorage.org/r/w1560/web/088/b15/056/088b1505674c40b48348bd76c7400b21.png)
Указываете адрес принимающей системы с портом. В следующей строке имя сервлета и TicketID. Согласно инструкции можно передавать еще и другие параметры от тикета, но у меня это не получилось. Поэтому все что мы имеем — номер тикета. Об этом чуть ниже.
![](https://habrastorage.org/r/w1560/web/a1e/4f1/1a6/a1e4f11a6c4748b49ef3a54fa2608d6f.png)
На этом настройки закончены. При наступлении события наша внешняя система получила запрос.
![](https://habrastorage.org/r/w1560/web/339/21b/644/33921b644ee344379021b40db58fc393.png)
За время настройки всплыло 2 момента:
Если у кого-то есть интересные замечания, комментарии — welcome :)
UP
Спасибо Mexonizator за ценное дополнение.
В 2020 году проблема остаётся актуальной — переменные, кроме TicketID, не резолвятся в строке запроса. Инфы в инете как не было, так и нет — ни в нашем, ни в зарубежном.
1. Прежде всего, как и сказано в комментах, надо добавить поля в Invoker'е:
Функция берёт из входных данных TicketID и делает запрос к базе на предмет нужного нам кастомного поля и отправляет всё это дальше.
2. Затем идём в /opt/otrs/Kernel/GenericInterface/Transport/HTTP/REST.pm. Нас интересует код начиная с:
Как видно, скрипт берёт строку GET-запроса, которую указали в гуе в настройках веб-сервиса, ищет знак вопроса и разбивает на две части. Далее обработка происходит в три этапа:
1) Строка до знака вопроса;
2) После него;
3) И дополнительные параметры, которые добавляются при соблюдении некоторых условий.
Нам здесь важна главным образом регулярка. Она активно используется на первых двух этапах, и именно из-за неё переменные не резолвятся.
Регулярка ожидает на входе не просто :VAR, а :VAR&, :VAR$ и т.п. в зависимости от участка кода. Соответственно, совпадения не происходит и переменная не резолвится.
Лечится изменением регулярки в зависимости от запроса к веб-сервису. Например, при такой регулярке (см. ниже) пройдёт вот такой запрос:
/search?:CustomField-:TicketID
Вот, собственно, и весь секрет. :)
Сейчас же я опишу, как произвести настройку OTRS в роли запрашивающего (requester). Когда в OTRS происходит какое-то событие и после этого идет обращение к внешней системе. А так же проблемы, с которыми столкнулся. Если заинтересовало, то прошу под кат.
![](https://habrastorage.org/web/ee0/373/158/ee0373158a894e1c85f588014c7726e1.png)
Итак, установили мы замечательный OTRS, начали в нем работать. Но захотелось от системы большего. В нашем случае — интеграции с телеграм ботом. Кейс следующий: если приходит тикет от ВИП пользователя, то бот должен сразу проинформировать об этом ответственных лиц.
А для этого надо, чтобы система сама могла с ним общаться и сообщать о приходе таких тикетов.
В самом начале нам надо включить invoker. По умолчанию их всего два, и как сделать больше, я не разбирался. Для задачи и одного вполне хватает.
Для этого заходим в “Конфигурация системы” и далее ищем.
Edit Config Settings in GenericInterface -> GenericInterface::Invoker::ModuleRegistration
![](https://habrastorage.org/web/455/a03/a1e/455a03a1ec004d208cd0af3011cdcfdb.png)
Просто включаем, ничего менять не надо.
Далее переходим в администрирование — > веб-сервисы.
![](https://habrastorage.org/web/9ca/73c/e0b/9ca73ce0bdf14a3295bdc3eb44f4a62a.png)
![](https://habrastorage.org/web/eb9/891/2fd/eb98912fdfa34bc3b9fdaa2a473b64b5.png)
Создаем новый веб сервис.
Вписываем название интерфейса
Выбираем сетевой транспорт HTTP::REST в блоке «OTRS как запрашивающий».
Жмем “Сохранить”.
![](https://habrastorage.org/web/ad2/00f/a62/ad200fa6232e438baf13f026d323a865.png)
После сохранения есть возможность выбрать Invoker и настроить транспорт.
Invoker у нас один, его и выбираем.
![](https://habrastorage.org/web/8da/4e1/feb/8da4e1febba943728e369895dc7b28cc.png)
После добавления invoker вам сразу предложат его настроить.
Имя — тут все понятно
Дальше две мапы параметров. Я не уверен, что они в принципе работают, поэтому просто настроил пробрасывать как есть.
Триггер события — я выбрал по созданию тикета. Важно — не забудьте нажать плюсик. Только после этого ваш триггер добавится. Я минут 5 разбирался, почему после перезахода в настройки не видел своего триггера.
Триггер может быть синхронным и асинхронным. Если синхронный, то пока OTRS не получит ответ, вы не сможете дальше работать с тикетом. Асинхронный — OTRS отправит запрос в фоне.
Пример проблемы синхронных вызовов:
Настроили триггер на смену сервиса в тикете.
Открываем в тикете окно смены сервиса, меняем сервис.
Пока внешняя система не даст ответ, окно не закроется. Хотя должно закрываться сразу.
![](https://habrastorage.org/web/f72/382/771/f72382771e6e417990b8c72736954649.png)
Далее настраиваем транспорт
![](https://habrastorage.org/web/088/b15/056/088b1505674c40b48348bd76c7400b21.png)
Указываете адрес принимающей системы с портом. В следующей строке имя сервлета и TicketID. Согласно инструкции можно передавать еще и другие параметры от тикета, но у меня это не получилось. Поэтому все что мы имеем — номер тикета. Об этом чуть ниже.
![](https://habrastorage.org/web/a1e/4f1/1a6/a1e4f11a6c4748b49ef3a54fa2608d6f.png)
На этом настройки закончены. При наступлении события наша внешняя система получила запрос.
![](https://habrastorage.org/web/339/21b/644/33921b644ee344379021b40db58fc393.png)
За время настройки всплыло 2 момента:
- Если у вас не отправляются запросы и в логах “Got no TicketNumber”, то вам нужно на сервере найти файл Test.pm и везде поменять “TicketNumber” на “TicketID”. В моем случае он лежал тут — /opt/otrs/Kernel/GenericInterface/Invoker/Test.
Спасибо этим ребятам за совет. - OTRS по наступлению события отправляет только id тикета. Заявлено, что можно передавать и другие параметры. Однако это так и не удалось. В результате при наступлении события внешняя система получает TicketID, и по нему сама обращается в OTRS за полной информацией. Ребята с форума приблизительно так же и поступили.Это плодит дополнительные обращения, но в нашем случае не критично.
Если у кого-то есть интересные замечания, комментарии — welcome :)
UP
Спасибо Mexonizator за ценное дополнение.
В 2020 году проблема остаётся актуальной — переменные, кроме TicketID, не резолвятся в строке запроса. Инфы в инете как не было, так и нет — ни в нашем, ни в зарубежном.
1. Прежде всего, как и сказано в комментах, надо добавить поля в Invoker'е:
package Kernel::GenericInterface::Invoker::Test::Test;
use strict;
use warnings;
use JSON::XS;
use Kernel::System::VariableCheck qw(IsString IsStringWithData);
use Kernel::System::ObjectManager;
local $Kernel::OM = Kernel::System::ObjectManager->new();
...
sub PrepareRequest {
my ( $Self, %Param ) = @_;
# we need a TicketID
if ( !IsStringWithData( $Param{Data}->{TicketID} ) ) {
return $Self->{DebuggerObject}->Error( Summary => 'Got no TicketID' );
}
my %ReturnData;
my $TicketID = $Param{Data}->{TicketID};
# Getting the fields' objects from OTRS
my $BackendObject = $Kernel::OM->Get('Kernel::System::DynamicField::Backend');
my $DynamicFieldObject = $Kernel::OM->Get('Kernel::System::DynamicField');
# Getting CustomField field properties
my $CustomFieldFieldConfig = $DynamicFieldObject->DynamicFieldGet(
Name => 'CustomField',
);
# Getting CustomField field value in a given ticket
my $CustomFieldFieldValue = $BackendObject->ValueGet(
DynamicFieldConfig => $CustomFieldFieldConfig,
ObjectID => $TicketID,
UserID => 1,
);
# Data in the request to a web-service
$ReturnData{TicketID} = $TicketID;
$ReturnData{CustomField} = $CustomFieldFieldValue;
Функция берёт из входных данных TicketID и делает запрос к базе на предмет нужного нам кастомного поля и отправляет всё это дальше.
2. Затем идём в /opt/otrs/Kernel/GenericInterface/Transport/HTTP/REST.pm. Нас интересует код начиная с:
my @RequestParam;
my $Controller = $Config->{InvokerControllerMapping}->{ $Param{Operation} }->{Controller};
# Remove any query parameters that might be in the config,
# For example, from the controller: /Ticket/:TicketID/?:UserLogin&:Password
# controller must remain /Ticket/:TicketID/
$Controller =~ s{([^?]+)(.+)?}{$1};
Как видно, скрипт берёт строку GET-запроса, которую указали в гуе в настройках веб-сервиса, ищет знак вопроса и разбивает на две части. Далее обработка происходит в три этапа:
1) Строка до знака вопроса;
2) После него;
3) И дополнительные параметры, которые добавляются при соблюдении некоторых условий.
Нам здесь важна главным образом регулярка. Она активно используется на первых двух этапах, и именно из-за неё переменные не резолвятся.
# Replace any URI params with their actual value.
# for example: from /Ticket/:TicketID/:Other
# to /Ticket/1/2 (considering that $Param{Data} contains TicketID = 1 and Other = 2).
for my $ParamName ( sort keys %{ $Param{Data} } ) {
if ( $Controller =~ m{:$ParamName(?=/|\?|$)}msx ) {
my $ParamValue = $Param{Data}->{$ParamName};
$ParamValue = URI::Escape::uri_escape_utf8($ParamValue);
$Controller =~ s{:$ParamName(?=/|\?|$)}{$ParamValue}msxg;
push @ParamsToDelete, $ParamName;
}
}
if ($QueryParamsStr) {
# Replace any query params with their actual value
# for example: from ?UserLogin:UserLogin&Password=:Password
# to ?UserLogin=user&Password=secret
# (considering that $Param{Data} contains UserLogin = 'user' and Password = 'secret').
my $ReplaceFlag;
for my $ParamName ( sort keys %{ $Param{Data} } ) {
if ( $QueryParamsStr =~ m{:$ParamName(?=&|$)}msx ) {
my $ParamValue = $Param{Data}->{$ParamName};
$ParamValue = URI::Escape::uri_escape_utf8($ParamValue);
$QueryParamsStr =~ s{:$ParamName(?=&|$)}{$ParamValue}msxg;
push @ParamsToDelete, $ParamName;
$ReplaceFlag = 1;
}
}
Регулярка ожидает на входе не просто :VAR, а :VAR&, :VAR$ и т.п. в зависимости от участка кода. Соответственно, совпадения не происходит и переменная не резолвится.
Лечится изменением регулярки в зависимости от запроса к веб-сервису. Например, при такой регулярке (см. ниже) пройдёт вот такой запрос:
/search?:CustomField-:TicketID
if ($QueryParamsStr) {
# Replace any query params with their actual value
# for example: from ?UserLogin:UserLogin&Password=:Password
# to ?UserLogin=user&Password=secret
# (considering that $Param{Data} contains UserLogin = 'user' and Password = 'secret').
my $ReplaceFlag;
for my $ParamName ( sort keys %{ $Param{Data} } ) {
if ( $QueryParamsStr =~ m{:$ParamName}msx ) {
my $ParamValue = $Param{Data}->{$ParamName};
$ParamValue = URI::Escape::uri_escape_utf8($ParamValue);
$QueryParamsStr =~ s{:$ParamName}{$ParamValue}msxg;
push @ParamsToDelete, $ParamName;
$ReplaceFlag = 1;
}
}
Вот, собственно, и весь секрет. :)