'subscr' capability для WebSocket User API
Обзор
С помощью модуля производится подписка на события системы. Происходящие в системе события подразделяются на классы, упаковываются в объекты с данными о событии и отправляются подписчикам с применением фильтров.
События отправляются только в рамках существующей подписки. Если одно и то же событие подходит под несколько подписок, то по каждой из них отправляется отдельное уведомление. Каждый пир может осуществить произвольное разумное число подписок.
Запросы и ответы
Для подписки необходимо отправить запрос subscribe, указав класс событий и сопутствующую идентификационную информацию.
Подписка имеет время жизни. Для организации длинных циклов требуется регулярное продление подписки. Рекомендованный режим - подписка с указанием уникального идентификатора на период до 3600 секунд, и затем заблаговременно до истечения времени предыдущей подписки отправка запроса с тем же идентификатором и тем же периодом. Остальные поля при этом могут не упоминаться. Для подписок периодом в 300 секунд отправлять повторную подписку оптимально через 240 секунд. Для подписок периодом 3600 секунд — через 3000 секунд.
Подписка может содержать фильтры. В зависимости от того, какой это класс событий, фильтры могут устанавливаться и применяться по разному.
Если запрос отправляется с идентификатором существующей подписки, то он рассматривается как продление (с ненулевым значением 'expires'), либо как отписка (с нулевым значением 'expires'). Удаление подписки производится путем отправки запроса "subscribe" с идентификатором интересующей подписки в поле 'id' и значением 'expires': 0. При удалении не осущетсвляется проверка прав доступа.
Если запрос на продление подписки содержит отличающиеся от оригинального запроса значения других полей ('events', 'objects', 'classpath', 'exargs'), то подписка изменяется в соотвествии с новыми данными. Такой способ изменения не является рекомендованным, поскольку может провоцировать коллизии в транзакциях при обработке плотных потоков событий.
[ "subscribe", { "qid": "16066361-dfc0-49f6-b734-9b3dda09db22", "objects": ..., "events": ..., "expires": ..., "id": ... "exargs": ... ... } ]
-
events
- список событий для фильтрации типов событий. Указывается список строк, каждая из которых представляет отдельный тип события, например "callevents.dlg_start". Для ряда классов допустимо указывать "callevents.*". -
objects
- список объектов для фильтрации событий. Может быть указано значение "any" или список значений, например '["sipuser1","sipuser2"]'. -
expires
- время жизни подписки в секундах. При необходимости подписчик должен продлить подписку повторной отправкой запроса с ненулевым значением 'expires'. -
id
- идентификатор подписки. Если не задан в запросе, то генерируется автоматически и в любом случае возвращается в ответе для того, чтобы можно было осуществлять управление подпиской: продление и удаление. -
exargs
- объект со специфическими настройками подписки. В частности, при подписке на "modelevents.data_changed" может быть установлен фильтр (filter) и маскировка (mask). -
classpath
- для событий класса 'modelevents' может быть указан вместо 'objects' и содержать полный путь (эндпойнт) класса, например "/rest/v1/model/my/Records" или "/rest/v1/uc/calls".
Допустима подписка на несколько событий одного класса одновременно и на несколько объектов одновременно.
[ "subscribe_result", { "qid": "16066361-dfc0-49f6-b734-9b3dda09db22", "success": true, "id": ..., "msg": "subscribed", "domain": ... } ]
-
success
- флаг-признак успешности подписки. -
id
- идентификатор подписки, с помощью которого можно осуществлять управление подпиской: продление и удаление. Также будет указан в уведомлениях соответствующих, отправленных по этой подписке. -
msg
- результат подписки. -
domain
- домен, в котором осуществлена подписка.
Уведомления
Уведомления отправляются подписчикам с учетом установленных объектов, фильтров, масок. Содержит поле 'data' с содержимым, соответствующим спецификации типа события.
[ "notify", { "class": string, "eventts": integer, "sid": string, "type": string, "data": object } ]
-
class
- класс события. -
type
- тип события. -
eventts
- таймштамп события. -
sid
- идентификатор подписки (обязателен для уведомлений класса 'modelevents', в других может отсутствовать). -
data
- содержимое события в соответствии со спецификацией в разделе События.
[ "notify", { "class": "modelevents.data_changed", "type": "data_changed", "eventts": 1692042096897, "sid": "81edebaa-0189-f5cd-80e2-7cd30a921f58", "data": { "classname": "sipusers", "classpath":"/rest/v1/uc/sipusers", "eid": "57278625-5fa4-eb0c-fb53-f6fa26881f25", "operation": "update", "entity": { "id":"57278625-5fa4-eb0c-fb53-f6fa26881f25","iduser":"e7adf0aa-05b7-8163-948c-3392a9660db9","lic":{"devices":123456789}, "login": "sip1", "name": "SIP1Сип-{D}", "phonenumber": "11", "reg": 1, "opts":{ "calltimesec": 37, "dlgtimesec": 300, "title": "" } }, "modifier_type": "user", "modifier_id": "e7adf0aa-05b7-8163-948c-3392a9660db9" } } ]
Классы событий
Выделяются следующие категории событий:
-
События об изменениях в модели данных - как статической, так и динамической (modelevents).
-
События телефонии (callevents).
-
События сценариев IVR (ivrevents).
Возможны уведомления также по проектным событиям, генерируемым в сценариях.
События класса modelevents
События этого типа генерируются сразу же при проведении изменений в модели данных - как статической (dc), так и динамической (dms). В классе существует единственное событие "data_changed". Подробное описание событий класса здесь.
При обработке запроса "subscribe" производится авторизация на операцию чтения элементов коллекции. Соответственно, пользователю должна быть добавлена роль, имеющая разрешение на маршрут к endpoint элемента коллекции методом 'GET'.
Событием "notify" доставляются данные о проведенной операции, а также текущее значение измененной сущности с поправкой на маску ('exargs.mask'). При создании подписки с фильтром ('exargs.filter'), события могут быть отфильтрованы. Фильтр и маска задаются в виде, аналогичном параметрам REST запроса GET коллекции элементов (Подробнее).
Доставляемые события по конкретной сущности всегда приводятся к "create", "update" и "delete" на основании сравнения предыдущего значения и нового значения с фильтром. Так, если предыдущее значение не попадало под фильтр, а после изменения попадает, то событие будет доставлено и иметь операцию "create" несмотря на то, что реально была проведена операция "update" или "replace".
Виды операций:
-
create
- сущность создана и попадает под фильтр, либо изменена так, что теперь попадает под фильтр подписки. -
update
- сущность изменена, и оба значения - прошлое и текущее - попадают под фильтр подписки. -
delete
- сущность удалена, либо изменена так, что теперь не попадает под фильтр подписки. -
reload
- очередь обработки запросов по классу перезагружена. Для обеспечения целостности требуется повторный REST-запрос на выборку элементов из коллекции с фильтром. -
clear
- коллекция очищена. -
corrupt
- при обработке запроса по сущности возникла проблема с хранилищем, текущее состояние сущности может быть рассогласовано, и требуется обновление сущности REST-запросом.
Корректный запрос на подписку должен содержать ровно один объект (название класса или путь к классу в REST-API) и ровно один тип событий — "modelevents.data_changed".
Для указания конкретной коллекции преимущественно применяется поле 'classpath', в этом случае поле 'objects' игнорируется.
При большых потоках событий использование масок и фильтров способно значимо сократить нагрузку на систему.
Большое количество одновременных подписок с различными фильтрами на одну и ту же коллекцию способно значимо увеличить нагрузку на систему.
Могут использоваться следующие подходы по работе с событиями класса: * подписка на коллекцию с маской, оставляющей только одно-два поля, и запрос на чтение объекта при поступлении события. * подписка без маски и использование объекта непосредственно из тела события. * подписка на короткое время, пока ведетя работа с данными. Например подписка на изменения объекта-запроса с фильтром по идентификатору и состоянию, создание объекта-запроса с этим идентификатором, отслеживание события о переходе объекта-запроса в интересующее состояние, удаление подписки. * кэширование объектов: подписка на коллекцию и следующая за ней вычитка данных в кэш; затем модификация данных в кеше при поступлении событий; периодическая полная вычитка данных в кэш с большим интервалом.
[ "subscribe", { "qid": 0.15994723, "id": "abcdabcd-abcd-abcd-abcd-abcdabcdabcd", "events": ["modelevents.data_changed"], "classpath": "/rest/v1/model/my/Records", "expires": 300, "exargs": { "filter": ["==",["property","code"],"aaa"], "mask": ["id","name"] } } ]
События класса callevents
События телефонии. Подробное описание событий класса здесь.
В поле 'events' могут быть перечислены интересующие события класса callevents, либо указана полная маска: "callevents.*".
Фильтр по абонентам-участникам диалога, упоминаемым в событии, может быть задан в поле 'objects'.
[ "subscribe", { "qid": 0.105938272, "id": "bcdebcde-bcde-bcde-bcde-bcdebcdebcde", "events": ["callevents.*"], "objects": ["11"], "expires": 300 } ]
События класса ivrevents
События сценариев IVR. Подробное описание событий класса здесь.
В поле 'events' могут быть перечислены интересующие события класса callevents, либо указана полная маска: "ivrevents.*".
Практическую пользу можно извлечь из событий ivrevents.api_start
и ivrevents.api_stop
, с помощью которых производится уведомление о готовности к передачи управления сценарием внешнему модулю.
[ "subscribe", { "qid": 0.105938272, "id": "cdefcdef-cdef-cdef-cdef-cdefcdefcdef", "events": ["ivrevents.api_start", "ivrevents.api_stop"], "objects": ["any"], "expires": 300 } ]