'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
}
]