Перейти к основному содержимому

Событие регистрации своих сущностей веб-хуков

[OnBuildWebhookEntityList] Своя сущность в веб-хуках модуля​

Доступно с версии 3.9.0

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

Модуль сам раскладывает по каталогу Битрикс шесть типов сущностей МойСклад: product, variant, bundle, service, productfolder и specialpricediscount. Событие позволяет вашему коду объявить свою сущность — например партию (consignment), контрагента (counterparty) или заказ покупателя (customerorder) — или встроиться в обработку штатной.

Объявление — это запись в реестре модуля: тип сущности, действия, ваш обработчик и режим. Дальше модуль:

  • подписывается в МойСклад на веб-хуки по объявленным действиям — сущность встаёт на вкладке «Веб-хуки» отдельным блоком с подписью «Обслуживает внешний обработчик: <обработчик> (<режим>)»;
  • вызывает ваш обработчик на каждый пришедший веб-хук по этой сущности — вместо своей обработки, до неё или после неё, в зависимости от режима.

Главное отличие от старого пути через OnBeforeWebHookOptionsBuild: объявленную сущность модуль не перехватит при обновлении. Если в следующей версии модуль научится обслуживать ту же сущность сам, в режиме replace веб-хуки по ней всё равно пойдут только в ваш обработчик.

Режимы​

РежимЧто происходит с веб-хуком
replace (по умолчанию)Веб-хук получает только ваш обработчик. Модуль сущность не трогает, даже если это одна из шести штатных. Штатную сущность в этом режиме модуль убирает из своих подписок — остаются только объявленные вами действия
beforeСначала ваш обработчик, затем — штатная обработка модуля. Обработчик может отменить обработку модулем, вернув EventResult с типом ERROR
afterСначала штатная обработка модуля, в том числе события OnWebhookUpdate и OnWebhookSkipped, затем — ваш обработчик с элементом, который модуль обработал

Для сущности, которую модуль сам не обслуживает, режимы before и after ничем не отличаются от replace: обработчик вызывается один раз, параметр phase равен объявленному режиму, элементов в параметрах нет.

Режим задаётся на сущность целиком, а не на отдельное действие.

Правила реестра​

  • У сущности один владелец. Если одну сущность объявили два обработчика, остаётся объявление того, кто вызвался первым; второе отбрасывается с записью в лог модуля.
  • Битое объявление отбрасывается целиком с записью в лог модуля, исключение не бросается. Причины: тип сущности не из строчных латинских букв, объявление не массив, actions пуст или содержит действие кроме CREATE, UPDATE, DELETE, handler не указан или его нельзя вызвать, mode не из трёх допустимых.
  • Обработчик получает только объявленные действия. Если сущность объявлена с ['CREATE', 'UPDATE'], а пришёл DELETE: в режиме replace веб-хук не обрабатывает никто (строка в лог модуля), в режимах before и after штатную сущность модуль обработает сам, без вашего обработчика.
  • Реестр один на все профили обмена. Профиль в параметрах не передаётся — см. шаг 3 во вкладке «Примеры использования».
  • Событие вызывается один раз за хит, дальше модуль берёт результат из своего кеша. Вызывается оно на странице настроек и на каждом входящем веб-хуке, поэтому обработчик события должен только вернуть описание: без запросов к API МойСклад и тяжёлых выборок.

Что модуль гарантирует к моменту вызова обработчика​

Обработчик вызывается не на сыром теле запроса. К этой точке модуль уже:

  • проверил соль в адресе веб-хука (webhook_url_salt) сравнением за постоянное время;
  • убедился, что обмен включён, и выбрал профиль обмена по profile_id;
  • нормализовал домен в meta->href;
  • проверил href на принадлежность эндпоинту МойСклад — чужой адрес в теле веб-хука до обработчика не доходит;
  • убедился, что действие входит в CREATE, UPDATE, DELETE и в объявленные вами actions;
  • отсёк повтор того же события в пределах времени дедупликации (cache_webhook_time, по умолчанию 5 секунд).
За объектом в МойСклад ходите сами

Кроме фазы after по штатной сущности, у обработчика нет объекта МойСклад — модуль не знает, как устроена ваша сущность, и в API за ней не обращается. Вам приходит href; запрос по нему — на вашей стороне.

Когда обработчик не вызывается​

СлучайПочему
Действие не входит в actions объявленияСм. «Правила реестра» выше
Действие вне CREATE / UPDATE / DELETEМодуль отбрасывает такой веб-хук раньше развилки по типу
href не принадлежит API МойСкладОтсекается защитой от подстановки чужого адреса
Повтор того же события в пределах cache_webhook_timeДедупликация: второй раз обработчик не вызывается
Событие не попало в первые webhook_limit_count событий запросаМодуль обрабатывает только начало пачки (настройка «Количество событий за раз» на вкладке «Веб-хуки»). Остаток пишется в лог и отдаётся событием OnWebhookEventsDropped
Обмен выключен или соль не совпалаПриём обрывается до разбора тела
Исключения из обработчика

Исключение из вашего обработчика модуль ловит и пишет в лог модуля строкой с именем обработчика, сущностью, действием и фазой — остальные события пачки обрабатываются дальше. В режиме before исключение обработку модулем не отменяет.

Параметры события

Событие не передаёт параметров.

Возвращаемое значение

EventResult::SUCCESS с ключом entities — массивом объявлений, где ключ — тип сущности МойСклад:

return new \Bitrix\Main\EventResult(\Bitrix\Main\EventResult::SUCCESS, [
'entities' => [
'consignment' => [
'actions' => ['CREATE', 'UPDATE', 'DELETE'],
'handler' => ['\My\Module\ConsignmentHook', 'handle'],
'mode' => 'replace',
],
],
]);

Поля объявления

ПолеОбязательноеТипОписание
ключ массивадаstringТип сущности МойСклад (meta->type веб-хука): только строчные латинские буквы
actionsдаstring[]Действия, на которые подписаться и которые отдавать обработчику: CREATE, UPDATE, DELETE
handlerдаcallableОбработчик веб-хука: имя функции, [класс, метод] или замыкание. Должен быть вызываемым в момент объявления
modeнетstringreplace (по умолчанию), before или after

Параметры обработчика веб-хука

Обработчик вызывается как обычный обработчик события Битрикс — с одним объектом \Bitrix\Main\Event (тип OnWebhookCustomEntity). Через EventManager этот объект не отправляется: подписаться на OnWebhookCustomEntity через addEventHandler нельзя, обработчик вызывается только из реестра.

ПараметрТипОписание
hookobjectОбъект события из тела веб-хука МойСклад, с уже нормализованным meta->href
entitystringТип сущности из объявления
actionstringДействие веб-хука: CREATE, UPDATE или DELETE
hrefstringСсылка на сущность в API МойСклад — то же, что hook->meta->href, строкой
modestringРежим из объявления
phasestringМомент вызова: replace, before или after
bx_elementarray|nullТолько в фазе after: элемент Битрикс, тот же, что уходит в OnWebhookUpdate. null, если модуль элемент не загрузил: действие DELETE, выключен импорт групп, модификации грузятся агентом, элемента нет в каталоге или сущность не своя
ms_elementobject|nullТолько в фазе after: объект МойСклад, загруженный модулем. null в тех же случаях

Возвращаемое значение обработчика

Читается только в фазе before: EventResult с типом ERROR отменяет обработку веб-хука модулем. Любой другой ответ, включая null, ничего не отменяет. В остальных фазах результат не читается.

Обновление на 3.9.0: что делать владельцу существующей доработки​

Как устроена ваша доработкаЧто делать при обновлении
Обработчики OnWebhookUpdate, OnWebhookSkipped, OnBeforeDeleteItem и события импортаНичего. Имена, параметры и момент вызова не изменились
Собственный приёмник веб-хуков на своём адресе, мимо модуляНичего. Модуль в этой цепочке не участвует
Подписка на свою сущность через OnBeforeWebHookOptionsBuildПодписка работает как раньше, но веб-хуки по сущности, которую не обслуживают ни модуль, ни реестр, модуль по-прежнему не обрабатывает — теперь он пишет о каждом таком веб-хуке строку в лог. Чтобы получать их, объявите сущность в OnBuildWebhookEntityList
В коде модуля правился список обслуживаемых типовПравка будет затёрта обновлением. Перенесите логику в обработчик из реестра: он переживёт все следующие обновления
Отдельно — обновление с версий ниже 3.8.0

В версиях до 3.8.0 веб-хук с действием DELETE по любому типу сущности шёл в штатное удаление: модуль искал в каталоге элемент, у которого XML_ID совпадает с идентификатором из href, и удалял или деактивировал его. Это работало и для сущностей, которые модуль не обслуживает, — и было закрыто в 3.8.0 как уязвимость: тело веб-хука недоверенное, а совпадение идентификатора ничего не подтверждает.

Если ваша доработка опиралась на этот побочный эффект, после обновления он не вернётся. Снимать элементы каталога по своей сущности теперь нужно самостоятельно, из обработчика реестра с действием DELETE.