Verdi
Описание команд всех сервисов, которые входят в состав Verdi.
Список версий
v1.8
Важные изменения
- Добавлен функционал лицензирования (сервисы apigateway и oberto)
- В verdi-libs добавлен фильтр для поиска по нескольким значениям (CURS-2433)
Исправлены разнообразные баги.
Mon
- Добавлен фильтр для полнотекстового поиска по ФИО пользователя (CURS-2246)
- добавлено разлогинивание пользователя при его блокировке (CURS-149)
- Подняты версии библиотек (CURS-2474)
Oberto
- Добавлены политики "по умолчанию" (CURS-111)
v1.7.1
Важные изменения
- Добавлены параметры подключения к базе данных в шаблон конфигурации и во все релевантные конфигурации сервисов (CURS-2415, SUP-118)
- Для лога реализована возможность отключения вывода в файл (CURS-2430, SUP-151)
v1.7
Важные изменения
Verdi-libs
- В целом по системе были подняты версии библиотек, чтобы избавиться от некоторых уязвимостей
- В senderlib реализован ретраер при синхронном обращении напрямую к сервису (CURS-163, SUP-10)
Mon
- Добавлена настройка использования атрибутов пользователя из Keycloak в атрибутах пользователя сервиса Mon (CURS-2358, SUP-81)
- Добавлена команда осуществляющая завершение сеансов пользователя (CURS-2331, SUP-135)
- Журналируются изменения пользователя или его групп при аутентификации через Keycloak (CURS-2368, SUP-137)
v1.6.1
Важные изменения
Verdi-libs
- Добавлена возможность разделить учётные данные кафки для доступов producer и consumer (SUP-132, CURS-2307)
Mon
- Возможность завершить сеанс пользователя (SUP-135, CURS-2331)
Oberto (и другие микросервисы)
- Реализованы доработки позволяющие работать в системе без развернутого authzforce
v1.6
Важные изменения
Verdi-libs
- Изменена работа с топиками в Kafka (аутентификация) (CURS-294)
- Сервисы теперь падают с ошибкой если на старте не смогли зарегистрироваться в консуле (CURS-303)
- Добавлены логи в формате CEF в сервисы Cursor/Verdi (CURS-36, SUP-98)
Erika
- Erika теперь умеет работать с файлами, у которых расширение в верхнем регистре (CURS-301)
Mon
- Переработана схема авторизации пользователя через AD (CUR-2157)
- Команда для получения групп ролей пользователей одним запросом (CURS-70, SUP-99)
Nestor
- Поддержка OpenSearch в Nestor (SUP-97)
Oberto
- Перенос функционала authzforze в сервис Oberto (CURS-2123) подзадачи (CURS-365, CURS-364, CURS-366)
UI
- Angular обновлен до 16 версии в Verdi в webclients, apigatewayclient, graph, ui-plugins и vespa (CUR-2083)
- Создана библиотека "Конструктор схем" (CUR-2137)
Исправлены множественные недочеты в Slate
Полный список задач релиза зафиксирован в Confluence
v1.5.3
Verdi-libs
- StoredFile перенесен в fsClient (SUP-66)
Erika
- Исправлены ошибки при генерации документов с метками в таблицах (SUP-67)
Mon
- Содержимое jwt токена теперь генерируется корректно (SUP-100)
- Значительное улучшение функционала связанного с логином через AD (SUP-59, CUR-2158)
Oberto
- XML для операции ActivateRoot теперь формируется правильно (CUR-2130)
В нескольких сервисах выставлено дефолтное значение allowInternalCommands=true (SUP-77)
Исправлены различные недочеты в Slate
v1.5.2
Технический хотфикс
v1.5.1
Версия от 27.06.2023. Список изменений:
Verdi-libs
- Исправлена работа с бакетами S3 (авторизация, алиасы)
UI
- Добавлена возможность конфигурации компонента cor-select (SUP-69)
v1.5
Версия от 06.06.2023. Список изменений:
Общее
- Исправлена и дополнена документация в различных разделах Verdi
Verdi-libs
- Изменена работа с бакетами S3 (авторизация) (SUP-50)
ApiGateway
- Настройки Akka HTTP связанные с таймаутами вынесены в переменные окружения (SUP-51)
UI
- Доработан компонент многострочного ввода (SUP-38)
- Доработан компонент выпадающего списка (SUP-43)
- Добавлена поддержка long-polling (SUP-55)
v1.4.1
Версия от 03.05.2023. Список изменений:
- Alexandrina:
- Добавлен http-route для проверки жизни сервиса с помощью ServiceHealthApi
- Attila:
- Добавлен http-route для проверки жизни сервиса с помощью ServiceHealthApi
- Mon:
- Добавлен http-route для проверки жизни сервиса с помощью ServiceHealthApi
- Tech-constants:
- Добавлен http-route для проверки жизни сервиса с помощью ServiceHealthApi
v1.4
Версия от 22.03.2023. Список изменений:
- Verdi-libs:
- Переработан механизм создания Kafka-консьюмеров. Они принимают на вход актор для управления состоянием консьюмера. Актор отслеживает состояние консьюмера, выполняет перезапуск при ошибках
- Добавлен
ServiceHealthApiдля отслеживания текущего статуса сервиса. Может интегрироваться с управляющим актором для Kafka-консьюмеров, в этом случае при падении консьюмера будет остановлен и сам сервис - Исправлена ошибка фильтрации открытых диапазонов
- Добавлена библиотека
validationдля валидации входных данных команд и обработки ошибок в общем формате
- ApiGateway:
- Добавлена обязательность настроек для подключения к S3-хранилищу. Теперь сервис падает, если не может подключиться, а не просто выводит ошибку в логах и 404 по url для файлов
- CommandStatus:
- Увеличена надежность Kafka-консьюмера. Добавлено управление состоянием и перезапуски (аналогично verdi-libs)
- Erika:
- Исправлена ошибка, из-за которой слова дробились на несколько спанов
- Добавлена возможность использовать условную метку внутри абзаца
- Mon:
- Добавлена команда смены пароля
- Команды для списков пользователей теперь принимают
Search - Добавлена поддержка kerberos-аутентификации
- Команда верификации регистрации теперь не возвращает
null - Исправлены ошибки в документации
- Buzzer-*:
- Исправлена документация по buzzer-personal-page и buzzer-email
- Команда для получения своих уведомлений в ЛК теперь принимает
Search - Добавлена команда для получения уведомлений в ЛК для любых пользователей
- Добавлена команда для отметки уведомлений прочитанными
- Добавлена команда для получения списка ошибок отправки почтовых уведомлений
- Alexandrina:
- Добавлено журналирование ошибок при вызове команд
- Attila:
- Добавлена поддержка полей для
ResourceType. Их должен публиковать сервис, отвечающий за соответствующийResourceType. Добавлены команды для такой публикации.
- Добавлена поддержка полей для
- Oberto:
- Исправлены ошибки с неотображением условий в политиках
- Исправлены ошибки с журналированием операций обновления и удаления политик
Общие типы данных
Search
Search - универсальный тип параметров списочного запроса. Он состоит из полей
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| query | String | Да | Строка с запросом, в котором указывается комбинация фильтров. Для запросов без фильтрации - пустая строка |
| context | JSON object | Да | Контекст фильтров запроса. Объект, в котором ключ - это название фильтра, а значение - это значение фильтра. Для запросов без фильтрации - пустой объект |
| sorting | Sorting | Нет | Параметры сортировки данных |
| paging | Paging | Нет | Параметры пейджинга. В случае отсутствия данного поля, считается что пейджинг указан как "Номер страницы - 1, число элементов - 10". |
Sorting
Sorting - параметры сортировки
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| fieldName | String | Да | Название поля для сортировки |
| order | String | Да | Направление сортировки. Допустимые значения: asc и desc для сортировки по возрастанию и убыванию соответственно |
Paging
Paging - параметры пейджинга
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| page | Integer | Да | Номер запрашиваемой страницы. Страницы нумеруются с 1 |
| count | Integer | Да | Количество элементов на странице |
Page
Страница списка элементов (часть списка с примененным пейджингом)
| Поле | Тип | Описание |
|---|---|---|
| items | Array of objects | Список элементов на запрошенной странице |
| total | Integer | Общее количество объектов в БД |
AuthorizationResult
Результат выполнения запроса авторизации
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| allow | Bool | Да | Разрешен ли доступ |
| entityFilters | String | Нет | Фильтры для сущности, которые нужно применить если "allow = true". Формат фильтров описан на странице Condition |
AttributeWithValues
Результат выполнения запроса авторизации
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| id | String | Да | Идентификатор атрибута |
| cat | String | Да | Идентификатор категории атрибута |
| values | List[String] | Да | Список значений атрибута |
В качестве значения поля "cat" может быть использована любая строка (нет ограничений), но в данный момент мы используем только следующие значения:
- "urn:oasis:names:tc:xacml:3.0:attribute-category:action"
- "urn:oasis:names:tc:xacml:3.0:attribute-category:environment"
- "urn:oasis:names:tc:xacml:3.0:attribute-category:resource"
- "urn:oasis:names:tc:xacml:1.0:subject-category:access-subject"
UserId
ID пользователя. Соответствует типу UUID.
UserStatus
Список вариантов статуса пользователя в системе. Имеет тип String.
| Название | Описание |
|---|---|
| Pending | Пользователь, ожидающий подтверждения регистрации |
| Activated | Обычный пользователь системы (выполнивший все условия/требования после регистрации) |
| Deactivated | Заблокированный пользователь системы |
UserContext
Данные пользователя необходимые для передачи в контексте запроса каждой команды
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| id | UserId | Да | Идентификатор пользователя |
| emailOpt | String | Нет | Уникальный email пользователя (опционален, т.к. отсутствует у анонимного пользователя) |
| groupIds | List[GroupId] | Да | Список идентификаторов групп пользователя |
UserContextWithJwtInvalidation
Данные пользователя необходимые для обработки запроса каждой команды
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| userContext | UserContext | Да | Контекст пользователя |
| userStatus | UserStatus | Да | Статус пользователя |
| jwtInvalidation | List[JwtInvalidation] | Да | Список правил для инвалидации JWT пользователя |
Функционал фильтров и сортировки
Для некоторых команд можно передавать параметры запроса в виде объекта Search, который содержит информацию о том как нужно отфильтровать, отсортировать запрос и как разделить его на страницы.
Фильтрация
{
"query": "count && (created || modified)",
"context": {
"count": "1_1000",
"created": "1630326000_",
"modified": "_1630327000"
}
}
Фильтрация состоит из двух полей:
queryописывает условие для фильтрации с использованием названий полейcontextописывает значение для полей, указанных вquery
Query поддерживает операторы && и || + скобки.
В Context-е можно описать несколько видов проверок для полей:
1) Условие равенства переданному значению.
{
"context": {
"fieldName": "какое-то значение"
}
}
2) Условие равенства какому-то одному значению из предоставленного списка.
{
"context": {
"fieldName": "[первое значение, второе, другое]"
}
}
или
{
"context": {
"fieldName": "any[первое значение, второе, другое]"
}
}
3) Условие наличия всех значений из предоставленного списка в массиве всех значений objectFieldName, которые присутствуют в объектах поля arrayFieldName.
Работает только для полей arrayFieldName, содержащих массив.
Предоставленный в запросе список должен содержать не менее двух значений.
{
"context": {
"arrayFieldName.objectFieldName": "all[первое значение, второе, другое]"
}
}
4) Условие соответствия паттерну
Значение поля обязательно должно начинаться со слова like.
{
"context": {
"fieldName": "like какое%значен__"
}
}
5) Условие вхождения значения в заданный диапазон. В данный момент, поддерживаются только целые числа.
Начало и конец диапазона разделяются знаком _. Оба конца диапазона опциональны и они включаются в диапазон.
Если передать просто _, то результатом будет пустой диапазон, в который не входит ни один элемент и ответ тоже будет пустой.
6) Условие текствого поиска PGSQL
Значение поля обязательно должно начинаться со слова ts.
{
"context": {
"fieldName": "ts (this | that) & the:*"
}
}
Сортировка
{
"sorting": {
"fieldName": "number",
"order": "desc"
}
}
Сортировка определяется двумя полями:
fieldNameназвание поля, по которому будет производиться сортировкаorderнаправление сортировки. Бывает двух видов:asc,desc.
Alexandrina
Сервис справочников.
Bibliotheca Alexandrina (лат.) - Александрийская библиотека.
Команды могут приходить как по HTTP, так и через Kafka в топик alexandrina_commands.
В сервисе реализовано 19 команд:
- Создать справочник
- Изменить справочник
- Удалить справочник
- Список справочников
- Получить данные справочника
- Получить справочник по коду
- Экспорт справочников
- Парсинг справочников
- Импорт справочников
- Создать элемент справочника
- Изменить элемент справочника
- Архивировать элемент справочника
- Удалить элемент справочника
- Удалить все элементы из справочника
- Список элементов справочника
- Список элементов справочника по коду
- Получить данные элемента справочника
- Получить элемент справочника по его коду
- Получить данные элементов справочников по списку id
У многих команд реализован механизм оптимистичных блокировок. Для этого в данных присутствует поле version.
Задача оптимистичных блокировок - предотвратить одновременное редактирование объекта из двух открытых форм.
Сначала фронтэнд запрашивает данные объекта и получает в ответе версию данных - поле version.
Он прикладывает эту версию в ответе. Если на бэкэнде она совпадает с исходной (не было других модификаций),
тогда данные сохраняются, а версия поднимается. Иначе сохранения не происходит, данные не перетираются.
Конфигурирование Alexandrina
Требования к запуску Alexandrina
Запуск сервиса осуществляется локально через sbt, на стенде в docker на jvm.
Для корректной минимальной работы сервиса требуется обязательное подключение к PostgreSQL, Kafka, Consul. Для настройки подключения к ним нужно заполнить обязательные переменные окружения из раздела ниже.
Для нормальной работы сервису дополнительно требуется окружение Verdi: CommandStatus и ApiGateway. В этом случае нужно уделить внимание настройкам, связанным с ServiceDiscovery и CommandDiscovery.
Список переменных окружения Alexandrina
Все доступные переменные окружения для настройки сервиса модели данных.
| Переменная | Тип | Обяза-тельная | Значение по умолчанию | Описание |
|---|---|---|---|---|
| ALEXANDRINA_HTTP_HOST | string | нет | "0.0.0.0" | Хост, на котором слушает HTTP-сервер |
| ALEXANDRINA_HTTP_PORT | int | нет | 8192 | Порт, на котором слушает HTTP-сервер |
| ALEXANDRINA_KAFKA_SERVERS | string | да | "localhost:9092" | Адрес Kafka |
| ALEXANDRINA_KAFKA_TOPIC | string | нет | "alexandrina_commands" | Название кафка-топика для получения команд. Сервис получает кафка-команды по нему, но и также сам публикует это название в CommandDiscovery. |
| ALEXANDRINA_CATALOG_EVENTS_TOPIC | string | нет | "catalogEvents" | Название кафка-топика для публикации событий над каталогами |
| ALEXANDRINA_KAFKA_CONSUMER_GROUP | string | нет | "alexandrina" | Имя consumer-группы для чтения из кафка-топика команд. Не должна меняться и не должна быть пустой, иначе сервис перечитает свои команды при перезапуске. |
| ALEXANDRINA_KAFKA_COMMANDEVENT_TOPIC | string | да | "commandevents" | Название кафка-топика для отправки сообщений со статусами выполняемых команд. ОБЯЗАТЕЛЬНО должно соответствовать названию этого топика в сервисе статуса команд. |
| ALEXANDRINA_KAFKA_PARTITIONS | int | да | 10 | Количество партиций в топике команд. |
| ALEXANDRINA_KAFKA_CONSUMER_RESTART_MIN_BACKOFF | duration string | нет | 1 second | Минимальная задержка перед перезапуском. |
| ALEXANDRINA_KAFKA_CONSUMER_RESTART_MAX_BACKOFF | duration string | нет | 30 seconds | Максимально возможная задержка перед перезапуском. |
| ALEXANDRINA_KAFKA_CONSUMER_RESTART_RANDOM_FACTOR | double | нет | 0.2 | Величина дополнительной случайной задержки: 0.0 - без случайно задержки, 1.0 - до 100% задрежки. |
| ALEXANDRINA_KAFKA_CONSUMER_RESTART_MAX_RESTARTS | int | нет | 5 | Максимальное количество перезапусков в заданный период времени. |
| ALEXANDRINA_KAFKA_CONSUMER_RESTART_MAX_RESTARTS_WITHIN | duration string | нет | 5 minutes | Период времени для ограничения перезапусков. |
| ALEXANDRINA_KAFKA_AUTH_USER | string | нет | "" | Название учетной записи Kafka. Если название не указано, то настройки авторизации не будут применены. |
| ALEXANDRINA_KAFKA_AUTH_PASSWORD | string | нет | "" | Пароль учетной записи Kafka. |
| ALEXANDRINA_KAFKA_AUTH_TRUSTSTORE_LOCATION | string | нет | "" | Путь до хранилища сертификатов (Java key store). Если путь не указан, то сертификат применяться не будет. |
| ALEXANDRINA_KAFKA_AUTH_TRUSTSTORE_PASSWORD | string | нет | "" | Пароль к хранилищу сертификатов. |
| ALEXANDRINA_KAFKA_AUTH_MODE | string | нет | "" | Режим аутентификации: static - одна учетная запись на все запросы, mapping - учетная запись зависит от запроса |
| ALEXANDRINA_KAFKA_AUTH_CONFIG | string | нет | "" | Настройки для аутентификации: если AuthMode = mapping, то необходим JSON с List[KafkaAuthConfig]. Переменная соответствует полю authConfig из KafkaAuthSettings. |
| ALEXANDRINA_KAFKA_AUTH_CACHE_SIZE | int | нет | Максимальный размер кеша для Kafka producer (количество активных соединений). | |
| ALEXANDRINA_KAFKA_AUTH_CACHE_TTL | duration string | нет | Время жизни Kafka producer в кеше. | |
| ALEXANDRINA_CONSUL_ADDR | url string | да | "http://localhost:8500" | Адрес Сonsul. |
| ALEXANDRINA_CONSUL_AUTH_USER | string | нет | "" | Название учетной записи Сonsul. Если название не указано, то настройки авторизации не будут применены. |
| ALEXANDRINA_CONSUL_AUTH_PASSWORD | string | нет | "" | Пароль учетной записи Сonsul. |
| ALEXANDRINA_TRACE_DURATION | boolean | нет | false | Признак необходимости трассировки выполнения команд |
| ALEXANDRINA_DISCOVERABLE_ID | string | нет | "alexandrina_instance" | ID сервиса в ServiceDiscovery |
| ALEXANDRINA_DISCOVERABLE_NAME | string | нет | "alexandrina" | Имя сервиса в ServiceDiscovery |
| ALEXANDRINA_DISCOVERABLE_HOST | string | да | "localhost" | Хост, публикуемый в ServiceDiscovery. По нему на данный сервис будут обращаться другие через HTTP. Указанный адрес должен быть виден другим сервисам. Пример: имя kubernetes/docker_swarm service |
| ALEXANDRINA_DISCOVERABLE_PORT | int | нет | Порт, публикуемый в ServiceDiscovery. По нему на данный сервис будут обращаться другие через HTTP. По умолчанию указывается порт, который слушает HTTP-сервер. | |
| ALEXANDRINA_DISCOVERABLE_LIVETIME | duration string | нет | 2 minutes | Период после последней отправки health check, в течение которого ServiceDiscovery считает данный сервис живым. |
| ALEXANDRINA_DISCOVERABLE_HEALTHPASS | string | нет | 1 minute | Периодичность отправки health check в ServiceDiscovery |
| ALEXANDRINA_AKKA_HTTP_CLIENT_MAXCON | int | нет | 512 | Максимальное число одновременных исходящих HTTP-соединений |
| ALEXANDRINA_AKKA_HTTP_CLIENT_MAXREQ | int | нет | 1024 | Максимальное число одновременных исходящих HTTP-запросов |
| ALEXANDRINA_AKKA_HTTP_SERVER_MAXCON | int | нет | 1024 | Максимальное число одновременных входящих HTTP-соединений |
| ALEXANDRINA_INTERNALCMD_ALLOW | bool | нет | false | Можно ли сервису отправлять внутрисистемные команды |
| ALEXANDRINA_SENDERLIB_COMMANDS _CACHE_UPDATEPERIOD | duration string | нет | 10 minutes | Время кэширования данных по командам из CommandDiscovery |
| ALEXANDRINA_SENDERLIB_SERVICES _CACHE_UPDATEPERIOD | duration string | нет | 30 seconds | Время кэширования данных по сервисам из ServiceDiscovery |
| ALEXANDRINA_DB_HOST | string | да | Хост БД | |
| ALEXANDRINA_DB_PORT | int | да | Порт БД | |
| ALEXANDRINA_DB_NAME | string | да | Имя базы в БД | |
| ALEXANDRINA_DB_URL | jdbc url string | нет | JDBC-url для соединения с БД. По умолчанию собирается из других обязательных переменных. Можно указать только его, если не хочется отдельно указывать хост/порт/имя базы. | |
| ALEXANDRINA_DB_USER | string | да | Пользователь БД | |
| ALEXANDRINA_DB_PASSWORD | string | да | Пароль пользователя БД | |
| ALEXANDRINA_DB_THREADS | int | нет | 10 | Количество потоков в пуле потоков для соединения с БД |
| ALEXANDRINA_DB_QUEUE_SIZE | int | нет | 300 | Размер очереди для действий базы данных, которые не могут быть выполнены немедленно, когда все потоки заняты. За пределами этого значения новые действия немедленно завершаются неудачей |
| ALEXANDRINA_DB_CONN_MAX | int | нет | 10 | Максимальное количество одновременных подключений к БД |
| ALEXANDRINA_DB_CONN_TIMEOUT | duration string | нет | 20 second | Максимальное время ожидания ответа для соединения к БД. Если это время превышено, а соединение не становится доступным, будет брошено исключение SQLException. 1000 мс — минимальное значение. |
| ALEXANDRINA_DB_ISOLATION | string | нет | "READ_COMMITTED" | Уровень изоляции транзакций для новых подключений. Допустимые значения: NONE, READ_COMMITTED, READ_UNCOMMITTED, REPEATABLE_READ, SERIALIZABLE. |
| ALEXANDRINA_DB_READONLY | boolean | нет | false | Read-only SQL транзакция может изменять только временные таблицы. Этот параметр управляет статусом «только для чтения» по умолчанию для каждой новой транзакции. |
| ALEXANDRINA_DB_CONN_MIN | int | нет | = DB_THREADS | Минимальное количество одновременных подключений к БД |
| ALEXANDRINA_DB_VALIDATION_TIMEOUT | duration string | нет | 1 seconds | Максимальное время, в течение которого соединение будет проверяться на работоспособность. 1000 мс — минимальное значение. |
| ALEXANDRINA_DB_IDLE_TIMEOUT | duration string | нет | 10 minutes | Максимальное время, в течение которого соединению разрешено простаивать в пуле. Значение 0 означает, что простаивающие соединения никогда не удаляются из пула. |
| ALEXANDRINA_DB_MAX_LIFETIME | duration string | нет | 30 minutes | Максимальное время жизни соединения в пуле. Когда простаивающее соединение достигает этого времени ожидания, даже если оно недавно использовалось, оно будет удалено из пула. Значение 0 указывает на отсутствие максимального срока службы. |
| ALEXANDRINA_DB_INITIALIZATION_FAIL_FAST | string | нет | false | Определяет, будет ли пул «быстро выходить из строя», если пул не может быть успешно заполнен начальными соединениями. Если соединения не могут быть созданы во время запуска пула, будет выдано исключение RuntimeException. Это свойство не имеет никакого эффекта, если minConnections равно 0. |
| ALEXANDRINA_DB_LEAK_DETECTION_THRESHOLD | int | нет | 0 | Время, в течение которого соединение может находиться вне пула, прежде чем будет зарегистрировано сообщение, указывающее на возможную утечку соединения. Значение 0 означает, что обнаружение утечек отключено. Наименьшее приемлемое значение для включения обнаружения утечек составляет 10 с. |
| ALEXANDRINA_DB_CONNECTION_TEST_QUERY | string | нет | "SELECT 1" | Выражение, которое будет выполнено непосредственно перед получением соединения из пула для проверки того, что соединение с базой данных все еще активно. Оно зависит от базы данных и должно представлять собой запрос, требующий минимальной обработки базой данных (например, «VALUES 1»). Если этот параметр не установлен, вместо него используется метод JDBC4 Connection.isValid(). |
| ALEXANDRINA_DB_REGISTER_MBEANS | boolean | нет | false | Зарегистрированы ли JMX Management Beans («MBeans») |
| ALEXANDRINA_TEMP_BUCKET | string | нет | temp | Бакет в файловом хранилище для временных файлов, загружаемых на gateway |
| ALEXANDRINA_EXPORT_BUCKET | string | нет | export | Бакет в файловом хранилище для генерируемых экспортом файлов |
| ALEXANDRINA_LOG_LEVEL | string | нет | INFO | Общий уровень логирования в сервисе |
| ALEXANDRINA_LOG_LEVEL_AKKA | string | нет | INFO | Уровень логирования для akka |
| ALEXANDRINA_LOG_LEVEL_LIQUIBASE | string | нет | INFO | Уровень логирования для liquibase (миграции) |
| ALEXANDRINA_LOG_LEVEL_APPLICATION | string | нет | DEBUG | Уровень логирования для application |
| ALEXANDRINA_LOG_LEVEL_SLICK_STATEMENT | string | нет | DEBUG | Уровень логирования запросов, отправляемых slick в БД |
| ALEXANDRINA_LOG_LEVEL_SLICK_BENCHMARK | string | нет | OFF | Уровень логирования бенчмарков выполнения запросов slick |
| ALEXANDRINA_LOG_LEVEL_KAFKA_PRODUCER | string | нет | WARN | Уровень логирования конфига kafka-producer |
| ALEXANDRINA_LOG_LEVEL_KAFKA_CONSUMER | string | нет | WARN | Уровень логирования конфига kafka-consumer |
| ALEXANDRINA_LOG_LEVEL_AKKAHTTPSENDER | string | нет | TRACE | Уровень логирования для отправки команд через HTTP. На уровне INFO логируется трассировка, если она включена |
| ALEXANDRINA_LOG_LEVEL_KAFKASENDER | string | нет | TRACE | Уровень логирования для отправки команд через Kafka. На уровне INFO логируется трассировка, если она включена |
| ALEXANDRINA_LOG_LEVEL_COMMANDSTATUSCLI | string | нет | TRACE | Уровень логирования для проверки состояний команд в сервисе статусов |
| ALEXANDRINA_LOG_OUTPUT | string | нет | "STDOUT" | Параметры вывода логов: STDOUT - обычный лог в консоль, STDOUT_CEF - лог в формате CEF в консоль, FILE - обычный лог в файл, FILE_CEF - лог в формате CEF в файл. Может принимать несколько значений через любой разделитель (например, "STDOUT FILE_CEF"). Преобразования, если указано несколько значений: присутствуют "STDOUT" + "STDOUT_CEF" = учитывается только "STDOUT_CEF"; "FILE" + "FILE_CEF" = "FILE_CEF"; |
| ALEXANDRINA_LOGGING_SRC_IP | string | нет | Параметр SRC (источник/source, на который ссылается событие) для логов в формате CEF. Если не установлена, src=notFound. Требуемый формат: IPv4, например "192.168.10.1". |
|
| ALEXANDRINA_LOGGING_SRC_HOST | string | нет | Параметр SHOST (источник/source, на который ссылается событие) для логов в формате CEF. Если не установлена, shost=notFound. Требуемый формат: fully qualified domain name (FQDN), например "host" или "host.domain.com". |
|
| ALEXANDRINA_LOGGING_DST_IP | string | нет | Параметр DST (получатель/destination, на который ссылается событие) для логов в формате CEF. Если не установлена, dst=notFound. Требуемый формат: IPv4, например "192.168.10.1". |
|
| ALEXANDRINA_LOGGING_CEF_VER | int | нет | 0 | Версия формата CEF: либо 0, либо 1. Рекомендуется использовать 0. |
| ALEXANDRINA_READABLE_NAME | string | нет | "Сервис справочников" | Читаемое название этого сервиса |
| ALEXANDRINA_DESCRIPTION | string | нет | "Сервис хранения данных справочников" | Читаемое описание этого сервиса |
| ALEXANDRINA_FS_URI | url string | нет | "http://localhost:9000" | Адрес для подключения к хранилищу файлов по S3-API |
| ALEXANDRINA_FS_ACCESS_KEY_ID | string | нет | minioadmin | Ключ доступа для хранилища файлов (aka логин) |
| ALEXANDRINA_FS_SECRET_ACCESS_KEY | string | нет | minioadmin | Секретный ключ для хранилища файлов (aka пароль) |
| ALEXANDRINA_FS_UPLOAD_PARALLELISM | int | нет | 4 | Параллелизм для загрузки файлов |
| ALEXANDRINA_FS_AUTH_MODE | string | нет | static | Режим аутентификации: static - одна учетная запись на все запросы, mapping - учетная запись зависит от запроса |
| ALEXANDRINA_FS_AUTH_CONFIG | string | нет | "" | Настройки для аутентификации: если AuthMode = mapping, то необходим JSON с маппингом учетных записей |
| ALEXANDRINA_FS_CACHE_SIZE | int | нет | 1 | Максимальный размер кеша клиентов для хранилища файлов (количество активных соединений). |
| ALEXANDRINA_FS_CACHE_TTL | duration string | нет | Время жизни клиента в кеше | |
| ALEXANDRINA_SENDERLIB_COMMANDS_HTTP_RETRY_ATTEMPTS | int | нет | 5 | Поле attempts из RetrySettings |
| ALEXANDRINA_SENDERLIB_COMMANDS_HTTP_RETRY_DELAY | duration string | нет | "5 seconds" | Поле delay из RetrySettings |
| ALEXANDRINA_SENDERLIB_COMMANDS_HTTP_RETRY_KIND | string | нет | "OnSomeExceptions(ConnectException)" | CommandResultRetryConditionKind |
Формат CEF для логов сервиса Alexandrina
У логов есть возможность включить формат CEF для логирования по следующему шаблону:
2022-12-14T16:56:31+0500 CEF:Version|Device Vendor|Device Product|Device Version|EventClassId|Message|Severity|src=? dst=? shost=? suid=? suser=? msg=? end=currentTimeMillis|.
Чтобы включить логирование в формате CEF, нужно задать переменную ALEXANDRINA_LOG_OUTPUT = STDOUT_CEF. Если логи пишутся в формате CEF, то необходимо задать следующие переменные, которые по умолчанию не заданы:
ALEXANDRINA_LOGGING_SRC_IP, для параметраsrcв логахALEXANDRINA_LOGGING_SRC_HOST, для параметраshostв логахALEXANDRINA_LOGGING_DST_IP, для параметраdstв логах
В файле boot/src/main/resources/logback.xml можно поменять class path для основного класса приложения (переменная projectMainClassPath), если это необходимо.
Команды для сервиса Alexandrina
CreateCatalog (Alexandrina)
На входе данные справочника
{
"code": "cities",
"title": "Города",
"metadata": {}
}
На выходе строка с id созданного справочника
"0effa436-06c2-42cc-befe-8ebec417fc5a"
Создание справочника. Отправляет событие о создании справочника.
Имя команды для вызова: createCatalog. Поддерживается асинхронный и синхронный вызов.
- На входе: CreateCatalogDTO
- На выходе: UUID string
UpdateCatalog (Alexandrina)
На входе данные справочника
{
"id": "0effa436-06c2-42cc-befe-8ebec417fc5a",
"code": "cities",
"title": "Города",
"metadata": {},
"version": 2
}
На выходе количество обновленных записей
0
Изменение данных справочника. Использует оптимистичную блокировку. Если изменяемый справочник был изменен параллельно, тогда вернется 0, показывающий, что не произошло обновления из-за некорректной версии. Отправляет событие об обновлении справочника.
Имя команды для вызова: updateCatalog. Поддерживается асинхронный и синхронный вызов.
- На входе: UpdateCatalogDTO
- На выходе: integer - количество обновленных записей (0 - не было успешного обновления, 1 - было)
RemoveCatalog (Alexandrina)
На входе ID справочника
"0effa436-06c2-42cc-befe-8ebec417fc5a"
На выходе признак успешности
true
Удаление справочника и всех его элементов. Отправляет событие об удалении справочника и всех его элементов.
Имя команды для вызова: removeCatalog. Поддерживается асинхронный и синхронный вызов.
- На входе: UUID string
- На выходе: boolean. True - удаление успешно, false - неуспешно.
ListCatalogs (Alexandrina)
Входных данных нет
На выходе список справочников
[
{
"id": "0effa436-06c2-42cc-befe-8ebec417fc5a",
"code": "cities",
"title": "Города",
"metadata": {},
"modified": 1632978484446,
"version": 1,
"items": 4
}
]
Получение списка всех справочников. Пока без пейджинга, сортировки, и т.п.
Имя команды для вызова: listCatalogs. Поддерживается только синхронный вызов.
- Команда не принимает параметров
- На выходе: List[Catalog]
GetCatalog (Alexandrina)
На входе ID справочника
"0effa436-06c2-42cc-befe-8ebec417fc5a"
На выходе данные справочника
{
"id": "0effa436-06c2-42cc-befe-8ebec417fc5a",
"code": "cities",
"title": "Города",
"metadata": {},
"modified": 1632978484446,
"version": 1,
"items": 4
}
Получение данных справочника по ID.
Имя команды для вызова: getCatalog. Поддерживается только синхронный вызов.
- На входе: UUID string
- На выходе: Catalog
GetCatalogByCode (Alexandrina)
На входе код справочника
"cities"
На выходе данные справочника
{
"id": "0effa436-06c2-42cc-befe-8ebec417fc5a",
"code": "cities",
"title": "Города",
"metadata": {},
"modified": 1632978484446,
"version": 1,
"items": 4
}
Получение данных справочника по коду.
Имя команды для вызова: getCatalogByCode. Поддерживается только синхронный вызов.
- На входе: string
- На выходе: Catalog
ExportCatalogs (Alexandrina)
На входе ID справочников и признак исключения из выгрузки (опциональный)
{
"ids": ["ac94000f-3b61-4327-b2db-2ed0c2f01b38", "0c69afa4-2a1f-45ad-8269-a9d14d7b7a7d"],
"exclude": false
}
На выходе URL сформированного JSON-файла
"export/71ab0531-f916-4b46-9bb5-e7684940d3df"
Выгрузка массива справочников в хранилище файлов в формате JSON. Результат команды - url этого файла,
который можно передать в метод /download.
Параметр exclude: false позволяет выгрузить справочники с переданными ID (по умолчанию), true - все справочники, кроме справочников с переданными ID.
Имя команды для вызова: exportCatalogs. Поддерживается только синхронный вызов.
- На входе: ExportParams
- На выходе: file URL string
ParseCatalogs (Alexandrina)
На входе ID файла
"71ab0531-f916-4b46-9bb5-e7684940d3df"
Пример JSON файла с указанным ID
{
"version" : "1.0",
"dataType" : "catalogs",
"data" : [
{
"code": "cities",
"title": "Города",
"metadata": {},
"items": [
{
"code": "Moscow",
"title": "Москва",
"archived": false,
"metadata": {
"status": "federal"
}
},
{
"code": "Tyumen",
"title": "Тюмень",
"archived": false,
"metadata": {}
}
]
},
{
"code": "countries",
"title": "Страны",
"metadata": {},
"items": []
}
]
}
На выходе массив справочников с элементами и признаком наличия
[
{
"code": "cities",
"title": "Города",
"metadata": {},
"items": [
{
"code": "Moscow",
"title": "Москва",
"archived": false,
"metadata": {
"status": "federal"
}
},
{
"code": "Tyumen",
"title": "Тюмень",
"archived": false,
"metadata": {}
}
],
"present": true
},
{
"code": "countries",
"title": "Страны",
"metadata": {},
"items": [],
"present": false
}
]
Парсинг JSON-файла со справочниками.
Параметр present: true - справочник с таким кодом существует, false - отсутствует.
Имя команды для вызова: parseCatalogs. Поддерживается только синхронный вызов.
- На входе: UUID string - ID json-файла со справочниками из временного бакета temp
- Формат файла: ExchangeTemplate
- На выходе: CatalogParseDTO array
ImportCatalogs (Alexandrina)
На входе массив справочников с элементами
[
{
"code": "countries",
"title": "Страны",
"metadata": {},
"items": [
{
"code": "Russia",
"title": "Россия",
"archived": false,
"metadata": {}
}
]
}
]
На выходе количество обновленных/добавленных справочников
1
Импорт массива справочников и массивов их элементов. Если справочник или элемент с таким кодом существует, он будет обновлен, если нет - будет создан новый. Допускается наличие посторонних полей, при импорте они будут игнорироваться. Отправляет события о созданных/обновленных справочниках и элементах.
Имя команды для вызова: importCatalogs. Поддерживается асинхронный и синхронный вызов.
- На входе: CatalogExchangeDTO array
- На выходе: integer - количество обновленных/добавленных справочников
CreateCatalogItem (Alexandrina)
На входе данные элемента справочника
{
"catalogId": "0effa436-06c2-42cc-befe-8ebec417fc5a",
"code": "moscow",
"title": "Москва",
"metadata": {}
}
На выходе строка с ID созданного элемента справочника
"cee05b97-ca41-4355-8565-d667c6807cbd"
Создание элемента справочника. Отправляет событие о создании элемента справочника, а также событие об обновлении справочника, так как изменилось количество элементов.
Имя команды для вызова: createCatalogItem. Поддерживается асинхронный и синхронный вызов.
- На входе: CreateCatalogItemDTO
- На выходе: UUID string
UpdateCatalogItem (Alexandrina)
На входе данные элемента справочника
{
"id": "cee05b97-ca41-4355-8565-d667c6807cbd",
"code": "moscow",
"title": "Москва",
"metadata": {},
"version": 2
}
На выходе количество измененных записей
0
Изменение элемента справочника. Использует оптимистичную блокировку. Если изменяемый элемент был изменен параллельно, тогда вернется 0, показывающий, что не произошло обновления из-за некорректной версии. Отправляет событие об обновлении элемента справочника.
Имя команды для вызова: updateCatalogItem. Поддерживается асинхронный и синхронный вызов.
- На входе: UpdateCatalogItemDTO
- На выходе: integer - количество обновленных записей (0 - не было успешного обновления, 1 - было)
ArchiveCatalogItem (Alexandrina)
На входе ID элемента справочника и признак архивности
{
"id": "cee05b97-ca41-4355-8565-d667c6807cbd",
"archived": true
}
На выходе признак успешности
true
Архивирование элемента справочника. Отправляет событие об обновлении элемента справочника.
Имя команды для вызова: archiveCatalogItem. Поддерживается асинхронный и синхронный вызов.
- На входе: ArchiveCatalogItemDTO
- На выходе: boolean. True - признак архивности изменен успешно, false - признак архивности не изменен.
RemoveCatalogItem (Alexandrina)
На входе ID элемента справочника
"cee05b97-ca41-4355-8565-d667c6807cbd"
На выходе признак успешности
true
Удаление элемента справочника. Отправляет событие об удалении элемента справочника, а также об обновлении справочника, так как количество элементов изменилось.
Имя команды для вызова: removeCatalogItem. Поддерживается асинхронный и синхронный вызов.
- На входе: UUID string
- На выходе: boolean. True - удаление успешно, false - неуспешно.
RemoveItemsFromCatalog (Alexandrina)
На входе ID справочника
"0effa436-06c2-42cc-befe-8ebec417fc5a"
На выходе количество удаленных элементов справочника
15
Удаление всех элементов из справочника.
Имя команды для вызова: removeItemsFromCatalog. Поддерживается асинхронный и синхронный вызов.
- На входе: UUID string
- На выходе: integer
ListItemsByCatalog (Alexandrina)
На входе параметры для поиска элементов справочника, ID которого указан
{
"data": "0effa436-06c2-42cc-befe-8ebec417fc5a",
"search": {
"query": "",
"context": {},
"sorting": {
"fieldName": "title",
"order": "asc"
},
"paging": {
"page": 1,
"count": 10
}
}
}
На выходе список элементов справочника
{
"items": [
{
"id": "cee05b97-ca41-4355-8565-d667c6807cbd",
"catalogId": "0effa436-06c2-42cc-befe-8ebec417fc5a",
"code": "moscow",
"title": "Москва",
"archived": false,
"metadata": {},
"created": 1632978484446,
"modified": 1632979205601,
"version": 2
}
],
"total": 1
}
Получение списка всех элементов указанного справочника.
Имя команды для вызова: listItemsByCatalog. Поддерживается только синхронный вызов.
- На входе: ListBy[CatalogId]
- На выходе: Page[CatalogItem]
ListItemsByCatalogCode (Alexandrina)
На входе параметры для поиска элементов справочника, код которого указан
{
"data": "cities",
"search": {
"query": "",
"context": {},
"sorting": {
"fieldName": "title",
"order": "asc"
},
"paging": {
"page": 1,
"count": 10
}
}
}
На выходе список элементов справочника
{
"items": [
{
"id": "cee05b97-ca41-4355-8565-d667c6807cbd",
"catalogId": "0effa436-06c2-42cc-befe-8ebec417fc5a",
"code": "moscow",
"title": "Москва",
"archived": false,
"metadata": {},
"created": 1632978484446,
"modified": 1632979205601,
"version": 2
}
],
"total": 1
}
Получение списка всех элементов указанного справочника по коду справочника.
Имя команды для вызова: listItemsByCatalogCode. Поддерживается только синхронный вызов.
- На входе: ListBy[CatalogCode]
- На выходе: Page[CatalogItem]
GetCatalogItem (Alexandrina)
На входе ID элемента справочника
"cee05b97-ca41-4355-8565-d667c6807cbd"
На выходе данные элемента
{
"id": "cee05b97-ca41-4355-8565-d667c6807cbd",
"catalogId": "0effa436-06c2-42cc-befe-8ebec417fc5a",
"code": "moscow",
"title": "Москва",
"archived": false,
"metadata": {},
"created": 1632979205601,
"modified": 1632979205601,
"version": 1
}
Получение данных по элементу справочника.
Имя команды для вызова: getCatalogItem. Поддерживается только синхронный вызов.
- На входе: UUID string
- На выходе: CatalogItem
GetCatalogItemByCode (Alexandrina)
На входе код справочника и элемента
{
"catalogCode": "cities",
"itemCode": "moscow"
}
На выходе данные элемента
{
"id": "cee05b97-ca41-4355-8565-d667c6807cbd",
"catalogId": "0effa436-06c2-42cc-befe-8ebec417fc5a",
"code": "moscow",
"title": "Москва",
"archived": false,
"metadata": {},
"created": 1632979205601,
"modified": 1632979205601,
"version": 1
}
Получение элемента справочника по коду.
Имя команды для вызова: getCatalogItemByCode. Поддерживается только синхронный вызов.
- На входе: GetCatalogItemDTO
- На выходе: CatalogItem
GetCatalogItemBatch (Alexandrina)
На входе список ID элементов справочников
["cee05b97-ca41-4355-8565-d667c6807cbd", "4a2424df-acf2-410f-8ed5-65b90675d5f1"]
На выходе список запрашиваемых элементов
[
{
"id": "cee05b97-ca41-4355-8565-d667c6807cbd",
"catalogId": "0effa436-06c2-42cc-befe-8ebec417fc5a",
"code": "moscow",
"title": "Москва",
"archived": false,
"metadata": {},
"created": 1632979205601,
"modified": 1632979205601,
"version": 1
},
{
"id": "4a2424df-acf2-410f-8ed5-65b90675d5f1",
"catalogId": "0effa436-06c2-42cc-befe-8ebec417fc5a",
"code": "Tyumen",
"title": "Тюмень",
"archived": false,
"metadata": {},
"created": 1632979205601,
"modified": 1632979205601,
"version": 1
}
]
Получение данных по элементам справочников по их id. Батчевая версия простого вызова данных одного элемента. При переданном пустом списке вернет все элементы всех справочников в базе.
Имя команды для вызова: getCatalogItemBatch. Поддерживается только синхронный вызов.
- На входе: List[UUID string]
- На выходе: List[CatalogItem]
Публикуемые события
События публикуются в ALEXANDRINA_CATALOG_EVENTS_TOPIC.
| eventType | payloadType | payloadId | Описание |
|---|---|---|---|
| catalogUpsert | catalog | Id каталога | Каталог создан или обновлен |
| catalogItemUpsert | catalogItem | Id элемента каталога | Элемент каталога создан или обновлен |
| catalogRemoval | removeCatalog | Id каталога | Каталог удален |
| catalogItemRemoval | removeCatalogItem | Id элемента каталога | Элемент каталога удален |
Модели сервиса Alexandrina
Типы данных, которые принимает на вход команд сервис справочников или возвращает в результате работы команды.
- CreateCatalogDTO
- UpdateCatalogDTO
- Catalog
- CreateCatalogItemDTO
- UpdateCatalogItemDTO
- CatalogItem
- GetCatalogItemDTO
- ArchiveCatalogItemDTO
- ExportParams
- CatalogItemExchangeDTO
- CatalogExchangeDTO
- CatalogParseDTO
- ExchangeTemplate
- ListBy[CatalogId]
- ListBy[CatalogCode]
CreateCatalogDTO (Alexandrina)
Создание справочника
| Поле | Тип | Описание |
|---|---|---|
| code | string | Строковый код справочника (32 символов) |
| title | string | Человекочитаемое название справочника (200 символов) |
| metadata | json object | Дополнительные данные справочника - объект JSON |
UpdateCatalogDTO (Alexandrina)
Обновление справочника
| Поле | Тип | Описание |
|---|---|---|
| id | uuid string | ID справочника |
| code | string | Строковый код справочника (32 символов) |
| title | string | Человекочитаемое название справочника (200 символов) |
| metadata | json object | Дополнительные данные справочника - объект JSON |
| version | integer | Версия, полученная при запросе данных справочника. Нужна для оптимистичных блокировок |
Catalog (Alexandrina)
Справочник
| Поле | Тип | Описание |
|---|---|---|
| id | uuid string | ID справочника |
| code | string | Строковый код справочника (32 символов) |
| title | string | Человекочитаемое название справочника (200 символов) |
| metadata | json object | Дополнительные данные справочника - объект JSON |
| modified | TimeStamp | Дата последнего изменения справочника |
| version | integer | Версия данных. Нужна для оптимистичных блокировок |
| items | integer | Количество элементов справочника (вычисляемое) |
CreateCatalogItemDTO (Alexandrina)
Создание элемента справочника
| Поле | Тип | Описание |
|---|---|---|
| catalogId | uuid string | ID справочника |
| code | string | Строковый код элемента (32 символов) |
| title | string | Человекочитаемое название элемента (200 символов) |
| metadata | json object | Дополнительные данные справочника - объект JSON |
UpdateCatalogItemDTO (Alexandrina)
Обновление элемента справочника
| Поле | Тип | Описание |
|---|---|---|
| id | uuid string | ID элемента справочника |
| code | string | Строковый код элемента (32 символов) |
| title | string | Человекочитаемое название элемента (200 символов) |
| metadata | json object | Дополнительные данные справочника - объект JSON |
| version | integer | Версия данных элемента. Нужна для оптимистичных блокировок |
CatalogItem (Alexandrina)
Элемент справочника
| Поле | Тип | Описание |
|---|---|---|
| id | uuid string | ID элемента справочника |
| catalogId | uuid string | ID справочника |
| code | string | Строковый код элемента (32 символов) |
| title | string | Человекочитаемое название элемента (200 символов) |
| archived | boolean | Признак архивности |
| metadata | json object | Дополнительные данные справочника - объект JSON |
| created | TimeStamp | Дата создания элемента справочника |
| modified | TimeStamp | Дата последнего изменения элемента справочника |
| version | integer | Версия данных элемента. Нужна для оптимистичных блокировок |
GetCatalogItemDTO (Alexandrina)
Получение элемента справочника по коду
| Поле | Тип | Описание |
|---|---|---|
| catalogCode | string | Код справочника |
| itemCode | string | Код элемента справочника |
ArchiveCatalogItemDTO (Alexandrina)
Данные для архивации/разархивации элемента справочника
| Поле | Тип | Описание |
|---|---|---|
| id | uuid string | ID элемента справочника |
| archived | boolean | Признак архивности |
ExportParams (Alexandrina)
Данные для экспорта справочника
| Поле | Тип | Описание |
|---|---|---|
| ids | uuid array | Массив ID справочников |
| exclude | boolean | Признак исключения из выгрузки (опциональный, по умолчанию - false) |
CatalogItemExchangeDTO (Alexandrina)
Данные элемента справочника для обмена
| Поле | Тип | Описание |
|---|---|---|
| code | string | Строковый код элемента (32 символов) |
| title | string | Человекочитаемое название элемента (200 символов) |
| archived | boolean | Признак архивности |
| metadata | json object | Дополнительные данные справочника - объект JSON |
CatalogExchangeDTO (Alexandrina)
Данные справочника для обмена
| Поле | Тип | Описание |
|---|---|---|
| code | string | Строковый код справочника (32 символов) |
| title | string | Человекочитаемое название справочника (200 символов) |
| metadata | json object | Дополнительные данные справочника - объект JSON |
| items | CatalogItemExchangeDTO array | Массив элементов справочника |
CatalogParseDTO (Alexandrina)
Данные парсинга справочника
| Поле | Тип | Описание |
|---|---|---|
| code | string | Строковый код справочника (32 символов) |
| title | string | Человекочитаемое название справочника (200 символов) |
| metadata | json object | Дополнительные данные справочника - объект JSON |
| items | CatalogItemExchangeDTO array | Массив элементов справочника |
| present | boolean | Признак наличия справочника |
ExchangeTemplate (Alexandrina)
Данные JSON-файла со справочниками
| Поле | Тип | Описание |
|---|---|---|
| version | string | Версия файла |
| dataType | string | Тип данных файла (всегда catalogs) |
| data | CatalogParseDTO array | Массив справочников |
ListBy[CatalogId] (Alexandrina)
Параметры для запроса списка элементов по идентификатору каталога
| Поле | Тип | Описание |
|---|---|---|
| data | String | Идентификатор каталога |
| search | Search | Параметры поиска для списка элементов. |
ListBy[CatalogCode] (Alexandrina)
Параметры для запроса списка элементов по коду каталога
| Поле | Тип | Описание |
|---|---|---|
| data | String | Код каталога |
| search | Search | Параметры поиска для списка элементов. |
Api Gateway
Сервис является точкой входа в систему. Все общение между front-end и back-end происходит через этот сервис. Выполняет маршрутизацию команды в нужный сервис, а также обеспечивает извлечение из запроса необходимых данных, например, для авторизации.
Выполняет отправку команд в систему синхронным или асинхронным способом. Большинство команд может быть выполнено как синхронно, так и асинхронно. Для команды, выполняемой асинхронно, нужно запрашивать ее статус. Только так можно понять, закончила она свое выполнение или нет. Если команда еще не закончена, нужно попробовать запросить ее статус позднее.
Имеет следующие URL для взаимодействия с системой и командами:
- Синхронный запрос
- Асинхронный запрос
- Получение статуса
- Open API
- Загрузка файла в систему
- Скачивание файла из системы
- Метаданные файла
Конфигурирование Api Gateway
Требования к запуску Api Gateway
Запуск сервиса осуществляется локально через sbt, на стенде в docker на jvm.
Для корректной минимальной работы сервиса требуется обязательное подключение к Kafka и Consul. Для настройки подключения к ним нужно заполнить обязательные переменные окружения из раздела ниже.
Обеспечивает работу окружения Verdi. Требует еще CommandStatus для работы этого окружения.
Анонимный доступ
Отправка запросов для анонимных пользователей контролируется переменными окружения: APIGATEWAY_ANONYMOUS_ACCESS_MODE и APIGATEWAY_ANONYMOUS_ACCESS_COMMANDS.
Проверка доступа к командам осуществляется при каждой обработке запроса (sync/async отправка, проверка статуса).
Список переменных окружения Api Gateway
| Переменная | Тип | Обяза-тельная | Значение по умолчанию | Описание |
|---|---|---|---|---|
| APIGATEWAY_HTTP_HOST | string | нет | "0.0.0.0" | Хост, на котором слушает HTTP-сервер |
| APIGATEWAY_HTTP_PORT | int | нет | 8080 | Порт, на котором слушает HTTP-сервер |
| APIGATEWAY_KAFKA_SERVERS | string | да | "localhost:9092" | Адрес Kafka |
| APIGATEWAY_KAFKA_COMMANDEVENT_TOPIC | string | да | commandevents | Название кафка-топика для отправки сообщений со статусами выполняемых команд. ОБЯЗАТЕЛЬНО должно соответствовать названию этого топика в сервисе статуса команд. |
| APIGATEWAY_KAFKA_AUTH_USER | string | нет | "" | Название учетной записи Kafka. Если название не указано, то настройки авторизации не будут применены. |
| APIGATEWAY_KAFKA_AUTH_PASSWORD | string | нет | "" | Пароль учетной записи Kafka. |
| APIGATEWAY_KAFKA_AUTH_TRUSTSTORE_LOCATION | string | нет | "" | Путь до хранилища сертификатов (Java key store). Если путь не указан, то сертификат применяться не будет. |
| APIGATEWAY_KAFKA_AUTH_TRUSTSTORE_PASSWORD | string | нет | "" | Пароль к хранилищу сертификатов. |
| APIGATEWAY_KAFKA_AUTH_MODE | string | нет | "static" | Режим аутентификации: static - одна учетная запись на все запросы, mapping - учетная запись зависит от запроса |
| APIGATEWAY_KAFKA_AUTH_CONFIG | string | нет | "" | Настройки для аутентификации: если AuthMode = mapping, то необходим JSON с List[KafkaAuthConfig]. Переменная соответствует полю authConfig из KafkaAuthSettings. |
| APIGATEWAY_KAFKA_AUTH_CACHE_SIZE | int | нет | Максимальный размер кеша для Kafka producer (количество активных соединений). | |
| APIGATEWAY_KAFKA_AUTH_CACHE_TTL | duration string | нет | Время жизни Kafka producer в кеше. | |
| APIGATEWAY_CONSUL_ADDR | url string | нет | "http://localhost:8500" | Адрес Consul |
| APIGATEWAY_CONSUL_AUTH_USER | string | нет | "" | Название учетной записи Сonsul. Если название не указано, то настройки авторизации не будут применены. |
| APIGATEWAY_CONSUL_AUTH_PASSWORD | string | нет | "" | Пароль учетной записи Сonsul. |
| APIGATEWAY_CONSUL_RETRY_MAX | int | нет | 20 | Количество попыток переподключения к Consul |
| APIGATEWAY_CONSUL_RETRY_DELAY | duration string | нет | 3 second | Задержка при попытках переподключения к Consul |
| APIGATEWAY_ANONYMOUS_ACCESS_MODE | string | нет | any | Режим доступа к командам для анонимных пользователей: any - все команды доступны; some - доступны команды из списка команд; none - ни одна команда не доступна |
| APIGATEWAY_ANONYMOUS_ACCESS_COMMANDS | string | нет | "" | Строка названиями команд, перечисленных через запятую "," |
| APIGATEWAY_TRACE_DURATION | boolean | нет | false | Проводить ли трассировку вызова команд (логировать временные метки) |
| APIGATEWAY_STACKTRACE_SHOW | boolean | нет | false | Выводить ли stack trace в ошибках |
| APIGATEWAY_POLL_STATUS_TIMEOUT | duration string | нет | 3 seconds | Максимальное время ожидания для команды PollStatus |
| APIGATEWAY_OPENAPI_TITLE | string | нет | API Gateway | Заголовок генерируемого OpenAPI |
| APIGATEWAY_OPENAPI_VERSION | string | нет | 0.5 | Версия, отображаемая в генерируемом OpenAPI |
| APIGATEWAY_OPENAPI_URLPREFIX | string | нет | Префикс для всех url, публикуемых в генерируемом OpenAPI | |
| APIGATEWAY_FS_REQUIRE | boolean | нет | false | Требуется ли использовать файловое хранилище. Если false, тогда не будут создаваться http-routes для работы с файлами |
| APIGATEWAY_JWT_SIGNATURE | string | нет | "my very secret string for signature" | Сигнатура для подписи и расшифровки jwt-токенов, используемых для аутентификации |
| APIGATEWAY_AKKA_HTTP_PARSING_MAX_CHUNK_SIZE | size string | нет | 10m | Максимальный размер данных из http-запроса, который может быть принят и распаршен |
| APIGATEWAY_AKKA_HTTP_CLIENT_MAXCON | int | нет | 512 | Максимальное количество входящих http-соединений |
| APIGATEWAY_AKKA_HTTP_CLIENT_MAXREQ | int | нет | 1024 | Максимальное количество одновременно обслуживаемых запросов |
| APIGATEWAY_AKKA_HTTP_CLIENT_RESP_SUBSCR_TIMEOUT | duration string | нет | 1 second | Таймаут, за который нужно подписаться на обработку тела http-запроса после обработки заголовков запроса |
| APIGATEWAY_AKKA_HTTP_CLIENT_IDLE_TIMEOUT | duration string | нет | 1 minute | Максимальное время на исходящий http-запрос (в какой-либо сервис) |
| APIGATEWAY_AKKA_HTTP_HOST_CONN_POOL_CLIENT_IDLE_TIMEOUT | duration string | нет | равно APIGATEWAY_AKKA_HTTP_CLIENT_IDLE_TIMEOUT | Позволяет отдельно настроить максимальное время на исходящий http-запрос, использующий пул соединений (т.е. для синхронной отправки синхронной команды) |
| APIGATEWAY_AKKA_HTTP_HOST_CONN_POOL_IDLE_TIMEOUT | duration string | нет | 30 second | Максимальное время жизни пула соединений (с каким-либо сервисом) при отсутствии исходящих соединений |
| APIGATEWAY_AKKA_HTTP_SERVER_MAXCON | int | нет | 1024 | Максимальное количество входящих http-соединений |
| APIGATEWAY_AKKA_HTTP_SERVER_IDLE_TIMEOUT | duration string | нет | 5 minutes | Максимальное время жизни входящего http-соединения |
| APIGATEWAY_AKKA_HTTP_SERVER_REQUEST_TIMEOUT | duration string | нет | 5 minutes | Максимальное время обработки входящего http-запроса. Должно быть не больше предыдущего значения, иначе входящее соединение оборвется по таймауту времени жизни запроса |
| APIGATEWAY_INTERNALCMD_ALLOW | boolean | нет | false | Позволять ли выполнять внутренние команды, не предназначенные для вызова с UI |
| APIGATEWAY_SENDERLIB_COMMANDS_CACHE_UPDATEPERIOD | duration string | нет | 10 minutes | Время жизни кэша команд, получаемых из Consul |
| APIGATEWAY_SENDERLIB_SERVICES_CACHE_UPDATEPERIOD | duration string | нет | 30 seconds | Время жизни кэша service discovery для данных адресов сервисов, получаемых из Consul |
| APIGATEWAY_SENDERLIB_KAFKA_POLLING_PERIOD | duration string | нет | 1 second | Период опроса статуса kafka-команды. Нужно для синхронного выполнения асинхронных команд |
| APIGATEWAY_SENDERLIB_KAFKA_POLLING_TIMEOUT | duration string | нет | 10 seconds | Таймаут для синхронного выполнения асинхронной kafka-команды |
| APIGATEWAY_FS_URI | url string | нет | "http://localhost:9000" | Адрес для подключения к хранилищу файлов по S3-API |
| APIGATEWAY_FS_ACCESS_KEY_ID | string | нет | minioadmin | Ключ доступа для хранилища файлов (aka логин) |
| APIGATEWAY_FS_SECRET_ACCESS_KEY | string | нет | minioadmin | Секретный ключ для хранилища файлов (aka пароль) |
| APIGATEWAY_FS_UPLOAD_PARALLELISM | int | нет | 4 | Параллелизм для загрузки файлов |
| APIGATEWAY_FS_AUTH_MODE | string | нет | static | Режим аутентификации: static - одна учетная запись на все запросы, mapping - учетная запись зависит от запроса |
| APIGATEWAY_FS_AUTH_CONFIG | string | нет | "" | Настройки для аутентификации: если AuthMode = mapping, то необходим JSON с маппингом учетных записей |
| APIGATEWAY_FS_CACHE_SIZE | int | нет | 1 | Максимальный размер кеша клиентов для хранилища файлов (количество активных соединений). |
| APIGATEWAY_FS_CACHE_TTL | duration string | нет | Время жизни клиента в кеше | |
| APIGATEWAY_TEMP_BUCKET | string | нет | temp | Бакет в файловом хранилище для временных файлов, загружаемых на gateway |
| APIGATEWAY_TEMP_USERCACHE_INIT_SIZE | int | нет | 16 | Изначальный размер кэша, хранящего сведения о том, какой пользователь загрузил какой временный файл (далее TEMP_USERCACHE). |
| APIGATEWAY_TEMP_USERCACHE_MAX_SIZE | int | нет | 1000 | Максимально допустимый размер кэша TEMP_USERCACHE |
| APIGATEWAY_TEMP_USERCACHE_DURATION | duration string | нет | 60 seconds | Время жизни записи с данными пользователя в кэше TEMP_USERCACHE |
| APIGATEWAY_CACHE_USERINFO_INIT_SIZE | int | нет | 16 | Изначальный размер кэша сведений о пользователе, таких как его email и список групп (далее CACHE_USERINFO) |
| APIGATEWAY_CACHE_USERINFO_MAX_SIZE | int | нет | 1000 | Максимально допустимый размер кэша CACHE_USERINFO |
| APIGATEWAY_CACHE_USERINFO_DURATION | duration string | нет | 60 seconds | Время жизни записи с данными пользователя в кэше CACHE_USERINFO |
| APIGATEWAY_KAFKA_CONSUMER_GROUP | string | нет | api-gateway | Имя consumer-группы для чтения из кафка-топиков. |
| APIGATEWAY_KAFKA_USEREVENT_TOPIC | string | нет | userEvents | Название кафка-топика для получения событий пользователей |
| APIGATEWAY_KAFKA_USEREVENT_PARTITIONS | int | нет | 1 | Число читаемых партиций из кафка-топика событий пользователей. |
| APIGATEWAY_KAFKA_CONSUMER_RESTART_MIN_BACKOFF | duration string | нет | 1 second | Изначальная задержка до рестарта консьюмера после падения (увеличивается в 2 раза после каждого рестарта) |
| APIGATEWAY_KAFKA_CONSUMER_RESTART_MAX_BACKOFF | duration string | нет | 30 seconds | Максимальное задержка до рестарта консьюмера после падения |
| APIGATEWAY_KAFKA_CONSUMER_RESTART_RANDOM_FACTOR | double | нет | 0.2 | Рандомный фактор для вычисления задержки перед следующим рестратом консьюмера (При значении 0.2 задержка может быть до 20% больше, чем при 0) |
| APIGATEWAY_KAFKA_CONSUMER_RESTART_MAX_RESTARTS | int | нет | 5 | Максимальное число рестартов консьюмера после падения (в пределах APIGATEWAY_KAFKA_CONSUMER_RESTART_MAX_RESTARTS_WITHIN) |
| APIGATEWAY_KAFKA_CONSUMER_RESTART_MAX_RESTARTS_WITHIN | duration string | нет | 5 minutes | Временной отрезок, в который APIGATEWAY_KAFKA_CONSUMER_RESTART_MAX_RESTARTS ограничивает число рестартов |
| APIGATEWAY_LOG_OUTPUT | string | нет | "STDOUT" | Параметры вывода логов: STDOUT - обычный лог в консоль, STDOUT_CEF - лог в формате CEF в консоль, FILE - обычный лог в файл, FILE_CEF - лог в формате CEF в файл. Может принимать несколько значений через любой разделитель (например, "STDOUT FILE_CEF"). Преобразования, если указано несколько значений: присутствуют "STDOUT" + "STDOUT_CEF" = учитывается только "STDOUT_CEF"; "FILE" + "FILE_CEF" = "FILE_CEF"; |
| APIGATEWAY_LOGGING_SRC_IP | string | нет | Параметр SRC (источник/source, на который ссылается событие) для логов в формате CEF. Если не установлена, src=notFound. Требуемый формат: IPv4, например "192.168.10.1". |
|
| APIGATEWAY_LOGGING_SRC_HOST | string | нет | Параметр SHOST (источник/source, на который ссылается событие) для логов в формате CEF. Если не установлена, shost=notFound. Требуемый формат: fully qualified domain name (FQDN), например "host" или "host.domain.com". |
|
| APIGATEWAY_LOGGING_DST_IP | string | нет | Параметр DST (получатель/destination, на который ссылается событие) для логов в формате CEF. Если не установлена, dst=notFound. Требуемый формат: IPv4, например "192.168.10.1". |
|
| APIGATEWAY_LOGGING_CEF_VER | int | нет | 0 | Версия формата CEF: либо 0, либо 1. Рекомендуется использовать 0. |
| APIGATEWAY_SENDERLIB_COMMANDS_HTTP_RETRY_ATTEMPTS | int | нет | 5 | Поле attempts из RetrySettings |
| APIGATEWAY_SENDERLIB_COMMANDS_HTTP_RETRY_DELAY | duration string | нет | "5 seconds" | Поле delay из RetrySettings |
| APIGATEWAY_SENDERLIB_COMMANDS_HTTP_RETRY_KIND | string | нет | "OnSomeExceptions(ConnectException)" | CommandResultRetryConditionKind |
| APIGATEWAY_LICENSE_PATH | string | да | "" | Путь до файла с лицензией (только binary format). |
| APIGATEWAY_LICENSE_KEY | string | да | "" | Путь до файла с публичным ключом лицензией. |
Формат CEF для логов сервиса Api Gateway
У логов есть возможность включить формат CEF для логирования по следующему шаблону:
2022-12-14T16:56:31+0500 CEF:Version|Device Vendor|Device Product|Device Version|EventClassId|Message|Severity|src=? dst=? shost=? suid=? suser=? msg=? end=currentTimeMillis|.
Чтобы включить логирование в формате CEF, нужно задать переменную APIGATEWAY_LOG_OUTPUT = STDOUT_CEF. Если логи пишутся в формате CEF, то необходимо задать следующие переменные, которые по умолчанию не заданы:
APIGATEWAY_LOGGING_SRC_IP, для параметраsrcв логахAPIGATEWAY_LOGGING_SRC_HOST, для параметраshostв логахAPIGATEWAY_LOGGING_DST_IP, для параметраdstв логах
В файле src/main/resources/logback.xml можно поменять class path для основного класса приложения (переменная projectMainClassPath), если это необходимо.
Синхронный запрос
Отправляем команду на выполнение
import com.embedika.verdi.senderlib.Dispatcher
import com.embedika.verdi.models.command.CommandName
import com.embedika.verdi.models.RequestContext
import io.circe.Json
implicit val rc: RequestContext = RequestContext.builder().build()
val dispatcher: Dispatcher = VerdiFactory.dispatcher
dispatcher.sendCommandSync(CommandName("pingpong_ping_post"), Some(Json.obj("value" -> Json.fromString("ping"))))
POST /send/sync/pingpong_ping_post
{
"value": "ping"
}
Синхронно получаем результат выполнения команды
import com.embedika.verdi.models.command.{CommandAggregatedStatus, CommandName}
import com.embedika.verdi.models.error.ErrorResponse
import com.embedika.verdi.models.RequestContext
import io.circe.Json
import scala.concurrent.Future
import io.circe.syntax._
implicit val rc: RequestContext = RequestContext.builder().build()
val commandResponse: Future[Either[ErrorResponse, CommandAggregatedStatus]] = dispatcher.sendCommandSync(CommandName("pingpong_ping_post"), Some(Json.obj("value" -> Json.fromString("ping"))))
val result = commandResponse.map { r => // Future response
r.map { cs => // Either command status
cs.value.as[PingResponse] // Option Json
}
}
{
"bodyLength": 172,
"value": "pong"
}
Отправляет команду на синхронное исполнение. Результат такого запроса возвращается сразу в ответе от Api Gateway. Запрос висит открытым все время обработки команды, пока не сформируется ответ. Применяется для простых команд, не требующих долгого выполнения. Например, запросы на чтение данных удобнее делать с помощью синхронных запросов.
Отправка команды
Чтобы синхронно выполнить команду необходимо знать ее название и
формат данных, которые она принимает.
Для этого нужно отправить POST-запрос, где в URL передать
название команды в {command_name}:
POST /send/sync/{command_name}
Формат тела запроса зависит от формата данных, которые принимает команда. Подразумевается, что при вызове команды мы знаем, какой формат данных принимает команда.
Результат
Результат выполнения команды будет доступен сразу в ответе. Тип ответа - JSON или файл, зависит от типа команды. Формат JSON зависит от конкретной выполняемой команды. Подразумевается, что при вызове команды мы знаем, какой формат данных возвращает команда.
Синхронный запрос через GET
GET /get/CommandName?data=%7B%22foo%22%3A123%2C%22bar%22%3A%22parameter%22%7D
Отправка команды
Тело запроса принимается в query параметре data как закодированный в urlencode json.
Например, при таком json
{
"foo": 123,
"bar": "parameter"
}
Запрос будет выглядеть так
GET /get/CommandName?data=%7B%22foo%22%3A123%2C%22bar%22%3A%22parameter%22%7D
Результат
Аналогичен результату синхронного запроса
Запросом через GET удобно скачивать файл. Для этого нужно вызвать команду, возвращающую файл (см. документацию других сервисов)
Асинхронный запрос
Отправляем команду на выполнение
import com.embedika.verdi.senderlib.Dispatcher
import com.embedika.verdi.models.error.ErrorResponse
import com.embedika.verdi.models.command.{CommandAggregatedStatus, CommandId, CommandName, CommandStatus}
import com.embedika.verdi.models.RequestContext
import scala.concurrent.Future
import io.circe.Json
implicit val rc: RequestContext = RequestContext.builder().build()
val dispatcher: Dispatcher = VerdiFactory.dispatcher
val commandIdF: Future[Either[ErrorResponse, CommandId]] = dispatcher.sendCommandAsync(CommandName("pingpong_ping_kafka"), Some(Json.obj("value" -> Json.fromString("ping"))))
POST /send/async/pingpong_ping_kafka
{
"value": "ping"
}
Получаем в ответе id созданной команды
import com.embedika.verdi.senderlib.Dispatcher
import com.embedika.verdi.models.error.ErrorResponse
import com.embedika.verdi.models.command.{CommandAggregatedStatus, CommandId, CommandName, CommandStatus}
import com.embedika.verdi.models.RequestContext
import scala.concurrent.Future
import io.circe.Json
implicit val rc: RequestContext = RequestContext.builder().build()
val dispatcher: Dispatcher = VerdiFactory.dispatcher
val commandIdF: Future[Either[ErrorResponse, CommandId]] = dispatcher.sendCommandAsync(CommandName("pingpong_ping_kafka"), Some(Json.obj("value" -> Json.fromString("ping"))))
"cd6fe966-9296-43bb-828d-7662cffb0c09"
Отправляет команду на асинхронное исполнение. Результат такого выполнения команды возвращается не сразу, а запрашивается дополнительно через некоторое время с помощью метода получения статуса. Запрос сразу возвращает id созданной команды и не висит открытым на протяжении всего выполнения команды. Применяется для сложных команд, выполнение которых может занять некоторое ощутимое время. Например, запросы на модификацию данных со сложными проверками, импорты, формирование документов, и т.п.
Отправка команды
Чтобы асинхронно выполнить команду необходимо знать ее название и
формат данных, которые она принимает.
Для этого нужно отправить POST-запрос, где в URL передать
название команды в {command_name}:
POST /send/async/{command_name}
Формат тела запроса зависит от формата данных, которые принимает команда. Подразумевается, что при вызове команды мы знаем, какой формат данных принимает команда.
Результат
В результате асинхронного запроса будет содержаться только одна строка с id команды, которая начала выполняться. Стоит отметить, что ответ будет JSON-строкой, а значит, содержать свое значение в кавычках.
Получение статуса
Запрашиваем статус GET-запросом, указывая id в URL
import com.embedika.verdi.senderlib.Dispatcher
import com.embedika.verdi.models.error.ErrorResponse
import com.embedika.verdi.models.command.{CommandAggregatedStatus, CommandId, CommandName, CommandStatus}
import com.embedika.verdi.models.RequestContext
import scala.concurrent.Future
import io.circe.Json
implicit val rc: RequestContext = RequestContext.builder().build()
val commandId: CommandId = ???
val dispatcher: Dispatcher = VerdiFactory.dispatcher
dispatcher.status(commandId)
}
GET /status/eee8f8d3-6e28-462a-be42-7e3d13b3bec3
Получаем описание текущего состояния команды
import com.embedika.verdi.senderlib.Dispatcher
import com.embedika.verdi.models.error.ErrorResponse
import com.embedika.verdi.models.command.{CommandAggregatedStatus, CommandId, CommandName, CommandStatus}
import com.embedika.verdi.models.RequestContext
import scala.concurrent.Future
import io.circe.Json
implicit val rc: RequestContext = RequestContext.builder().build()
val commandId: CommandId = ???
val dispatcher: Dispatcher = VerdiFactory.dispatcher
val status: Future[Either[ErrorResponse, Option[CommandAggregatedStatus]]] = dispatcher.status(commandId)
status.flatMap {
case Right(Some(CommandAggregatedStatus(CommandStatus.Completed, _, jsonValue, _))) =>
// команда выполнилась
doSomeUsefulAction()
case Right(Some(CommandAggregatedStatus(CommandStatus.InProcess, _, jsonValue, _))) =>
// команда еще выполнеяется
Future.successful(true)
case _ =>
Future.successful(false)
}
// команда еще выполняется
{
"status": "InProcess",
"timestamp": "2021-03-23T09:00:15.674384Z"
}
// команда выполнилась
{
"status": "Completed",
"timestamp": "2021-03-23T09:01:20.190815Z",
"value": {
"value": "pong"
}
}
Для запущенной команды получает ее текущий статус выполнения.
Применяется для асинхронных запросов. Клиент, вызвавший команду асинхронно,
должен на своей стороне организовать пуллинг состояний с помощью
этого вызова. Пуллинг должен продолжаться до тех пор, пока не вернется
состояние команды Completed. В этом случае в поле
value будут данные с ответом по команде.
Также нужно иметь в виду, что если вызвать status сразу же после ответа async-запроса, то status может вернуть ошибку 404. Это вызвано тем, что статус команды не успевает дойти до сервиса CommandStatus через kafka. Механизм пуллинга должен это учитывать.
Получение статуса с задержкой
GET /pollStatus/eee8f8d3-6e28-462a-be42-7e3d13b3bec3
Работа с данной командой аналогична работе с командой status
Для запущенной команды получает ее текущий статус выполнения. Если результат выполнения еще не доступен, то команда ждет некоторое время (это время можно настроить с помощью переменной окружения). Если по прошествию этого времени результат все еще не доступен, то вместо него возвращается последний статус команды. Если нет никакого статуса (например, указан несуществующий ID команды), то команда точно так же ждет некоторое время, а потом выдает либо появившийся статус, либо ошибку 404. Таким образом, эта команда может быть использована как альтернатива "пуллингу" для быстрых команд.
Формат запроса
Для получения статуса запущенной команды нужно вызвать GET-запрос,
в котором в URL передать id запущенной команды в
{command_id}:
GET /status/{command_id}
Формат ответа
В ответе будет JSON с состоянием команды, временем последнего изменения
этого состояния и значение, которое вернула команда. Это значение
будет отсутствовать, если состояние команды отлично от
Created.
Поля в JSON ответа:
| Поле | Тип | Описание |
|---|---|---|
| status | Строка | Код текущего состояния команды. Список кодов смотри ниже |
| timestamp | Instant | Строковое представление даты последнего изменения состояния команды в UTC, соответствующее java-типу java.time.Instant |
| value | Json | Опциональное значение произвольного формата. Обычно некоторый JSON-объект. Зависит от вызываемой команды |
Статусы команды
| Код статуса | Описание |
|---|---|
| Created | Команда только что создана и не начата исполняться |
| InProcess | Сервис принял команду и начал ее выполнять |
| Completed | Команда успешно выполнена |
| Failed | Команда выполнена с ошибкой |
| TimedOut | Команда не успела выполниться за отведенное ей время |
OpenAPI
{
"openapi":"3.0.3",
"info":{
"title":"API Gateway",
"version":"0.4"
},
"paths":{
"/sync/UpdateObjectByCode/accessMatrixRoleRBAM/{entityId}":{
"post":{
"description":"Обновляет объект по его Code",
"operationId":"postSyncUpdateobjectbycodeAccessmatrixrolerbamEntityid",
"parameters":[
{
"name":"entityId",
"in":"path",
"required":true,
"schema":{"type":"string"}
}
],
"requestBody":{
"description":"Command arguments as JSON object",
"content":{
"text/plain":{"schema":{"type":"string"}}
},
"required":true},
"responses":{
"200":{
"description":"Command execution result as JSON object",
"content":{
"text/plain":{"schema":{"type":"string"}}
}
}
}
}
}
}
}
// не применимо
По url /openapi.json доступна спецификация OpenAPI
описывающая все доступные команды в виде разных URL.
Применяется для интеграции Api Gateway с различными системами.
Кроме того, полезно при разработке фронта.
Upload
Загрузка файла
POST /upload
// не применимо
Результат вызова
{
"fileId": "1c689788-9617-4c93-bcee-6eae8909a0ba",
"url": "temp/1c689788-9617-4c93-bcee-6eae8909a0ba"
}
// не применимо
Загрузить файл в систему. Загруженный файл помещается во временный бакет APIGATEWAY_TEMP_BUCKET.
Файл передается в multipart-formdata в поле file. Принимается запрос POST на url /upload.
На выходе возвращается объект, содержащий id загруженного файла и относительный URL, по которому доступен этот файл. Далее можно использовать этот id для передачи в сервисы.
При каждой загрузке, файл "привязывается" к текущему пользователю (ожидается, что в заголовках запроса будет Cookie с JWT), чтобы пользователи имели доступ только к "привязанным" файлам.
Анонимные пользователи воспринимаются как один и тот же пользователь, т.е. любой анонимный пользователь имеет доступ ко всем файлам, что были загружены другими анонимными пользователями.
В текущей реализации, анонимный пользователь отличается от "залогиненого", т.е. "залогиненый" пользователь не имеет доступа к файлам от "анонимов".
Чтобы получить такой доступ, нужно выйти из системы и обратиться к файлу в качестве "анонима".
Download
При скачивании через этот метод не происходит проверки прав.
Поэтому лучше использовать команду getFile синхронно.
Скачивание файла
GET /download/temp/1c689788-9617-4c93-bcee-6eae8909a0ba
// не применимо
Скачивание файла из системы. Для скачивания передается GET-запрос. В url после /download/ передать url файла.
В этот url входит бакет, путь внутри бакета и id файла. HTTP-ответ формируется как файл.
При попытке обращения к файлу, происходит проверка наличия "связи" между пользователем и файлом (ожидается, что в заголовках запроса будет Cookie с JWT). Если такой связи нет - возвращается статус 404 Not Found, т.к. для данного пользователя файла не существует.
Создание "связи" описано в Загрузке файла в систему.
Metadata
Получение метаданных файла
GET /metadata/temp/1c689788-9617-4c93-bcee-6eae8909a0ba
// не применимо
Результат с метаданными
{
"fileId": "1c689788-9617-4c93-bcee-6eae8909a0ba",
"name": "Текстовый документ.pdf",
"size": 301,
"url": "temp/1c689788-9617-4c93-bcee-6eae8909a0ba",
"md5": "953c3ff8a7afa87d759fb606eabc8438",
"contentType": "application/pdf"
}
// не применимо
Получение метаданных файла в системе. Передается GET-запрос. В url после /metadata/ передать url файла.
В этот url входит бакет, путь внутри бакета и id файла. Возвращается json с метаданными файла.
При попытке обращения к метаданным файла, происходит проверка наличия "связи" между пользователем и файлом (ожидается, что в заголовках запроса будет Cookie с JWT). Если такой связи нет - возвращается статус 404 Not Found, т.к. для данного пользователя файла не существует.
Создание "связи" описано в Загрузке файла в систему.
Attila-client: клиент для сервиса Attila
Предоставляемые методы: - TBD
Ключевые сущности: - TBD
Attila: сервис для работы с композитными правилами ограничения доступа
Данные сервиса хранятся в PostgreSQL. Команды могут приходить как по HTTP, так и по Kafka.
Сервис разбит на несколько модулей, в виде sbt проектов:
domain, в котором содержатся все доменные модели и их различные представления.infrastructure, в котором содержатся базовые компоненты для взаимодействия с инфраструктурой.store, содержит сервисы для сохранения и чтения их разных хранилищ доменных моделей.service, содержит сервисы бизнес логии, которые определяют правила взаимодействия компонентов между собой.route, содержит HTTP роуты.boot, содержит зависимости и описание для сборки и запуска сервиса.
В сервисе реализованны следующие команды:
- Получение списка команд
- Получение списка ресурсов
- Получение списка полей для ресурсов
- Добавление полей для ресурса
- Удаление полей из ресурса
- Получение списка действий для "низкоуровневых" правил
- Получение списка UI действий для "высокоуровневых" правил
- Добавление данных для авторизации команды
- Удаление данных для авторизации команды
- Обновление правило с новой версией
- Удаление версии правила по ID
- Получение версии правила по ID
- Получение списка правил с указанием добавленных версий
Локальный запуск сервиса Attila
При запуске сервиса ожидается, что уже развернута необходимая инфраструктура:
- PostgreSQL база по адресу
localhost:5432/attila_db - Kafka по адресу
localhost:9092 - Consul по адресу
localhost:8500 - AuthZforce server по адресу
http://localhost:8080/authzforce-ce - В Consul будет добавлены следующие ключи или они указаны в
application.conf:- адрес Kafka по ключу
kafka_bootstrap_server - адрес AuthZforce по ключу
authzforce_server_address- В рантайме, исходя из наличия адреса AuthZforce, будет попытка получить доступный DomainID для AuthZforce (
также его можно указать в
application.conf)
- В рантайме, исходя из наличия адреса AuthZforce, будет попытка получить доступный DomainID для AuthZforce (
также его можно указать в
- адрес Kafka по ключу
- Добавлены необходимые переменные окружения:
- ATTILA_DB_HOST
- ATTILA_DB_PORT
- ATTILA_DB_NAME
- ATTILA_DB_USER
- ATTILA_DB_PASSWORD
Запуск Attila из консоли с помощью SBT
ATTILA_DB_HOST=localhost ATTILA_DB_PORT=5432 ATTILA_DB_NAME=attila_db ATTILA_DB_USER=postgres ATTILA_DB_PASSWORD=12345 sbt boot/run
Список всех переменных окружения для сервиса Attila
| Переменная | Тип | Обяза-тельная | Значение по умолчанию | Описание |
|---|---|---|---|---|
| ATTILA_HTTP_HOST | string | да | "0.0.0.0" | Хост для HttpListener |
| ATTILA_HTTP_PORT | int | да | 8192 | Порт для HttpListener |
| ATTILA_KAFKA_SERVERS | string | да | "localhost:9092" | Адрес Kafka |
| ATTILA_KAFKA_TOPIC | string | нет | "attila_commands" | Название топика для входящих команд |
| ATTILA_KAFKA_COMMANDEVENT_TOPIC | string | нет | "commandevents" | Название топика для входящих/исходящих событий |
| ATTILA_KAFKA_CONSUMER_GROUP | string | да | Название группы для чтения топика событий | |
| ATTILA_KAFKA_TOPIC_PARTITIONS | int | да | 10 | Количество партиций в топике команд. |
| ATTILA_KAFKA_CONSUMER_RESTART_MIN_BACKOFF | duration string | нет | "1 second" | Минимальная задержка перед перезапуском. |
| ATTILA_KAFKA_CONSUMER_RESTART_MAX_BACKOFF | duration string | нет | "30 seconds" | Максимально возможная задержка перед перезапуском. |
| ATTILA_KAFKA_CONSUMER_RESTART_RANDOM_FACTOR | double | нет | 0.2 | Величина дополнительной случайной задержки: 0.0 - без случайно задержки, 1.0 - до 100% задрежки. |
| ATTILA_KAFKA_CONSUMER_RESTART_MAX_RESTARTS | int | нет | 5 | Максимальное количество перезапусков в заданный период времени. |
| ATTILA_KAFKA_CONSUMER_RESTART_MAX_RESTARTS_WITHIN | string | нет | "5 minutes" | Период времени для ограничения перезапусков. |
| ATTILA_KAFKA_AUTH_USER | string | нет | "" | Название учетной записи Kafka. Если название не указано, то настройки авторизации не будут применены. |
| ATTILA_KAFKA_AUTH_PASSWORD | string | нет | "" | Пароль учетной записи Kafka. |
| ATTILA_KAFKA_AUTH_TRUSTSTORE_LOCATION | string | нет | "" | Путь до хранилища сертификатов (Java key store). Если путь не указан, то сертификат применяться не будет. |
| ATTILA_KAFKA_AUTH_TRUSTSTORE_PASSWORD | string | нет | "" | Пароль к хранилищу сертификатов. |
| ATTILA_KAFKA_AUTH_MODE | string | нет | Режим аутентификации: static - одна учетная запись на все запросы, mapping - учетная запись зависит от запроса | |
| ATTILA_KAFKA_AUTH_CONFIG | string | нет | Настройки для аутентификации: если AuthMode = mapping, то необходим JSON с List[KafkaAuthConfig]. Переменная соответствует полю authConfig из KafkaAuthSettings. | |
| ATTILA_KAFKA_AUTH_CACHE_SIZE | int | нет | Максимальный размер кеша для Kafka producer (количество активных соединений). | |
| ATTILA_KAFKA_AUTH_CACHE_TTL | duration string | нет | Время жизни Kafka producer в кеше. | |
| ATTILA_CONSUL_ADDR | string | да | "http://localhost:8500" | Адрес Сonsul. |
| ATTILA_CONSUL_AUTH_USER | string | нет | "" | Название учетной записи Сonsul. Если название не указано, то настройки авторизации не будут применены. |
| ATTILA_CONSUL_AUTH_PASSWORD | string | нет | "" | Пароль учетной записи Сonsul. |
| ATTILA_AUTHZFORCE_ADDR | string | нет | "http://localhost:8080/authzforce-ce" | Адрес AuthZforce server |
| ATTILA_AUTHZFORCE_DOMAIN | string | нет | Доступный DomainID в AuthZforce server | |
| ATTILA_TRACE_DURATION | boolean | нет | false | Нужно ли логировать длительность выполнения команд |
| ATTILA_DISCOVERABLE_ID | string | нет | "another_attila_instance" | ID данного сервиса |
| ATTILA_DISCOVERABLE_NAME | string | нет | "attila" | Название группы сервисов к которой принадлежит данный |
| ATTILA_DISCOVERABLE_HOST | string | нет | "localhost" | Адрес текущего инстанса сервиса, который будет виден через ServiceDiscovery |
| ATTILA_DISCOVERABLE_PORT | string | нет | {ATTILA_HTTP_PORT} | Адрес текущего инстанса сервиса, который будет виден через ServiceDiscovery |
| ATTILA_DISCOVERABLE_LIVETIME | duration string | нет | "2 minutes" | Время с последнего HeathCheck, в течении которого сервис считается "живым" |
| ATTILA_DISCOVERABLE_HEALTHPASS | duration string | нет | "1 minute" | Периодичность отправки HeathCheck |
| ATTILA_INTERNALCMD_ALLOW | boolean | нет | true | Возможно ли сервису отправлять "внутренние" команды |
| ATTILA_SENDERLIB_COMMANDS_CACHE_UPDATEPERIOD | duration string | нет | "10 minutes" | Время на которое будут кешироваться данные о командах |
| ATTILA_SENDERLIB_SERVICES_CACHE_UPDATEPERIOD | duration string | нет | "30 seconds" | Время на которое будут кешироваться данные о сервисах |
| ATTILA_DB_HOST | string | да | Хост сервера базы данных | |
| ATTILA_DB_PORT | string | да | Порт сервера базы данных | |
| ATTILA_DB_NAME | string | да | Название базы | |
| ATTILA_DB_USER | string | да | Пользователь для базы данных | |
| ATTILA_DB_PASSWORD | string | да | Пароль для пользователь для базы данных | |
| ATTILA_DB_THREADS | int | нет | 10 | Количество потоков в пуле потоков для соединения с БД |
| ATTILA_DB_QUEUE_SIZE | int | нет | 300 | Размер очереди для действий базы данных, которые не могут быть выполнены немедленно, когда все потоки заняты. За пределами этого значения новые действия немедленно завершаются неудачей |
| ATTILA_DB_CONN_MAX | int | нет | 10 | Максимальное количество одновременных подключений к БД |
| ATTILA_DB_CONN_TIMEOUT | duration string | нет | 20 second | Максимальное время ожидания ответа для соединения к БД. Если это время превышено, а соединение не становится доступным, будет брошено исключение SQLException. 1000 мс — минимальное значение. |
| ATTILA_DB_ISOLATION | string | нет | "READ_COMMITTED" | Уровень изоляции транзакций для новых подключений. Допустимые значения: NONE, READ_COMMITTED, READ_UNCOMMITTED, REPEATABLE_READ, SERIALIZABLE. |
| ATTILA_DB_READONLY | boolean | нет | false | Read-only SQL транзакция может изменять только временные таблицы. Этот параметр управляет статусом «только для чтения» по умолчанию для каждой новой транзакции. |
| ATTILA_DB_CONN_MIN | int | нет | = DB_THREADS | Минимальное количество одновременных подключений к БД |
| ATTILA_DB_VALIDATION_TIMEOUT | duration string | нет | 1 seconds | Максимальное время, в течение которого соединение будет проверяться на работоспособность. 1000 мс — минимальное значение. |
| ATTILA_DB_IDLE_TIMEOUT | duration string | нет | 10 minutes | Максимальное время, в течение которого соединению разрешено простаивать в пуле. Значение 0 означает, что простаивающие соединения никогда не удаляются из пула. |
| ATTILA_DB_MAX_LIFETIME | duration string | нет | 30 minutes | Максимальное время жизни соединения в пуле. Когда простаивающее соединение достигает этого времени ожидания, даже если оно недавно использовалось, оно будет удалено из пула. Значение 0 указывает на отсутствие максимального срока службы. |
| ATTILA_DB_INITIALIZATION_FAIL_FAST | string | нет | false | Определяет, будет ли пул «быстро выходить из строя», если пул не может быть успешно заполнен начальными соединениями. Если соединения не могут быть созданы во время запуска пула, будет выдано исключение RuntimeException. Это свойство не имеет никакого эффекта, если minConnections равно 0. |
| ATTILA_DB_LEAK_DETECTION_THRESHOLD | int | нет | 0 | Время, в течение которого соединение может находиться вне пула, прежде чем будет зарегистрировано сообщение, указывающее на возможную утечку соединения. Значение 0 означает, что обнаружение утечек отключено. Наименьшее приемлемое значение для включения обнаружения утечек составляет 10 с. |
| ATTILA_DB_CONNECTION_TEST_QUERY | string | нет | "SELECT 1" | Выражение, которое будет выполнено непосредственно перед получением соединения из пула для проверки того, что соединение с базой данных все еще активно. Оно зависит от базы данных и должно представлять собой запрос, требующий минимальной обработки базой данных (например, «VALUES 1»). Если этот параметр не установлен, вместо него используется метод JDBC4 Connection.isValid(). |
| ATTILA_DB_REGISTER_MBEANS | boolean | нет | false | Зарегистрированы ли JMX Management Beans («MBeans») |
| ATTILA_LOG_LEVEL_HLRULE_CONVERTER | string | нет | "INFO" | Уровень логов для процесса конвертации высокоуровневых правил |
| ATTILA_READABLE_NAME | string | нет | "Сервис композитных правил" | Читаемое название этого сервиса |
| ATTILA_DESCRIPTION | string | нет | "Сервис для работы с композитными правилами ограничения доступа" | Читаемое описание этого сервиса |
| ATTILA_RULE_UPDATE_TIMEOUT | duration string | нет | "10 minutes" | Таймаут на обновление HighLevelRule в сервисе Оberto |
| ATTILA_DEFAULT_RESOURCES | string | нет | "User{id:Идентификатор пользователя;email:Email пользователя;groupId:Идентификаторы групп пользователя}" | Описание ресурсов по умолчанию. Разделитель между ресурсами и полями в них ";". Символы для названий ресурсов и полей "a-zA-Z_\-0-9". Для описания разрешено все, кроме ";" и "}". |
| ATTILA_AUTHORIZER_KIND | string | нет | "authzforce" | Вид авторизатора, который используется в сервисе. Допустимые значения: "authzforce", "oberto". При указании неизвестного значения будет использовано значение по-умолчанию. |
| ATTILA_LOG_OUTPUT | string | нет | "STDOUT" | Параметры вывода логов: STDOUT - обычный лог в консоль, STDOUT_CEF - лог в формате CEF в консоль, FILE - обычный лог в файл, FILE_CEF - лог в формате CEF в файл. Может принимать несколько значений через любой разделитель (например, "STDOUT FILE_CEF"). Преобразования, если указано несколько значений: присутствуют "STDOUT" + "STDOUT_CEF" = учитывается только "STDOUT_CEF"; "FILE" + "FILE_CEF" = "FILE_CEF"; |
| ATTILA_LOGGING_SRC_IP | string | нет | Параметр SRC (источник/source, на который ссылается событие) для логов в формате CEF. Если не установлена, src=notFound. Требуемый формат: IPv4, например "192.168.10.1". |
|
| ATTILA_LOGGING_SRC_HOST | string | нет | Параметр SHOST (источник/source, на который ссылается событие) для логов в формате CEF. Если не установлена, shost=notFound. Требуемый формат: fully qualified domain name (FQDN), например "host" или "host.domain.com". |
|
| ATTILA_LOGGING_DST_IP | string | нет | Параметр DST (получатель/destination, на который ссылается событие) для логов в формате CEF. Если не установлена, dst=notFound. Требуемый формат: IPv4, например "192.168.10.1". |
|
| ATTILA_LOGGING_CEF_VER | int | нет | 0 | Версия формата CEF: либо 0, либо 1. Рекомендуется использовать 0. |
| ATTILA_SENDERLIB_COMMANDS_HTTP_RETRY_ATTEMPTS | int | нет | 5 | Поле attempts из RetrySettings |
| ATTILA_SENDERLIB_COMMANDS_HTTP_RETRY_DELAY | duration string | нет | "5 seconds" | Поле delay из RetrySettings |
| ATTILA_SENDERLIB_COMMANDS_HTTP_RETRY_KIND | string | нет | "OnSomeExceptions(ConnectException)" | CommandResultRetryConditionKind |
Формат CEF для логов сервиса Attila
У логов есть возможность включить формат CEF для логирования по следующему шаблону:
2022-12-14T16:56:31+0500 CEF:Version|Device Vendor|Device Product|Device Version|EventClassId|Message|Severity|src=? dst=? shost=? suid=? suser=? msg=? end=currentTimeMillis|.
Чтобы включить логирование в формате CEF, нужно задать переменную ATTILA_LOG_OUTPUT = STDOUT_CEF. Если логи пишутся в формате CEF, то необходимо задать следующие переменные, которые по умолчанию не заданы:
ATTILA_LOGGING_SRC_IP, для параметраsrcв логахATTILA_LOGGING_SRC_HOST, для параметраshostв логахATTILA_LOGGING_DST_IP, для параметраdstв логах
В файле boot/src/main/resources/logback.xml можно поменять class path для основного класса приложения (переменная projectMainClassPath), если это необходимо.
Список команд сервиса Attila
В описании команд используется путь/route для отправки команды в сам сервис, а не в ApiGateway. В качестве Input-а для команд, сервис всегда ожидает CommandRequest (как и любой другой сервис, принимающий команды), так что в описании команды указано лишь описание поля payload для CommandRequest.
Информация по добавлению команд можно прочитать в описании шаблона
Список реализованных в сервисе команд, моделей EntityType и действий Actions, которые можно использовать при настройке авторизации.
| Название команды | EntityType | Actions |
|---|---|---|
| attila_GetResourceTypeFields | - | - |
| attila_AddResourceTypeFields | - | - |
| attila_RemoveResourceTypeFields | - | - |
| attila_AddAuthorizationInfo | - | - |
| attila_DeleteAuthorizationInfo | - | - |
| attila_ListCommandAuthorizationInfo | AuthorizationInfo | AuthorizationInfo_ViewCommands |
| attila_ListResourceTypeAuthorizationInfo | AuthorizationInfo | AuthorizationInfo_ViewResources |
| attila_ListRuleActionAuthorizationInfo | AuthorizationInfo | AuthorizationInfo_ViewRuleActions |
| attila_ListUIActionAuthorizationInfo | AuthorizationInfo | AuthorizationInfo_ViewUIActions |
| attila_UpdateHighLevelRule | HighLevelRule | HighLevelRule_Update |
| attila_DeleteHighLevelRule | HighLevelRule | HighLevelRule_Delete |
| attila_GetHighLevelRule | HighLevelRule | HighLevelRule_View |
| attila_ListHighLevelRules | HighLevelRule | HighLevelRule_ViewList |
ListCommands (attila)
Payload для команды
{
"query": "resource",
"context": {
"resource": "like %A"
},
"sorting": {
"fieldName": "ruleAction",
"order": "desc"
},
"paging": {
"page": 1,
"count": 3
}
}
Результат выполнения команды
{
"items": [
"commandName_EoverA",
"commandName_DoverA",
"commandName_CoverA"
],
"total": 5
}
Возвращает список названий команд, которые удовлетворяют заданным фильтрам. Поддерживается только синхронный вызов.
- Payload: Search
- Result: Page[CommandName]
| Команда | Путь |
|---|---|
| attila_ListCommandAuthorizationInfo | HTTP POST "/v1/authz/command/list" |
ListResources (attila)
Payload для команды
{
"query": "resource",
"context": {
"resource": "like %A"
},
"sorting": {
"fieldName": "ruleAction",
"order": "desc"
},
"paging": {
"page": 1,
"count": 3
}
}
Результат выполнения команды
{
"items": [
"resource_BA",
"resource_A"
],
"total": 2
}
Возвращает список названий ресурсов, которые удовлетворяют заданным фильтрам. Поддерживается только синхронный вызов.
- Payload: Search
- Result: Page[ResourceType]
| Команда | Путь |
|---|---|
| attila_ListResourceTypeAuthorizationInfo | HTTP POST "/v1/authz/resource/list" |
ListRuleActions (attila)
Payload для команды
{
"query": "resource",
"context": {
"resource": "like %A"
},
"sorting": {
"fieldName": "ruleAction",
"order": "desc"
},
"paging": {
"page": 1,
"count": 3
}
}
Результат выполнения команды
{
"items": [
"ruleAction_EoverA",
"ruleAction_DoverA",
"ruleAction_CoverA"
],
"total": 5
}
Возвращает список действий для низкоуровневых правил, которые удовлетворяют заданным фильтрам. Поддерживается только синхронный вызов.
- Payload: Search
- Result: Page[RuleAction]
| Команда | Путь |
|---|---|
| attila_ListRuleActionAuthorizationInfo | HTTP POST "/v1/authz/ruleaction/list" |
ListUIActions (attila)
Payload для команды
{
"query": "resource",
"context": {
"resource": "like %A"
},
"sorting": {
"fieldName": "ruleAction",
"order": "desc"
},
"paging": {
"page": 1,
"count": 3
}
}
Результат выполнения команды
{
"items": [
"uiAction_A"
],
"total": 1
}
Возвращает список UI действий, которые удовлетворяют заданным фильтрам. Поддерживается только синхронный вызов.
| Команда | Путь |
|---|---|
| attila_ListUIActionAuthorizationInfo | HTTP POST "/v1/authz/uiaction/list" |
AddAuthorizationInfo (attila)
Payload для команды
[
{
"commandName": "Command_Executed_When_Viewing_Register",
"dependencies": [
["resource_A", "view_register_of_a", ["SectionForA_View_Register"]],
["resource_A", "other_action_on_a", []]
]
},
{
"commandName": "Other_Command",
"dependencies": [
["AttractiveResourceName", "Some_Action_On_AttractiveResource", []]
]
},
{
"commandName": "Command_With_Resource_A",
"dependencies": [
["resource_A", "ruleAction_BoverA", ["uiAction_A"]]
]
}
]
Результат выполнения команды
{
"Other_Command": true,
"Command_Executed_When_Viewing_Register": true,
"Command_With_Resource_A": true
}
Возвращает список команд с результатом добавления соответствующей информации для авторизации. Поддерживается асинхронный и синхронный вызов.
Команда только внутренняя (нельзя вызвать через Api Gateway), не требует аутентификации.
- Payload: [List[CommandAuthorizationInfo]
- Result: Map[CommandName, Boolean]
| Команда | Путь |
|---|---|
| attila_AddAuthorizationInfo | kafka topic "attila_commands" |
DeleteAuthorizationInfo (attila)
Payload для команды
[
"Other_Command"
]
Результат выполнения команды
1
Возвращает количество удаленных записей с информацией для авторизации. Одна запись это картеж из (CommandName, ResourceName, RuleAction, UiAction), т.е. количество записей не всегда равно количеству добавленных команд. Поддерживается асинхронный и синхронный вызов.
Команда только внутренняя (нельзя вызвать через Api Gateway), не требует аутентификации.
- Payload: List[CommandName]
- Result: Int
| Команда | Путь |
|---|---|
| attila_DeleteAuthorizationInfo | kafka topic "attila_commands" |
GetHighLevelRule (attila)
Payload для команды
"Rule_AccessTo_ResourceA"
Результат выполнения команды
{
"id": "Rule_AccessTo_ResourceA",
"name": "Rule_AccessTo_ResourceA",
"description": "Some very high rule",
"action": "SectionForA_View_Register",
"condition": "User.groupId == 'users' && A.authorId == User.id",
"priority": 100,
"effect": true
}
Возвращает по указаному идентификатору высокоуровневое правило, если оно существует.
Иначе возвращает статус 404 Not Found.
Поддерживается только синхронный вызов.
- Payload: HighLevelRuleId
- Result: HighLevelRule
| Команда | Путь |
|---|---|
| attila_GetHighLevelRule | HTTP POST "/v1/rules/get" |
ListHighLevelRules (attila)
Payload для команды
{
"query": "resource",
"context": {
"resource": "like %A"
},
"sorting": {
"fieldName": "priority",
"order": "asc"
},
"paging": {
"page": 12,
"count": 1
}
}
Результат выполнения команды
[
{
"id": "Rule_AccessTo_ResourceA",
"name": "Rule_AccessTo_ResourceA",
"description": "Some very high rule",
"action": "SectionForA_View_Register",
"condition": "User.groupId == 'users' && A.authorId == User.id",
"priority": 100,
"effect": true
},
{
"id": "Rule_View_List_Of_Data",
"name": "Rule_View_List_Of_Data",
"description": "Another rule",
"action": "A_ViewList",
"condition": "User.groupId == 'users'",
"priority": 20,
"effect": true
},
{
"id": "Rule_Action_Over_A",
"name": "Rule_Action_Over_A",
"description": "Just a high rule",
"action": "VerySmartAction",
"condition": "User.groupId == 'users' && A.authorId == User.id",
"priority": 30,
"effect": true
}
]
Возвращает список высокоуровневых правил с набором версий, версии которых удовлетворяют заданным фильтрам (paging игнорируется, т.к. возвращается весь список).
Для сортировки можно использовать следующие поля: "priority" (значение по-умолчанию), "id", "name", "description", "effect", "action", "modified".
Поддерживается только синхронный вызов.
- Payload: Search
- Result: List[HighLevelRule]
| Команда | Путь |
|---|---|
| attila_ListHighLevelRules | HTTP POST "/v1/rules/list" |
UpdateHighLevelRule (attila)
Payload для команды
{
"id": "Rule_AccessTo_ResourceA",
"name": "Rule_AccessTo_ResourceA",
"previousId": "Rule_AccessTo_ResourceA",
"description": "Some very high rule",
"action": "SectionForA_View_Register",
"condition": "User.groupId == 'users' && A.authorId == User.id",
"priority": 100,
"effect": true
}
Результат выполнения команды
true
Возвращает индикатор успеха добавления новой версии правила. Поддерживается асинхронный и синхронный вызов.
- Payload: HighLevelRuleUpdateDTO
- Result: Boolean
| Команда | Путь |
|---|---|
| attila_UpdateHighLevelRule | kafka topic "attila_commands" |
DeleteHighLevelRule (attila)
Payload для команды
"Rule_AccessTo_ResourceA"
Результат выполнения команды
true
Возвращает индикатор успеха удаления указанной версии правила. Поддерживается асинхронный и синхронный вызов.
- Payload: HighLevelRuleId
- Result: Boolean
| Команда | Путь |
|---|---|
| attila_DeleteHighLevelRule | kafka topic "attila_commands" |
GetResourceTypeFields (attila)
Payload для команды
{
"query": "resourceType",
"context": {
"resource": "like %A"
},
"sorting": {
"fieldName": "fieldName",
"order": "asc"
},
"paging": {
"page": 1,
"count": 3
}
}
Результат выполнения команды
{
"Resource_A": {
"field_1": "title_1",
"field_2": "title_2"
},
"Resource_ABA": {
"field_1": "title_123",
"field_2": "title_2"
}
}
Возвращает список полей, которые удовлетворяют заданным фильтрам. Поддерживается только синхронный вызов.
Для данной команды, настройки пагинации игнорируются.
При сортировке не по полю "resourceType", resourceType будут сортироваться по мере появления (т.к. на беке список плоский, без вложенностей).
Например, при сортировке по "fieldName":
ABC(f1,t1), CBA(f23,t2), ABC(f30,t12), ZS(f2,t3), CBA(f0, t43), ABC(f3,t1) =>
CBA(f0, t43), ABC(f1,t1), ZS(f2,t3), ABC(f3,t1), CBA(f23,t2), ABC(f30,t12) =>
CBA((f0, t43), (f23,t2)), ABC((f1,t1), (f3,t1), (f30,t12)), ZS(f2,t3)
| Поле для сортировки | Поддерживаемые фильтры |
|---|---|
| resourceType | inSet: [ab, cd], abc; like: like ab% |
| fieldName | inSet: [ab, cd], abc; like: like ab% |
| title | inSet: [ab, cd], abc; like: like ab% |
- Payload: Search
- Result: Map[ResourceType, Map[FieldName, Title]]
| Команда | Путь |
|---|---|
| attila_GetResourceTypeFields | HTTP POST "/v1/resources/fields/list" |
AddResourceTypeFields (attila)
Payload для команды
"Rule_AccessTo_ResourceA"
Результат выполнения команды
true
Добавление набора полей для определенного типа ресурсов. Поддерживается асинхронный и синхронный вызов.
Команда только внутренняя (нельзя вызвать через Api Gateway), не требует аутентификации.
- Payload: Map[ResourceType, Map[FieldName, Title]]
- Result: Map[ResourceType, Map[FieldName, Boolean]]
| Команда | Путь |
|---|---|
| attila_AddResourceTypeFields | kafka topic "attila_commands" |
RemoveResourceTypeFields (attila)
Payload для команды
"Rule_AccessTo_ResourceA"
Результат выполнения команды
true
Удаляет указанные поля из определенного типа ресурсов. Поддерживается асинхронный и синхронный вызов.
Команда только внутренняя (нельзя вызвать через Api Gateway), не требует аутентификации.
| Команда | Путь |
|---|---|
| attila_RemoveResourceTypeFields | kafka topic "attila_commands" |
Модели сервиса Attila
CommandName
Наименование команды. Имеет тип String.
ResourceType
Наименование типа ресурса. Имеет тип String.
RuleAction
Наименование действия, которое используется в низкоуровневых правилах. Имеет тип String.
UIAction
Наименование UI действия, которое используется в высокоуровневых правилах. Имеет тип String.
FieldName
Наименование поля. Имеет тип String.
Title
Наименование чего-либо для отображения в интерфейсе. Имеет тип String.
CommandAuthorizationInfo
Информация для авторизации определенной команде.
| Поле | Описание |
|---|---|
| commandName | Наименование команды. Имеет тип CommandName |
| dependencies | Список констант, которые используются для авторизации команды. Константы образуют кортеж, обозначающее одно действие, которое необходимо авторизовать, где: ResourceType - ресурс, к которому осуществляется доступ, RuleAction - название действия для авторизации, UIAction - названия UI действий, которыми выражается RuleAction в терминах пользовательского интерфейса. Имеет тип List[(ResourceType, RuleAction, List[UIAction])] |
HighLevelRuleId
Идентификатор высокоуровневого правила. Имеет тип String.
HighLevelRule
Описание версии высокоуровневого правила
| Поле | Описание | Тип данных |
|---|---|---|
| id | Идентификатор правила. | HighLevelRuleId |
| name | Название правила для отображения в интерфейсе. | HighLevelRuleVersion |
| description | Описание правила. | String |
| action | UI действие, для которого необходимо применять данное правило. | UIAction |
| condition | Условие применения правила. Поле опционально. | Option[Condition] |
| priority | Приоритет правила : чем больше значение, тем "дальше" в очереди на применение будет распологаться правило | Int |
| effect | Является ли правило "разрешающим". | Boolean |
HighLevelRuleUpdateDTO
Описание версии высокоуровневого правила
| Поле | Описание | Тип данных |
|---|---|---|
| id | Идентификатор правила. | HighLevelRuleId |
| previousId | Используемый раньше идентификатор правила. Для новых правил, может быть пустым или равным текущему ID. | HighLevelRuleId |
| name | Название правила для отображения в интерфейсе. | HighLevelRuleVersion |
| description | Описание правила. | String |
| action | UI действие, для которого необходимо применять данное правило. | UIAction |
| condition | Условие применения правила. Поле опционально. | Option[Condition] |
| priority | Приоритет правила : чем больше значение, тем "дальше" в очереди на применение будет распологаться правило | Int |
| effect | Является ли правило "разрешающим". | Boolean |
Attribute provider
Является расширением для Authzforce server (Community edition). В терминах XACML является PIP.
Репозиторий Authzforce server: https://github.com/authzforce/server
Wiki Authzforce server: https://github.com/authzforce/core/wiki/Attribute-Providers
Спецификация XACML 3.0: http://docs.oasis-open.org/xacml/3.0/errata01/os/xacml-3.0-core-spec-errata01-os-complete.html
Расширение представляет собой jar файл с фабрикой для класса реализующего интерфейс CloseableNamedAttributeProvider (
предоставляется authzforce-ce-core-pdp-api):
AttributeValueProviderDescriptorописание провайдера вXMLAttributeValueProviderFactoryфабрика (создается при стартеAuthzforce server)AttributeValueProviderкласс реализующийCloseableNamedAttributeProvider
Предоставляемые атрибуты
Атрибуты предоставляются для объектов сервиса DataModel,
где AttributeId = <Название типа ресурса>.<Путь к полю Json-объекта в поле "data">.
Например, запрашиваеммый атрибут MyModel.firstField будет
получен по пути MyModel.data.firstField. Значения всех атрибутов имеют тип String или List[String], в зависимости от структуры.
Атрибуты связанных объектов
Атрибуты связанных объектов указываются с использованием relation-поля:
<Название типа ресурса>.<Название relation-поля>.<Путь к полю Json-объекта в поле "data" связанного объекта>.
Например,elibJournalArticle.links.journalId.
| Тип отношения между объектами | Тип значения атрибута | Если в поле связанного объекта хранится список |
|---|---|---|
| one-to-one | String | List[String] |
| one-to-many | List[String] | List[List[String]].flatten преобразуется в List[String] |
| many-to-many | List[String] | List[List[String]].flatten преобразуется в List[String] |
Список переменных окружения
Список переменных окружения для настройки AttributeProvider. Переменные для логов находятся в
файле resources/logback.xml.
| Переменная | Описание | Значение по-умолчанию |
|---|---|---|
| MAVEN_HOME | Домашняя директория для MAVEN, где находится bin/mvn. Используется только во время сборки. |
"/usr/share/maven" |
| AUTHZFORCE_CONSUL_ADDR | Адрес Consul | "http://localhost:8500" |
| AUTHZFORCE_CONSUL_AUTH_USER | Адрес Consul | "http://localhost:8500" |
| AUTHZFORCE_CONSUL_AUTH_PASSWORD | Адрес Consul | "http://localhost:8500" |
| AUTHZFORCE_KAFKA_ADDR | Название топика для входящих/исходящих событий | "localhost:9092" |
| AUTHZFORCE_KAFKA_EVENTS_TOPIC | Название топика для входящих/исходящих событий | "commandevents" |
| AUTHZFORCE_KAFKA_AUTH_USER | Название учетной записи Kafka. Если название не указано, то настройки авторизации не будут применены. | "" |
| AUTHZFORCE_KAFKA_AUTH_PASSWORD | Пароль учетной записи Kafka. | "" |
| AUTHZFORCE_KAFKA_AUTH_TRUSTSTORE_LOCATION | Путь до хранилища сертификатов (Java key store). Если путь не указан, то сертификат применяться не будет. | "" |
| AUTHZFORCE_KAFKA_AUTH_TRUSTSTORE_PASSWORD | Пароль к хранилищу сертификатов. | "" |
| AUTHZFORCE_KAFKA_AUTH_MODE | Режим аутентификации: static - одна учетная запись на все запросы, mapping - учетная запись зависит от запроса | "static" |
| AUTHZFORCE_KAFKA_AUTH_CONFIG | Настройки для аутентификации: если AuthMode = mapping, то необходим JSON с List[KafkaAuthConfig]. Переменная соответствует полю authConfig из KafkaAuthSettings. | |
| AUTHZFORCE_KAFKA_AUTH_CACHE_SIZE | Максимальный размер кеша для Kafka producer (количество активных соединений). | |
| AUTHZFORCE_KAFKA_AUTH_CACHE_TTL | Время жизни Kafka producer в кеше. | |
| AUTHZFORCE_COMMANDS_PREFIX | Префикс в Consul для ключей команд | "commands" |
| AUTHZFORCE_CACHE_LIFETIME | Время жизни в кеше (актуальность) 1 результата выполнения команды | "20 minutes" |
| AUTHZFORCE_CACHE_SIZE | Максимальное количество закешированных результатов команд | "10000" |
| AUTHZFORCE_CACHE_REQ_TIMEOUT | Хост для HttpListener | "10 seconds" |
| AUTHZFORCE_LOG_LEVEL | Общий уровень логов | "INFO" |
| AUTHZFORCE_LOG_OUTPUT | Параметры вывода логов: STDOUT - обычный лог в консоль, STDOUT_CEF - лог в формате CEF в консоль, FILE - обычный лог в файл, FILE_CEF - лог в формате CEF в файл. | "FILE" |
| AUTHZFORCE_LOGGING_SRC_IP | Параметр SRC (источник/source, на который ссылается событие) для логов в формате CEF. Если не установлена, src=notFound. Требуемый формат: IPv4, например "192.168.10.1". |
|
| AUTHZFORCE_LOGGING_SRC_HOST | Параметр SHOST (источник/source, на который ссылается событие) для логов в формате CEF. Если не установлена, shost=notFound. Требуемый формат: fully qualified domain name (FQDN), например "host" или "host.domain.com". |
|
| AUTHZFORCE_LOGGING_DST_IP | Параметр DST (получатель/destination, на который ссылается событие) для логов в формате CEF. Если не установлена, dst=notFound. Требуемый формат: IPv4, например "192.168.10.1". |
|
| AUTHZFORCE_LOGGING_CEF_VER | Версия формата CEF: либо 0, либо 1. Рекомендуется использовать 0. |
0 |
| AUTHZFORCE_SENDERLIB_COMMANDS_HTTP_RETRY_ATTEMPTS | Поле attempts из RetrySettings | 5 |
| AUTHZFORCE_SENDERLIB_COMMANDS_HTTP_RETRY_DELAY | Поле delay из RetrySettings | "5 seconds" |
| AUTHZFORCE_SENDERLIB_COMMANDS_HTTP_RETRY_KIND | CommandResultRetryConditionKind | "OnSomeExceptions(ConnectException)" |
Формат CEF для authzfoce-provider
У логов есть возможность включить формат CEF для логирования по следующему шаблону:
2022-12-14T16:56:31+0500 CEF:Version|Device Vendor|Device Product|Device Version|EventClassId|Message|Severity|src=? dst=? shost=? suid=? suser=? msg=? end=currentTimeMillis|.
Чтобы включить логирование в формате CEF, нужно задать переменную AUTHZFORCE_LOG_OUTPUT = STDOUT_CEF. Если логи пишутся в формате CEF, то необходимо задать следующие переменные, которые по умолчанию не заданы:
AUTHZFORCE_LOGGING_SRC_IP, для параметраsrcв логахAUTHZFORCE_LOGGING_SRC_HOST, для параметраshostв логахAUTHZFORCE_LOGGING_DST_IP, для параметраdstв логах
В файле boot/src/main/resources/logback.xml можно поменять class path для основного класса приложения (переменная projectMainClassPath), если это необходимо.
Maven
В проекте используется Maven, путь к которому должен быть указан в MAVEN_HOME. Для
генерации AttributeValueProviderDescriptor по XML описанию использутся maven
плагин org.jvnet.jaxb2.maven2 % maven-jaxb2-plugin, т.к. он умеет работать с jar в качестве источника XML схем.
Github репозиторий плагина: https://github.com/highsource/maven-jaxb2-plugin.
Конфигурация для плагин находится в файле pom.xml, которй также описывает maven проект. Плагин вызывается во время
компиляции sbt проекта. За вызов плагина отвечает sbt task cachedFilesGeneration и класс MavenInvoker.
Сгененрированные классы кешируются до следующего изменения файлов с конфигурацией для генерации.
Используемы классы
EntityObject
Описывает возвращаемый объект из сервиса dataModel
EntityObjectsRepository
Репозиторий с доступом к экземплярам EntityObject по идентификатору EntityObjectIdentity.
Содержит кеш для хранения объектов. При отсутсвии объекта в кеше, отправляет команду с запросом объекта в сервис
dataModel.
FieldValuesExtractor
Вспомогательный объект для получения полей из json-а EntityObject
Алгоритм обновления jar в Authzforce server
- Создать jar через
sbt assembly(target/scala-2.13/attribute-provider.jar) - Добавление namespace (выполняется при первом добавлении attribute-provider.jar)
- Добавить
<xs:import namespace="http://embedika.com/verdi/generated"/>вconf/authzforce-ext.xsd(путь в докер образе/opt/authzforce-ce-server/conf/authzforce-ext.xsd) - Добавить
<uri name="http://embedika.com/verdi/generated" uri="classpath:xsd/com.embedika.verdi.xmlschema.xsd"/>вconf/catalog.xml(путь в докер образе/opt/authzforce-ce-server/conf/catalog.xml)
- Добавить
- Добавить
target/scala-2.13/attribute-provider.jarв/opt/authzforce-ce-server/webapp/WEB-INF/lib - Перезапустить tomcat
Добавление конфига для AttributeValueProvider в необходимый домен:
- Файл конфиг
/opt/authzforce-ce-server/data/domains/{{DOMAIN_ID}}/pdp.xml - URL для PUT запроса изменения конфига
http://localhost:8080/authzforce-ce/domains/{{DOMAIN_ID}}/pap/attribute.providers - Добавление конфигурации для AttributeValueProvider, активирует его в качестве "attribute provider" для указанных атрибутов
Пример тела запроса на изменение конфигурации AttributeValueProvider
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:attributeProviders xmlns="urn:oasis:names:tc:xacml:3.0:core:schema:wd-17" xmlns:ns2="http://authzforce.github.io/rest-api-model/xmlns/authz/5" xmlns:ns3="http://www.w3.org/2005/Atom" xmlns:ns4="http://authzforce.github.io/core/xmlns/pdp/7" xmlns:ns5="http://authzforce.github.io/pap-dao-flat-file/xmlns/properties/3.6">
<ns2:attributeProvider xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns7="http://embedika.com/verdi/generated" xsi:type="ns7:AttributeValueProviderDescriptor" id="valuesProvider">
<Attributes Category="urn:oasis:names:tc:xacml:3.0:attribute-category:resource">
<Attribute AttributeId="MyModel.firstField" IncludeInResult="false">
<AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">StubValue</AttributeValue>
</Attribute>
<Attribute AttributeId="MyModel.testField" IncludeInResult="false">
<AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">StubValue</AttributeValue>
</Attribute>
<Attribute AttributeId="MyModel.additionalField" IncludeInResult="false">
<AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">StubValue</AttributeValue>
</Attribute>
</Attributes>
</ns2:attributeProvider>
</ns2:attributeProviders>
Authzforce-client
Клиент для Authzforce-server предоставляет 3 интерфейса:
Основная особенность, о которой стоит помнить, в том, что клиент не предоставляет многопоточный доступ к API, т.к. скорее всего будет необходима синхронизация не только потоков, но и экземпляров сервисов, которые используют данный клиент.
Authorizer (Authzforce)
Предоставляет API для авторизации. Авторизация происходит на основе "контекста авторизации", т.е. набора полей с заранее известными значениями и набором полей, значения которых вычисляются динамически. Результатом авторизации является решение (Boolean) и набор фильтров, которые необходимо применить к набору обрабатываемых данных.
PolicyUpdater (Authzforce)
Предоставляет API для изменения политик.
Так как все политики должны быть включены в корневую политику (PolicySetId = root), почти все методы вносят изменения в корневую политику.
Данное API не обеспечивает безопасный конкуретный доступ.
AttributeProviderApi (Authzforce)
Предоставляет API для изменения конфигурации AttributeProvider-ов. Конфигурация изменяется атомарно, но возможно "состояние гонки" при доступе к файлу настроек. Данное API не обеспечивает безопасный конкуретный доступ.
Базовая библиотека сервисов уведомлений Buzzer
В библиотеку вынесен общий для сервисов уведомлений код.
Модели
Notification
Базовое представление уведомления
| Название поля | Тип | Обязательное | Описание |
|---|---|---|---|
| code | string | да | Код уведомления, по сути идентификатор |
| userId | uuid string | да | Идентификатор пользователя для которого уведомление предназначено |
| data | object | да | Данные уведомления |
MustacheTemplate
Шаблон уведомления
| Название поля | Тип | Обязательное | Описание |
|---|---|---|---|
| key | string | да | Идентификатор шаблона |
| content | string | да | Данные шаблона |
Код для работы с шаблонизатором mustache
MustacheHelper
Хелпер для преобразования io.circe.Json в Java объект, который потом можно использовать как источник данных в шаблоне
MustacheService
Сервис для контролирования эффектов загрузки mustache шаблонов. Библиотека mustache поддерживает импорты шаблонов, но по умолчанию импортируемый шаблон ищется в локальной файловой структуре. Сервис переопределяет это поведение и оборачивает всю логику в алгебру.
Сервис отправки уведомлений на электронную почту
Сервис получает через Kafka уведомления и отправляет их на электронную почту. Уведомления отправляются через рассылки по определенному расписанию. Каждая рассылка описывает шаблон отображения уведомления в письме. Уведомления могут группироваться в одном письме. Уведомления, рассылки и группы связываются через настройки.
Сервис предоставляет следующие команды:
- CreateSettingEmail
- GetSettingEmail
- DeleteSettingEmail
- UpdateSettingEmail
- ListSettingEmail
- CreateGroupEmail
- GetGroupEmail
- DeleteGroupEmail
- UpdateGroupEmail
- ListGroupEmail
- CreateDeliveryTemplate
- DeliveryTemplatesEmail
- CreateTemplateEmail
- GetTemplateEmail
- DeleteTemplateEmail
- UpdateTemplateEmail
- ListTemplateEmail
- CreateDeliveryEmail
- GetDeliveryEmail
- DeleteDeliveryEmail
- UpdateDeliveryEmail
- ListDeliveryEmail
- ListNotificationErrors
Конфигурирование сервиса отправки уведомлений на электронную почту
Требования к запуску сервиса отправки уведомлений на электронную почту
Запуск сервиса осуществляется локально через sbt, на стенде в docker на jvm.
Для корректной минимальной работы сервиса требуется обязательное подключение к PostgreSQL, Kafka, Consul. Для настройки подключения к ним нужно заполнить обязательные переменные окружения из раздела ниже.
Для нормальной работы сервису дополнительно требуется окружение Verdi: CommandStatus и ApiGateway. В этом случае нужно уделить внимание настройкам, связанным с ServiceDiscovery и CommandDiscovery.
Список переменных окружения сервиса
Все доступные переменные окружения для настройки сервиса отправки уведомлений на электронную почту.
| Переменная | Тип | Обяза-тельная | Значение по умолчанию | Описание |
|---|---|---|---|---|
| SERVER_PORT | int | да | 8080 | Порт, на котором слушает HTTP-сервер |
| CONSUMER_TOPIC | string | да | "email" | Название кафка-топика, куда отправляются запросы на выполнение команд. |
| CONSUMER_GROUP | string | да | "email" | Имя consumer-группы для чтения из CONSUMER_TOPIC. Не должна меняться и не должна быть пустой, иначе сервис будет перечитывать все входящие команды при перезапуске. |
| VERDI_HOST | string | да | "localhost" | Адрес, который будет зарегистрирован за данным экземпляром сервиса |
| VERDI_TTL | duration string | да | 30 seconds | Время, в течение которого экземпляр сервиса считается "активным". Таймер обновляется каждый раз, когда сервис присылает "health check" |
| VERDI_HEALTH_CHECK | duration string | да | 10 seconds | Периодичность отправки "health check" |
| VERDI_COMMAND_STORAGE_UPDATE_PERIOD | duration string | да | 1 minute | Время жизни статуса команд в кеше сервиса статусов (после получения последнего статуса из кафка). |
| VERDI_SERVICE_DISCOVERY_UPDATE_PERIOD | duration string | да | 30 seconds | Период проверки кеша со статусами команд для удаления статусов, время жизни которых превысило COMMAND_STATUS_SERVICE_STATUS_CACHE_TTL. |
| VERDI_ALLOW_INTERNAL_COMMANDS | bool | да | true | Период проверки Command discovery для очистки команд, привязанных к сервисам, которые не считаются "живыми". |
| VERDI_KAFKA | string | да | "localhost:9092" | Адрес Kafka |
| VERDI_KAFKA_TOPIC | string | да | "eventStatus" | Название кафка-топика, куда отправляются сообщения со статусами выполняемых команд. |
| VERDI_KAFKA_AUTH_USER | string | да | "" | Название учетной записи Kafka. Если название не указано, то настройки авторизации не будут применены. |
| VERDI_KAFKA_AUTH_PASSWORD | string | да | "" | Пароль учетной записи Kafka. |
| VERDI_KAFKA_AUTH_TRUSTSTORE_LOCATION | string | да | "" | Путь до хранилища сертификатов (Java key store). Если путь не указан, то сертификат применяться не будет. |
| VERDI_KAFKA_AUTH_TRUSTSTORE_PASSWORD | string | да | "" | Пароль к хранилищу сертификатов. |
| VERDI_KAFKA_AUTH_MODE | string | нет | "static" | Режим аутентификации: static - одна учетная запись на все запросы, mapping - учетная запись зависит от запроса |
| VERDI_KAFKA_AUTH_CONFIG | string | нет | "" | Настройки для аутентификации: если AuthMode = mapping, то необходим JSON с List[KafkaAuthConfig]. Переменная соответствует полю authConfig из KafkaAuthSettings. |
| VERDI_KAFKA_AUTH_CACHE_SIZE | int | нет | Максимальный размер кеша для Kafka producer (количество активных соединений). | |
| VERDI_KAFKA_AUTH_CACHE_TTL | duration string | нет | Время жизни Kafka producer в кеше. | |
| VERDI_CONSUL | url string | да | "http://localhost:8500" | Адрес Сonsul. |
| VERDI_CONSUL_AUTH_USER | string | да | "" | Название учетной записи Сonsul. Если название не указано, то настройки авторизации не будут применены. |
| VERDI_CONSUL_AUTH_PASSWORD | string | да | "" | Пароль учетной записи Сonsul. |
| VERDI_CONSUL_CONNECTION_MAX_RETRY | int | да | 5 | Максимальное количество попыток подключения к Consul после обрыва соединения |
| VERDI_CONSUL_CONNECTION_RETRY_DELAY | duration string | да | 1 seconds | Задержка перед попыткой подключения к Consul |
| DB_HOST | string | да | Хост БД | |
| DB_PORT | int | да | Порт БД | |
| DB_NAME | string | да | Имя базы в БД | |
| DB_JDBC_URL | jdbc url string | да | JDBC-url для соединения с БД. По умолчанию собирается из других обязательных переменных. Можно указать только его, если не хочется отдельно указывать хост/порт/имя базы. | |
| DB_USER | string | да | postgres | Пользователь БД |
| DB_PASSWORD | string | да | 12345 | Пароль пользователя БД |
| DB_THREADS | int | нет | 10 | Количество потоков в пуле потоков для соединения с БД |
| DB_QUEUE_SIZE | int | нет | 300 | Размер очереди для действий базы данных, которые не могут быть выполнены немедленно, когда все потоки заняты. За пределами этого значения новые действия немедленно завершаются неудачей |
| DB_CONN_MAX | int | нет | 10 | Максимальное количество одновременных подключений к БД |
| DB_CONN_TIMEOUT | duration string | нет | 20 second | Максимальное время ожидания ответа для соединения к БД. Если это время превышено, а соединение не становится доступным, будет брошено исключение SQLException. 1000 мс — минимальное значение. |
| DB_ISOLATION | string | нет | "READ_COMMITTED" | Уровень изоляции транзакций для новых подключений. Допустимые значения: NONE, READ_COMMITTED, READ_UNCOMMITTED, REPEATABLE_READ, SERIALIZABLE. |
| DB_READONLY | boolean | нет | false | Read-only SQL транзакция может изменять только временные таблицы. Этот параметр управляет статусом «только для чтения» по умолчанию для каждой новой транзакции. |
| DB_CONN_MIN | int | нет | = DB_THREADS | Минимальное количество одновременных подключений к БД |
| DB_VALIDATION_TIMEOUT | duration string | нет | 1 seconds | Максимальное время, в течение которого соединение будет проверяться на работоспособность. 1000 мс — минимальное значение. |
| DB_IDLE_TIMEOUT | duration string | нет | 10 minutes | Максимальное время, в течение которого соединению разрешено простаивать в пуле. Значение 0 означает, что простаивающие соединения никогда не удаляются из пула. |
| DB_MAX_LIFETIME | duration string | нет | 30 minutes | Максимальное время жизни соединения в пуле. Когда простаивающее соединение достигает этого времени ожидания, даже если оно недавно использовалось, оно будет удалено из пула. Значение 0 указывает на отсутствие максимального срока службы. |
| DB_INITIALIZATION_FAIL_FAST | string | нет | false | Определяет, будет ли пул «быстро выходить из строя», если пул не может быть успешно заполнен начальными соединениями. Если соединения не могут быть созданы во время запуска пула, будет выдано исключение RuntimeException. Это свойство не имеет никакого эффекта, если minConnections равно 0. |
| DB_LEAK_DETECTION_THRESHOLD | int | нет | 0 | Время, в течение которого соединение может находиться вне пула, прежде чем будет зарегистрировано сообщение, указывающее на возможную утечку соединения. Значение 0 означает, что обнаружение утечек отключено. Наименьшее приемлемое значение для включения обнаружения утечек составляет 10 с. |
| DB_CONNECTION_TEST_QUERY | string | нет | "SELECT 1" | Выражение, которое будет выполнено непосредственно перед получением соединения из пула для проверки того, что соединение с базой данных все еще активно. Оно зависит от базы данных и должно представлять собой запрос, требующий минимальной обработки базой данных (например, «VALUES 1»). Если этот параметр не установлен, вместо него используется метод JDBC4 Connection.isValid(). |
| DB_REGISTER_MBEANS | boolean | нет | false | Зарегистрированы ли JMX Management Beans («MBeans») |
| LOG_OUTPUT | string | нет | STDOUT | Параметры вывода логов: STDOUT - обычный лог, STDOUT_CEF - лог в формате CEF |
| LOG_LEVEL_HTTP | string | нет | WARN | Уровень логирования HTTP-сервера |
| LOGGING_SRC_IP | string | нет | Параметр SRC (источник/source, на который ссылается событие) для логов в формате CEF. Если не установлена, src=notFound. Требуемый формат: IPv4, например "192.168.10.1". |
|
| LOGGING_SRC_HOST | string | нет | Параметр SHOST (источник/source, на который ссылается событие) для логов в формате CEF. Если не установлена, shost=notFound. Требуемый формат: fully qualified domain name (FQDN), например "host" или "host.domain.com". |
|
| LOGGING_DST_IP | string | нет | Параметр DST (получатель/destination, на который ссылается событие) для логов в формате CEF. Если не установлена, dst=notFound. Требуемый формат: IPv4, например "192.168.10.1". |
|
| LOGGING_CEF_VER | int | нет | 0 | Версия формата CEF: либо 0, либо 1. Рекомендуется использовать 0. |
| VERDI_COMMANDS_HTTP_RETRY_ATTEMPTS | int | нет | 5 | Поле attempts из RetrySettings |
| VERDI_COMMANDS_HTTP_RETRY_DELAY | duration string | нет | "5 seconds" | Поле delay из RetrySettings |
| VERDI_COMMANDS_HTTP_RETRY_KIND | string | нет | "OnSomeExceptions(ConnectException)" | CommandResultRetryConditionKind |
Формат CEF для логов сервиса Buzzer-Email
У логов есть возможность включить формат CEF для логирования по следующему шаблону:
2022-12-14T16:56:31+0500 CEF:Version|Device Vendor|Device Product|Device Version|EventClassId|Message|Severity|src=? dst=? shost=? suid=? suser=? msg=? end=currentTimeMillis|.
Чтобы включить логирование в формате CEF, нужно задать переменную LOG_OUTPUT = STDOUT_CEF. Если логи пишутся в формате CEF, то необходимо задать следующие переменные, которые по умолчанию не заданы:
LOGGING_SRC_IP, для параметраsrcв логахLOGGING_SRC_HOST, для параметраshostв логахLOGGING_DST_IP, для параметраdstв логах
В файле src/main/resources/log4j.xml можно поменять class path для основного класса приложения (переменная projectMainClassPath), если это необходимо.
Список команд сервиса уведомлений на электронную почту
Заготовка для списка реализованных в сервисе команд, моделей EntityType и действий Actions, которые можно использовать при настройке авторизации.
На данный момент, ни в одной команде сервиса нет авторизации.
| Название команды | EntityType | Actions |
|---|---|---|
| CreateSettingEmail | Setting | - |
| GetSettingEmail | Setting | - |
| DeleteSettingEmail | Setting | - |
| UpdateSettingEmail | Setting | - |
| ListSettingEmail | Setting | - |
| CreateGroupEmail | Group | - |
| GetGroupEmail | Group | - |
| DeleteGroupEmail | Group | - |
| UpdateGroupEmail | Group | - |
| ListGroupEmail | Group | - |
| CreateTemplateEmail | Template | - |
| GetTemplateEmail | Template | - |
| DeleteTemplateEmail | Template | - |
| UpdateTemplateEmail | Template | - |
| ListTemplateEmail | Template | - |
| CreateDeliveryEmail | Delivery | - |
| GetDeliveryEmail | Delivery | - |
| DeleteDeliveryEmail | Delivery | - |
| UpdateDeliveryEmail | Delivery | - |
| ListDeliveryEmail | Delivery | - |
| CreateDeliveryTemplate | Delivery | - |
| DeliveryTemplatesEmail | Delivery | - |
| ListNotificationErrors | NotificationError | - |
CreateSettingEmail (Buzzer-Email)
Payload для команды
{
"id": "testCode",
"deliveryId": "testDelivery",
"groupId": "testGroup",
"version": 1
}
Результат выполнения команды
"testCode"
Создание настройки. Поддерживается только синхронный вызов.
- Payload: Setting
- Result: String (идентификатор настройки)
| Команда | Путь |
|---|---|
| CreateSettingEmail | HTTP POST "/createSetting" |
GetSettingEmail (Buzzer-Email)
Payload для команды
"testCode"
Результат выполнения команды
{
"id": "testCode",
"deliveryId": "testDelivery",
"groupId": "testGroup",
"version": 1
}
Получение настройки. Поддерживается только синхронный вызов.
- Payload: String (идентификатор настройки)
- Result: Setting
| Команда | Путь |
|---|---|
| GetSettingEmail | HTTP POST "/getSetting" |
DeleteSettingEmail (Buzzer-Email)
Payload для команды
"testCode"
Результат выполнения команды
1
Удаление настройки. Поддерживается только синхронный вызов.
- Payload: String (идентификатор настройки)
- Result: Int (кол-во измененных записей)
| Команда | Путь |
|---|---|
| DeleteSettingEmail | HTTP POST "/deleteSetting" |
UpdateSettingEmail (Buzzer-Email)
Payload для команды
{
"id": "testCode",
"deliveryId": "another-delivery",
"groupId": "testGroup",
"version": 2
}
Результат выполнения команды
1
Обновление настройки. Поддерживается только синхронный вызов.
- Payload: Setting
- Result: Int (кол-во измененных записей)
| Команда | Путь |
|---|---|
| UpdateSettingEmail | HTTP POST "/updateSetting" |
ListSettingEmail (Buzzer-Email)
Payload для команды
null
Результат выполнения команды
[
{
"id": "testCode",
"deliveryId": "another-delivery",
"groupId": "testGroup",
"version": 2
}
]
Получение списка настроек. Поддерживается только синхронный вызов.
- Payload: null
- Result: List[Setting]
| Команда | Путь |
|---|---|
| ListSettingEmail | HTTP POST "/listSetting" |
CreateGroupEmail (Buzzer-Email)
Payload для команды
{
"id": "testGroup",
"name": "test group",
"version": 1
}
Результат выполнения команды
"testCode"
Создание группы. Поддерживается только синхронный вызов.
- Payload: Group
- Result: String (идентификатор группы)
| Команда | Путь |
|---|---|
| CreateGroupEmail | HTTP POST "/createGroup" |
GetGroupEmail (Buzzer-Email)
Payload для команды
"testCode"
Результат выполнения команды
{
"id": "testGroup",
"name": "test group",
"version": 1
}
Получение группы. Поддерживается только синхронный вызов.
- Payload: String (идентификатор группы)
- Result: Group
| Команда | Путь |
|---|---|
| GetGroupEmail | HTTP POST "/getGroup" |
DeleteGroupEmail (Buzzer-Email)
Payload для команды
"testCode"
Результат выполнения команды
1
Удаление группы. Поддерживается только синхронный вызов.
- Payload: String (идентификатор группы)
- Result: Int (кол-во измененных записей)
| Команда | Путь |
|---|---|
| DeleteGroupEmail | HTTP POST "/deleteGroup" |
UpdateGroupEmail (Buzzer-Email)
Payload для команды
{
"id": "testGroup",
"name": "test group for notification delivery",
"version": 2
}
Результат выполнения команды
1
Обновление группы. Поддерживается только синхронный вызов.
- Payload: Group
- Result: Int (кол-во измененных записей)
| Команда | Путь |
|---|---|
| UpdateGroupEmail | HTTP POST "/updateGroup" |
ListGroupEmail (Buzzer-Email)
Payload для команды
null
Результат выполнения команды
[
{
"id": "testGroup",
"name": "test group for notification delivery",
"version": 2
}
]
Получение списка групп. Поддерживается только синхронный вызов.
- Payload: null
- Result: List[Group]
| Команда | Путь |
|---|---|
| ListGroupEmail | HTTP POST "/listGroup" |
CreateTemplateEmail (Buzzer-Email)
Payload для команды
{
"id": "testTemplate",
"title": "some title",
"content": "some content",
"version": 1
}
Результат выполнения команды
"testCode"
Создание шаблона. Поддерживается только синхронный вызов.
- Payload: Template
- Result: String (идентификатор шаблона)
| Команда | Путь |
|---|---|
| CreateTemplateEmail | HTTP POST "/createTemplate" |
GetTemplateEmail (Buzzer-Email)
Payload для команды
"testCode"
Результат выполнения команды
{
"id": "testTemplate",
"title": "some title",
"content": "some content",
"version": 1
}
Получение шаблона. Поддерживается только синхронный вызов.
- Payload: String (идентификатор шаблона)
- Result: Template
| Команда | Путь |
|---|---|
| GetTemplateEmail | HTTP POST "/getTemplate" |
DeleteTemplateEmail (Buzzer-Email)
Payload для команды
"testCode"
Результат выполнения команды
1
Удаление шаблона. Поддерживается только синхронный вызов.
- Payload: String (идентификатор шаблона)
- Result: Int (кол-во измененных записей)
| Команда | Путь |
|---|---|
| DeleteTemplateEmail | HTTP POST "/deleteTemplate" |
UpdateTemplateEmail (Buzzer-Email)
Payload для команды
{
"id": "testTemplate",
"title": "Object deletion",
"content": "Объект {{data.title}} удален",
"version": 2
}
Результат выполнения команды
1
Обновление шаблона. Поддерживается только синхронный вызов.
- Payload: Template
- Result: Int (кол-во измененных записей)
| Команда | Путь |
|---|---|
| UpdateTemplateEmail | HTTP POST "/updateTemplate" |
ListTemplateEmail (Buzzer-Email)
Payload для команды
null
Результат выполнения команды
[
{
"id": "testTemplate",
"title": "Object deletion",
"content": "Объект {{data.title}} удален",
"version": 2
}
]
Получение списка шаблонов. Поддерживается только синхронный вызов.
- Payload: null
- Result: List[Template]
| Команда | Путь |
|---|---|
| ListTemplateEmail | HTTP POST "/listTemplate" |
CreateDeliveryEmail (Buzzer-Email)
Payload для команды
{
"id": "testDelivery",
"schedule": "0 12 * * *"
}
Результат выполнения команды
"testCode"
Создание рассылки. Поддерживается только синхронный вызов.
- Payload: Delivery
- Result: String (идентификатор рассылки)
| Команда | Путь |
|---|---|
| CreateDeliveryEmail | HTTP POST "/createDelivery" |
GetDeliveryEmail (Buzzer-Email)
Payload для команды
"testDelivery"
Результат выполнения команды
{
"id": "testDelivery",
"schedule": "0 12 * * *",
"version": 1
}
Получение рассылки. Поддерживается только синхронный вызов.
- Payload: String (идентификатор рассылки)
- Result: Delivery
| Команда | Путь |
|---|---|
| GetDeliveryEmail | HTTP POST "/getDelivery" |
DeleteDeliveryEmail (Buzzer-Email)
Payload для команды
"testDelivery"
Результат выполнения команды
1
Удаление рассылки. Поддерживается только синхронный вызов.
- Payload: String (идентификатор рассылки)
- Result: Int (кол-во измененных записей)
| Команда | Путь |
|---|---|
| DeleteDeliveryEmail | HTTP POST "/deleteDelivery" |
UpdateDeliveryEmail (Buzzer-Email)
Payload для команды
{
"id": "testDelivery",
"schedule": "00 14 1 * *"
}
Результат выполнения команды
1
Обновление рассылки. Поддерживается только синхронный вызов.
- Payload: Delivery
- Result: Int (кол-во измененных записей)
| Команда | Путь |
|---|---|
| UpdateDeliveryEmail | HTTP POST "/updateDelivery" |
ListDeliveryEmail (Buzzer-Email)
Payload для команды
null
Результат выполнения команды
[
{
"id": "testDelivery",
"schedule": "00 14 1 * *",
"version": 2
}
]
Получение списка рассылок. Поддерживается только синхронный вызов.
- Payload: null
- Result: List[Delivery]
| Команда | Путь |
|---|---|
| ListDeliveryEmail | HTTP POST "/listDelivery" |
ListNotificationErrors (Buzzer-email)
Payload для команды
{
"sorting": {
"fieldName": "created",
"order": "asc"
},
"paging": {
"page": 1,
"count": 20
},
"query": "isRead",
"context": {
"isRead": "false"
}
}
Результат выполнения команды
{
"total": 1,
"items": [
{
"id": "a133f0c8-a516-4537-8f3f-915acb335e2c",
"errorMessage": "Test error message",
"notificationId": "d95c6a34-9c06-4bd3-9c65-3e6323dc33a2",
"created": "1671179029000",
"stackTrace": null
}
]
}
Список ошибок отправки уведомлений. Поддерживается только синхронный вызов.
- Payload: Search
- Result: Page[NotificationError]
| Команда | Путь |
|---|---|
| ListNotificationErrors | HTTP POST "/listNotificationErrors" |
Доступные поля для фильтрации и виды фильтров по ним:
| Поле | Виды фильтров |
|---|---|
| created | InSetQuery, LikeQuery, RangeQuery |
| errorMessage | InSetQuery, LikeQuery |
| notificationId | InSetQuery, LikeQuery |
| id | InSetQuery |
Доступные поля для сортировки:
| Поле |
|---|
| created |
| errorMessage |
| notificationId |
| id |
CreateDeliveryTemplate (buzzer-email)
Payload для команды
{
"templateId": "testTemplate",
"deliveryId": "testDelivery"
}
Результат выполнения команды
1
Связывание рассылки и шаблона. Поддерживается только синхронный вызов.
- Payload: DeliveryTemplateDTO
- Result: Int (кол-во измененных записей)
| Команда | Путь |
|---|---|
| CreateDeliveryTemplate | HTTP POST "/createDeliveryTemplate" |
DeliveryTemplatesEmail (buzzer-email)
Payload для команды
"testDelivery"
Результат выполнения команды
[
{
"id": "testTemplate",
"title": "Object deletion",
"content": "Объект {{data.title}} удален",
"version": 2
}
]
Получения списка шаблонов привязанных к рассылке. Поддерживается только синхронный вызов.
- Payload: String (идентификатор настройки)
- Result: List[Template]
| Команда | Путь |
|---|---|
| DeliveryTemplatesEmail | HTTP POST "/deliveryTemplates" |
Модели сервиса отправки уведомлений на электронную почту
Delivery (Buzzer-email)
Рассылка
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| id | String | Да | Идентификатор рассылки |
| schedule | String | Да | Расписание в виде строки для cron |
| version | Long | Да | Версия рассылки, unix timestamp даты создания |
CreateDelivery (Buzzer-email)
Запрос создания рассылки
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| id | String | Да | Идентификатор рассылки |
| schedule | String | Да | Расписание в виде строки для cron |
| templateId | String | Да | Идентификатор шаблона |
DeliveryTemplate (Buzzer-email)
Связь рассылки и шаблона
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| id | UUID | Да | Идентификатор связи |
| deliveryId | String | Да | Идентификатор рассылки |
| templateId | String | Да | Идентификатор шаблона |
DeliveryTemplateDTO (Buzzer-email)
Запрос создания связи рассылки и шаблона
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| templateId | String | Да | Идентификатор шаблона |
| deliveryId | String | Да | Идентификатор рассылки |
Template (Buzzer-email)
Шаблон отображения уведомления или группы уведомлений в письме
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| id | String | Да | Идентификатор |
| title | String | Да | Заголовок |
| content | String | Да | Шаблон рендеринга уведомлений |
| version | Int | Нет | Версия. Значение по умолчанию: 1 |
Group (Buzzer-email)
Группа уведомлений для отображения в письме
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| id | String | Да | Идентификатор |
| name | String | Да | Название группы |
| version | Int | Нет | Версия. Значение по умолчанию: 1 |
Setting (Buzzer-email)
Настройка рассылок и групп уведомлений
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| id | String | Да | Идентификатор, должен соответствовать коду уведомления |
| deliveryId | String | Да | Идентификатор рассылки |
| groupId | String | Да | Идентификатор группы |
| version | Int | Нет | Версия. Значение по умолчанию: 1 |
StoredNotification (Buzzer-email)
Сохраняемые уведомления
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| id | UUID | Да | Идентификатор уведомления |
| code | String | Да | Код уведомления |
| userId | UUID | Да | Идентификатор пользователя |
| data | Json | Да | Данные уведомления |
| String | Да | Email пользователя | |
| created | LocalDateTime | Да | Дата сохранения. A date-time without a time-zone in the ISO-8601 calendar system, such as "2007-12-03T10:15:30" |
| isSend | Boolean | Да | Отметка об успешной отправке |
| numberOfAttempts | Int | Да | Количество попыток отправки |
NotificationError (Buzzer-email)
Ошибки отправки уведомлений
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| id | UUID | Да | Идентификатор ошибки |
| notificationId | UUID | Да | Идентификатор уведомления |
| errorMessage | String | Нет | Сообщение об ошибке |
| stackTrace | String | Нет | Дополнительная подробная информация об ошибке |
| created | TimeStamp | Да | Дата сохранения (unix timestamp) |
Envelop (Buzzer-email)
Промежуточная структура для представления письма
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| to | String | Да | Адресат письма |
| subject | String | Да | Заголовок письма |
| content | String | Да | Содержимое письма |
Сервис уведомлений в личном кабинете
Сервис получает через Kafka уведомления, сохраняет и затем по запросу возвращает для личного кабинета пользователя. Уведомления могут быть поделены по разделам личного кабинета. Уведомления отображаются в личном кабинете по определенным шаблонам. Уведомления, разделы и шаблоны связываются через настройки.
Сервис предоставляет следующие команды:
- searchPPNotifications
- auditSearchPPNotifications
- changeNotificationStatus
- createSectionPp
- getSectionPp
- deleteSectionPp
- updateSectionPp
- listSectionPp
- createSettingPp
- getSettingPp
- deleteSettingPp
- updateSettingPp
- listSettingPp
- createTemplatePp
- getTemplatePp
- deleteTemplatePp
- updateTemplatePp
- listTemplatePp
Список переменных окружения сервиса
Все доступные переменные окружения для настройки сервиса уведомлений в личном кабинете.
| Переменная | Тип | Обяза-тельная | Значение по умолчанию | Описание |
|---|---|---|---|---|
| SERVER_PORT | int | да | 8080 | Порт, на котором слушает HTTP-сервер |
| CONSUMER_TOPIC | string | да | "personal-page" | Название кафка-топика, куда отправляются запросы на выполнение команд. |
| CONSUMER_GROUP | string | да | "personal-page" | Имя consumer-группы для чтения из CONSUMER_TOPIC. Не должна меняться и не должна быть пустой, иначе сервис будет перечитывать все входящие команды при перезапуске. |
| CONSUMER_CHUNK_SIZE | int | да | 8080 | Максимальный размер "пачки сообщений", которые будут вычитываться из CONSUMER_TOPIC |
| CONSUMER_CHUNK_WITHIN | duration string | да | 8080 | Время в течении которого будет ожидаться "пачка сообщений" (максимальная длительность набора "пачки") |
| VERDI_HOST | string | да | "localhost" | Адрес который будет зарегистрирован за данным экземпляром сервиса |
| VERDI_TTL | duration string | да | 30 seconds | Время в течении которого экземпляр сервиса считается "активным". Таймер обновляется каждый раз, когда сервис присылает "health check" |
| VERDI_HEALTH_CHECK | duration string | да | 10 seconds | Периодичность отправки "health check" |
| VERDI_COMMAND_STORAGE_UPDATE_PERIOD | duration string | да | 1 minute | Время жизни статуса команд в кеше сервиса статусов (после получения последнего статуса из кафка). |
| VERDI_SERVICE_DISCOVERY_UPDATE_PERIOD | duration string | да | 30 seconds | Период проверки кеша со статусами команд для удаления статусов, время жизни которых превысило COMMAND_STATUS_SERVICE_STATUS_CACHE_TTL. |
| VERDI_ALLOW_INTERNAL_COMMANDS | bool | да | true | Период проверки Command discovery для очистки команд, привязанных к сервисам которые не считаются "живыми". |
| VERDI_KAFKA | string | да | "localhost:9092" | Адрес Kafka |
| VERDI_KAFKA_TOPIC | string | да | "eventStatus" | Название кафка-топика, куда отправляются сообщения со статусами выполняемых команд. |
| VERDI_KAFKA_AUTH_USER | string | да | "" | Название учетной записи Kafka. Если название не указано, то настройки авторизации не будут применены. |
| VERDI_KAFKA_AUTH_PASSWORD | string | да | "" | Пароль учетной записи Kafka. |
| VERDI_KAFKA_AUTH_TRUSTSTORE_LOCATION | string | да | "" | Путь до хранилища сертификатов (Java key store). Если путь не указан, то сертификат применяться не будет. |
| VERDI_KAFKA_AUTH_TRUSTSTORE_PASSWORD | string | да | "" | Пароль к хранилищу сертификатов. |
| VERDI_KAFKA_AUTH_MODE | string | нет | "static" | Режим аутентификации: static - одна учетная запись на все запросы, mapping - учетная запись зависит от запроса |
| VERDI_KAFKA_AUTH_CONFIG | string | нет | "" | Настройки для аутентификации: если AuthMode = mapping, то необходим JSON с List[KafkaAuthConfig]. Переменная соответствует полю authConfig из KafkaAuthSettings. |
| VERDI_KAFKA_AUTH_CACHE_SIZE | int | нет | Максимальный размер кеша для Kafka producer (количество активных соединений). | |
| VERDI_KAFKA_AUTH_CACHE_TTL | duration string | нет | Время жизни Kafka producer в кеше. | |
| VERDI_CONSUL | url string | да | "http://localhost:8500" | Адрес Сonsul. |
| VERDI_CONSUL_AUTH_USER | string | да | "" | Название учетной записи Сonsul. Если название не указано, то настройки авторизации не будут применены. |
| VERDI_CONSUL_AUTH_PASSWORD | string | да | "" | Пароль учетной записи Сonsul. |
| VERDI_CONSUL_CONNECTION_MAX_RETRY | int | да | 5 | Максимальное количество попыток подключения к Consul после обрыва соединения |
| VERDI_CONSUL_CONNECTION_RETRY_DELAY | duration string | да | 1 seconds | Задержка перед попыткой подключения к Consul |
| DB_HOST | string | да | Хост БД | |
| DB_PORT | int | да | Порт БД | |
| DB_NAME | string | да | Имя базы в БД | |
| DB_JDBC_URL | jdbc url string | да | JDBC-url для соединения с БД. По умолчанию собирается из других обязательных переменных. Можно указать только его, если не хочется отдельно указывать хост/порт/имя базы. | |
| DB_USER | string | да | postgres | Пользователь БД |
| DB_PASSWORD | string | да | 12345 | Пароль пользователя БД |
| DB_THREADS | int | нет | 10 | Количество потоков в пуле потоков для соединения с БД |
| DB_QUEUE_SIZE | int | нет | 300 | Размер очереди для действий базы данных, которые не могут быть выполнены немедленно, когда все потоки заняты. За пределами этого значения новые действия немедленно завершаются неудачей |
| DB_CONN_MAX | int | нет | 10 | Максимальное количество одновременных подключений к БД |
| DB_CONN_TIMEOUT | duration string | нет | 20 second | Максимальное время ожидания ответа для соединения к БД. Если это время превышено, а соединение не становится доступным, будет брошено исключение SQLException. 1000 мс — минимальное значение. |
| DB_ISOLATION | string | нет | "READ_COMMITTED" | Уровень изоляции транзакций для новых подключений. Допустимые значения: NONE, READ_COMMITTED, READ_UNCOMMITTED, REPEATABLE_READ, SERIALIZABLE. |
| DB_READONLY | boolean | нет | false | Read-only SQL транзакция может изменять только временные таблицы. Этот параметр управляет статусом «только для чтения» по умолчанию для каждой новой транзакции. |
| DB_CONN_MIN | int | нет | = DB_THREADS | Минимальное количество одновременных подключений к БД |
| DB_VALIDATION_TIMEOUT | duration string | нет | 1 seconds | Максимальное время, в течение которого соединение будет проверяться на работоспособность. 1000 мс — минимальное значение. |
| DB_IDLE_TIMEOUT | duration string | нет | 10 minutes | Максимальное время, в течение которого соединению разрешено простаивать в пуле. Значение 0 означает, что простаивающие соединения никогда не удаляются из пула. |
| DB_MAX_LIFETIME | duration string | нет | 30 minutes | Максимальное время жизни соединения в пуле. Когда простаивающее соединение достигает этого времени ожидания, даже если оно недавно использовалось, оно будет удалено из пула. Значение 0 указывает на отсутствие максимального срока службы. |
| DB_INITIALIZATION_FAIL_FAST | string | нет | false | Определяет, будет ли пул «быстро выходить из строя», если пул не может быть успешно заполнен начальными соединениями. Если соединения не могут быть созданы во время запуска пула, будет выдано исключение RuntimeException. Это свойство не имеет никакого эффекта, если minConnections равно 0. |
| DB_LEAK_DETECTION_THRESHOLD | int | нет | 0 | Время, в течение которого соединение может находиться вне пула, прежде чем будет зарегистрировано сообщение, указывающее на возможную утечку соединения. Значение 0 означает, что обнаружение утечек отключено. Наименьшее приемлемое значение для включения обнаружения утечек составляет 10 с. |
| DB_CONNECTION_TEST_QUERY | string | нет | "SELECT 1" | Выражение, которое будет выполнено непосредственно перед получением соединения из пула для проверки того, что соединение с базой данных все еще активно. Оно зависит от базы данных и должно представлять собой запрос, требующий минимальной обработки базой данных (например, «VALUES 1»). Если этот параметр не установлен, вместо него используется метод JDBC4 Connection.isValid(). |
| DB_REGISTER_MBEANS | boolean | нет | false | Зарегистрированы ли JMX Management Beans («MBeans») |
| LOG_OUTPUT | string | нет | STDOUT | Параметры вывода логов: STDOUT - обычный лог, STDOUT_CEF - лог в формате CEF |
| LOG_LEVEL_HTTP | string | нет | WARN | Уровень логирования HTTP-сервера |
| LOGGING_SRC_IP | string | нет | Параметр SRC (источник/source, на который ссылается событие) для логов в формате CEF. Если не установлена, src=notFound. Требуемый формат: IPv4, например "192.168.10.1". |
|
| LOGGING_SRC_HOST | string | нет | Параметр SHOST (источник/source, на который ссылается событие) для логов в формате CEF. Если не установлена, shost=notFound. Требуемый формат: fully qualified domain name (FQDN), например "host" или "host.domain.com". |
|
| LOGGING_DST_IP | string | нет | Параметр DST (получатель/destination, на который ссылается событие) для логов в формате CEF. Если не установлена, dst=notFound. Требуемый формат: IPv4, например "192.168.10.1". |
|
| LOGGING_CEF_VER | int | нет | 0 | Версия формата CEF: либо 0, либо 1. Рекомендуется использовать 0. |
| VERDI_COMMANDS_HTTP_RETRY_ATTEMPTS | int | нет | 5 | Поле attempts из RetrySettings |
| VERDI_COMMANDS_HTTP_RETRY_DELAY | duration string | нет | "5 seconds" | Поле delay из RetrySettings |
| VERDI_COMMANDS_HTTP_RETRY_KIND | string | нет | "OnSomeExceptions(ConnectException)" | CommandResultRetryConditionKind |
Формат CEF для логов сервиса Buzzer-Personal-Page
У логов есть возможность включить формат CEF для логирования по следующему шаблону:
2022-12-14T16:56:31+0500 CEF:Version|Device Vendor|Device Product|Device Version|EventClassId|Message|Severity|src=? dst=? shost=? suid=? suser=? msg=? end=currentTimeMillis|.
Чтобы включить логирование в формате CEF, нужно задать переменную LOG_OUTPUT = STDOUT_CEF. Если логи пишутся в формате CEF, то необходимо задать следующие переменные, которые по умолчанию не заданы:
LOGGING_SRC_IP, для параметраsrcв логахLOGGING_SRC_HOST, для параметраshostв логахLOGGING_DST_IP, для параметраdstв логах
В файле src/main/resources/log4j.xml можно поменять class path для основного класса приложения (переменная projectMainClassPath), если это необходимо.
Список команд сервиса уведомлений в личном кабинете
searchPPNotifications (Buzzer-personal-page)
Payload для команды
{
"search": {
"sorting": {
"fieldName": "created",
"order": "asc"
},
"paging": {
"page": 1,
"count": 20
},
"query": "isRead",
"context": {
"isRead": "false"
}
}
}
Результат выполнения команды
{
"total": 1,
"items": [
{
"id": "a133f0c8-a516-4537-8f3f-915acb335e2c",
"code": "testCode",
"userId": "a133f0c8-a516-4537-8f3f-915acb335e2c",
"data": {},
"created": 1673942815499,
"isRead": false,
"render": null
}
]
}
Поиск уведомлений в личном кабинете (ЛК). В качестве фильтра по пользователю, всегда используется текущий пользователь, который отправил запрос.
Если в Search-объекте был передан фильтр "userId", то он будет переписан на текущего пользователя.
Поддерживается только синхронный вызов.
- Payload: SearchRequest
- Result: Page[NotificationPP]
| Команда | Путь |
|---|---|
| searchPPNotifications | HTTP POST "/search" |
Доступные поля для фильтрации и виды фильтров по ним:
| Поле | Виды фильтров |
|---|---|
| userId | Всегда используется текущий пользователь |
| sectionId | InSetQuery |
| isRead | InSetQuery |
| id | InSetQuery |
Доступные поля для сортировки:
| Поле |
|---|
| created |
| isRead |
auditSearchPPNotifications (Buzzer-personal-page)
Payload для команды
{
"search": {
"sorting": {
"fieldName": "created",
"order": "asc"
},
"paging": {
"page": 1,
"count": 20
},
"query": "userId",
"context": {
"userId": "[a133f0c8-a516-4537-8f3f-915acb335e2c, aa1ceac9-a21c-4eba-9268-c62ce7fde9b3, d1533243-10f1-40d8-bd88-ffe6cef735aa]"
}
}
}
Результат выполнения команды
{
"total": 1,
"items": [
{
"id": "a133f0c8-a516-4537-8f3f-915acb335e2c",
"code": "testCode",
"userId": "a133f0c8-a516-4537-8f3f-915acb335e2c",
"data": {},
"created": 1673942815499,
"isRead": false,
"render": null
}
]
}
Поиск уведомлений в личном кабинете (ЛК), в котором можно указать любых пользователей в фильтре "userId". Поддерживается только синхронный вызов.
- Payload: SearchRequest
- Result: Page[NotificationPP]
| Команда | Путь |
|---|---|
| auditSearchPPNotifications | HTTP POST "/auditSearch" |
Доступные поля для фильтрации и виды фильтров по ним:
| Поле | Виды фильтров |
|---|---|
| userId | InSetQuery |
| sectionId | InSetQuery |
| isRead | InSetQuery |
| id | InSetQuery |
Доступные поля для сортировки:
| Поле |
|---|
| created |
| isRead |
changeNotificationStatus (Buzzer-personal-page)
Payload для команды
{
"ids": [
"00000000-0000-0000-0000-000000000001",
"00000000-0000-0000-0000-000000000002"
],
"isRead": true,
"allNotifications": false
}
Результат выполнения команды
true
Поиск уведомлений в личном кабинете (ЛК). Поддерживается только синхронный вызов.
- Payload: ChangeNotificationsDTO
- Result: Boolean
| Команда | Путь |
|---|---|
| changeNotificationStatus | HTTP POST "/changeNotificationStatus" |
createSectionPp (Buzzer-personal-page)
Payload для команды
{
"id": "section1",
"name": "section name",
"version": 1
}
Результат выполнения команды
"section1"
Создание раздела. Поддерживается только синхронный вызов.
- Payload: Section
- Result: String (идентификатор раздела)
| Команда | Путь |
|---|---|
| createSectionPp | HTTP POST "/createSection" |
getSectionPp (Buzzer-personal-page)
Payload для команды
"section1"
Результат выполнения команды
{
"id": "section1",
"name": "section name",
"version": 1
}
Получение раздела. Поддерживается только синхронный вызов.
- Payload: String (идентификатор раздела)
- Result: Section
| Команда | Путь |
|---|---|
| getSectionPp | HTTP POST "/getSection" |
deleteSectionPp (Buzzer-personal-page)
Payload для команды
"section1"
Результат выполнения команды
1
Удаление раздела. Поддерживается только синхронный вызов.
- Payload: String (идентификатор раздела)
- Result: Int (кол-во измененных записей)
| Команда | Путь |
|---|---|
| deleteSectionPp | HTTP POST "/deleteSection" |
updateSectionPp (Buzzer-personal-page)
Payload для команды
{
"id": "section1",
"name": "other section name",
"version": 2
}
Результат выполнения команды
1
Обновление раздела. Поддерживается только синхронный вызов.
- Payload: Section
- Result: Int (кол-во измененных записей)
| Команда | Путь |
|---|---|
| updateSectionPp | HTTP POST "/updateSection" |
listSectionPp (Buzzer-personal-page)
Payload для команды
null
Результат выполнения команды
[
{
"id": "section1",
"name": "other section name",
"version": 2
}
]
Получение списка разделов. Поддерживается только синхронный вызов.
- Payload: null
- Result: List[Section]
| Команда | Путь |
|---|---|
| listSectionPp | HTTP POST "/listSection" |
createSettingPp (Buzzer-personal-page)
Payload для команды
{
"id": "testCode",
"sectionId": "section1",
"version": 1
}
Результат выполнения команды
"testCode"
Создание настройки. Поддерживается только синхронный вызов.
- Payload: Setting
- Result: String (идентификатор настройки)
| Команда | Путь |
|---|---|
| createSettingPp | HTTP POST "/createSetting" |
getSettingPp (Buzzer-personal-page)
Payload для команды
"testCode"
Результат выполнения команды
{
"id": "testCode",
"sectionId": "section1",
"version": 1
}
Получение настройки. Поддерживается только синхронный вызов.
- Payload: String (идентификатор настройки)
- Result: Setting
| Команда | Путь |
|---|---|
| getSettingPp | HTTP POST "/getSetting" |
deleteSettingPp (Buzzer-personal-page)
Payload для команды
"testCode"
Результат выполнения команды
1
Удаление настройки. Поддерживается только синхронный вызов.
- Payload: String (идентификатор настройки)
- Result: Int (кол-во измененных записей)
| Команда | Путь |
|---|---|
| deleteSettingPp | HTTP POST "/deleteSetting" |
updateSettingPp (Buzzer-personal-page)
Payload для команды
{
"id": "testCode",
"sectionId": "section2",
"version": 2
}
Результат выполнения команды
1
Обновление настройки. Поддерживается только синхронный вызов.
- Payload: Setting
- Result: Int (кол-во измененных записей)
| Команда | Путь |
|---|---|
| updateSettingPp | HTTP POST "/updateSetting" |
listSettingPp (Buzzer-personal-page)
Payload для команды
null
Результат выполнения команды
[
{
"id": "testCode",
"sectionId": "section2",
"version": 2
}
]
Получение списка настроек. Поддерживается только синхронный вызов.
- Payload: null
- Result: List[Setting]
| Команда | Путь |
|---|---|
| listSettingPp | HTTP POST "/listSetting" |
createTemplatePp (Buzzer-personal-page)
Payload для команды
{
"id": "template1",
"settingId": "testCode",
"title": "",
"content": "",
"version": 1
}
Результат выполнения команды
"template1"
Создание шаблона. Поддерживается только синхронный вызов.
- Payload: Template
- Result: String (идентификатор шаблона)
| Команда | Путь |
|---|---|
| createTemplatePp | HTTP POST "/createTemplate" |
getTemplatePp (Buzzer-personal-page)
Payload для команды
"template1"
Результат выполнения команды
{
"id": "template1",
"settingId": "testCode",
"title": "",
"content": "",
"version": 1
}
Получение шаблона. Поддерживается только синхронный вызов.
- Payload: String (идентификатор шаблона)
- Result: Template
| Команда | Путь |
|---|---|
| getTemplatePp | HTTP POST "/getTemplate" |
deleteTemplatePp (Buzzer-personal-page)
Payload для команды
"template1"
Результат выполнения команды
1
Удаление шаблона. Поддерживается только синхронный вызов.
- Payload: String (идентификатор шаблона)
- Result: Int (кол-во измененных записей)
| Команда | Путь |
|---|---|
| deleteTemplatePp | HTTP POST "/deleteTemplate" |
updateTemplatePp (Buzzer-personal-page)
Payload для команды
{
"id": "template1",
"settingId": "testCode",
"title": "Template name",
"content": "",
"version": 1
}
Результат выполнения команды
1
Обновление шаблона. Поддерживается только синхронный вызов.
- Payload: Template
- Result: Int (кол-во измененных записей)
| Команда | Путь |
|---|---|
| updateTemplatePp | HTTP POST "/updateTemplate" |
listTemplatePp (Buzzer-personal-page)
Payload для команды
null
Результат выполнения команды
[
{
"id": "template1",
"settingId": "testCode",
"title": "Template code",
"content": "",
"version": 1
}
]
Получение списка шаблонов. Поддерживается только синхронный вызов.
- Payload: null
- Result: List[Template]
| Команда | Путь |
|---|---|
| listTemplatePp | HTTP POST "/listTemplate" |
Модели сервиса уведомлений в личном кабинете
NotificationPP (Buzzer-personal-page)
Уведомление личного кабинета
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| id | UUID | Да | Идентификатор уведомления |
| code | String | Да | Код уведомления |
| userId | UUID | Да | Идентификатор пользователя |
| data | Json | Да | Данные уведомления |
| created | TimeStamp | Да | Дата сохранения |
| isRead | Boolean | Да | Отметка о прочтении |
| render | String | Нет | Рендер уведомления |
SearchRequest (Buzzer-personal-page)
Поисковый запрос
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| search | Search | Да | Параметры поиска |
| markRead | Boolean | Нет | Нужно ли помечать уведомления как прочитанные |
| render | Boolean | Нет | Нужно ли рендерить уведомления |
ChangeNotificationsDTO (Buzzer-personal-page)
Данные для изменения статуса уведомлений
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| ids | List[UUID] | Да | Список идентификаторов уведомлений для изменения |
| isRead | Boolean | Да | Новый статус уведомлений. True - "прочитано", False - "не прочитано" |
| allNotifications | Boolean | Да | Применение изменений ко всем уведомлениям. True - "игнорирование списка ID", False - "изменить только переданные ID" |
Section (Buzzer-personal-page)
Раздел личного кабинета
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| id | String | Да | Идентификатор |
| name | String | Да | Название раздела |
| version | Int | Нет | Версия. Значение по умолчанию: 1 |
Setting (Buzzer-personal-page)
Настройка раздела личного кабинета
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| id | String | Да | Идентификатор, должен соответствовать коду уведомления |
| sectionId | String | Да | Идентификатор раздела |
| version | Int | Нет | Версия. Значение по умолчанию: 1 |
StoredNotification (Buzzer-personal-page)
Сохраняемые уведомления
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| id | UUID | Да | Идентификатор |
| code | String | Да | Код уведомления |
| userId | UUID | Да | Идентификатор пользователя |
| data | Json | Да | Данные уведомления |
| created | TimeStamp | Да | Дата сохранения |
| isRead | Boolean | Да | Отметка о прочтении |
Template (Buzzer-personal-page)
Шаблон
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| id | String | Да | Идентификатор |
| settingId | String | Да | Идентификатор настройки |
| title | String | Да | Заголовок |
| content | String | Да | Шаблон рендеринга уведомлений |
| version | Int | Нет | Версия. Значение по умолчанию: 1 |
Сервис подписок
Сервис подписок обеспечивает генерацию уведомлений при поступлении от бизнес-логики событий, приходящих через Kafka в топик businessevent.
Также сервис принимает HTTP команды и хранит в PostgreSQL информацию о настройках подписки для событий и подписках пользователя.
Локальный запуск сервиса подписок
При запуске сервиса ожидается, что уже развернута необходимая инфраструктура:
- PostgreSQL база по адресу
localhost:5432/buzzer-subscription - Kafka по адресу
localhost:9092 - Consul по адресу
localhost:8500 - В Consul будет добавлен адрес Kafka по ключу
kafka_bootstrap_serverили адрес будет указан вapplication.conf. - Добавлены необходимые переменные окружения:
- DB_HOST
- DB_PORT
- DB_NAME
- DB_USER
- DB_PASSWORD
Запуск сервиса подписок из консоли с помощью SBT
DB_HOST=localhost DB_PORT=5432 DB_NAME=buzzer-subscription DB_USER=postgres DB_PASSWORD=12345 sbt run
Список переменных окружения сервиса подписок
| Переменная | Значение по умолчанию | Описание |
|---|---|---|
| SERVER_PORT | 8080 | Порт для HttpListener |
| CONSUMER_TOPIC | "entityObjectEvent" | Название топика для чтения событий |
| CONSUMER_GROUP | "notification" | Название группы для чтения событий |
| SENDER_TOPIC | "notification" | Название топика для отправки уведомлений |
| VERDI_CONSUL | "http://localhost:8500" | Адрес Сonsul |
| VERDI_CONSUL_AUTH_USER | "" | Название учетной записи Сonsul. Если название не указано, то настройки авторизации не будут применены. |
| VERDI_CONSUL_AUTH_PASSWORD | "" | Пароль учетной записи Сonsul. |
| VERDI_KAFKA | "localhost:9092" | Адрес Kafka |
| VERDI_KAFKA_TOPIC | "commandevents" | Название топика для событий |
| VERDI_KAFKA_AUTH_USER | "" | Название учетной записи Kafka. Если название не указано, то настройки авторизации не будут применены. |
| VERDI_KAFKA_AUTH_PASSWORD | "" | Пароль учетной записи Kafka. |
| VERDI_KAFKA_AUTH_TRUSTSTORE_LOCATION | "" | Путь до хранилища сертификатов (Java key store). Если путь не указан, то сертификат применяться не будет. |
| VERDI_KAFKA_AUTH_TRUSTSTORE_PASSWORD | "" | Пароль к хранилищу сертификатов. |
| VERDI_KAFKA_AUTH_MODE | "static" | Режим аутентификации: static - одна учетная запись на все запросы, mapping - учетная запись зависит от запроса |
| VERDI_KAFKA_AUTH_CONFIG | "" | Настройки для аутентификации: если AuthMode = mapping, то необходим JSON с List[KafkaAuthConfig]. Переменная соответствует полю authConfig из KafkaAuthSettings. |
| VERDI_KAFKA_AUTH_CACHE_SIZE | Максимальный размер кеша для Kafka producer (количество активных соединений). | |
| VERDI_KAFKA_AUTH_CACHE_TTL | Время жизни Kafka producer в кеше. | |
| VERDI_COMMAND_STORAGE_UPDATE_PERIOD | 1 minutes | Время кэширования данных по командам из CommandDiscovery |
| VERDI_SERVICE_DISCOVERY_UPDATE_PERIOD | 30 seconds | Время кэширования данных по сервисам из ServiceDiscovery |
| VERDI_ALLOW_INTERNAL_COMMANDS | true | Можно ли сервису отправлять внутрисистемные команды |
| VERDI_CONSUL_CONNECTION_MAX_RETRY | 5 | Максимальное количество попыток подключений к Consul |
| VERDI_CONSUL_CONNECTION_RETRY_DELAY | 1 seconds | Таймаут между неудачными попытками подключения к Consul |
| VERDI_HOST | "localhost" | Адрес текущего инстанса сервиса, который будет виден через ServiceDiscovery |
| DB_JDBC_URL | "jdbc:postgresql://localhost:5432/buzzer-subscription" | Адрес базы данных. Можно использовать вместо DB_HOST, DB_PORT и DB_NAME |
| DB_HOST | Хост сервера базы данных | |
| DB_PORT | Порт сервера базы данных | |
| DB_NAME | Название базы данных | |
| DB_USER | "postgres" | Пользователь базы данных |
| DB_PASSWORD | 12345 | Пароль для пользователя базы данных |
| DB_THREADS | 10 | Количество потоков в пуле потоков для соединения с БД |
| DB_QUEUE_SIZE | 300 | Размер очереди для действий базы данных, которые не могут быть выполнены немедленно, когда все потоки заняты. За пределами этого значения новые действия немедленно завершаются неудачей |
| DB_CONN_MAX | 10 | Максимальное количество одновременных подключений к БД |
| DB_CONN_TIMEOUT | 20 second | Максимальное время ожидания ответа для соединения к БД. Если это время превышено, а соединение не становится доступным, будет брошено исключение SQLException. 1000 мс — минимальное значение. |
| DB_ISOLATION | "READ_COMMITTED" | Уровень изоляции транзакций для новых подключений. Допустимые значения: NONE, READ_COMMITTED, READ_UNCOMMITTED, REPEATABLE_READ, SERIALIZABLE. |
| DB_READONLY | false | Read-only SQL транзакция может изменять только временные таблицы. Этот параметр управляет статусом «только для чтения» по умолчанию для каждой новой транзакции. |
| DB_CONN_MIN | = DB_THREADS | Минимальное количество одновременных подключений к БД |
| DB_VALIDATION_TIMEOUT | 1 seconds | Максимальное время, в течение которого соединение будет проверяться на работоспособность. 1000 мс — минимальное значение. |
| DB_IDLE_TIMEOUT | 10 minutes | Максимальное время, в течение которого соединению разрешено простаивать в пуле. Значение 0 означает, что простаивающие соединения никогда не удаляются из пула. |
| DB_MAX_LIFETIME | 30 minutes | Максимальное время жизни соединения в пуле. Когда простаивающее соединение достигает этого времени ожидания, даже если оно недавно использовалось, оно будет удалено из пула. Значение 0 указывает на отсутствие максимального срока службы. |
| DB_INITIALIZATION_FAIL_FAST | false | Определяет, будет ли пул «быстро выходить из строя», если пул не может быть успешно заполнен начальными соединениями. Если соединения не могут быть созданы во время запуска пула, будет выдано исключение RuntimeException. Это свойство не имеет никакого эффекта, если minConnections равно 0. |
| DB_LEAK_DETECTION_THRESHOLD | 0 | Время, в течение которого соединение может находиться вне пула, прежде чем будет зарегистрировано сообщение, указывающее на возможную утечку соединения. Значение 0 означает, что обнаружение утечек отключено. Наименьшее приемлемое значение для включения обнаружения утечек составляет 10 с. |
| DB_CONNECTION_TEST_QUERY | "SELECT 1" | Выражение, которое будет выполнено непосредственно перед получением соединения из пула для проверки того, что соединение с базой данных все еще активно. Оно зависит от базы данных и должно представлять собой запрос, требующий минимальной обработки базой данных (например, «VALUES 1»). Если этот параметр не установлен, вместо него используется метод JDBC4 Connection.isValid(). |
| DB_REGISTER_MBEANS | false | Зарегистрированы ли JMX Management Beans («MBeans») |
| LOG_OUTPUT | STDOUT | Параметры вывода логов: STDOUT - обычный лог, STDOUT_CEF - лог в формате CEF |
| LOG_LEVEL_HTTP | WARN | Уровень логирования HTTP-сервера |
| LOGGING_SRC_IP | Параметр SRC (источник/source, на который ссылается событие) для логов в формате CEF. Если не установлена, src=notFound. Требуемый формат: IPv4, например "192.168.10.1". |
|
| LOGGING_SRC_HOST | Параметр SHOST (источник/source, на который ссылается событие) для логов в формате CEF. Если не установлена, shost=notFound. Требуемый формат: fully qualified domain name (FQDN), например "host" или "host.domain.com". |
|
| LOGGING_DST_IP | Параметр DST (получатель/destination, на который ссылается событие) для логов в формате CEF. Если не установлена, dst=notFound. Требуемый формат: IPv4, например "192.168.10.1". |
|
| LOGGING_CEF_VER | 0 | Версия формата CEF: либо 0, либо 1. Рекомендуется использовать 0. |
| VERDI_COMMANDS_HTTP_RETRY_ATTEMPTS | 5 | Поле attempts из RetrySettings |
| VERDI_COMMANDS_HTTP_RETRY_DELAY | "5 seconds" | Поле delay из RetrySettings |
| VERDI_COMMANDS_HTTP_RETRY_KIND | "OnSomeExceptions(ConnectException)" | CommandResultRetryConditionKind |
Формат CEF для логов сервиса Buzzer-Subscription
У логов есть возможность включить формат CEF для логирования по следующему шаблону:
2022-12-14T16:56:31+0500 CEF:Version|Device Vendor|Device Product|Device Version|EventClassId|Message|Severity|src=? dst=? shost=? suid=? suser=? msg=? end=currentTimeMillis|.
Чтобы включить логирование в формате CEF, нужно задать переменную LOG_OUTPUT = STDOUT_CEF. Если логи пишутся в формате CEF, то необходимо задать следующие переменные, которые по умолчанию не заданы:
LOGGING_SRC_IP, для параметраsrcв логахLOGGING_SRC_HOST, для параметраshostв логахLOGGING_DST_IP, для параметраdstв логах
В файле src/main/resources/log4j.xml можно поменять class path для основного класса приложения (переменная projectMainClassPath), если это необходимо.
Команды сервиса подписок
В качестве тела команд сервис всегда ожидает CommandRequest. В описании команд указано описание поля payload для CommandRequest и путь для отправки команды в сам сервис (не в ApiGateway).
В сервисе реализованы следующие команды:
- CreateEventSettings
- GetEventSettings
- UpdateEventSettings
- DeleteEventSettings
- ListEventSettings
- SearchEventSettings
- CreateSubscription
- GetSubscription
- UpdateSubscription
- DeleteSubscription
- ListSubscription
CreateEventSettings (Buzzer-subscription)
Передаем JSON с данными для создания настроек события
{
"entityType": "Document",
"eventType": "Create",
"enabled": true,
"notificationCode": "DocumentCreated"
}
Получаем Id настроек события
"4345011c-c579-4e1b-b84b-94e4eed2f2da"
Создать настройки события из данных EventSettingsDTO.
Гарантируется уникальность для набора полей: entityType, eventType.
В ответ приходит Id настроек события.
- Input: EventSettingsDTO
- Output: UUID
| Команда | Путь |
|---|---|
| createEventSettingsBuzzer-subscription | HTTP POST "/createEventSettings" |
GetEventSettings (Buzzer-subscription)
Передаем Id настроек события
"ed2de80e-0289-43c2-a944-70e9a4277d36"
Получаем JSON с настройками события
{
"id": "ed2de80e-0289-43c2-a944-70e9a4277d36",
"entityType": "Employee",
"eventType": "Create",
"enabled": true,
"notificationCode": "EmployeeCreated",
"version": 1
}
Возвращает настройки события EventSettings по Id.
- Input: UUID
- Output: EventSettings
| Команда | Путь |
|---|---|
| getEventSettingsBuzzer-subscription | HTTP POST "/getEventSettings" |
UpdateEventSettings (Buzzer-subscription)
Передаем JSON с настройками события
{
"id": "0b27209a-1931-4cd7-a19a-7739bd1dfb4f",
"entityType": "DocumentFile",
"eventType": "Delete",
"enabled": true,
"notificationCode": "DocumentFileDeleted",
"version": 1
}
Получаем результат
1
Обновить существующие настройки события из EventSettings.
Если значение, передаваемое в version, отличается от текущего, обновление не происходит.
При успешном обновлении version формы увеличивается на единицу.
- Input: EventSettings
- Output: Int
| Команда | Путь |
|---|---|
| updateEventSettingsBuzzer-subscription | HTTP POST "/updateEventSettings" |
DeleteEventSettings (Buzzer-subscription)
Передаем Id настроек события
"0ccd05ac-50c0-4e7e-906b-6893eaa8d9de"
Получаем результат
1
Удалить существующие настройки события по Id.
- Input: UUID
- Output: Int
| Команда | Путь |
|---|---|
| deleteEventSettingsBuzzer-subscription | HTTP POST "/deleteEventSettings" |
ListEventSettings (Buzzer-subscription)
Получаем JSON со списком всех существующих настроек событий
[
{
"id": "f3287c8e-efa5-45d9-ba38-76ef78f09fe0",
"entityType": "Document",
"eventType": "Delete",
"enabled": true,
"notificationCode": "DocumentDeleted",
"version": 1
},
{
"id": "34c919ae-a73c-4705-8833-0a1b0434e04c",
"entityType": "Employee",
"eventType": "Update",
"enabled": false,
"notificationCode": "EmployeeUpdated",
"version": 2
}
]
Возвращает список всех существующих настроек событий.
- Input: -
- Output: List[EventSettings]
| Команда | Путь |
|---|---|
| listEventSettingsBuzzer-subscription | HTTP POST "/listEventSettings" |
SearchEventSettings (Buzzer-subscription)
Передаем поисковой запрос
{
"query": "eventType",
"context": {"eventType": "Delete"},
"sorting": {"fieldName": "eventType", "order": "Asc"},
"paging": {"page": 2, "count": 10}
}
Получаем JSON со списком отфильтрованных настроек событий
{
"items": [
{
"id": "f3287c8e-efa5-45d9-ba38-76ef78f09fe0",
"entityType": "Document",
"eventType": "Delete",
"enabled": true,
"notificationCode": "DocumentDeleted",
"version": 1
},
{
"id": "34c919ae-a73c-4705-8833-0a1b0434e04c",
"entityType": "Employee",
"eventType": "Update",
"enabled": false,
"notificationCode": "EmployeeUpdated",
"version": 2
}
],
"total": 12
}
Возвращает отфильтрованный список настроек событий.
Доступные для сортировки поля:
- enabled
- eventType
- notificationCode
- entityType
Доступные для фильтрации поля и виды фильтров по ним:
| Поле | Виды фильтров |
|---|---|
| id | InSetQuery |
| eventType | InSetQuery, LikeQuery |
| notificationCode | InSetQuery, LikeQuery |
| entityType | InSetQuery, LikeQuery |
- Input: -
- Output: Page[EventSettings]
| Команда | Путь |
|---|---|
| searchEventSettingsBuzzer-subscription | HTTP POST "/searchEventSettings" |
CreateSubscription (Buzzer-subscription)
Передаем JSON с данными для создания подписки
{
"entityType": "Document",
"entityId": "fd460be0-99f6-4cfc-95eb-2e3f6a847ef5",
"eventType": "Create",
"enabled": true,
"userId": "fd460be0-99f6-4cfc-95eb-2e3f6a847efb"
}
Получаем Id подписки
"d053e6c2-877e-4dc4-83c6-d7f80911e1f1"
Создать подписки из данных SubscriptionDTO.
Гарантируется уникальность для набора полей: entityType, entityId, eventType, userId.
Поле eventType опциональное, в случае его отсутствия подписка будет генерировать уведомления на все типы событий.
В ответ приходит Id подписки.
- Input: SubscriptionDTO
- Output: UUID
| Команда | Путь |
|---|---|
| createSubscriptionBuzzer-subscription | HTTP POST "/createSubscription" |
GetSubscription (Buzzer-subscription)
Передаем Id подписки
"d053e6c2-877e-4dc4-83c6-d7f80911e1f1"
Получаем JSON с подпиской
{
"id": "d053e6c2-877e-4dc4-83c6-d7f80911e1f1",
"entityType": "Document",
"entityId": "fd460be0-99f6-4cfc-95eb-2e3f6a847ef5",
"eventType": "Create",
"enabled": true,
"userId": "fd460be0-99f6-4cfc-95eb-2e3f6a847efa",
"version": 1
}
Возвращает подписку Subscription по Id.
- Input: UUID
- Output: Subscription
| Команда | Путь |
|---|---|
| getSubscriptionBuzzer-subscription | HTTP POST "/getSubscription" |
UpdateSubscription (Buzzer-subscription)
Передаем JSON с подпиской
{
"id": "69c33893-b769-4292-8259-3295fe0d7450",
"entityType": "DocumentFile",
"entityId": "a7833b48-aeb6-4048-99fd-e90bf1780b7e",
"eventType": "Create",
"enabled": true,
"userId": "fd460be0-99f6-4cfc-95eb-2e3f6a847efe",
"version": 1
}
Получаем результат
1
Обновить существующую подписку из Subscription.
Если значение, передаваемое в version, отличается от текущего, обновление не происходит.
При успешном обновлении version формы увеличивается на единицу.
- Input: Subscription
- Output: Int
| Команда | Путь |
|---|---|
| updateSubscriptionBuzzer-subscription | HTTP POST "/updateSubscription" |
DeleteSubscription (Buzzer-subscription)
Передаем Id подписки
"954855b6-5390-45ca-9280-2746563e6905"
Получаем результат
1
Удалить существующую подписку по Id.
- Input: UUID
- Output: Int
| Команда | Путь |
|---|---|
| deleteSubscriptionBuzzer-subscription | HTTP POST "/deleteSubscription" |
ListSubscription (Buzzer-subscription)
Получаем JSON со списком всех существующих подписок
[
{
"id": "69c33893-b769-4292-8259-3295fe0d7450",
"entityType": "Document",
"entityId": "53cc3dec-29c3-4814-a2e7-69f435f74a1f",
"eventType": "Update",
"enabled": true,
"userId": "fd460be0-99f6-4cfc-95eb-2e3f6a847efe",
"version": 2
},
{
"id": "018815b5-6fa3-4b8c-8bec-374bb3c20574",
"entityType": "Organization",
"entityId": "1bce425e-f1ae-4729-a36f-8a16dd6afa58",
"enabled": false,
"userId": "018815b5-6fa3-4b8c-8bec-374bb3c20574",
"version": 1
}
]
Возвращает список всех существующих настроек событий.
- Input: -
- Output: List[Subscription]
| Команда | Путь |
|---|---|
| listSubscriptionBuzzer-subscription | HTTP POST "/listSubscription" |
Модели сервиса подписок
EventSettingsDTO (Buzzer-subscription)
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| entityType | String | да | Тип сущности |
| eventType | String | да | Тип события |
| enabled | Boolean | да | Активность подписки |
| notificationCode | String | да | Код уведомления |
EventSettings (Buzzer-subscription)
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| id | UUID | да | Id настроек события |
| entityType | String | да | Тип сущности |
| eventType | String | да | Тип события |
| enabled | Boolean | да | Активность подписки |
| notificationCode | String | да | Код уведомления |
| version | Int | да | Версия настроек события |
SubscriptionDTO (Buzzer-subscription)
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| entityType | String | да | Тип сущности |
| entityId | String | да | Id сущности |
| eventType | String | нет | Тип события |
| enabled | Boolean | да | Активность подписки |
| userId | UUID | да | Id пользователя |
Subscription (Buzzer-subscription)
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| id | UUID | да | Id подписки |
| entityType | String | да | Тип сущности |
| entityId | String | да | Id сущности |
| eventType | String | нет | Тип события |
| enabled | Boolean | да | Активность подписки |
| userId | UUID | да | Id пользователя |
| version | Int | да | Версия настроек события |
Сервис уведомлений
Сервис получает через Kafka уведомления и перенаправляет их в сервисы отправки. На данный момент реализованы 2 таких сервиса - Buzzer email и Buzzer personal page.
Конфигурирование сервиса уведомлений
Требования к запуску
Для корректной минимальной работы сервиса требуется обязательное подключение к PostgreSQL, Kafka, Consul. Для настройки подключения к ним нужно заполнить обязательные переменные окружения из раздела ниже.
Для нормальной работы сервису дополнительно требуется окружение Verdi: CommandStatus и ApiGateway. В этом случае нужно уделить внимание настройкам, связанным с ServiceDiscovery и CommandDiscovery.
Список переменных окружения сервиса уведомлений
| Переменная | Значение по умолчанию | Описание |
|---|---|---|
| SERVER_PORT | 8080 | Порт для HttpListener |
| CONSUMER_TOPIC | "notification" | Название топика для уведомлений к отправке, поступающих из других сервисов |
| CONSUMER_SEND_NOTIFICATION_COMMAND_TOPIC | "send_notification_command" | Название топика для Verdi-команды отправки уведомления |
| CONSUMER_GROUP | "notification" | Название группы для чтения из топиков Kafka |
| VERDI_HOST | "localhost" | Адрес текущего инстанса сервиса, который будет виден через ServiceDiscovery |
| VERDI_TTL | 30 seconds | Период после последней отправки health check, в течение которого ServiceDiscovery считает данный сервис живым. |
| VERDI_HEALTH_CHECK | 10 seconds | Периодичность отправки health check в ServiceDiscovery |
| VERDI_CONSUL | http://localhost:8500 | Адрес Consul |
| VERDI_CONSUL_AUTH_USER | "" | Название учетной записи Сonsul. Если название не указано, то настройки авторизации не будут применены. |
| VERDI_CONSUL_AUTH_PASSWORD | "" | Пароль учетной записи Сonsul. |
| VERDI_KAFKA | "localhost:9092" | Адрес брокера Kafka. |
| VERDI_KAFKA_TOPIC | "commandevents" | Название топика для событий |
| VERDI_KAFKA_AUTH_USER | "" | Название учетной записи Kafka. Если название не указано, то настройки авторизации не будут применены. |
| VERDI_KAFKA_AUTH_PASSWORD | "" | Пароль учетной записи Kafka. |
| VERDI_KAFKA_AUTH_TRUSTSTORE_LOCATION | "" | Путь до хранилища сертификатов (Java key store). Если путь не указан, то сертификат применяться не будет. |
| VERDI_KAFKA_AUTH_TRUSTSTORE_PASSWORD | "" | Пароль к хранилищу сертификатов. |
| VERDI_KAFKA_AUTH_MODE | "static" | Режим аутентификации: static - одна учетная запись на все запросы, mapping - учетная запись зависит от запроса |
| VERDI_KAFKA_AUTH_CONFIG | "" | Настройки для аутентификации: если AuthMode = mapping, то необходим JSON с List[KafkaAuthConfig]. Переменная соответствует полю authConfig из KafkaAuthSettings. |
| VERDI_KAFKA_AUTH_CACHE_SIZE | Максимальный размер кеша для Kafka producer (количество активных соединений). | |
| VERDI_KAFKA_AUTH_CACHE_TTL | Время жизни Kafka producer в кеше. | |
| VERDI_COMMAND_STORAGE_UPDATE_PERIOD | 1 minutes | Время кэширования данных по командам из CommandDiscovery |
| VERDI_SERVICE_DISCOVERY_UPDATE_PERIOD | 30 seconds | Время кэширования данных по сервисам из ServiceDiscovery |
| VERDI_ALLOW_INTERNAL_COMMANDS | true | Можно ли сервису отправлять внутрисистемные команды |
| VERDI_CONSUL_CONNECTION_MAX_RETRY | 5 | Максимальное количество попыток подключений к Consul |
| VERDI_CONSUL_CONNECTION_RETRY_DELAY | 1 seconds | Таймаут между неудачными попытками подключения к Consul |
| DB_JDBC_URL | Адрес базы данных. Можно использовать вместо DB_HOST, DB_PORT и DB_NAME | |
| DB_HOST | Хост сервера базы данных | |
| DB_PORT | Порт сервера базы данных | |
| DB_NAME | Название базы данных | |
| DB_USER | "postgres" | Пользователь базы данных |
| DB_PASSWORD | 12345 | Пароль для пользователя базы данных |
| DB_THREADS | 10 | Количество потоков в пуле потоков для соединения с БД |
| DB_QUEUE_SIZE | 300 | Размер очереди для действий базы данных, которые не могут быть выполнены немедленно, когда все потоки заняты. За пределами этого значения новые действия немедленно завершаются неудачей |
| DB_CONN_MAX | 10 | Максимальное количество одновременных подключений к БД |
| DB_CONN_TIMEOUT | 20 second | Максимальное время ожидания ответа для соединения к БД. Если это время превышено, а соединение не становится доступным, будет брошено исключение SQLException. 1000 мс — минимальное значение. |
| DB_ISOLATION | "READ_COMMITTED" | Уровень изоляции транзакций для новых подключений. Допустимые значения: NONE, READ_COMMITTED, READ_UNCOMMITTED, REPEATABLE_READ, SERIALIZABLE. |
| DB_READONLY | false | Read-only SQL транзакция может изменять только временные таблицы. Этот параметр управляет статусом «только для чтения» по умолчанию для каждой новой транзакции. |
| DB_CONN_MIN | = DB_THREADS | Минимальное количество одновременных подключений к БД |
| DB_VALIDATION_TIMEOUT | 1 seconds | Максимальное время, в течение которого соединение будет проверяться на работоспособность. 1000 мс — минимальное значение. |
| DB_IDLE_TIMEOUT | 10 minutes | Максимальное время, в течение которого соединению разрешено простаивать в пуле. Значение 0 означает, что простаивающие соединения никогда не удаляются из пула. |
| DB_MAX_LIFETIME | 30 minutes | Максимальное время жизни соединения в пуле. Когда простаивающее соединение достигает этого времени ожидания, даже если оно недавно использовалось, оно будет удалено из пула. Значение 0 указывает на отсутствие максимального срока службы. |
| DB_INITIALIZATION_FAIL_FAST | false | Определяет, будет ли пул «быстро выходить из строя», если пул не может быть успешно заполнен начальными соединениями. Если соединения не могут быть созданы во время запуска пула, будет выдано исключение RuntimeException. Это свойство не имеет никакого эффекта, если minConnections равно 0. |
| DB_LEAK_DETECTION_THRESHOLD | 0 | Время, в течение которого соединение может находиться вне пула, прежде чем будет зарегистрировано сообщение, указывающее на возможную утечку соединения. Значение 0 означает, что обнаружение утечек отключено. Наименьшее приемлемое значение для включения обнаружения утечек составляет 10 с. |
| DB_CONNECTION_TEST_QUERY | "SELECT 1" | Выражение, которое будет выполнено непосредственно перед получением соединения из пула для проверки того, что соединение с базой данных все еще активно. Оно зависит от базы данных и должно представлять собой запрос, требующий минимальной обработки базой данных (например, «VALUES 1»). Если этот параметр не установлен, вместо него используется метод JDBC4 Connection.isValid(). |
| DB_REGISTER_MBEANS | false | Зарегистрированы ли JMX Management Beans («MBeans») |
| LOG_OUTPUT | STDOUT | Параметры вывода логов: STDOUT - обычный лог, STDOUT_CEF - лог в формате CEF |
| LOG_LEVEL_HTTP | WARN | Уровень логирования HTTP-сервера |
| LOGGING_SRC_IP | Параметр SRC (источник/source, на который ссылается событие) для логов в формате CEF. Если не установлена, src=notFound. Требуемый формат: IPv4, например "192.168.10.1". |
|
| LOGGING_SRC_HOST | Параметр SHOST (источник/source, на который ссылается событие) для логов в формате CEF. Если не установлена, shost=notFound. Требуемый формат: fully qualified domain name (FQDN), например "host" или "host.domain.com". |
|
| LOGGING_DST_IP | Параметр DST (получатель/destination, на который ссылается событие) для логов в формате CEF. Если не установлена, dst=notFound. Требуемый формат: IPv4, например "192.168.10.1". |
|
| LOGGING_CEF_VER | 0 | Версия формата CEF: либо 0, либо 1. Рекомендуется использовать 0. |
| VERDI_COMMANDS_HTTP_RETRY_ATTEMPTS | 5 | Поле attempts из RetrySettings |
| VERDI_COMMANDS_HTTP_RETRY_DELAY | "5 seconds" | Поле delay из RetrySettings |
| VERDI_COMMANDS_HTTP_RETRY_KIND | "OnSomeExceptions(ConnectException)" | CommandResultRetryConditionKind |
Формат CEF для логов сервиса Buzzer
У логов есть возможность включить формат CEF для логирования по следующему шаблону:
2022-12-14T16:56:31+0500 CEF:Version|Device Vendor|Device Product|Device Version|EventClassId|Message|Severity|src=? dst=? shost=? suid=? suser=? msg=? end=currentTimeMillis|.
Чтобы включить логирование в формате CEF, нужно задать переменную LOG_OUTPUT = STDOUT_CEF. Если логи пишутся в формате CEF, то необходимо задать следующие переменные, которые по умолчанию не заданы:
LOGGING_SRC_IP, для параметраsrcв логахLOGGING_SRC_HOST, для параметраshostв логахLOGGING_DST_IP, для параметраdstв логах
В файле src/main/resources/log4j.xml можно поменять class path для основного класса приложения (переменная projectMainClassPath), если это необходимо.
Список команд сервиса уведомлений
В сервисе реализованы следующие команды:
- Регистрация сервиса отправки
- Получение сервиса отправки
- Получение списка сервисов отправки
- Обновление сервиса отправки
- Отмена регистрации сервиса отправки
- Создание пользовательской настройки
- Получение пользовательской настройки
- Получение списка пользовательских настроек
- Поиск пользовательских настроек
- Обновление пользовательской настройки
- Удаление пользовательской настройки
- Создание системной настройки
- Получение системной настройки
- Получение системных списка настроек
- Поиск системных настроек
- Обновление системной настройки
- Удаление системной настройки
- Отправить уведомление
CreateSendingBuzzer
Передаем JSON с описанием сервиса отправки
{
"id": "profile",
"topic": "personal-page",
"version": 1
}
Получаем код сервиса
"profile"
Зарегистрировать сервис отправки.
Имя команды для вызова: createSendingBuzzer. Поддерживается только синхронный вызов.
- На входе: Sending.
- На выходе: String
GetSendingBuzzer
Передаем код сервиса отправки
"profile"
Получаем JSON с описанием сервиса отправки
{
"id": "profile",
"topic": "personal-page",
"version": 1
}
Получить описание сервиса отправки.
Имя команды для вызова: getSendingBuzzer. Поддерживается только синхронный вызов.
- На входе: String
- На выходе: Sending
ListSendingBuzzer
Получаем JSON со списком всех существующих сервисов отправки
[
{
"id": "profile",
"topic": "personal-page",
"version": 1
},
{
"id": "email",
"topic": "email",
"version": 1
}
]
Возвращает список всех зарегистрированных сервисов отправки
Имя команды для вызова: listSendingBuzzer. Поддерживается только синхронный вызов.
- На входе: -
- На выходе: List[Sending]
UpdateSendingBuzzer
Передаем JSON с описанием сервиса отправки
{
"id": "profile",
"topic": "personal-page",
"version": 1
}
Получаем результат
1
Обновить существующий сервис отправки.
Если значение, передаваемое в version, отличается от текущего, обновление не происходит.
При успешном обновлении version формы увеличивается на единицу.
Имя команды для вызова: updateSendingBuzzer. Поддерживается только синхронный вызов.
- На входе: Sending
- На выходе: 1 - в случае успеха, 0 - при неудаче
DeleteSendingBuzzer
Передаем код сервиса отправки
"email"
Получаем результат
1
Отменить регистрацию сервиса отправки.
Имя команды для вызова: deleteSendingBuzzer. Поддерживается только синхронный вызов.
- На входе: String
- На выходе: 1 - в случае успеха, 0 - при неудаче
CreateSettingBuzzer
Передаем JSON с данными для создания пользовательских настроек уведомления
{
"code": "buzzerNotification",
"sendingIds": ["profile"]
}
Получаем ID новой настройки
"4345011c-c579-4e1b-b84b-94e4eed2f2da"
Создать пользовательские настройки уведомления.
Имя команды для вызова: createSettingBuzzer. Поддерживается только синхронный вызов.
- На входе: SettingCreateDTO.
- На выходе: UUID
GetSettingBuzzer
Передаем ID настроек
"ed2de80e-0289-43c2-a944-70e9a4277d36"
Получаем JSON с настройками уведомления
{
"id": "e3901923-21dc-4f95-98d0-cb2ea29abe1e",
"code": "buzzerNotification",
"sendingIds": [
"profile",
"email"
],
"userId": "016d04f4-fc1a-4629-808b-e5e1c35bb348",
"version": 1
}
Получить настройки уведомления.
Имя команды для вызова: getSettingBuzzer. Поддерживается только синхронный вызов.
- На входе: UUID
- На выходе: Setting
ListSettingBuzzer
Получаем JSON со списком всех пользовательских настроек уведомлений
[
{
"id": "e3901923-21dc-4f95-98d0-cb2ea29abe1e",
"code": "buzzerNotification",
"sendingIds": [
"profile",
"email"
],
"userId": "016d04f4-fc1a-4629-808b-e5e1c35bb348",
"version": 1
}
]
Возвращает список всех существующих пользовательских настроек уведомлений.
Имя команды для вызова: listSettingBuzzer. Поддерживается только синхронный вызов.
- На входе: -
- На выходе: List[Setting]
SearchSettingBuzzer
Передаем поисковой запрос
{
"query": "sendingid",
"context": {"sendingid": "profile"},
"sorting": {"fieldName": "code", "order": "Asc"},
"paging": {"page": 1, "count": 10}
}
Получаем JSON со списком отфильтрованных пользовательских настроек уведомлений
{
"items": [
{
"id": "e3901923-21dc-4f95-98d0-cb2ea29abe1e",
"code": "buzzerNotification",
"sendingIds": [
"profile",
"email"
],
"userId": "016d04f4-fc1a-4629-808b-e5e1c35bb348",
"version": 2
},
{
"id": "ec44f7d8-4e8b-4259-8c86-2cd9b2472451",
"code": "buzzerNotification2",
"sendingIds": [
"profile"
],
"userId": "016d04f4-fc1a-4629-808b-e5e1c35bb348",
"version": 1
}
],
"total": 2
}
Возвращает отфильтрованный список пользовательских настроек уведомлений. Доступные для фильтрации поля и виды фильтров по ним:
| Поле | Виды фильтров |
|---|---|
| id | InSetQuery |
| code | InSetQuery, LikeQuery |
| sendingid | InSetQuery |
Доступные для сортировки поля:
- code
Имя команды для вызова: searchSettingBuzzer. Поддерживается только синхронный вызов.
UpdateSettingBuzzer
Передаем JSON с пользовательскими настройками уведомления
{
"id": "e3901923-21dc-4f95-98d0-cb2ea29abe1e",
"code": "buzzerNotification",
"sendingIds": [
"profile",
"email"
],
"version": 1
}
Получаем результат
1
Обновить существующие пользовательские настройки уведомления.
Если значение, передаваемое в version, отличается от текущего, обновление не происходит.
При успешном обновлении version формы увеличивается на единицу.
Имя команды для вызова: updateSettingBuzzer. Поддерживается только синхронный вызов.
- На входе: SettingUpdateDTO
- На выходе: 1 - в случае успеха, 0 - при неудаче
DeleteSettingBuzzer
Передаем ID пользовательских настроек уведомления
"0ccd05ac-50c0-4e7e-906b-6893eaa8d9de"
Получаем результат
1
Удалить существующие пользовательские настройки события по их ID.
Имя команды для вызова: deleteSettingBuzzer. Поддерживается только синхронный вызов.
- На входе: UUID
- На выходе: 1 - в случае успеха, 0 - при неудаче
CreateSystemSettingBuzzer
Передаем JSON с данными для создания системных настроек
{
"code": "buzzerNotification",
"sendingIds": ["profile", "email"]
}
Получаем ID новой системной настройки
"4345011c-c579-4e1b-b84b-94e4eed2f2da"
Создать системные настройки уведомления.
Имя команды для вызова: createSystemSettingBuzzer. Поддерживается только синхронный вызов.
- На входе: SettingCreateDTO.
- На выходе: UUID
GetSystemSettingBuzzer
Передаем ID системных настроек
"ed2de80e-0289-43c2-a944-70e9a4277d36"
Получаем JSON с системными настройками уведомления
{
"id": "e3901923-21dc-4f95-98d0-cb2ea29abe1e",
"code": "buzzerNotification",
"sendingIds": [
"profile",
"email"
],
"userId": null,
"version": 1
}
Получить системные настройки уведомления.
Имя команды для вызова: getSystemSettingBuzzer. Поддерживается только синхронный вызов.
- На входе: UUID
- На выходе: Setting
ListSystemSettingBuzzer
Получаем JSON со списком всех системных настроек уведомлений
[
{
"id": "e3901923-21dc-4f95-98d0-cb2ea29abe1e",
"code": "buzzerNotification",
"sendingIds": [
"profile",
"email"
],
"userId": null,
"version": 1
}
]
Возвращает список всех существующих системных настроек уведомлений.
Имя команды для вызова: listSystemSettingBuzzer. Поддерживается только синхронный вызов.
- На входе: -
- На выходе: List[Setting]
SearchSystemSettingBuzzer
Передаем поисковой запрос
{
"query": "sendingid",
"context": {"sendingid": "profile"},
"sorting": {"fieldName": "code", "order": "Asc"},
"paging": {"page": 1, "count": 10}
}
Получаем JSON со списком отфильтрованных системных настроек уведомлений
{
"items": [
{
"id": "e3901923-21dc-4f95-98d0-cb2ea29abe1e",
"code": "buzzerNotification",
"sendingIds": [
"profile",
"email"
],
"userId": null,
"version": 2
},
{
"id": "ec44f7d8-4e8b-4259-8c86-2cd9b2472451",
"code": "buzzerNotification2",
"sendingIds": [
"profile"
],
"userId": null,
"version": 1
}
],
"total": 2
}
Возвращает отфильтрованный список системных настроек уведомлений. Доступные для фильтрации поля и виды фильтров по ним:
| Поле | Виды фильтров |
|---|---|
| id | InSetQuery |
| code | InSetQuery, LikeQuery |
| sendingid | InSetQuery |
Доступные для сортировки поля:
- code
Имя команды для вызова: searchSystemSettingBuzzer. Поддерживается только синхронный вызов.
UpdateSystemSettingBuzzer
Передаем JSON с системными настройками уведомления
{
"id": "e3901923-21dc-4f95-98d0-cb2ea29abe1e",
"code": "buzzerNotification",
"sendingIds": [
"profile",
"email"
],
"version": 1
}
Получаем результат
1
Обновить существующие системные настройки уведомления.
Если значение, передаваемое в version, отличается от текущего, обновление не происходит.
При успешном обновлении version формы увеличивается на единицу.
Имя команды для вызова: updateSystemSettingBuzzer. Поддерживается только синхронный вызов.
- На входе: SettingUpdateDTO
- На выходе: 1 - в случае успеха, 0 - при неудаче
DeleteSystemSettingBuzzer
Передаем ID системных настроек уведомления
"0ccd05ac-50c0-4e7e-906b-6893eaa8d9de"
Получаем результат
1
Удалить существующие системные настройки события по ID.
Имя команды для вызова: deleteSystemSettingBuzzer. Поддерживается только синхронный вызов.
- На входе: UUID
- На выходе: 1 - в случае успеха, 0 - при неудаче
SendNotification
Передаем данные отправляемого уведомления
{
"code": "buzzerNotification",
"userId": "016d04f4-fc1a-4629-808b-e5e1c35bb348",
"data": {"field": "value"}
}
Получаем результат
true
Отправка уведомления пользователю.
Имя команды для вызова: sendNotification. Поддерживается синхронный и асинхронный вызовы.
- На входе: Notification
- На выходе: true (признак завершения отправки)
Модели сервиса уведомлений
Sending (Buzzer)
Описание сервиса отправки
| Название поля | Тип | Обязательное | Описание |
|---|---|---|---|
| id | uuid | да | Идентификатор сервиса |
| topic | string | да | Очередь сервиса отправки в Kafka, в которую нужно перенаправить уведомление |
| version | number | нет | Версия описания (опциональная, по умолчанию - 1) |
Setting (Buzzer)
Настройка связи уведомления с сервисами отправки
| Название поля | Тип | Обязательное | Описание |
|---|---|---|---|
| id | uuid | да | Идентификатор настройки |
| code | string | да | Код уведомления |
| sendingIds | string[] | да | Список сервисов отправки |
| userId | uuid string | нет | Идентификатор пользователя, для которого определяется настройка. Отсутствует у системных настроек. |
| version | number | да | Версия настройки |
SettingCreateDTO (Buzzer)
Объект для создания настройки связи уведомления с сервисами отправки
| Название поля | Тип | Обязательное | Описание |
|---|---|---|---|
| code | string | да | Код уведомления |
| sendingIds | string[] | да | Список сервисов отправки |
SettingUpdateDTO (Buzzer)
Объект для обновления настройки связи уведомления с сервисами отправки
| Название поля | Тип | Обязательное | Описание |
|---|---|---|---|
| id | uuid | да | Идентификатор настройки |
| code | string | да | Код уведомления |
| sendingIds | string[] | да | Список сервисов отправки |
| version | number | да | Версия настройки |
Сервис статуса команд
Сервис обеспечивает трассировку выполнения команд и хранение их промежуточных “текущих” состояний и результатов. Также он позволяет простым и быстрым способом запрашивать данные по этим текущим состояниям команд.
Сервис предоставляет следующие команды:
Список переменных окружения сервиса статуса команд
Все доступные переменные окружения для настройки сервиса модели данных.
| Переменная | Тип | Обяза-тельная | Значение по умолчанию | Описание |
|---|---|---|---|---|
| COMMAND_STATUS_SERVICE_PUBLISH_HOST | string | нет | "0.0.0.0" | Хост, на котором слушает HTTP-сервер |
| COMMAND_STATUS_SERVICE_PUBLISH_PORT | int | нет | 8080 | Порт, на котором слушает HTTP-сервер |
| COMMAND_STATUS_SERVICE_KAFKA_SERVERS | string | да | "localhost:9092" | Адрес Kafka |
| COMMAND_STATUS_SERVICE_STATUS_TOPIC | string | да | "commandevents" | Название кафка-топика, куда отправляются сообщения со статусами выполняемых команд. |
| COMMAND_STATUS_SERVICE_CONSUMER_GROUP | string | нет | "statusGroup" | Имя consumer-группы для чтения из кафка-топика команд. Не должна меняться и не должна быть пустой, иначе сервис перечитает свои команды при перезапуске. |
| COMMAND_STATUS_SERVICE_TOPIC_PARTITIONS | int | нет | 10 | Число читаемых партиций из кафка-топика со статусами. |
| COMMAND_STATUS_SERVICE_KAFKA_CONSUMER_RESTART_MIN_BACKOFF | duration string | нет | 3 seconds | Изначальная задержка до рестарта консьюмера после падения. С каждым рестартом, задержка увелчивается по формуле "delay = restartsNumber * initDelay" |
| COMMAND_STATUS_SERVICE_KAFKA_CONSUMER_RESTART_MAX_BACKOFF | duration string | нет | 1 minute | Максимальное задержка до рестарта консьюмера после падения |
| COMMAND_STATUS_SERVICE_KAFKA_CONSUMER_RESTART_MAX_RESTARTS | int | нет | 20 | Максимальное число попыток переподключения консьюмера. После достижения максимального, роут /health будет возвращать 503, что приведет к рестарту сервиса в k8s. |
| COMMAND_STATUS_SERVICE_KAFKA_AUTH_USER | string | нет | "" | Название учетной записи Kafka. Если название не указано, то настройки авторизации не будут применены. |
| COMMAND_STATUS_SERVICE_KAFKA_AUTH_PASSWORD | string | нет | "" | Пароль учетной записи Kafka. |
| COMMAND_STATUS_SERVICE_KAFKA_AUTH_TRUSTSTORE_LOCATION | string | нет | "" | Путь до хранилища сертификатов (Java key store). Если путь не указан, то сертификат применяться не будет. |
| COMMAND_STATUS_SERVICE_KAFKA_AUTH_TRUSTSTORE_PASSWORD | string | нет | "" | Пароль к хранилищу сертификатов. |
| COMMAND_STATUS_SERVICE_KAFKA_AUTH_MODE | string | нет | "" | Режим аутентификации: static - одна учетная запись на все запросы, mapping - учетная запись зависит от запроса |
| COMMAND_STATUS_SERVICE_KAFKA_AUTH_CONFIG | string | нет | "" | Настройки для аутентификации: если AuthMode = mapping, то необходим JSON с List[KafkaAuthConfig]. Переменная соответствует полю authConfig из KafkaAuthSettings. |
| COMMAND_STATUS_SERVICE_KAFKA_AUTH_CACHE_SIZE | int | нет | Максимальный размер кеша для Kafka producer (количество активных соединений). | |
| COMMAND_STATUS_SERVICE_KAFKA_AUTH_CACHE_TTL | duration string | нет | Время жизни Kafka producer в кеше. | |
| COMMAND_STATUS_SERVICE_CONSUL_ADDRESS | url string | нет | "http://localhost:8500" | Адрес Сonsul. |
| COMMAND_STATUS_SERVICE_CONSUL_RETRY_MAX | int | нет | 20 | Максимальное количество подключений к Consul. |
| COMMAND_STATUS_SERVICE_CONSUL_RETRY_DELAY | duration string | нет | 3 seconds | Задержка между попытками подключения к Consul. |
| COMMAND_STATUS_SERVICE_CONSUL_AUTH_USER | string | нет | "" | Название учетной записи Сonsul. Если название не указано, то настройки авторизации не будут применены. |
| COMMAND_STATUS_SERVICE_CONSUL_AUTH_PASSWORD | string | нет | "" | Пароль учетной записи Сonsul. |
| COMMAND_STATUS_SERVICE_DISCOVERABLE_ID | string | нет | "CommandStatusService" | ID сервиса в ServiceDiscovery |
| COMMAND_STATUS_SERVICE_DISCOVERABLE_NAME | string | нет | "CommandStatusService" | Имя сервиса в ServiceDiscovery |
| COMMAND_STATUS_SERVICE_DISCOVERABLE_HOST | string | да | "localhost" | Хост, публикуемый в ServiceDiscovery. По нему на данный сервис будут обращаться другие через HTTP. Указанный адрес должен быть виден другим сервисам. Пример: имя kubernetes/docker_swarm service |
| COMMAND_STATUS_SERVICE_DISCOVERABLE_PORT | int | нет | Порт, публикуемый в ServiceDiscovery. По нему на данный сервис будут обращаться другие через HTTP. По умолчанию указывается порт, который слушает HTTP-сервер. | |
| COMMAND_STATUS_SERVICE_DISCOVERABLE_LIVETIME | duration string | нет | 5 minutes | Период после последней отправки health check, в течение которого ServiceDiscovery считает данный сервис живым. |
| COMMAND_STATUS_SERVICE_DISCOVERABLE_HEALTHPASS | duration string | нет | 1 minute | Периодичность отправки health check в ServiceDiscovery |
| COMMAND_STATUS_SERVICE_DISCOVERABLE_VERSION | string | нет | "v1" | Версия сервиса, публикуемого в ServiceDiscovery. |
| COMMAND_STATUS_SERVICE_AKKA_HTTP_MAXCON | int | нет | 1024 | Максимальное число одновременных исходящих HTTP-соединений |
| COMMAND_STATUS_SERVICE_AKKA_HTTP_IDLE_TIMEOUT | duration string | нет | 60 seconds | Максимальное время жизни бездействующего соединения |
| COMMAND_STATUS_SERVICE_AKKA_HTTP_REQUEST_TIMEOUT | duration string | нет | 30 seconds | Максимальное время ожидания исполнения запроса; должно быть меньше COMMAND_STATUS_SERVICE_AKKA_HTTP_IDLE_TIMEOUT |
| COMMAND_STATUS_SERVICE_SENDERLIB_COMMANDS_CACHE_UPDATEPERIOD | duration string | нет | 10 minutes | Время кэширования данных по командам из CommandDiscovery |
| COMMAND_STATUS_SERVICE_SENDERLIB_SERVICES_CACHE_UPDATEPERIOD | duration string | нет | 0 minutes | Время кэширования данных по сервисам из ServiceDiscovery |
| DB_HOST | string | да | Хост БД | |
| DB_PORT | int | да | Порт БД | |
| DB_NAME | string | да | Имя базы в БД | |
| DB_URL | jdbc url string | нет | JDBC-url для соединения с БД. По умолчанию собирается из других обязательных переменных. Можно указать только его, если не хочется отдельно указывать хост/порт/имя базы. | |
| DB_USER | string | да | Пользователь БД | |
| DB_PASSWORD | string | да | Пароль пользователя БД | |
| COMMAND_STATUS_SERVICE_STATUS_CACHE_TTL | duration string | нет | 1 minute | Время жизни статуса команд в кеше сервиса статусов (после получения последнего статуса из кафка). |
| COMMAND_STATUS_SERVICE_STATUS_CACHE_CLEAN_CHECK | duration string | нет | 10 milliseconds | Период проверки кеша со статусами команд для удаления статусов, время жизни которых превысило COMMAND_STATUS_SERVICE_STATUS_CACHE_TTL. |
| COMMAND_STATUS_COMMAND_CLEANER_CHECK | duration string | нет | 1 hour | Период проверки Command discovery для очистки команд, привязаных к сервисам которые не считаются "живыми". |
| COMMAND_STATUS_COMMAND_CLEANER_DELETE_ERRORS | boolean | нет | true | Удалять ли принудительно команды из Command discovery, с которыми возникли ошибки во время "очистки". |
| COMMAND_STATUS_COMMAND_NOT_LOG_VALUE_FOR_STATUSES | string | нет | Список CommandStatus, для которых не нужно логировать поле "value". Статусы перечисляются в одной строке через запятую ",". | |
| COMMAND_STATUS_SERVICE_LOG_LEVEL | string | нет | INFO | Уровень логирования по умолчанию для всех логгеров |
| COMMAND_STATUS_SERVICE_LOG_LEVEL_AKKAHTTPSENDER | string | нет | INFO | Уровень логирования для отправки команд через HTTP. На уровне INFO логируется трассировка, если она включена |
| COMMAND_STATUS_SERVICE_LOG_LEVEL_KAFKASENDER | string | нет | INFO | Уровень логирования для отправки команд через Kafka. На уровне INFO логируется трассировка, если она включена |
| COMMAND_STATUS_SERVICE_LOG_LEVEL_COMMANDSTATUSCLI | string | нет | INFO | Уровень логирования для проверки состояний команд в сервисе статусов |
| COMMAND_STATUS_SERVIC_LOG_OUTPUT | string | нет | STDOUT | Параметры вывода логов: STDOUT - обычный лог в консоль, STDOUT_CEF - лог в формате CEF в консоль, FILE - обычный лог в файл, FILE_CEF - лог в формате CEF в файл. Может принимать несколько значений через любой разделитель (например, "STDOUT FILE_CEF"). Преобразования, если указано несколько значений: присутствуют "STDOUT" + "STDOUT_CEF" = учитывается только "STDOUT_CEF"; "FILE" + "FILE_CEF" = "FILE_CEF"; |
| COMMAND_STATUS_SERVICE_LOGGING_SRC_IP | string | нет | Параметр SRC (источник/source, на который ссылается событие) для логов в формате CEF. Если не установлена, src=notFound. Требуемый формат: IPv4, например "192.168.10.1". |
|
| COMMAND_STATUS_SERVICE_LOGGING_SRC_HOST | string | нет | Параметр SHOST (источник/source, на который ссылается событие) для логов в формате CEF. Если не установлена, shost=notFound. Требуемый формат: fully qualified domain name (FQDN), например "host" или "host.domain.com". |
|
| COMMAND_STATUS_SERVICE_LOGGING_DST_IP | string | нет | Параметр DST (получатель/destination, на который ссылается событие) для логов в формате CEF. Если не установлена, dst=notFound. Требуемый формат: IPv4, например "192.168.10.1". |
|
| COMMAND_STATUS_SERVICE_LOGGING_CEF_VER | int | нет | 0 | Версия формата CEF: либо 0, либо 1. Рекомендуется использовать 0. |
| COMMAND_STATUS_SERVICE_SENDERLIB_COMMANDS_HTTP_RETRY_ATTEMPTS | int | нет | 5 | Поле attempts из RetrySettings |
| COMMAND_STATUS_SERVICE_SENDERLIB_COMMANDS_HTTP_RETRY_DELAY | duration string | нет | "5 seconds" | Поле delay из RetrySettings |
| COMMAND_STATUS_SERVICE_SENDERLIB_COMMANDS_HTTP_RETRY_KIND | string | нет | "OnSomeExceptions(ConnectException)" | CommandResultRetryConditionKind |
Формат CEF для логов сервиса Command-status
У логов есть возможность включить формат CEF для логирования по следующему шаблону:
2022-12-14T16:56:31+0500 CEF:Version|Device Vendor|Device Product|Device Version|EventClassId|Message|Severity|src=? dst=? shost=? suid=? suser=? msg=? end=currentTimeMillis|.
Чтобы включить логирование в формате CEF, нужно задать переменную COMMAND_STATUS_SERVICE_LOG_OUTPUT = STDOUT_CEF. Если логи пишутся в формате CEF, то необходимо задать следующие переменные, которые по умолчанию не заданы:
COMMAND_STATUS_SERVICE_LOGGING_SRC_IP, для параметраsrcв логахCOMMAND_STATUS_SERVICE_LOGGING_SRC_HOST, для параметраshostв логахCOMMAND_STATUS_SERVICE_LOGGING_DST_IP, для параметраdstв логах
В файле boot/src/main/resources/logback.xml можно поменять class path для основного класса приложения (переменная projectMainClassPath), если это необходимо.
GetStatus
import com.embedika.senderlib.{Discpatcher, VerdiFactory}
import com.embedika.senderlib.models.command._
val factory: VerdiFactory = VerdiFactory.create(/**params here*/)
val dispatcher: Dispatcher = factory.dispatcher
Передаем строку
dispatcher.status(CommandId("a9f813eb-22ff-4982-9828-c3471cec76fe"))
// or
dispatcher.sendCommandSync(CommandName("getStatus"), CommandId("a9f813eb-22ff-4982-9828-c3471cec76fe"))
"a9f813eb-22ff-4982-9828-c3471cec76fe"
Получаем JSON
val result: Future[Either[DispatchError, (CommandId, CommandStatusRepresentation)]] = dispatcher.sendCommandSync(CommandName("getStatus"), CommandId("a9f813eb-22ff-4982-9828-c3471cec76fe"))
result.transform {
case Success(Some(Right(id, result))) =>
val status = result.status
val json = result.json
// process result
case _ => // process errors
}
{"status": "Completed", "timestamp": 1657797778943, "value": "test completed"}
Получает текущий статус исполняемой команды. Dispatcher для команд
имеет вспомогательный метод .status() для упрощенного
вызова этой команды.
Свойства команды
| Свойство | Значение | Описание |
|---|---|---|
| Версия | v1 | |
| Название команды | getStatus | Название команды для прямого вызова |
| Тип вызова | HTTP | |
| HTTP-метод | POST | |
| Предоставляющий сервис | CommandStatusService | |
| Url | /v1/status | |
| Права доступа | [] | |
| Внутренняя команда | нет | |
| Сервисная команда | да | для служебных команд не ведется жизненный цикл, не пишутся состояния |
Принимает данные
Одна строка - ID команды
"123"
Возвращает данные
Если по команде найдены актуальные данные, тогда возвращается результат в виде JSON следующего формата
{"status": "Created", "timestamp": 1657797778943}
{"status": "Completed", "timestamp": 1657797778943, "value": "test completed"}
В этом случае Dispatcher вернет
DispatchError - CannotSend.
Вспомогательный метод .getStatus() же вернет
None.
PollStatus
Передаем ID команды и таймаут поллинга
{
"commandId": "efef5704-5827-49f8-8b3f-a00df3db7f5a",
"timeoutSeconds": 5
}
Получаем JSON
{"status": "Completed", "timestamp": 1657797778943, "value": "test completed"}
Ожидает завершение команды в течение указанного времени (long-polling) и возвращает результат.
Если команда не завершилась за указанное время, вернет ее текущий статус.
Свойства команды
| Свойство | Значение | Описание |
|---|---|---|
| Версия | v1 | |
| Название команды | pollStatus | Название команды для прямого вызова |
| Тип вызова | HTTP | |
| HTTP-метод | POST | |
| Предоставляющий сервис | CommandStatusService | |
| Url | /v1/pollStatus | |
| Права доступа | [] | |
| Внутренняя команда | нет | |
| Сервисная команда | да | для служебных команд не ведется жизненный цикл, не пишутся состояния |
Возвращает данные
Если команда была завершена ранее, или завершилась в течение указанного времени ожидания, то возвращает результат выполнения:
{"status": "Completed", "timestamp": 1657797778943, "value": "test completed"}
Если команда не была завершена в указанный срок, возвращает последний актуальный статус:
{"status": "InProcess", "timestamp": 1657797763941,"value": null}
Сервис печатных форм Erika
В сервисе реализованы следующие команды
Общее описание архитектуры сервиса Erika
Конфигурирование сервиса Erika
Список переменных окружения сервиса Erika
| Переменная | Тип | Обязательная | Значение по умолчанию | Описание |
|---|---|---|---|---|
| SERVER_PORT | int | нет | 8080 | Порт, на котором слушает HTTP-сервер |
| SERVER_IDLE_TIMEOUT | duration string | нет | 2 hours | Period of Time a connection can remain idle before the connection is timed out and disconnected. |
| SERVER_RESPONSE_TIMEOUT | duration string | нет | 1 hour | Time from when the request is made until a response line is generated before a 503 response is returned and the HttpApp is canceled |
| PROCESSING_QUEUE_SIZE | int | нет | 5 | Размер очередь для всех задач, т.е. уровень буферизации |
| PROCESSING_WORKERS_COUNT | int | нет | 2 | Количество воркеров, т.е. уровень параллельности |
| PROCESSING_FILE_READ_CHUNK_SIZE | int | нет | 16384 | Размер буфера в байтах для результатов генерации отчетов |
| PROCESSING_TIMEOUT | duration string | нет | 10 minutes | Таймаут на выполнение задач воркером |
| PROCESSING_GROUP_MAX_PARALLELISM | int | нет | 5 | Параллелизм обработки групп отчетов |
| PROCESSING_IMAGES_DIR | string | нет | "/tmp" | Директория для скачивания изображений |
| PROCESSING_FIX_JUSTIFY | bool | нет | false | Костыль для корректного отображения на фронте сгенерировных pdf файлов |
| PROCESSING_FULLNESS_LIMIT | int | нет | 10 | Тоже отчасти костыль, который ограничивает время в течении которого все воркеры могут быть заняты. Добавлен т.к. spire.doc регулярно падает в бесконечный цикл, и свободные воркеры заканчиваются |
| PROCESSING_WATCHER_DURATION | duration string | нет | 5 seconds | Периодичность проверки на исчерпание воркеров. При достижении PROCESSING_FULLNESS_LIMIT следующая проверка завершит приложение |
| SPIRE_DOC_LICENSE_KEY | string | нет | "" | Лицензионный ключ библиотеки spire.doc Если оставить пустым то будет работать в режиме trial-версии |
| PROCESSING_SPIRE_DOC_NEW_ENGINE | bool | нет | true | Использовать новый движок spire.doc при конвертации в pdf: ожидается, что он выдает более соответствующий исходному документу результат, но не исключается появление новых багов |
| PROCESSING_PDF_BUCKET | string | нет | Название бакета в файловом хранилище для хранения pdf-файлов. Бакет с указанным именем автоматически создается при старте сервиса. Если эта переменная равна пустой строке, то бакет не создается при старте. | |
| PROCESSING_REPORT_BUCKET | string | нет | report | Название бакета в файловом хранилище для хранения сгенерированных отчетов. Бакет с указанным именем автоматически создается при старте сервиса. Если эта переменная равна пустой строке, то бакет не создается при старте. |
| VERDI_CONSUL | string | нет (если сервис используется без verdi) | http://localhost:8500 | Адрес Consul |
| VERDI_CONSUL_AUTH_USER | string | нет | "" | Название учетной записи Сonsul. Если название не указано, то настройки авторизации не будут применены. |
| VERDI_CONSUL_AUTH_PASSWORD | string | нет | "" | Пароль учетной записи Сonsul. |
| VERDI_KAFKA_ADDRESS | string | да | localhost:9092 | Адрес брокера Kafka. |
| VERDI_KAFKA_TOPIC | string | нет | erika | Название кафка-топика для отправки сообщений со статусами выполняемых команд. ОБЯЗАТЕЛЬНО должно соответствовать названию этого топика в сервисе статуса команд. |
| VERDI_KAFKA_AUTH_USER | string | нет | "" | Название учетной записи Kafka. Если название не указано, то настройки авторизации не будут применены. |
| VERDI_KAFKA_AUTH_PASSWORD | string | нет | "" | Пароль учетной записи Kafka. |
| VERDI_KAFKA_AUTH_TRUSTSTORE_LOCATION | string | нет | "" | Путь до хранилища сертификатов (Java key store). Если путь не указан, то сертификат применяться не будет. |
| VERDI_KAFKA_AUTH_TRUSTSTORE_PASSWORD | string | нет | "" | Пароль к хранилищу сертификатов. |
| VERDI_KAFKA_AUTH_MODE | string | нет | "" | Режим аутентификации: static - одна учетная запись на все запросы, mapping - учетная запись зависит от запроса |
| VERDI_KAFKA_AUTH_CONFIG | string | нет | "" | Настройки для аутентификации: если AuthMode = mapping, то необходим JSON с List[KafkaAuthConfig]. Переменная соответствует полю authConfig из KafkaAuthSettings. |
| VERDI_KAFKA_AUTH_CACHE_SIZE | int | нет | Максимальный размер кеша для Kafka producer (количество активных соединений). | |
| VERDI_KAFKA_AUTH_CACHE_TTL | duration string | нет | Время жизни Kafka producer в кеше. | |
| VERDI_HOST | string | нет (если сервис используется без verdi) | localhost | Хост, публикуемый в ServiceDiscovery. По нему на данный сервис будут обращаться другие через HTTP. Указанный адрес должен быть виден другим сервисам. Пример: имя kubernetes/docker_swarm service |
| VERDI_TTL | duration string | нет | 30 seconds | Период после последней отправки health check, в течение которого ServiceDiscovery считает данный сервис живым. |
| VERDI_HEALTH_CHECK | duration string | нет | 10 seconds | Периодичность отправки health check в ServiceDiscovery |
| VERDI_COMMAND_STORAGE_UPDATE_PERIOD | duration string | нет | 1 minutes | Время кэширования данных по командам из CommandDiscovery |
| VERDI_SERVICE_DISCOVERY_UPDATE_PERIOD | duration string | нет | 30 seconds | Время кэширования данных по сервисам из ServiceDiscovery |
| VERDI_ALLOW_INTERNAL_COMMANDS | bool | нет | true | Можно ли сервису отправлять внутрисистемные команды |
| VERDI_CONSUL_CONNECTION_MAX_RETRY | int | нет | 5 | Максимальное количество попыток подключений к Consul |
| VERDI_CONSUL_CONNECTION_RETRY_DELAY | duration string | нет | 1 seconds | Таймаут между неудачными попытками подключения к Consul |
| FS_URI | url string | нет (если сервис используется без verdi) | http://localhost:9002 | Адрес S3 файлового хранилища minio |
| FS_ACCESS_KEY_ID | string | нет (если сервис используется без verdi) | admin | Идентификатор доступа к minio |
| FS_SECRET_ACCESS_KEY | string | нет (если сервис используется без verdi) | admin | Секретный ключ доступа к minio |
| FS_UPLOAD_PARALLELISM | int | нет | 4 | Параллелизм загрузки файлов в minio |
| FS_AUTH_MODE | string | нет | static | Режим аутентификации: static - одна учетная запись на все запросы, mapping - учетная запись зависит от запроса |
| FS_AUTH_CONFIG | string | нет | "" | Настройки для аутентификации: если AuthMode = mapping, то необходим JSON с маппингом учетных записей |
| FS_CACHE_SIZE | int | нет | 1 | Максимальный размер кеша клиентов для хранилища файлов (количество активных соединений). |
| FS_CACHE_TTL | duration string | нет | Время жизни клиента в кеше | |
| VERDI_ALLOWED | bool | нет | true | Интеграция сервиса с verdi |
| LOG_OUTPUT | string | нет | STDOUT | Параметры вывода логов: STDOUT - обычный лог, STDOUT_CEF - лог в формате CEF. |
| LOG_LEVEL_HTTP | string | нет | WARN | Уровень логирования HTTP-сервера |
| LOGGING_SRC_IP | string | нет | Параметр SRC (источник/source, на который ссылается событие) для логов в формате CEF. Если не установлена, src=notFound. Требуемый формат: IPv4, например "192.168.10.1". |
|
| LOGGING_SRC_HOST | string | нет | Параметр SHOST (источник/source, на который ссылается событие) для логов в формате CEF. Если не установлена, shost=notFound. Требуемый формат: fully qualified domain name (FQDN), например "host" или "host.domain.com". |
|
| LOGGING_DST_IP | string | нет | Параметр DST (получатель/destination, на который ссылается событие) для логов в формате CEF. Если не установлена, dst=notFound. Требуемый формат: IPv4, например "192.168.10.1". |
|
| LOGGING_CEF_VER | int | нет | 0 | Версия формата CEF: либо 0, либо 1. Рекомендуется использовать 0. |
| VERDI_COMMANDS_HTTP_RETRY_ATTEMPTS | int | нет | 5 | Поле attempts из RetrySettings |
| VERDI_COMMANDS_HTTP_RETRY_DELAY | duration string | нет | "5 seconds" | Поле delay из RetrySettings |
| VERDI_COMMANDS_HTTP_RETRY_KIND | string | нет | "OnSomeExceptions(ConnectException)" | CommandResultRetryConditionKind |
Формат CEF для логов сервиса Erika
У логов есть возможность включить формат CEF для логирования по следующему шаблону:
2022-12-14T16:56:31+0500 CEF:Version|Device Vendor|Device Product|Device Version|EventClassId|Message|Severity|src=? dst=? shost=? suid=? suser=? msg=? end=currentTimeMillis|.
Чтобы включить логирование в формате CEF, нужно задать переменную LOG_OUTPUT = STDOUT_CEF. Если логи пишутся в формате CEF, то необходимо задать следующие переменные, которые по умолчанию не заданы:
LOGGING_SRC_IP, для параметраsrcв логахLOGGING_SRC_HOST, для параметраshostв логахLOGGING_DST_IP, для параметраdstв логах
В файле src/main/resources/log4j.xml можно поменять class path для основного класса приложения (переменная projectMainClassPath), если это необходимо.
Модели сервиса Erika
Progress
Прогресс генерации группы отчетов
| Название поля | Тип | Описание |
|---|---|---|
| id | UUID | Идентификатор генерации |
| rowCount | Int | Общее кол-во групп |
| rowNumber | Int | Кол-во обработанных групп |
StampedDocumentForm
Форма для конвертации документа со штампом
| Название поля | Тип | Описание |
|---|---|---|
| file | File | Документ для конвертации |
| stamp | File | Штамп |
| stampHeight | Float | Высота штампа |
| stampWidth | Float | Ширина штампа |
MergeMode
Тип-сумма режимов объединения групп документов
AllInOne
Группа документов объединяется в один документ. Часть тип-суммы MergeMode
Zip
Группа документов объединяется в один архив. Часть тип-суммы MergeMode
GenerateMode
Тип-сумма режимов генерации отчетов
Single
Режим генерации единичного отчета. Часть тип-суммы GenerateMode
GroupGeneration
Режим генерации группы отчетов. Часть тип-суммы GenerateMode
| Название поля | Тип | Описание | ||
|---|---|---|---|---|
| mergeMode | MergeMode | Режим объединения групп документов | ||
| progressQueue | Option[zio.Queue[Progress]] | Опциональная очередь для отслеживания прогресса |
ReportCommand
Данные для генерации отчета
| Название поля | Тип | Обязательное | Описание |
|---|---|---|---|
| mode | GenerateMode | да | Режим генерации отчета |
| name | string | нет | Имя файла. Расширение (и имя, если не указано) подставляется от шаблона. |
| templateFile | com.embedika.verdi.fsClient.StoredFile | да | Ссылка на шаблон отчета |
| json | io.circe.JsonObject | да | Данные отчета |
Команды сервиса Erika
createReport
Создание отчета
На входе данные для генерации отчета
{
"mode": "single",
"name": "new_report",
"templateFile": "/tmp/55497df9-bf03-4364-bada-881bbd5531f8",
"json": {
"key": "value"
}
}
В результате ссылка на файл отчета
"/report/55497df9-bf03-4364-bada-881bbd5531f8"
Поддерживается только синхронный вызов.
- На входе: ReportCommand
- На выходе: com.embedika.verdi.fsClient.StoredFile
extractVars
Извлечение переменных
На входе ссылка на шаблона отчета
"/tmp/55497df9-bf03-4364-bada-881bbd5531f8"
В результате переменные из шаблона
[
"key1",
"key2"
]
Поддерживается только синхронный вызов.
- На входе: com.embedika.verdi.fsClient.StoredFile
- На выходе: Сериализованный в Json List[String]
convertToPdf
На входе ссылка на файл для конвертации
"temp/55497df9-bf03-4364-bada-881bbd5531f8"
В результате ссылка на pdf версию
"pdf/55497df9-bf03-4364-bada-881bbd5531f8"
Конвертация в pdf. Поддерживаются файлы с расширениями .txt, .doc и .docx.
Имя команды для вызова: convertToPdf. Поддерживается только синхронный вызов.
- На входе: String (URL файла для конвертации)
- На выходе: String (URL сконвертированного файла в бакете pdf)
Event Status: сервис информации о событиях
Сервис принимает запросы для работы со статусом обработки событий. На данный момент все состояние сервиса хранится в
памяти.
Команды могут приходить как по HTTP, так и через Kafka в топик event_status_commands.
В сервисе реализованно 2 команды:
Сервис разбит на несколько модулей, в виде sbt проектов:
domain, в котором содержатся все доменные модели и их различные представления.commands, в котором содержатся все? что связано с командами, их описанием и обработчиками.infrastructure, в котором содержится описание взаимодействия с другими сервисами.storage, содержит сервисы для сохранения и чтения их разных хранилищ доменных моделей.service, содержит сервисы бизнес логики, которые определяют правила взаимодействия компонентов между собой.boot, содержит зависимости и описание для сборки и запуска сервиса.
Информация по добавлению команд можно прочитать в описании шаблона
Локальный запуск сервиса Event Status
При запуске сервиса ожидается, что уже развернута необходимая инфраструктура:
- PostgreSQL база по адресу
localhost:5432/eventStatus_db - Kafka по адресу
localhost:9092 - Consul по адресу
localhost:8500 - Адрес Kafka будет указан в
application.conf - Добавлены необходимые переменные окружения:
- EVENT_STATUS_DB_HOST
- EVENT_STATUS_DB_PORT
- EVENT_STATUS_DB_NAME
- EVENT_STATUS_DB_USER
- EVENT_STATUS_DB_PASSWORD
Запуск из консоли с помощью SBT сервиса Event Status
EVENT_STATUS_DB_HOST=localhost EVENT_STATUS_DB_PORT=5432 EVENT_STATUS_DB_NAME=eventStatus_db EVENT_STATUS_DB_USER=postgres EVENT_STATUS_DB_PASSWORD=12345 sbt boot/run
Список переменных окружения сервиса Event Status
Все доступные переменные окружения для настройки сервиса.
| Переменная | Тип | Обязательная | Значение по умолчанию | Описание |
|---|---|---|---|---|
| EVENT_STATUS_HTTP_HOST | string | нет | "0.0.0.0" | Хост, на котором слушает HTTP-сервер |
| EVENT_STATUS_HTTP_PORT | int | нет | 8192 | Порт, на котором слушает HTTP-сервер |
| EVENT_STATUS_KAFKA_SERVERS | string | да | "localhost:9092" | Адрес Kafka |
| EVENT_STATUS_KAFKA_TOPIC | string | нет | "event_status_commands" | Название кафка-топика для получения команд. Сервис получает кафка-команды по нему, но и также сам публикует это название в CommandDiscovery. |
| EVENT_STATUS_KAFKA_CONSUMER_GROUP | string | нет | "event_status_consumer_group" | Имя consumer-группы для чтения из кафка-топика команд. Не должна меняться и не должна быть пустой, иначе сервис перечитает свои команды при перезапуске. |
| EVENT_STATUS_KAFKA_PARTITIONS | int | нет | 10 | Число читаемых партиций из кафка-топика команд. |
| EVENT_STATUS_KAFKA_CONSUMER_RESTART_MIN_BACKOFF | duration string | нет | 1 second | Изначальная задержка до рестарта консьюмера после падения (увеличивается в 2 раза после каждого рестарта) |
| EVENT_STATUS_KAFKA_CONSUMER_RESTART_MAX_BACKOFF | duration string | нет | 30 seconds | Максимальное задержка до рестарта консьюмера после падения |
| EVENT_STATUS_KAFKA_CONSUMER_RESTART_RANDOM_FACTOR | double | нет | 0.2 | Рандомный фактор для вычисления задержки перед следующим рестратом консьюмера (При значении 0.2 задержка может быть до 20% больше, чем при 0) |
| EVENT_STATUS_KAFKA_CONSUMER_RESTART_MAX_RESTARTS | int | нет | 5 | Максимальное число рестартов консьюмера после падения (в пределах EVENT_STATUS_KAFKA_CONSUMER_RESTART_MAX_RESTARTS_WITHIN) |
| EVENT_STATUS_KAFKA_CONSUMER_RESTART_MAX_RESTARTS_WITHIN | duration string | нет | 5 minutes | Временной отрезок, в который EVENT_STATUS_KAFKA_CONSUMER_RESTART_MAX_RESTARTS ограничивает число рестартов |
| EVENT_STATUS_KAFKA_COMMANDEVENT_TOPIC | string | да | "commandevents" | Название кафка-топика для отправки сообщений со статусами выполняемых команд. ОБЯЗАТЕЛЬНО должно соответствовать названию этого топика в сервисе статуса команд. |
| EVENT_STATUS_KAFKA_AUTH_USER | string | нет | "" | Название учетной записи Kafka. Если название не указано, то настройки авторизации не будут применены. |
| EVENT_STATUS_KAFKA_AUTH_PASSWORD | string | нет | "" | Пароль учетной записи Kafka. |
| EVENT_STATUS_KAFKA_AUTH_TRUSTSTORE_LOCATION | string | нет | "" | Путь до хранилища сертификатов (Java key store). Если путь не указан, то сертификат применятся не будет. |
| EVENT_STATUS_KAFKA_AUTH_TRUSTSTORE_PASSWORD | string | нет | "" | Пароль к харнилищу сертификатов. |
| EVENT_STATUS_KAFKA_AUTH_MODE | string | нет | "" | Режим аутентификации: static - одна учетная запись на все запросы, mapping - учетная запись зависит от запроса |
| EVENT_STATUS_KAFKA_AUTH_CONFIG | string | нет | "" | Настройки для аутентификации: если AuthMode = mapping, то необходим JSON с List[KafkaAuthConfig] соответствует полю authConfig KafkaAuthSettings |
| EVENT_STATUS_KAFKA_AUTH_CACHE_SIZE | int | нет | Максимальный размер кэша для Kafka producer (количество активных соединений). | |
| EVENT_STATUS_KAFKA_AUTH_CACHE_TTL | duration string | нет | Время жизни Kafka producer в кэше. | |
| EVENT_STATUS_CONSUL_ADDR | url string | нет | "http://localhost:8500" | Адрес Сonsul. |
| EVENT_STATUS_CONSUL_AUTH_USER | string | нет | "" | Название учетной записи Сonsul. Если название не указано, то настройки авторизации не будут применены. |
| EVENT_STATUS_CONSUL_AUTH_PASSWORD | string | нет | "" | Пароль учетной записи Сonsul. |
| EVENT_STATUS_TRACE_DURATION | boolean | нет | false | Признак необходимости трассировки выполнения команд |
| EVENT_STATUS_DISCOVERABLE_ID | string | нет | "another_EVENT_STATUS_service_instance" | ID сервиса в ServiceDiscovery |
| EVENT_STATUS_DISCOVERABLE_NAME | string | нет | "eventStatus" | Имя сервиса в ServiceDiscovery |
| EVENT_STATUS_DISCOVERABLE_HOST | string | да | "localhost" | Хост, публикуемый в ServiceDiscovery. По нему на данный сервис будут обращаться другие через HTTP. Указанный адрес должен быть виден другим сервисам. Пример: имя kubernetes/docker_swarm service |
| EVENT_STATUS_DISCOVERABLE_PORT | int | нет | Порт, публикуемый в ServiceDiscovery. По нему на данный сервис будут обращаться другие через HTTP. По умолчанию указывается порт, который слушает HTTP-сервер. | |
| EVENT_STATUS_DISCOVERABLE_LIVETIME | duration string | нет | 2 minutes | Период после последней отправки health check, в течение которого ServiceDiscovery считает данный сервис живым. |
| EVENT_STATUS_DISCOVERABLE_HEALTHPASS | string | нет | 1 minute | Периодичность отправки health check в ServiceDiscovery |
| EVENT_STATUS_SERVICE_TITLE | string | нет | "Event Status" | Название сервиса для отображения |
| EVENT_STATUS_SERVICE_DESCRIPTION | string | нет | "Service EVENT_STATUS" | Описание сервиса для отображения |
| EVENT_STATUS_AKKA_HTTP_CLIENT_MAXCON | int | нет | 512 | Максимальное число одновременных исходящих HTTP-соединений |
| EVENT_STATUS_AKKA_HTTP_CLIENT_MAXREQ | int | нет | 1024 | Максимальное число одновременных исходящих HTTP-запросов |
| EVENT_STATUS_AKKA_HTTP_SERVER_MAXCON | int | нет | 1024 | Максимальное число одновременных входящих HTTP-соединений |
| EVENT_STATUS_INTERNALCMD_ALLOW | bool | нет | false | Можно ли сервису отправлять внутрисистемные команды |
| EVENT_STATUS_SENDERLIB_COMMANDS_CACHE_UPDATEPERIOD | duration string | нет | 10 minutes | Время кэширования данных по командам из CommandDiscovery |
| EVENT_STATUS_SENDERLIB_SERVICES_CACHE_UPDATEPERIOD | duration string | нет | 30 seconds | Время кэширования данных по сервисам из ServiceDiscovery |
| EVENT_STATUS_DB_HOST | string | да | Хост БД | |
| EVENT_STATUS_DB_PORT | int | да | Порт БД | |
| EVENT_STATUS_DB_NAME | string | да | Имя базы в БД | |
| EVENT_STATUS_DB_URL | jdbc url string | нет | JDBC-url для соединения с БД. По умолчанию собирается из других обязательных переменных. Можно указать только его, если не хочется отдельно указывать хост/порт/имя базы. | |
| EVENT_STATUS_DB_USER | string | да | Пользователь БД | |
| EVENT_STATUS_DB_PASSWORD | string | да | Пароль пользователя БД | |
| EVENT_STATUS_AUTHZFORCE_ADDR | string | нет | "http://localhost:8080/authzforce-ce" | Адрес AuthZforce server |
| EVENT_STATUS_AUTHZFORCE_DOMAIN | string | нет | "" | Доступный DomainID в AuthZforce server |
| EVENT_STATUS_LOG_LEVEL | string | нет | INFO | Общий уровень логирования в сервисе |
| EVENT_STATUS_LOG_LEVEL_AKKA | string | нет | INFO | Уровень логирования для akka |
| EVENT_STATUS_LOG_LEVEL_LIQUIBASE | string | нет | INFO | Уровень логирования для liquibase (миграции) |
| EVENT_STATUS_LOG_LEVEL_APPLICATION | string | нет | DEBUG | Уровень логирования для application |
| EVENT_STATUS_LOG_LEVEL_SLICK_STATEMENT | string | нет | DEBUG | Уровень логирования запросов, отправляемых slick в БД |
| EVENT_STATUS_LOG_LEVEL_SLICK_BENCHMARK | string | нет | OFF | Уровень логирование бенчмарков выполнения запросов slick |
| EVENT_STATUS_LOG_LEVEL_KAFKA_PRODUCER | string | нет | WARN | Уровень логирования конфига kafka-producer |
| EVENT_STATUS_LOG_LEVEL_KAFKA_CONSUMER | string | нет | WARN | Уровень логирования конфига kafka-consumer |
| EVENT_STATUS_LOG_LEVEL_HTTP_SERVER | string | нет | WARN | Уровень логирования HTTP-сервера |
| EVENT_STATUS_LOG_LEVEL_AKKAHTTPSENDER | string | нет | TRACE | Уровень логирования для отправки команд через HTTP. На уровне INFO логируется трассировка, если она включена |
| EVENT_STATUS_LOG_LEVEL_KAFKASENDER | string | нет | TRACE | Уровень логирования для отправки команд через Kafka. На уровне INFO логируется трассировка, если она включена |
| EVENT_STATUS_LOG_LEVEL_COMMANDSTATUSCLI | string | нет | TRACE | Уровень логирования для проверки состояний команд в сервисе статусов |
| EVENT_STATUS_EVENT_CACHE_MAX_SIZE | int | нет | 1000 | Максимальное количество событий в кэше |
| EVENT_STATUS_EVENT_CACHE_EXPIRE_AFTER_WRITE_SECONDS | int | нет | 60 | Время в секундах, после которого элементы убираются из кэша |
Список команд сервиса Event Status
Команды сервиса информации о событиях:
| Команда | EntityType | Actions |
|---|---|---|
| eventStatus_http_publishHandledEvent | - | - |
| eventStatus_longPool_waitForEvent | - | - |
| eventStatus_longPool_waitForMultipleEvents | - | - |
publishHandledEvent
На входе описание события
{
"eventType": "test",
"payloadType": "test",
"payloadId": "test",
"serviceName": "test",
"payloadVersion": 5
}
На выходе ничего
{
"status": "Completed",
"timestamp": 1683184345008,
"value": {}
}
Публикует информацию, что серивис завершил обработку события и разблокирует всех кто ждет данное событие.
Имя команды для вызова eventStatus_http_publishHandledEvent
Поддерживает только синхронный вызов.
- На входе: HandledEventDto
- На выходе: Unit
waitForEvent
На входе описание события и таймаут
{
"timeoutSeconds": 30,
"eventToWait": {
"eventType": "test",
"payloadType": "test",
"payloadId": "test",
"serviceName": "test",
"payloadVersion": 5
}
}
На выходе true, если событие уже обработано
{
"status": "Completed",
"timestamp": 1683184345008,
"value": true
}
Дожидается публикации информации о событии в сервисе.
Имя команды для вызова eventStatus_longPool_waitForEvent
Поддерживает только синхронный вызов. Работает через механизм Long-Pool
- На входе: WaitForEventDto
- На выходе: Boolean
waitForMultipleEvents
На входе описание событий и timeout
{
"timeoutSeconds": 5,
"eventsToWait": [{
"eventType": "test",
"payloadType": "test",
"payloadId": "test",
"serviceName": "test",
"payloadVersion": 5
}]
}
На выходе список Boolean, аналогично waitForEvent
{
"status": "Completed",
"timestamp": 1683184345008,
"value": [true]
}
Дожидается публикации информации о всех событиях в сервисе. По истечению timeout возвращает результат по каждому событию.
- На входе: [WaitForEventsDto]
- На выходе: List[Boolean]
Объекты сервиса EventStatus
HandledEventDto
Модель для уведомления, что serviceName обработал событие. Хранится в verdi-models.
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| eventType | String | да | Тип события |
| payloadType | String | да | Тип объекта в событии |
| payloadId | String | да | Id объекта в событии |
| serviceName | String | да | Имя сервиса, обработкал событие |
| payloadVersion | Long | нет | Версия объекта в событии |
WaitForEventDto
Модель для ожидания события. Хранится в verdi-models.
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| timeoutSeconds | Int | да | Максимальное ожидание в секундах |
| eventToWait | HandledEventDto | да | Событие, которого нужно дождаться |
WaitForEventsDto
Модель для ожидания события. Хранится в verdi-models.
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| timeoutSeconds | Int | да | Максимальное ожидание в секундах |
| eventsToWait | HandledEventDto | да | События, которых нужно дождаться |
FSClient: библиотека для работы с файловым хранилищем
Библиотека предоставляет интерфейс и реализацию клиента файлового хранилища.
Структуры FSClient
FSConfigRep (FSClient)
Отображение FSConfig в "плоской форме", как он выглядит в конфиге приложения application.conf.
| Поле | Тип | Обязательное | Описание | Ограничения |
|---|---|---|---|---|
| uri | String | Да | Адрес файлового хранилища для подключения. | Не пустое |
| uploadParallelism | Int | Да | Параллелизм загрузки файлов в файловое хранилище. | > 0 |
| accessKeyId | String | Да, если authMode=Static | ID для подключения (не зависит от бакета). | Не пустое |
| secretAccessKey | String | Да, если authMode=Static | Key для подключения (не зависит от бакета). | Не пустое |
| authMode | String | Нет | Режим аутентификации клиента: Static - данные для подключения не зависят от бакета, Mapping - данные зависят от бакета. По-умолчанию Static. | "Static", "Mapping" |
| authConfig | String | Да, если authMode=Mapping | Настройки для выбранного режима аутентификации. Если authMode=Mapping, то ожидается Seq[FSBucketConfig] в формате JSON. | |
| clientsCache | VerdiCacheConfig | Нет | Настройки кеша для клиентов файлового хранилища. |
FSConfig (FSClient)
Конфигурация клиента файлового хранилища.
| Поле | Тип | Обязательное | Описание | Ограничения |
|---|---|---|---|---|
| uri | String | Да | Адрес файлового хранилища для подключения | Не пустое |
| uploadParallelism | Int | Да | Параллелизм загрузки файлов в файловое хранилище | > 0 |
| credentials | Seq[FSBucketConfig] | Да | Список данных для учетных записей для подключения к бакетам | |
| clientsCache | VerdiCacheConfig | Да | Настройки кеша для клиентов файлового хранилища |
FSBucketConfig (FSClient)
Данные для аутентификации, которые привязаны к конкретному бакету.
| Поле | Тип | Обязательное | Описание | Ограничения |
|---|---|---|---|---|
| bucketName | String | Да | Название бакета. Если значение пустое, то данные подходят для любого бакета. | |
| bucketAlias | List[String] | Нет | Список альтернативных названий бакета. | Не пустое |
| credentials | FSCredentials | Да | Данные учетной записи для подключения к бакету. |
FSCredentials (FSClient)
Данные для аутентификации.
| Поле | Тип | Обязательное | Описание | Ограничения |
|---|---|---|---|---|
| accessKeyId | String | Да | ID для подключения | Не пустое |
| secretAccessKey | String | Да | Key для подключения | Не пустое |
JWT: библиотека для работы с JWT
Библиотека предоставляет вспомогательные методы для создания и работы с JWT.
TODO: описать JwtAuthenticatorPac4j, JwtPac4j, JwtClaim и как их использовать
Structs (jwt)
JwtInvalidationId (jwt)
Идентификатор для правила инвалидации JwtInvalidation. Может быть либо "global" - для всех пользователей, либо UserId - для конкретного пользователя. Соответствует типу String.
JwtInvalidation (jwt)
Параметры для хранилища сертификатов (Java key store)
| Поле | Тип | Обязательное | Описание | Ограничения |
|---|---|---|---|---|
| id | JwtInvalidationId | Да | Идентификатор правила инвалидации JWT | Либо "global" - для всех пользователей, либо UserId - для конкретного пользователя |
| condition | string | Да | Правило инвалидации JWT | Формат должен совпадать с JwtInvalidationCondition |
JwtInvalidationCondition (jwt)
Формат правила для инвалидации
- всех JWT выданных до определенного времени: "1700544874", где 1700544874 - это Unix timestamp (milliseconds)
License models: модели данных для работы с лицензией
Библиотека предоставляет вспомогательные методы для чтения файла лицензии и структуру с данными лицензии.
Модели данных (license-model)
LicenseDTO (license-model)
Данные выданной лицензии.
| Поле | Тип | Обязательное | Описание | Значение по умолчанию |
|---|---|---|---|---|
| kind | LicenseKind | Нет | Вид лицензии | "Prod" |
| expiredAt | TimeStamp | Нет | Время, когда лицензия истекает | 0 |
| globalSessionsLimit | Int | Нет | Глобальное ограничение на количество сессий: "-1" = "без ограничений" | -1 |
| sessionsLimitPerUser | Int | Нет | Ограничение на количество сессий для одного пользователя: "-1" = "без ограничений" | -1 |
| sessionWatchSec | Int | Нет | Время в секундах, в течении которого сессия учитывается в подсчете доступного лимита | 300 |
| allowedActions | List[String] | Нет | Действия, которые разрешены для выполнения под лицензией (другие - автоматически запрещены) | [] |
| privilegedEmails | List[UserEmail] | Нет | Список email-ов привилегированных пользователей | [] |
LicenseKind (license-model)
Вид лицензии. Соответствует типу String. Возможные значения: "Prod", "Dev".
Mon (門): сервис аутентификации
Сервис аутентификации отвечает за создание пользователей и вход пользователей в систему. Данные сервиса хранятся в PostgreSQL. Команды могут приходить только по HTTP.
Сервис разбит на несколько модулей, в виде sbt проектов:
domain, в котором содержатся все доменные модели и их различные представления.infrastructure, в котором содержатся базовые компоненты для взаимодействия с инфраструктурой.store, содержит сервисы для сохранения и чтения из разных хранилищ доменных моделей.service, содержит сервисы бизнес логики, которые определяют правила взаимодействия компонентов между собой.route, содержит HTTP роуты.boot, содержит зависимости и описание для сборки и запуска сервиса.
Особенности Kerberos authentication
После добавления Kerberos authentication, сервис стал зависеть от пакета com.sun.security.auth.module, которого
нет, например, в IBM JRE.
Поэтому рекомендуется использовать OpenJDK или Oracle.
Дополнительно: "the browser URL must match the fully-qualified-domain-name as specified in ServicePrincipal".
Локальный запуск сервиса Mon
При запуске сервиса ожидается, что уже развернута необходимая инфраструктура:
- PostgreSQL база по адресу
localhost:5432/mon_db - Kafka по адресу
localhost:9092 - Consul по адресу
localhost:8500 - AuthZforce server по адресу
http://localhost:8080/authzforce-ce - В Consul будут добавлены следующие ключи или они указаны в
application.conf:- адрес Kafka по ключу
kafka_bootstrap_server - адрес AuthZforce по ключу
authzforce_server_address- В рантайме, исходя из наличия адреса AuthZforce, будет попытка получить доступный DomainID для AuthZforce (
также его можно указать в
application.conf)
- В рантайме, исходя из наличия адреса AuthZforce, будет попытка получить доступный DomainID для AuthZforce (
также его можно указать в
- адрес Kafka по ключу
- Добавлены необходимые переменные окружения:
- MON_DB_HOST
- MON_DB_PORT
- MON_DB_NAME
- MON_DB_USER
- MON_DB_PASSWORD
Запуск Mon из консоли с помощью SBT
MON_DB_HOST=localhost MON_DB_PORT=5432 MON_DB_NAME=mon_db MON_DB_USER=postgres MON_DB_PASSWORD=12345 sbt boot/run
Список всех переменных окружения для сервиса Mon
| Переменная | Описание | Значение по-умолчанию |
|---|---|---|
| MON_HTTP_HOST | Хост для HttpListener | "0.0.0.0" |
| MON_HTTP_PORT | Порт для HttpListener | 8192 |
| MON_KAFKA_SERVERS | Адрес Kafka | "localhost:9092" |
| MON_KAFKA_TOPIC | Название топика для входящих команд | "MON_commands" |
| MON_KAFKA_PARTITIONS | Количество партиций в топике команд. | 10 |
| MON_KAFKA_COMMANDEVENT_TOPIC | Название топика для входящих/исходящих событий | "commandevents" |
| MON_KAFKA_CONSUMER_GROUP | Название группы для чтения топика событий | |
| MON_KAFKA_USEREVENT_TOPIC | Название топика для отправки событий о действиях с пользователями | userEvents |
| MON_KAFKA_PARTITIONS | Количество партиций в топике команд. | 10 |
| MON_KAFKA_CONSUMER_RESTART_MIN_BACKOFF | Минимальная задержка перед перезапуском. | 1 second |
| MON_KAFKA_CONSUMER_RESTART_MAX_BACKOFF | Максимально возможная задержка перед перезапуском. | 30 seconds |
| MON_KAFKA_CONSUMER_RESTART_RANDOM_FACTOR | Величина дополнительной случайной задержки: 0.0 - без случайно задержки, 1.0 - до 100% задержки. | 0.2 |
| MON_KAFKA_CONSUMER_RESTART_MAX_RESTARTS | Максимальное количество перезапусков в заданный период времени. | 5 |
| MON_KAFKA_CONSUMER_RESTART_MAX_RESTARTS_WITHIN | Период времени для ограничения перезапусков. | 5 minutes |
| MON_KAFKA_AUTH_USER | Название учетной записи Kafka. Если название не указано, то настройки авторизации не будут применены. | "" |
| MON_KAFKA_AUTH_PASSWORD | Пароль учетной записи Kafka. | "" |
| MON_KAFKA_AUTH_TRUSTSTORE_LOCATION | Путь до хранилища сертификатов (Java key store). Если путь не указан, то сертификат применяться не будет. | "" |
| MON_KAFKA_AUTH_TRUSTSTORE_PASSWORD | Пароль к хранилищу сертификатов. | "" |
| MON_KAFKA_AUTH_MODE | Режим аутентификации: static - одна учетная запись на все запросы, mapping - учетная запись зависит от запроса | "static" |
| MON_KAFKA_AUTH_CONFIG | Настройки для аутентификации: если AuthMode = mapping, то необходим JSON с List[KafkaAuthConfig]. Переменная соответствует полю authConfig из KafkaAuthSettings. | "" |
| MON_KAFKA_AUTH_CACHE_SIZE | Максимальный размер кеша для Kafka producer (количество активных соединений). | |
| MON_KAFKA_AUTH_CACHE_TTL | Время жизни Kafka producer в кеше. | |
| MON_CONSUL_ADDR | Адрес Сonsul. | "http://localhost:8500" |
| MON_CONSUL_AUTH_USER | Название учетной записи Сonsul. Если название не указано, то настройки авторизации не будут применены. | "" |
| MON_CONSUL_AUTH_PASSWORD | Пароль учетной записи Сonsul. | "" |
| MON_AUTHORIZER_KIND | Вид авторизатора, который используется в сервисе. Допустимые значения: "authzforce", "oberto". При указании неизвестного значения будет использовано значение по-умолчанию. | "authzforce" |
| MON_AUTHZFORCE_ADDR | Адрес AuthZforce server | "http://localhost:8080/authzforce-ce" |
| MON_AUTHZFORCE_DOMAIN | Доступный DomainID в AuthZforce server | |
| MON_TRACE_DURATION | Нужно ли логировать длительность выполнения команд | false |
| MON_USERGROUP_DEFAULT | Список групп пользователей, в которые необходимо добавлять новых пользователей (группы по-умолчанию). Список групп задается в виде одной строки, в которой пары GroupId и названия записываются так "id1,name1;id2,name2". Пустая строка = "никакие группы добавлять не нужно". | "" т.е. пустая строка |
| MON_JWT_SIGNATURE | "my very secret string for signature" | |
| MON_JWT_LIFETIME | Время действия токена JWT | "12 hours" |
| MON_DISCOVERABLE_ID | ID данного сервиса | "another_mon_instance" |
| MON_DISCOVERABLE_NAME | Название группы сервисов к которой принадлежит данный | "mon" |
| MON_DISCOVERABLE_HOST | Адрес текущего инстанса сервиса, который будет виден через ServiceDiscovery | "localhost" |
| MON_DISCOVERABLE_PORT | Адрес текущего инстанса сервиса, который будет виден через ServiceDiscovery | {MON_HTTP_PORT} |
| MON_DISCOVERABLE_LIVETIME | Время с последнего HealthCheck, в течении которого сервис считается "живым" | "2 minutes" |
| MON_DISCOVERABLE_HEALTHPASS | Периодичность отправки HealthCheck | "1 minute" |
| MON_INTERNALCMD_ALLOW | Возможно ли сервису отправлять "внутренние" команды | false |
| MON_SENDERLIB_COMMANDS_CACHE_UPDATEPERIOD | Время на которое будут кешироваться данные о командах | "10 minutes" |
| MON_SENDERLIB_SERVICES_CACHE_UPDATEPERIOD | Время на которое будут кешироваться данные о сервисах | "30 seconds" |
| MON_DB_HOST | Хост сервера базы данных | |
| MON_DB_PORT | Порт сервера базы данных | |
| MON_DB_NAME | Название базы | |
| MON_DB_USER | Пользователь для базы данных | |
| MON_DB_PASSWORD | Пароль для пользователь для базы данных | |
| MON_DB_THREADS | Количество потоков в пуле потоков для соединения с БД | 10 |
| MON_DB_QUEUE_SIZE | Размер очереди для действий базы данных, которые не могут быть выполнены немедленно, когда все потоки заняты. За пределами этого значения новые действия немедленно завершаются неудачей | 300 |
| MON_DB_CONN_MAX | Максимальное количество одновременных подключений к БД | 10 |
| MON_DB_CONN_TIMEOUT | Максимальное время ожидания ответа для соединения к БД. Если это время превышено, а соединение не становится доступным, будет брошено исключение SQLException. 1000 мс — минимальное значение. | 20 second |
| MON_DB_ISOLATION | Уровень изоляции транзакций для новых подключений. Допустимые значения: NONE, READ_COMMITTED, READ_UNCOMMITTED, REPEATABLE_READ, SERIALIZABLE. |
"READ_COMMITTED" |
| MON_DB_READONLY | Read-only SQL транзакция может изменять только временные таблицы. Этот параметр управляет статусом «только для чтения» по умолчанию для каждой новой транзакции. | false |
| MON_DB_CONN_MIN | Минимальное количество одновременных подключений к БД | = DB_THREADS |
| MON_DB_VALIDATION_TIMEOUT | Максимальное время, в течение которого соединение будет проверяться на работоспособность. 1000 мс — минимальное значение. | 1 seconds |
| MON_DB_IDLE_TIMEOUT | Максимальное время, в течение которого соединению разрешено простаивать в пуле. Значение 0 означает, что простаивающие соединения никогда не удаляются из пула. | 10 minutes |
| MON_DB_MAX_LIFETIME | Максимальное время жизни соединения в пуле. Когда простаивающее соединение достигает этого времени ожидания, даже если оно недавно использовалось, оно будет удалено из пула. Значение 0 указывает на отсутствие максимального срока службы. | 30 minutes |
| MON_DB_INITIALIZATION_FAIL_FAST | Определяет, будет ли пул «быстро выходить из строя», если пул не может быть успешно заполнен начальными соединениями. Если соединения не могут быть созданы во время запуска пула, будет выдано исключение RuntimeException. Это свойство не имеет никакого эффекта, если minConnections равно 0. | false |
| MON_DB_LEAK_DETECTION_THRESHOLD | Время, в течение которого соединение может находиться вне пула, прежде чем будет зарегистрировано сообщение, указывающее на возможную утечку соединения. Значение 0 означает, что обнаружение утечек отключено. Наименьшее приемлемое значение для включения обнаружения утечек составляет 10 с. | 0 |
| MON_DB_CONNECTION_TEST_QUERY | Выражение, которое будет выполнено непосредственно перед получением соединения из пула для проверки того, что соединение с базой данных все еще активно. Оно зависит от базы данных и должно представлять собой запрос, требующий минимальной обработки базой данных (например, «VALUES 1»). Если этот параметр не установлен, вместо него используется метод JDBC4 Connection.isValid(). |
"SELECT 1" |
| MON_DB_REGISTER_MBEANS | Зарегистрированы ли JMX Management Beans («MBeans») | false |
| MON_READABLE_NAME | Читаемое название этого сервиса | "Сервис пользователей" |
| MON_DESCRIPTION | Читаемое описание этого сервиса | "Предназначен для определения схемы и хранения объектов" |
| MON_AUTHN_EXTERNAL_SESSION_LIFETIME | Продолжительность внешней сессии (например, в случае сессии, хранящей данные о процессе OpenId Connect аутентификации - время, за которое пользователь должен завершить вход после получения ссылки на форму входа) | "10 minutes" |
| MON_KRB_SVC_PRINCIPAL | ID сервера в ActiveDirectory (если значение отсутствует/пустое, то Kerberos authentication не будет работать) | "" |
| MON_KRB_SVC_KEYTAB | Путь к keytab файлу для MON_KRB_SVC_PRINCIPAL с настройками доступа к ActiveDirectory (если значение отсутствует/пустое, то Kerberos authentication не будет работать) | "" |
| MON_KRB_CALLBACK_URL | Callback URL для редиректа после Kerberos authentication (в версии <= 1.6 не используется?) (если значение отсутствует/пустое, то Kerberos authentication не будет работать) | "" |
| MON_KRB_DEBUG | Debug логгирование Kerberos authentication в сторонней библиотеке. | false |
| MON_KRB_LDAP_HOST | Хост ActiveDirectory (LDAP протокол) (если значение отсутствует/пустое, то Kerberos authentication не будет работать). Пример: "cloud.dev.embedika.ru". |
"localhost" |
| MON_KRB_LDAP_PORT | Порт ActiveDirectory (LDAP протокол) (если значение отсутствует/некорректное (port < 1), то Kerberos authentication не будет работать). | 389 |
| MON_KRB_LDAP_BIND_USER | Имя пользователя для привязки всех запросов в ActiveDirectory (если значение отсутствует/пустое, то Kerberos authentication не будет работать). Пример: "cn=Cursor Dev,cn=Users,dc=dev,dc=embedika,dc=ru". |
"" |
| MON_KRB_LDAP_BIND_CREDS | Пароль для MON_KRB_LDAP_BIND_USER (если значение отсутствует/пустое, то Kerberos authentication не будет работать). | "" |
| MON_KRB_LDAP_SERVER_BASE_DN | Базовый DN в ActiveDirectory для всех объектов (если значение отсутствует/пустое, то Kerberos authentication не будет работать). Пример: "dc=dev,dc=embedika,rc=ru". |
"" |
| MON_KRB_LDAP_CONNPOOL_SIZE_MIN | Минимальный размер пула подключений к ActiveDirectory | 2 |
| MON_KRB_LDAP_CONNPOOL_SIZE_MAX | Максимальный размер пула подключений к ActiveDirectory | 128 |
| MON_KRB_LDAP_CONNPOOL_TIMEOUT_WAIT | Максимальное время ожидания свободного подключения в пуле | "10 seconds" |
| MON_KRB_LDAP_CONNPOOL_TIMEOUT_CONN | Максимальное время ожидания подключения к ActiveDirectory | "500 millis" |
| MON_KRB_LDAP_CONNPOOL_TIMEOUT_RESP | Максимальное время ожидания ответа от ActiveDirectory | "1 seconds" |
| MON_KRB_LDAP_USER_FIRSTNAME | Настройки получения атрибута из профиля в ActiveDirectory для UserInfo.firstName. Строка в формате AttributeMapping. |
"["givenName", "cn;1; ;3", "displayName;1; ;3"]" |
| MON_KRB_LDAP_USER_LASTNAME | Настройки получения атрибута из профиля в ActiveDirectory для UserInfo.lastName. Строка в формате AttributeMapping. |
"["sn", "cn;0; ;3", "displayName;0; ;3"]" |
| MON_KRB_LDAP_USER_MIDDLENAME | Настройки получения атрибута из профиля в ActiveDirectory для UserInfo.middleName. Строка в формате AttributeMapping. |
"["middleName", "cn;2; ;3", "displayName;2; ;3"]" |
| MON_KRB_LDAP_USER_EMAIL | Настройки получения атрибута из профиля в ActiveDirectory для User.email. Строка в формате AttributeMapping. |
"["mail", "emailaddress"]" |
| MON_KRB_GROUPS_MAPPING | Конвертация групп из Active Directory в группы Mon. Формат "ADName1=MonName1;ADName2=MonName2". Подробнее в AD groups mapping | "" |
| MON_KRB_GROUPS_NOT_REMOVE | Группы Mon, из которых не нужно удалять пользователя, если они больше не приходят из Active Directory. Формат "MonName1;MonName2". | "" |
| MON_KEYCLOAK_CLIENT_ID | ID клиента в Keycloak (если значение отсутствует, то Keycloak authentication не будет работать) | "" |
| MON_KEYCLOAK_CLIENT_SECRET | Secret клиента в Keycloak (если значение отсутствует, то Keycloak authentication не будет работать) | "" |
| MON_KEYCLOAK_REALM | Используемый Realm в Keycloak (если значение отсутствует, то Keycloak authentication не будет работать) | "" |
| MON_KEYCLOAK_BASE_URI | URI Keycloak (если значение отсутствует, то Keycloak authentication не будет работать). Пример: "http://keycloak:8080" | "" |
| MON_KEYCLOAK_CALLBACK_URL | Callback URL для редиректа после входа по Keycloak (если значение отсутствует, то Keycloak authentication не будет работать). Пример: "http://cursor.dev/keycloak-callback" | "" |
| MON_KEYCLOAK_PKCE_METHOD | Используемый Challenge Method для PKCE (Proof Key for Code Exchange). Доступные варианты: "plain" и "S256". (если значение отсутствует, то Keycloak authentication не будет работать) | "S256" |
| MON_KEYCLOAK_GROUPS_MAPPING | Конвертация ролей из Keycloak в группы Mon. Формат "KCName1=MonName1;KCName2=MonName2". | "" |
| MON_KEYCLOAK_GROUPS_NOT_REMOVE | Группы Mon, из которых не нужно удалять пользователя, если соответствующие роли больше не приходят из Keycloak. Формат "MonName1;MonName2". | "" |
| MON_KEYCLOAK_USER_FIRSTNAME | Настройки получения атрибута из профиля в Keycloak для UserInfo.firstName. Строка в формате AttributeMapping. |
"["givenName", "given_name;2; ;3"]" |
| MON_KEYCLOAK_USER_LASTNAME | Настройки получения атрибута из профиля в Keycloak для UserInfo.lastName. Строка в формате AttributeMapping. |
"["family_name"]" |
| MON_KEYCLOAK_USER_MIDDLENAME | Настройки получения атрибута из профиля в Keycloak для UserInfo.middleName. Строка в формате AttributeMapping. |
"["middleName", "middle_name", "given_name;3; ;3"]" |
| MON_KEYCLOAK_USER_EMAIL | Настройки получения атрибута из профиля в Keycloak для User.email. Строка в формате AttributeMapping. |
"["email", "mail", "emailaddress"]" |
| MON_LOG_LEVEL_AUTH | Уровень логирования операций во время аутентификации. | "INFO" |
| MON_LOG_LEVEL_AUTH_CONTEXT | Уровень логирования доступа к контексту аутентификации. Работает только в DEBUG. | "INFO" |
| MON_LOG_OUTPUT | Параметры вывода логов: STDOUT - обычный лог в консоль, STDOUT_CEF - лог в формате CEF в консоль, FILE - обычный лог в файл, FILE_CEF - лог в формате CEF в файл. Может принимать несколько значений через любой разделитель (например, "STDOUT FILE_CEF"). Преобразования, если указано несколько значений: присутствуют "STDOUT" + "STDOUT_CEF" = учитывается только "STDOUT_CEF"; "FILE" + "FILE_CEF" = "FILE_CEF"; |
"STDOUT" |
| MON_LOGGING_SRC_IP | Параметр SRC (источник/source, на который ссылается событие) для логов в формате CEF. Если не установлена, src=notFound. Требуемый формат: IPv4, например "192.168.10.1". |
|
| MON_LOGGING_SRC_HOST | Параметр SHOST (источник/source, на который ссылается событие) для логов в формате CEF. Если не установлена, shost=notFound. Требуемый формат: fully qualified domain name (FQDN), например "host" или "host.domain.com". |
|
| MON_LOGGING_DST_IP | Параметр DST (получатель/destination, на который ссылается событие) для логов в формате CEF. Если не установлена, dst=notFound. Требуемый формат: IPv4, например "192.168.10.1". |
|
| MON_LOGGING_CEF_VER | Версия формата CEF: либо 0, либо 1. Рекомендуется использовать 0. |
0 |
| MON_SENDERLIB_COMMANDS_HTTP_RETRY_ATTEMPTS | Поле attempts из RetrySettings | 5 |
| MON_SENDERLIB_COMMANDS_HTTP_RETRY_DELAY | Поле delay из RetrySettings | "5 seconds" |
| MON_SENDERLIB_COMMANDS_HTTP_RETRY_KIND | CommandResultRetryConditionKind | "OnSomeExceptions(ConnectException)" |
Формат CEF для логов сервиса Mon
У логов есть возможность включить формат CEF для логирования по следующему шаблону:
2022-12-14T16:56:31+0500 CEF:Version|Device Vendor|Device Product|Device Version|EventClassId|Message|Severity|src=? dst=? shost=? suid=? suser=? msg=? end=currentTimeMillis|.
Чтобы включить логирование в формате CEF, нужно задать переменную MON_LOG_OUTPUT = STDOUT_CEF. Если логи пишутся в формате CEF, то необходимо задать следующие переменные, которые по умолчанию не заданы:
MON_LOGGING_SRC_IP, для параметраsrcв логахMON_LOGGING_SRC_HOST, для параметраshostв логахMON_LOGGING_DST_IP, для параметраdstв логах
В файле boot/src/main/resources/logback.xml можно поменять class path для основного класса приложения (переменная projectMainClassPath), если это необходимо.
Список команд сервиса Mon
В описании команд используется путь/route для отправки команды в сам сервис, а не в ApiGateway. В качестве Input-а для команд, сервис всегда ожидает CommandRequest (как и любой другой сервис, принимающий команды), так что в описании команды указано лишь описание поля payload для CommandRequest. Все команды в сервисе - "синхронные".
В сервисе реализованы следующие команды:
- Регистрация нового пользователя
- Подтверждение регистрации пользователя
- Вход пользователя в систему (Логин-пароль)
- Вход пользователя в систему (Kerberos)
- Генерация ссылки на форму входа Keycloak
- Вход пользователя в систему (Keycloak)
- Выход пользователя из системы
- Список всех пользователей
- Данные текущего пользователя
- Данные пользователя для контекста команды
- Создать группу пользователей
- Получить группу пользователей по ID
- Отредактировать группу пользователей
- Удалить группу пользователей
- Список всех групп пользователей
- Список групп в которых состоит пользователь
- Список пользователей которые состоят в группе
- Добавить пользователей в группу
- Удалить пользователей из группы
- Завершить все сессии (выданные JWT) для 1 пользователя
- Удалить правило инвалидации JWT
Информация по добавлению команд можно прочитать в описании шаблона
| Название команды | EntityType | Actions | UIAction |
|---|---|---|---|
| mon_http_LoginByCredentials | - | - | - |
| mon_http_LoginByKerberos | - | - | - |
| mon_http_GetKeycloakAuthUrl | - | - | - |
| mon_http_LoginByKeycloak | - | - | - |
| mon_http_Logout | - | - | - |
| mon_http_RegisterUser | - | - | - |
| mon_http_VerifyUserRegistration | - | - | - |
| mon_http_ChangeUserStatus | - | - | - |
| mon_http_ChangeUserPassword | - | - | - |
| mon_http_CloseAllUserSessions | User | InvalidateSession | - |
| mon_http_DeleteJwtInvalidation | User | InvalidateSession | - |
| mon_http_ListUsers | - | - | - |
| mon_http_ListUsersWithGroupsCount | - | - | - |
| mon_http_ListUsersByGroupId | - | - | - |
| mon_http_ListGroups | - | - | - |
| mon_http_ListGroupsByUserId | - | - | - |
| mon_http_ListGroupsByUserIds | - | - | - |
| mon_http_GetGroup | - | - | - |
| mon_http_CreateGroup | - | - | - |
| mon_http_UpdateGroup | - | - | - |
| mon_http_DeleteGroup | - | - | - |
| mon_http_AddUsersToGroup | - | - | - |
| mon_http_RemoveUsersFromGroup | - | - | - |
RegisterUser (mon)
Payload для команды
{
"email": "emailname@mail.io",
"password": "strongpassword",
"info": {
"firstName": "John",
"lastName": "Week",
"middleName": "Jovonovich"
}
}
Результат выполнения команды
{
"userId": "a133f0c8-a516-4537-8f3f-915acb335e2c",
"tokenId": "e5cdd1e3-84ef-4d85-98d4-b56bac91093d",
"tokenKind": "VerifyRegistration",
"tokenValue": "e1b53859-0a73-4ab1-bd04-9a23dbcced07"
}
Добавляет нового пользователя со статусом "Pending". Уникальность пользователя определяется по полю Email. Отправляет событие о регистрации нового пользователя. Поддерживается только синхронный вызов.
- Payload: UserRegistrationDto
- Result: UserRegistrationResult
| Команда | Путь |
|---|---|
| mon_http_RegisterUser | HTTP POST "/v1/user/register" |
VerifyUserRegistration (mon)
Payload для команды
"e5cdd1e3-84ef-4d85-98d4-b56bac91093d"
Результат выполнения команды
{
"id": "a133f0c8-a516-4537-8f3f-915acb335e2c",
"email": "emailname@mail.io",
"status": "Activated",
"info": {
"firstName": "John",
"lastName": "Week",
"middleName": "Jovonovich"
}
}
Подтверждает регистрацию нового пользователя и изменяет его статус "Pending" => "Activated". Отправляет событие о подтверждении нового пользователя. Поддерживается только синхронный вызов.
- Payload: ActionTokenId
- Result: User
| Команда | Путь |
|---|---|
| mon_http_VerifyUserRegistration | HTTP POST "/v1/user/verify" |
LoginByCredentials (mon)
Payload для команды
{
"email": "emailname@mail.io",
"password": "strongpassword"
}
Результат выполнения команды (Header ответа)
Set-Cookie: jwt_token=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJTb21lKDRiZTgxOGFlLTUwNTMtNDM2Zi1hZTEzLWE3NDY5MDgwMDhkYykiLCJleHAiOjE2MjkzMDIyMzUsInVzZXJJZCI6IjRiZTgxOGFlLTUwNTMtNDM2Zi1hZTEzLWE3NDY5MDgwMDhkYyJ9.XxPMZUtIez9pG2zfwrIxBofQN5b2dQ2p3Q4XPPCLB-E; Expires=Wed, 18 Aug 2021 15:57:15 GMT; Path=/; HttpOnly
Осуществляет вход пользователя в систему по логину и паролю. Поддерживается только синхронный вызов.
- Payload: LoginCredentials
- Result: HTTP Header "Set-Cookie:jwt_token=???; Expires=???; Path=/; HttpOnly"
| Команда | Путь |
|---|---|
| mon_http_LoginByCredentials | HTTP POST "/v1/login" |
LoginByKerberos (mon)
Payload для команды (Header запроса, base64)
Authorization: Negotiate YIIB1wYJKoZIhvcSAQICAQBuggHGMIIBwqADAgEFoQMCAQ6iBwMFAAAAAACjge5hgeswge
Результат выполнения команды (Header ответа, base64)
WWW-Authenticate: Negotiate ???, где ??? может быть или токен в base64 или пустая строка (т.е. значение Header будет просто "Negotiate")
Set-Cookie: jwt_token=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJTb21lKDRiZTgxOGFlLTUwNTMtNDM2Zi1hZTEzLWE3NDY5MDgwMDhkYykiLCJleHAiOjE2MjkzMDIyMzUsInVzZXJJZCI6IjRiZTgxOGFlLTUwNTMtNDM2Zi1hZTEzLWE3NDY5MDgwMDhkYyJ9.XxPMZUtIez9pG2zfwrIxBofQN5b2dQ2p3Q4XPPCLB-E; Expires=Wed, 18 Aug 2021 15:57:15 GMT; Path=/; HttpOnly
Осуществляет вход пользователя в систему на основе профиля пользователя из Active Directory.
Если пользователь с указанным профилем не зарегистрирован,
то происходит процесс регистрации БЕЗ верификации (статус пользователя будет сразу Activated).
Результат выполнения команды журналируется.
Поддерживается только синхронный вызов.
- Payload: HTTP Header "Authorization: Negotiate ???"
- Result: HTTP Header "Set-Cookie: jwt_token=???; Expires=???; Path=/; HttpOnly"
| Команда | Путь |
|---|---|
| mon_http_LoginByKerberos | HTTP POST "/v1/loginKerberos" |
GetKeycloakAuthUrl (mon)
Payload для команды
null
Результат выполнения команды (тело ответа)
"http://keycloak.url/realms/verdi/protocol/openid-connect/auth?scope=openid+profile+email&response_type=code&redirect_uri=http%3A%2F%2Fverdi.dev%2Fkeycloak-callback"
Результат выполнения команды (Header ответа)
Set-Cookie: external_session_id=802bf1d6-366c-420e-9196-5645ce9953bb; Max-Age=600; Path=/; HttpOnly
Генерирует и возвращает ссылку на форму входа в Keycloak, а также устанавливает коротко живущую Cookie, необходимую для последующей аутентификации в Keycloak.
Поддерживается только синхронный вызов.
- Payload: -
- Result: String (URL на форму входа)
| Команда | Путь |
|---|---|
| mon_http_GetKeycloakAuthUrl | HTTP POST "/v1/keycloakAuthUrl" |
LoginByKeycloak (mon)
Payload для команды
{
"queryParameters": {
"client_name": "KeycloakOidcClient",
"session_state": "914b01b8-19ca-42f4-af11-96fa49da0caa",
"code": "b164638b-05a8-413d-aa86-1442a7f10252.35216f68-c210-447e-ade4-d0a7035254d6.0974025f-9aa6-4daf-b755-80ba35c64576",
"state": "ce901f20522"
}
}
Результат выполнения команды (Header ответа)
Set-Cookie: jwt_token=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJTb21lKDRiZTgxOGFlLTUwNTMtNDM2Zi1hZTEzLWE3NDY5MDgwMDhkYykiLCJleHAiOjE2MjkzMDIyMzUsInVzZXJJZCI6IjRiZTgxOGFlLTUwNTMtNDM2Zi1hZTEzLWE3NDY5MDgwMDhkYyJ9.XxPMZUtIez9pG2zfwrIxBofQN5b2dQ2p3Q4XPPCLB-E; Expires=Wed, 18 Aug 2021 15:57:15 GMT; Path=/; HttpOnly
Осуществляет вход пользователя в систему на основе профиля в Keycloak.
Если пользователь с указанным профилем не зарегистрирован, то происходит процесс регистрации БЕЗ верификации (статус пользователя будет сразу Activated).
Результат выполнения команды журналируется.
Поддерживается только синхронный вызов.
- Payload: HTTP Header "Cookie: external_session_id=???"
- Payload: QueryParameters
- Result: HTTP Header "Set-Cookie: jwt_token=???; Expires=???; Path=/; HttpOnly"
| Команда | Путь |
|---|---|
| mon_http_LoginByKeycloak | HTTP POST "/v1/loginKeycloak" |
Logout (mon)
Payload для команды
null
Результат выполнения команды (Header ответа)
Set-Cookie:jwt_token=deleted; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Path=/; HttpOnly
Осуществляет выход текущего пользователя из системы. Поддерживается только синхронный вызов.
- Payload: - (данные берутся из CommandRequest)
- Result: HTTP Header "Set-Cookie:jwt_token=deleted; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Path=/; HttpOnly"
| Команда | Путь |
|---|---|
| mon_http_Logout | HTTP POST "/v1/logout" |
ChangeUserStatus (mon)
Payload для команды
{
"userId": "a133f0c8-a516-4537-8f3f-915acb335e2c",
"status": "Deactivated"
}
Результат выполнения команды
{
"id": "a133f0c8-a516-4537-8f3f-915acb335e2c",
"email": "emailname@mail.io",
"status": "Deactivated",
"info": {
"firstName": "John",
"lastName": "Week",
"middleName": "Jovonovich"
}
}
Изменяет статус пользователя на тот, что указан во входных данных. Возможны любые переходы.
Результат выполнения команды журналируется.
Поддерживается только синхронный вызов.
- Payload: UserIdWithStatus
- Result: User
| Команда | Путь |
|---|---|
| mon_http_ChangeUserStatus | HTTP POST "/v1/user/changeStatus" |
ChangeUserPassword (mon)
Payload для команды
{
"oldPassword": "pass123",
"newPassword": "new_Pa$$w0rd"
}
Результат выполнения команды
true
Изменяет пароль пользователя со "старого" на "новый", что указан во входных данных. "Старый" пароль должен совпадать с текущим паролем пользователя. В ином случае, вовращается ошибка неверного пользователя/пароля. Поддерживается только синхронный вызов.
- Payload: ChangePasswordDto
- Result: Boolean
| Команда | Путь |
|---|---|
| mon_http_ChangeUserPassword | HTTP POST "/v1/user/changePassword" |
ListUsers (mon)
Payload для команды
{
"sorting": {
"fieldName": "lastname",
"order": "asc"
},
"paging": {
"page": 1,
"count": 20
},
"query": "firstname",
"context": {
"firstname": "John"
}
}
Результат выполнения команды
{
"total": 1,
"items": [
{
"id": "a133f0c8-a516-4537-8f3f-915acb335e2c",
"email": "emailname@mail.io",
"status": "Activated",
"info": {
"firstName": "John",
"lastName": "Week",
"middleName": "Jovonovich"
}
}
]
}
Возвращает список всех существующих пользователей. Поддерживается только синхронный вызов.
| Команда | Путь |
|---|---|
| mon_http_ListUsers | HTTP POST "/v1/user/list" |
Доступные поля для фильтрации и виды фильтров по ним:
| Поле | Виды фильтров |
|---|---|
| userId | InSetQuery |
| status | InSetQuery |
| InSetQuery, LikeQuery | |
| lastName | InSetQuery, LikeQuery |
| firstName | InSetQuery, LikeQuery |
| middleName | InSetQuery, LikeQuery |
| fio | TsQuery |
Доступные поля для сортировки:
| Поле |
|---|
| userId |
| status |
| lastName |
| firstName |
| middleName |
MyData (mon)
Payload для команды
null
Результат выполнения команды
{
"id": "a133f0c8-a516-4537-8f3f-915acb335e2c",
"email": "emailname@mail.io",
"status": "Activated",
"info": {
"firstName": "John",
"lastName": "Week",
"middleName": "Jovonovich"
}
}
Возвращает информацию о текущем пользователе. Поддерживается только синхронный вызов.
- Payload: -
- Result: User
| Команда | Путь |
|---|---|
| mon_http_MyData | HTTP POST "v1/user/mydata" |
UserCommandContextData (mon)
Payload для команды
null
Результат выполнения команды
{
"userContext": {
"id": "a133f0c8-a516-4537-8f3f-915acb335e2c",
"emailOpt": "emailname@mail.io",
"groupIds": [
"first_group_id",
"second_group_id",
"third_group_id"
]
},
"jwtInvalidation": [
{
"id": "d07c0cbc-93ee-4d5c-b9af-21b1aaf92a40",
"condition": "1700545830"
}
]
}
Возвращает информацию о пользователе для контекста запроса команды CommandRequest. Поддерживается только синхронный вызов.
- Payload: -
- Result: UserContextWithInvalidations
| Команда | Путь |
|---|---|
| mon_http_UserCommandContextData | HTTP POST "v1/user/contextdata" |
CloseAllUserSessions (mon)
Payload для команды
"a133f0c8-a516-4537-8f3f-915acb335e2c"
Результат выполнения команды
true
Завершает все активные сессии выбранного пользователя. Завершение заключается в
добавлении правила для инвалидации JWT с UserId.
Результат выполнения команды журналируется.
Поддерживается только синхронный вызов.
- Payload: UserId
- Result: Boolean
| Команда | Путь |
|---|---|
| mon_http_CloseAllUserSessions | HTTP POST "/v1/user/closeAllSessions" |
DeleteJwtInvalidation (mon)
Payload для команды
"a133f0c8-a516-4537-8f3f-915acb335e2c"
Результат выполнения команды
1
Удаляет из БД правило для инвалидации JWT с указанным идентификатором.
Результат выполнения команды журналируется.
Поддерживается только синхронный вызов.
- Payload: JwtInvalidationId
- Result: Int
| Команда | Путь |
|---|---|
| mon_http_DeleteJwtInvalidation | HTTP POST "/v1/user/deleteJwtInvalidation" |
CreateGroup (mon)
Payload для команды
{
"id": "id_of_my_first_group",
"name": "Название группы на руском языке",
"description": "Описание группы"
}
Результат выполнения команды
{
"id": "id_of_my_first_group",
"name": "Название группы на руском языке",
"modified": 1631707774,
"description": "Описание группы"
}
Добавляет новую группу пользователей с заданными параметрами. Отправляет событие о создании группы. Поддерживается только синхронный вызов.
- Payload: UpdateGroupDTO
- Result: GroupDto
| Команда | Путь |
|---|---|
| mon_http_CreateGroup | HTTP POST "/v1/group/create" |
GetGroup (mon)
Payload для команды
"id_of_my_first_group"
Результат выполнения команды
{
"id": "id_of_my_first_group",
"name": "Название группы на руском языке",
"modified": 1631707774,
"description": "Описание группы"
}
Поиск группы пользователей с заданным идентифкатором. Поддерживается только синхронный вызов.
| Команда | Путь |
|---|---|
| mon_http_GetGroup | HTTP POST "/v1/group/single" |
UpdateGroup (mon)
Payload для команды
{
"id": "id_of_my_first_group",
"name": "Новое название группы на каком-то языке",
"description": "Измененное описание группы"
}
Результат выполнения команды
{
"id": "id_of_my_first_group",
"name": "Новое название группы на каком-то языке",
"modified": 1631707774,
"description": "Измененное описание группы"
}
Обновляет данные о группе пользователей. Отправляет событие об обновлении группы. Поддерживается только синхронный вызов.
- Payload: UpdateGroupDTO
- Result: GroupDto
| Команда | Путь |
|---|---|
| mon_http_UpdateGroup | HTTP POST "/v1/group/update" |
DeleteGroup (mon)
Payload для команды
"id_of_my_first_group"
Результат выполнения команды
true
Удаляет группу пользователей с заданным идентифкатором. Отправляет событие об удалении группы. Поддерживается только синхронный вызов.
- Payload: GroupId
- Result: Boolean
| Команда | Путь |
|---|---|
| mon_http_DeleteGroup | HTTP POST "/v1/group/delete" |
ListGroups (mon)
Payload для команды
{
"sorting": {
"fieldName": "name",
"order": "asc"
},
"paging": {
"page": 1,
"count": 20
},
"query": "modified",
"context": {
"modified": "1631707774"
}
}
Результат выполнения команды
{
"total": 2,
"items": [
{
"group": {
"id": "id_of_my_first_group",
"name": "Новое название группы на каком-то языке",
"modified": 1631707774,
"description": "Измененное описание группы"
},
"usersCount": 2
},
{
"group": {
"id": "anotherGroup",
"name": "Название группы",
"modified": 1631707774,
"description": null
},
"usersCount": 0
}
]
}
Возвращает список всех групп пользователей. Поддерживается только синхронный вызов.
| Команда | Путь |
|---|---|
| mon_http_ListGroups | HTTP POST "/v1/group/list" |
Доступные поля для фильтрации и виды фильтров по ним:
| Поле | Виды фильтров |
|---|---|
| groupId | InSetQuery |
| name | InSetQuery, LikeQuery |
| description | InSetQuery, LikeQuery |
| modified | InSetQuery, LikeQuery, RangeQuery |
Доступные поля для сортировки:
| Поле |
|---|
| groupId |
| name |
| description |
| modified |
ListGroupsByUserId (mon)
Payload для команды
{
"userId": "a133f0c8-a516-4537-8f3f-915acb335e2c",
"options": {
"sorting": {
"fieldName": "name",
"order": "asc"
},
"paging": {
"page": 1,
"count": 20
},
"query": "",
"context": {}
}
}
Результат выполнения команды
{
"total": 1,
"items": [
{
"group": {
"id": "id_of_my_first_group",
"name": "Новое название группы на каком-то языке",
"modified": 1631707774,
"description": "Измененное описание группы"
},
"usersCount": 2
}
]
}
Возвращает список всех групп пользователей, в которых состоит указанный пользователь. Поддерживается только синхронный вызов.
- Payload: UserIdWithOptions
- Result: Page[GroupInfo]
| Команда | Путь |
|---|---|
| mon_http_ListGroupsByUserId | HTTP POST "/v1/group/listByUser" |
Доступные поля для фильтрации и виды фильтров по ним:
| Поле | Виды фильтров |
|---|---|
| groupId | InSetQuery |
| name | InSetQuery, LikeQuery |
| description | InSetQuery, LikeQuery |
| modified | InSetQuery, LikeQuery, RangeQuery |
Доступные поля для сортировки:
| Поле |
|---|
| groupId |
| name |
| description |
| modified |
ListGroupsByUserIds (mon)
Payload для команды
[
"9e04e443-9478-4f19-be0f-cac81aa17c7d",
"7d21f4e6-1031-42f9-8610-d07d6b15f976",
"7d21f4e6-1031-42f9-8610-d07d6b15f977"
]
Результат выполнения команды
[
{
"user": "9e04e443-9478-4f19-be0f-cac81aa17c7d",
"groups": [
{
"id": "users",
"name": "Пользователи",
"modified": 1675845168767,
"description": "Все пользователи системы"
}
]
},
{
"user": "7d21f4e6-1031-42f9-8610-d07d6b15f976",
"groups": []
},
{
"user": "7d21f4e6-1031-42f9-8610-d07d6b15f977",
"groups": [
{
"id": "users",
"name": "Пользователи",
"modified": 1675845168767,
"description": "Все пользователи системы"
},
{
"id": "admins",
"name": "Администраторы",
"modified": 1674741697650,
"description": null
}
]
}
]
Возвращает массив объектов, каждый из которых содержит id пользователя и массив всех групп, в которых состоит пользователь. Поддерживается только синхронный вызов. Команда не следит за существованием переданных id пользователей. Т.е. если передать во входных данных несуществующий id, то объект с таким id (с пустым списком групп) вернется в ответе
- Payload: List[UserId]
- Result: List[UserWithGroups]
| Команда | Путь |
|---|---|
| mon_http_ListGroupsByUserIds | HTTP POST "/v1/group/listByUsers" |
ListUsersByGroupId (mon)
Payload для команды
{
"groupId": "id_of_my_first_group",
"options": {
"sorting": {
"fieldName": "name",
"order": "asc"
},
"paging": {
"page": 1,
"count": 20
},
"query": "status",
"context": {
"status": "Activated"
}
}
}
Результат выполнения команды
{
"total": 2,
"items": [
{
"user": {
"id": "a133f0c8-a516-4537-8f3f-915acb335e2c",
"email": "emailname@mail.io",
"status": "Activated",
"info": {
"firstName": "John",
"lastName": "Week",
"middleName": "Jovonovich"
}
},
"groupsCount": 1
},
{
"user": {
"id": "2595becc-e4a2-4507-88bf-e0114ab6d677",
"email": "abc@gmail.ru",
"status": "Activated",
"info": {
"firstName": "Ivan",
"lastName": "Petrov",
"middleName": "Vasilyevich"
}
},
"groupsCount": 10
}
]
}
Возвращает список всех пользователей, которые состоят в указанной группе. Поддерживается только синхронный вызов.
- Payload: GroupIdWithOptions
- Result: List[UserWithGroupCount]
| Команда | Путь |
|---|---|
| mon_http_ListUsersByGroupId | HTTP POST "/v1/user/listByGroup" |
Доступные поля для фильтрации и виды фильтров по ним:
| Поле | Виды фильтров |
|---|---|
| userId | InSetQuery |
| status | InSetQuery |
| InSetQuery, LikeQuery | |
| lastName | InSetQuery, LikeQuery |
| firstName | InSetQuery, LikeQuery |
| middleName | InSetQuery, LikeQuery |
| count | InSetQuery, LikeQuery, RangeQuery |
Доступные поля для сортировки:
| Поле |
|---|
| userId |
| status |
| lastName |
| firstName |
| middleName |
| count |
ListUsersWithGroupsCount (mon)
Payload для команды
{
"sorting": {
"fieldName": "name",
"order": "asc"
},
"paging": {
"page": 1,
"count": 20
},
"query": "status",
"context": {
"status": "Activated"
}
}
Результат выполнения команды
{
"total": 2,
"items": [
{
"user": {
"id": "a133f0c8-a516-4537-8f3f-915acb335e2c",
"email": "emailname@mail.io",
"status": "Activated",
"info": {
"firstName": "John",
"lastName": "Week",
"middleName": "Jovonovich"
}
},
"groupsCount": 1
},
{
"user": {
"id": "2595becc-e4a2-4507-88bf-e0114ab6d677",
"email": "abc@gmail.ru",
"status": "Activated",
"info": {
"firstName": "Ivan",
"lastName": "Petrov",
"middleName": "Vasilyevich"
}
},
"groupsCount": 10
}
]
}
Возвращает список всех пользователей и кол-во групп, в которых они состоят. Поддерживается только синхронный вызов.
- Payload: Search
- Result: List[UserWithGroupCount]
| Команда | Путь |
|---|---|
| mon_http_ListUsersWithGroupsCount | HTTP POST "/v1/user/listWithGroupsCount" |
Доступные поля для фильтрации и виды фильтров по ним:
| Поле | Виды фильтров |
|---|---|
| userId | InSetQuery |
| status | InSetQuery |
| InSetQuery, LikeQuery | |
| lastName | InSetQuery, LikeQuery |
| firstName | InSetQuery, LikeQuery |
| middleName | InSetQuery, LikeQuery |
| fio | TsQuery |
| count | InSetQuery, LikeQuery, RangeQuery |
Доступные поля для сортировки:
| Поле |
|---|
| userId |
| status |
| lastName |
| firstName |
| middleName |
| count |
AddUsersToGroup (mon)
Payload для команды
{
"groupIds": [
"id_of_my_first_group"
],
"users": [
"a133f0c8-a516-4537-8f3f-915acb335e2c",
"2595becc-e4a2-4507-88bf-e0114ab6d677"
]
}
Результат выполнения команды
true
Добавляет новых пользователей в существующую группу. Отправляет событие о добавлении пользователей в группу. Поддерживается только синхронный вызов.
- Payload: UsersInGroups]
- Result: Boolean
| Команда | Путь |
|---|---|
| mon_http_AddUsersToGroup | HTTP POST "/v1/group/addUsers" |
RemoveUsersFromGroup (mon)
Payload для команды
{
"groupIds": [
"id_of_my_first_group"
],
"users": [
"a133f0c8-a516-4537-8f3f-915acb335e2c",
"2595becc-e4a2-4507-88bf-e0114ab6d677"
]
}
Результат выполнения команды
true
Удаляет пользователей, которые состоят в указанной группе. Отправляет событие об удалении пользователей из группы. Поддерживается только синхронный вызов.
- Payload: UsersInGroups]
- Result: Boolean
| Команда | Путь |
|---|---|
| mon_http_RemoveUsersFromGroup | HTTP POST "/v1/group/removeUsers" |
Публикуемые события
События публикуются в MON_KAFKA_USEREVENT_TOPIC.
| eventType | payloadType | payloadId | Описание |
|---|---|---|---|
| registration | user | Id зарегистрированного пользователя | Пользователь зарегистрирован |
| registrationVerification | userId | Id верифицированного пользователя | Пользователь подтвержден |
| groupCreation | group | Id группы | Создана группа |
| groupUpdate | group | Id группы | Обновлена группа |
| groupRemoval | groupId | Id группы | Удалена группа |
| additionToGroup | usersInGroups | "" | Пользователи добавлены в группы |
| removalFromGroup | usersInGroups | "" | Пользователи удалены из группы |
Модели сервиса Mon
User (mon)
Структура, описывающая сущность "пользователь"
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| id | UserId | Да | Идентификатор пользователя |
| String | Да | Уникальный email пользователя | |
| status | UserStatus | Да | Статус пользователя в виде строки |
| info | UserInfo | Нет | Опциональная информация о пользователе |
UserInfo (mon)
Дополнительная информация о пользователе.
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| firstName | String | Нет | Имя пользователя |
| lastName | String | Нет | Фамилия пользователя |
| middleName | String | Нет | Отчество пользователя |
| data | Json | Нет | Дополнительные данные профиля пользователя |
UserRegistrationInfo (mon)
Дополнительная информация о пользователе, которая используется только при регистрации.
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| firstName | String | Да | Имя пользователя |
| lastName | String | Да | Фамилия пользователя |
| middleName | String | Нет | Отчество пользователя |
UserStatus (mon)
Список вариантов статуса пользователя в системе. Имеет тип String.
| Название | Описание |
|---|---|
| Pending | Пользователь, ожидающий подтверждения регистрации |
| Activated | Обычный пользователь системы (выполнивший все условия/требования после регистрации) |
| Deactivated | Заблокированный пользователь системы |
UserWithGroupCount (mon)
Информация о пользователе и количестве групп, в которых он состоит.
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| user | User | Да | Пользователь |
| groupsCount | Int | Да | Количество групп, в которых состоит пользователь |
LoginCredentials (mon)
Данные необходимые для выполнения входа пользователя в систему
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| String | Да | Email пользователя | |
| password | String | Да | Пароль пользователя |
SuccessAuthentication (mon)
Результат, указывающий на успешный вход пользователя в систему. Содержит JWT.
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| accessToken | String | Да | JWT в виде строки |
| expiresIn | Int | Да | Время жизни JWT в секундах |
UserRegistrationDto (mon)
Данные, необходимые для регистрации пользователя в системе.
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| String | Да | Email пользователя | |
| password | String | Да | Пароль, который будет использован для аутентификации |
| info | UserRegistrationInfo | Да | Дополнительная информация о пользователе |
UserRegistrationResult (mon)
Результат, указывающий на успешный вход пользователя в систему. Содержит JWT.
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| userId | UserId | Да | Идентификатор группы пользователей |
| tokenId | ActionTokenId | Да | Идентификатор токена |
| tokenKind | String | Да | Вид токена |
| tokenValue | String | Нет | Значение токена в виде строки |
ActionTokenId (mon)
Идентификатор токена (UUIDv4) для какого-либо действия.
В данный момент, доступно только 1 действие - подтверждение
регистрации пользователя. Вид действия для которого был создан токен определяется с помощью типа ActionTokenKind (
название вида токена, представлен в виде строки).
GroupId (mon)
Идентификатор группы пользователей, может быть строкой любого формата.
GroupDTO (mon)
Структура для описания группы пользователей
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| id | GroupId | Да | Идентификатор группы пользователей |
| name | String | Да | Название группы для отображения (любая строка) |
| modified | TimeStamp | Да | Представляет время (UTC) в миллисекундах от 01.01.1970 |
| description | String | Нет | Описание группы (необязательное поле) |
UpdateGroupDTO (mon)
Структура для передачи измененных атрибутов группы пользователей
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| id | GroupId | Да | Идентификатор группы пользователей |
| name | String | Да | Название группы для отображения (любая строка) |
| description | String | Нет | Описание группы (необязательное поле) |
GroupInfo (mon)
Информация о группе пользователей и количестве ее участников
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| group | GroupDTO | Да | Информация о группе пользователей |
| usersCount | Int | Да | Количество пользователей в группе |
UsersInGroups (mon)
Структура для оперирования некоторыми пользователями из группы пользователей
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| groupId | List[GroupId] | Да | Список идентификаторов групп пользователей |
| users | List[UserId] | Да | Список идентификаторов пользователей |
CommandRequest (mon)
Структура для описания запроса на выполнение команды
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| commandName | String | Да | Название команды |
| context | Json | Да | Контекст запроса. Содержит информацию о пользователе, который отправил запрос и другие дополнительные параметры |
| payload | Json | Да | Данные для команды, которые она получит в качестве аргументов |
UserIdWithOptions (mon)
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| userId | UserId | Да | Идентификатор пользователя |
| options | Search | Нет | Опции для списка |
UserIdWithStatus (mon)
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| userId | UserId | Да | Идентификатор пользователя |
| status | UserStatus | Да | Статус пользователя |
GroupIdWithOptions (mon)
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| groupId | GroupId | Да | Идентификатор группы пользователей |
| options | Search | Нет | Опции для списка |
QueryParameters (mon)
Структура для передачи параметров, которые обычно должны быть переданы в качестве query string (например при входе по OpenID Connect провайдерам)
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| queryParameters | Json | Да | Json-объект со строковыми значениями, являющийся перечнем query string параметров |
ChangePasswordDto (mon)
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| oldPassword | String | Да | Текущий пароль пользователя |
| newPassword | String | Да | Желаемый пароль пользователя |
UserAttributesMapping (mon)
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| firstName | String | Да | Настройка маппинга атрибутов для поля UserInfo.firstName. Строка в формате AttributeMapping. |
| lastName | String | Да | Настройка маппинга атрибутов для поля UserInfo.lastName. Строка в формате AttributeMapping. |
| middleName | String | Да | Настройка маппинга атрибутов для поля UserInfo.middleName. Строка в формате AttributeMapping. |
| String | Да | Настройка маппинга атрибутов для поля User.email. Строка в формате AttributeMapping. |
AttributeParsingSettings (mon)
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| attribute | String | Да | Название атрибута из которого нужно использовать значение. Регистр не учитывается. |
| position | Int | Нет | Индекс сегмента, который нужно использовать из значения атрибута, если значение атрибута было разделено. Если указано поле delimiter, то это поле становится обязательным. |
| delimiter | String | Нет | Символ по которому будет разделено значение атрибута. Если указано поле position, то это поле становится обязательным. |
| limit | Int | Нет | Максимальное количество сегментов, на которое нужно разделить значение атрибута. Если не задано, то количество сегментов не ограничено. |
UserWithGroups (mon)
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| user | UserId | Да | Идентификатор пользователя |
| groups | List[GroupDTO] | Да | Массив групп пользователя |
ActiveDirectory groups mapping (mon)
Для указания маппинга групп используются формат ADName=MonName, где
"ADName" может быть или GroupSID или Base64 строкой. "MonName" может быть только GroupId сервиса Mon.
Пример: S-1-5-21-4036403032-2978644966-1872548419-1100=TechAdmins;AQUAAAAAAAUVAAAAWJ+W8OaDirFD0pxvUgQAAA===Users.
Чтобы посмотреть информацию о группе, можно использовать команду
ldapsearch -H ldap://10.168.22.245 -x -W -D "tester@dev.embedika.ru" -b "dc=dev,dc=embedika,dc=ru" "(sAMAccountName=sso)", где
10.168.22.245 - адрес AD,
tester@dev.embedika.ru - пользователь AD, от которого делается запрос (должен иметь доступ к просмотру группы),
sso - название группы.
В ответе, нас интересует поле objectSid.
Пример ответа ldapsearch
# sso, Users, dev.embedika.ru
dn: CN=sso,CN=Users,DC=dev,DC=embedika,DC=ru
objectClass: top
objectClass: group
cn: sso
member:: Q0490KTRgNC+0L3RgtC10L3QtNC10YAsQ049VXNlcnMsREM9ZGV2LERDPWVtYmVkaWthLERDPXJ1
member:: Q0490KLQtdGB0YLQtdGALENOPVVzZXJzLERDPWRldixEQz1lbWJlZGlrYSxEQz1ydQ==
distinguishedName: CN=sso,CN=Users,DC=dev,DC=embedika,DC=ru
instanceType: 4
whenCreated: 20221129100547.0Z
whenChanged: 20221129100626.0Z
uSNCreated: 12851
uSNChanged: 12857
name: sso
objectGUID:: +4z7l0Mwe0qRQohm48uhCw==
objectSid:: AQUAAAAAAAUVAAAAWJ+W8OaDirFD0pxvUgQAAA==
sAMAccountName: sso
sAMAccountType: 268435456
groupType: -2147483646
objectCategory: CN=Group,CN=Schema,CN=Configuration,DC=dev,DC=embedika,DC=ru
dSCorePropagationData: 16010101000000.0Z
# search reference
ref: ldap://ForestDnsZones.dev.embedika.ru/DC=ForestDnsZones,DC=dev,DC=embedika,DC=ru
# search reference
ref: ldap://DomainDnsZones.dev.embedika.ru/DC=DomainDnsZones,DC=dev,DC=embedika,DC=ru
# search reference
ref: ldap://dev.embedika.ru/CN=Configuration,DC=dev,DC=embedika,DC=ru
# search result
search: 2
result: 0 Success
# numResponses: 5
# numEntries: 1
# numReferences: 3
External profile attributes mapping (mon)
Для указания атрибутов профиля из внешнего хранилища (ActiveDirectory, Keycloak и т.п.), из которых нужно получить значения для профиля пользователя (например, для полей User или UserInfo), используется JSON строка со списком настроек. Каждый элемент списка является строкой со структурой AttributeParsingSettings.
Формат строки со структурой AttributeParsingSettings:
"attributeName;position;delimiter;limit",
где ; является разделителем между полями структуры. Пробелы игнорируются для всех полей, кроме delimiter (данное поле может содержать пробелы).
Указание названия атрибута с разделителем может пригодится, если нужно использовать только какой-то сегмент (по индексу из поля position), а не все значение. В качестве примера можно привести атрибут displayName, который содержит ФИО, и поле UserInfo.firstName, для которого требуется только Имя:
Атрибут: "displayName"
Значение атрибута: "Толстой Алексей Константинович"
Маппинг: "displayName; 1; ", где displayName - название атрибута, 1 - индекс сегмента (начиная с 0), (пробел) - разделитель для сегментов. Limit сегментов не указан, т.к. опционален.
Выделение сегмента из значения атрибута реализовано через java-вый String.split(delimiter, limit).
Особенности: - Регистр названия атрибута не учитывается. - Если указывается поле "position", то "delimiter" тоже нужно указывать (и наоборот).
Примеры строк с AttributeParsingSettings:
- AttributeParsingSettings("mail", None, None, None) -> "mail"
При маппинге будет использовано значение из атрибута "mail" без изменений.
- AttributeParsingSettings("cn", Some(0), Some(" "), None) -> "cn;0; ".
При маппинге значение из атрибута "cn" будет разделено по символу " " и, из получившегося списка, будет использован первый элемент.
- AttributeParsingSettings("displayName", Some(2), Some(" "), Some(3)) -> "displayName; 2; ; 3".
При маппинге значение из "displayName" будет разделено по символу " " с ограничением по длине списка в три элемента. Из получившегося списка будет использован третий элемент.
Примеры списка с настройками:
- ["surName"]
- ["lastName", "sn", "cn;2; ;3", "displayName; 2; ; 3"]
Требования к настройкам клиента Keycloak
- Должны использоваться
Client Type = OpenId ConnectсAuthentication Flow = Standart Flow Client Authenticationдолжно быть включено, типCredentialsдолжен бытьClient Id and Secret.- Должен быть настроен
Valid redirect URIs, соответствующий URL, по которому фронт-энд отрабатывает коллбек от Keycloak. Пример такого URL:https://cursor.dev/keycloak-callback*(звездочка на конце необходима, чтобы можно было передавать query string параметры)
Сервис журналирования
В сервисе реализованы следующие команды:
Общее описание архитектуры сервиса журналирования
Для работы с журналом необходимы действия: добавить одно событие, запросить событие по идентификатору, отфильтровать события. Для каждого из этих действий Nestor предоставляет verdi-команду. Добавление это асинхронная команда, все события поступаю в очередь кафки и сохраняются пачками, поэтому будет существовать временной лаг между созданием события и возможностью его просмотра через Nestor. Сохранение пачками сделано для снижения нагрузки на хранилище. Размер пачки событий ограничен максимальным кол-вом событий, а так же временем формирования пачки (см. groupedWithin)
Список переменных окружения Nestor
| Переменная | Тип | Обязательная | Значение по умолчанию | Описание |
|---|---|---|---|---|
| SERVER_PORT | int | нет | 8080 | Порт, на котором слушает HTTP-сервер |
| CONSUMER_TOPIC | string | нет | document | Название Kafka-топика для команд журналирования событий |
| CONSUMER_CHUNK_SIZE | string | нет | 1000 | Размер пачки обрабатываемых событий |
| CONSUMER_CHUNK_WITHIN | duration string | нет | 10 seconds | Время, за которое должна обработаться пачка событий, что определяет производительность сервиса |
| CONSUMER_GROUP | string | нет | document | Имя группы-консьюмеров сервиса журналирования |
| VERDI_CONSUL | string | да | http://localhost:8500 | Адрес Consul |
| VERDI_CONSUL_AUTH_USER | string | нет | "" | Название учетной записи Сonsul. Если название не указано, то настройки авторизации не будут применены. |
| VERDI_CONSUL_AUTH_PASSWORD | string | нет | "" | Пароль учетной записи Сonsul. |
| VERDI_KAFKA_ADDRESS | string | да | localhost:9092 | Адрес брокера Kafka. |
| VERDI_KAFKA_TOPIC | string | нет | eventStatus | Название Kafka-топика для отправки сообщений со статусами выполняемых команд. ОБЯЗАТЕЛЬНО должно соответствовать названию этого топика в сервисе статуса команд. |
| VERDI_KAFKA_AUTH_USER | string | нет | "" | Название учетной записи Kafka. Если название не указано, то настройки авторизации не будут применены. |
| VERDI_KAFKA_AUTH_PASSWORD | string | нет | "" | Пароль учетной записи Kafka. |
| VERDI_KAFKA_AUTH_TRUSTSTORE_LOCATION | string | нет | "" | Путь до хранилища сертификатов (Java key store). Если путь не указан, то сертификат применяться не будет. |
| VERDI_KAFKA_AUTH_TRUSTSTORE_PASSWORD | string | нет | "" | Пароль к хранилищу сертификатов. |
| VERDI_KAFKA_AUTH_MODE | string | нет | "" | Режим аутентификации: static - одна учетная запись на все запросы, mapping - учетная запись зависит от запроса |
| VERDI_KAFKA_AUTH_CONFIG | string | нет | "" | Настройки для аутентификации: если AuthMode = mapping, то необходим JSON с List[KafkaAuthConfig]. Переменная соответствует полю authConfig из KafkaAuthSettings. |
| VERDI_KAFKA_AUTH_CACHE_SIZE | int | нет | Максимальный размер кеша для Kafka producer (количество активных соединений). | |
| VERDI_KAFKA_AUTH_CACHE_TTL | duration string | нет | Время жизни Kafka producer в кеше. | |
| VERDI_HOST | string | да | localhost | Хост, публикуемый в ServiceDiscovery. По нему на данный сервис будут обращаться другие через HTTP. Указанный адрес должен быть виден другим сервисам. Пример: имя kubernetes/docker_swarm service |
| VERDI_TTL | duration string | нет | 30 seconds | Период после последней отправки health check, в течение которого ServiceDiscovery считает данный сервис живым. |
| VERDI_HEALTH_CHECK | duration string | нет | 10 seconds | Периодичность отправки health check в ServiceDiscovery |
| VERDI_COMMAND_STORAGE_UPDATE_PERIOD | duration string | нет | 1 minutes | Время кэширования данных по командам из CommandDiscovery |
| VERDI_SERVICE_DISCOVERY_UPDATE_PERIOD | duration string | нет | 30 seconds | Время кэширования данных по сервисам из ServiceDiscovery |
| VERDI_ALLOW_INTERNAL_COMMANDS | bool | нет | true | Можно ли сервису отправлять внутрисистемные команды |
| VERDI_CONSUL_CONNECTION_MAX_RETRY | int | нет | 5 | Максимальное количество попыток подключений к Consul |
| VERDI_CONSUL_CONNECTION_RETRY_DELAY | duration string | нет | 1 seconds | Таймаут между неудачными попытками подключения к Consul |
| VERDI_KAFKA_SYNC_POLL_PERIOD | duration string | нет | 1 seconds | Периодичность запроса статуса команды отправленной через Kafka. |
| VERDI_KAFKA_SYNC_POLL_TIMEOUT | duration string | нет | 1 minute | Время ожидания завершения команды отправленной через Kafka. |
| TECHCONSTANT_SERVICE_NAME | string | нет | Сервис журналирования событий | Название сервиса для TechConstant |
| TECHCONSTANT_SERVICE_DESC | string | нет | Сервис для хранения и доступа к событиям | Описание сервиса для TechConstant |
| TECHCONSTANT_REGISTRATION_BASEDELAY | duration string | нет | 30 seconds | Базовая задержка для "retry с экспоненциальной задержкой" |
| ELASTIC_HOST | string | нет | localhost | Хост elasticsearch |
| ELASTIC_PORT | int | нет | 9200 | Порт elasticsearch |
| ELASTIC_SSL | bool | нет | false | Использовать ли SSL для соединении с elasticsearch |
| ELASTIC_AUTH_USER | string | нет | "" | Название учетной записи для elasticsearch |
| ELASTIC_AUTH_PASSWORD | string | нет | "" | Пароль учетной записи для elasticsearch |
| ELASTIC_AUTH_STORE_TYPE | string | нет | pem | Тип хранилища сертификата для elasticsearch |
| ELASTIC_AUTH_STORE_LOCATION | string | нет | "" | Путь до хранилища сертификата для elasticsearch |
| EVENTSTORAGE_ELASTIC_INDEX | string | нет | events-journal | Название индекса для хранения журналов в elasticsearch |
| EVENTSTORAGE_RETENTION | duration string | нет | 7 days | Продолжительность хранения журналов |
| EVENTSTORAGE_MEMORY_CAP | string | нет | 64 MB | Максимальная доступная память для хранилища журналов. Необходимо для расчета процента используемой памяти. Возможные сокращения: b, kb, mb, gb |
| EVENTSTORAGE_MEMORY_ALERT_PERCENT | int | нет | 70 | Процент используемой памяти, при котором будет сформировано событие в журнал |
| EVENTSTORAGE_MEMORY_REWRITE_PERCENT | int | нет | 90 | Процент используемой памяти, при котором САМЫЕ старые индексы должны быть удалены, чтобы место было переиспользовано новыми индексами |
| EVENTSTORAGE_MEMORY_CHECK_DELAY | duration string | нет | 10 seconds | Период проверки используемой хранилищем памяти |
| EVENTSTORAGE_ROLLOVER | string | нет | daily | Периодичность создания новых индексов (т.к. нет возможности хранить все данные в одном): hourly - каждый час, daily - каждый день, weekly - каждую неделю. |
| EVENTSTORAGE_ROLLOVER_ENABLED | bool | нет | true | Включен ли rollover для хранилища. OpenSearch не поддерживает rollover |
| EVENTSTORAGE_DEFAULT_PAGE_SIZE | int | нет | 10 | Размер страницы по умолчанию, при выдачи списка событий |
| EVENTSTORAGE_DEFAULT_SORT_FIELD | string | нет | eventDate | Поле для сортировки по умолчанию, при выдачи списка событий |
| EVENTSTORAGE_DEFAULT_SORT_ORDER | string | нет | Desc | Направление для сортировки по умолчанию, при выдачи списка событий: Asc - "по возрастанию", Desc - "по убыванию" |
| ENGINE_TYPE | string | нет | elastic | Тип хранилища данных "elastic" или "openSearch" |
| OPEN_SEARCH_HOST | string | нет | localhost | хост openSearch |
| OPEN_SEARCH_PORT | int | нет | 9200 | Порт openSearch |
| OPEN_SEARCH_SSL | bool | нет | false | Использовать ли SSL для соединении с openSearch |
| OPEN_SEARCH_AUTH_USER | string | нет | "" | Название учетной записи для openSearch |
| OPEN_SEARCH_AUTH_PASSWORD | string | нет | "" | Пароль учетной записи для openSearch |
| OPEN_SEARCH_AUTH_STORE_TYPE | string | нет | pem | Тип хранилища сертификата для openSearch |
| OPEN_SEARCH_AUTH_STORE_LOCATION | string | нет | "" | Путь до хранилища сертификата для openSearch |
| LOG_OUTPUT | string | нет | STDOUT | Параметры вывода логов: STDOUT - обычный лог, STDOUT_CEF - лог в формате CEF. |
| LOG_LEVEL_HTTP | string | нет | WARN | Уровень логирования HTTP-сервера |
| LOGGING_SRC_IP | string | нет | Параметр SRC (источник/source, на который ссылается событие) для логов в формате CEF. Если не установлена, src=notFound. Требуемый формат: IPv4, например "192.168.10.1". |
|
| LOGGING_SRC_HOST | string | нет | Параметр SHOST (источник/source, на который ссылается событие) для логов в формате CEF. Если не установлена, shost=notFound. Требуемый формат: fully qualified domain name (FQDN), например "host" или "host.domain.com". |
|
| LOGGING_DST_IP | string | нет | Параметр DST (получатель/destination, на который ссылается событие) для логов в формате CEF. Если не установлена, dst=notFound. Требуемый формат: IPv4, например "192.168.10.1". |
|
| LOGGING_CEF_VER | int | нет | 0 | Версия формата CEF: либо 0, либо 1. Рекомендуется использовать 0. |
| VERDI_COMMANDS_HTTP_RETRY_ATTEMPTS | int | нет | 5 | Поле attempts из RetrySettings |
| VERDI_COMMANDS_HTTP_RETRY_DELAY | duration string | нет | "5 seconds" | Поле delay из RetrySettings |
| VERDI_COMMANDS_HTTP_RETRY_KIND | string | нет | "OnSomeExceptions(ConnectException)" | CommandResultRetryConditionKind |
Формат CEF для логов сервиса Nestor
У логов есть возможность включить формат CEF для логирования по следующему шаблону:
2022-12-14T16:56:31+0500 CEF:Version|Device Vendor|Device Product|Device Version|EventClassId|Message|Severity|src=? dst=? shost=? suid=? suser=? msg=? end=currentTimeMillis|.
Чтобы включить логирование в формате CEF, нужно задать переменную LOG_OUTPUT = STDOUT_CEF. Если логи пишутся в формате CEF, то необходимо задать следующие переменные, которые по умолчанию не заданы:
LOGGING_SRC_IP, для параметраsrcв логахLOGGING_SRC_HOST, для параметраshostв логахLOGGING_DST_IP, для параметраdstв логах
В файле src/main/resources/log4j.xml можно поменять class path для основного класса приложения (переменная projectMainClassPath), если это необходимо.
Список команд сервиса Nestor
CreateEvent (Nestor)
На входе данные нового события
{
"eventDate": 1683901401795,
"eventType": "journalDeleteGroup",
"userId": "0e7c15e6-2a7b-46c2-8571-d9dc9d6e3726",
"eventCategory": "Success",
"application": "Mon",
"data": "{\"eventType\":\"journalDeleteGroup\",\"payload\":\"CODECODE\",\"result\":true,\"data\":\"Наименование наименование\"}",
"dependencies": []
}
На выходе сообщение об успешности выполнения команды
{
"result": "success"
}
Сохраняет новое событие в журнале.
Имя команды для вызова: createEvent. Поддерживается асинхронный и синхронный вызов.
Команда только внутренняя (нельзя вызвать через Api Gateway), не требует аутентификации.
- На входе: NewEvent
- На выходе: JSON с полем
resultи значениемsuccess
GetEvent (Nestor)
На входе JSON-объект с ID запрашиваемого события
{
"id": "42883340-f2cd-4eac-92ca-36ef46d4d038"
}
На выходе данные запрашиваемого события
{
"id": "42883340-f2cd-4eac-92ca-36ef46d4d038",
"eventDate": 1683901401795,
"storeDate": 1683901411816,
"eventType": "journalDeleteGroup",
"userId": "0e7c15e6-2a7b-46c2-8571-d9dc9d6e3726",
"eventCategory": "Success",
"application": "Mon",
"data": "{\"eventType\":\"journalDeleteGroup\",\"payload\":\"CODECODE\",\"result\":true,\"data\":\"Наименование наименование\"}",
"dependencies": []
}
Получение события по его ID.
Имя команды для вызова: getEvent. Поддерживается только синхронный вызов.
- На входе: JSON с полем
id, в значении которого указан ID искомого события - На выходе: Event
SearchEvents (Nestor)
На входе параметры поиска событий
{
"query": "eventType",
"context": {
"eventType": "[importCatalogs, journalDeleteGroup]"
},
"sorting": {
"fieldName": "eventDate",
"order": "Desc"
},
"paging": {
"page": 1,
"count": 2
}
}
На выходе результат поиска
{
"items": [
{
"id": "42883340-f2cd-4eac-92ca-36ef46d4d038",
"eventDate": 1683901401795,
"storeDate": 1683901411816,
"eventType": "journalDeleteGroup",
"userId": "0e7c15e6-2a7b-46c2-8571-d9dc9d6e3726",
"eventCategory": "Success",
"application": "Mon",
"data": "{\"eventType\":\"journalDeleteGroup\",\"payload\":\"CODECODE\",\"result\":true,\"data\":\"Наименование наименование\"}",
"dependencies": []
},
{
"id": "881b3ffa-6273-44ed-a798-08e75959a644",
"eventDate": 1682515867969,
"storeDate": 1682515877987,
"eventType": "journalDeleteGroup",
"userId": "d5b8f850-1af6-4f94-82ef-fe304bb90618",
"eventCategory": "Success",
"application": "Mon",
"data": "{\"eventType\":\"journalDeleteGroup\",\"payload\":\"Test1\",\"result\":true,\"data\":\"Тестовая 1\"}",
"dependencies": []
}
],
"total": 206
}
Возвращает список событий, подходящих под критерии поиска и пагинации.
Доступные поля для фильтрации и виды фильтров по ним:
NB! InSetQuery корректно работает только для значений, не содержащих запятые.
NB! Для поля data фильтрация доступна только для значений, содержащих меньше 8191 символов. Все значения поля data будут отображаться в полном объеме при фильтрации по другим полям, но фильтрация по самому полю data будет давать результат, содержащий только значения короче 8191 символов.
| Поле | Виды фильтров | Регистронезависимость |
|---|---|---|
| id | InSetQuery | только для ASCII-символов |
| eventDate | InSetQuery, RangeQuery | только для ASCII-символов |
| storeDate | InSetQuery, RangeQuery | только для ASCII-символов |
| eventType | InSetQuery, LikeQuery | только для ASCII-символов |
| userId | InSetQuery, LikeQuery | только для ASCII-символов |
| eventCategory | InSetQuery, LikeQuery | только для ASCII-символов |
| application | InSetQuery, LikeQuery | только для ASCII-символов |
| data | InSetQuery, LikeQuery | полная |
| dependencies.entityClass | InSetQuery, AllInSetQuery, LikeQuery | только для ASCII-символов |
| dependencies.entityId | InSetQuery, AllInSetQuery, LikeQuery | только для ASCII-символов |
| dependencies.dependencyType | InSetQuery, AllInSetQuery, LikeQuery | только для ASCII-символов |
Маппинг фильтров nestor в запросы elasticSearch/openSearch:
| Фильтр | Запрос к nestor | Запрос elasticSearch/openSearch |
|---|---|---|
| InSetQuery("value1", "value2") | {"query": "fieldName", "context" : {"fieldName": "[value1, value2]"}} | {"query" : {"bool" : { "should" : [{"term" : {"fieldName" : {"value" : "value1", "case_insensitive": true}}}, {"term" : {"fieldName" : {"value" : "value2", , "case_insensitive": true}}}] }}} |
| LikeQuery("value1") | {"query": "fieldName", "context" : {"fieldName": "like value1"}} | {"query" : {"term" : {"fieldName" : {"value" : "value1", "case_insensitive": true}}}} |
| LikeQuery("%value1") | {"query": "fieldName", "context" : {"fieldName": "like %value1"}} | {"query" : {"regexp" : {"fieldName" : {"value" :".*value1", "case_insensitive": true}}}} |
| LikeQuery("__value1") | {"query": "fieldName", "context" : {"fieldName": "like __value1"}} | {"query" : {"regexp" : {"fieldName" : {"value" :".{2}value1", "case_insensitive": true}}}} |
| RangeQuery(1, 4) | {"query": "fieldName", "context" : {"fieldName": "1_4"}} | {"query" : {"range" : {"fieldName" : {"gte" : 1, "lte" : 2}}}} |
| AllInSetQuery("value1", "value2") (для полей объектов массива) | {"query": "arrayFieldName.objectFieldName", "context" : {"arrayFieldName.objectFieldName": "all[value1, value2]"}} | {"query" : {"bool" : {"must" : [{"nested": {"path": "arrayFieldName", "query": {"term" : {"arrayFieldName.objectFieldName" : {"value" : "value1", "case_insensitive": true}}} }}, {"nested": {"path": "arrayFieldName", "query": {"term" : {"arrayFieldName.objectFieldName" : {"value" : "value2", "case_insensitive": true}}} }}] }}} |
Доступные поля для сортировки:
| Поле | Примечание |
|---|---|
| id | - |
| eventDate | - |
| storeDate | - |
| eventType | - |
| userId | - |
| eventCategory | - |
| application | - |
| dependencies.entityClass | - |
| dependencies.entityId | - |
| dependencies.dependencyType | - |
Если поле sorting отсутствует, то список сортируется согласно соответствующей конфигурации сервиса
(см. параметры EVENTSTORAGE_DEFAULT_SORT_FIELD и EVENTSTORAGE_DEFAULT_SORT_ORDER)
Имя команды для вызова: searchEvents. Поддерживается только синхронный вызов.
Модели сервиса Nestor
Dependency (Nestor)
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| entityClass | String | Да | Класс зависимости |
| entityId | String | Да | Идентификатор зависимости |
| dependencyType | String | Да | Тип зависимости |
case class Dependency(entityClass: String, entityId: String, dependencyType: String)
NewEvent (Nestor)
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| eventDate | Long | Да | Дата события |
| eventType | String | Да | Тип события |
| userId | UUID | Да | Идентификатор связанного с событием пользователя |
| eventCategory | String | Да | Статус события |
| application | String | Да | Идентификатор связанного с событием приложения |
| data | String | Да | Данные события |
| dependencies | List[Dependency] | Да | Иные связанные с событием сущности Dependency |
case class NewEvent(
eventDate: Long,
eventType: String,
userId: String,
eventCategory: String,
application: String,
data: String,
dependencies: List[Dependency])
Event (Nestor)
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| id | UUID | Да | Идентификатор события |
| eventDate | TimeStamp | Да | Дата события |
| storeDate | TimeStamp | Да | Дата сохранения события |
| eventType | String | Да | Тип события |
| userId | UUID | Да | Идентификатор связанного с событием пользователя |
| eventCategory | String | Да | Статус события |
| application | String | Да | Идентификатор связанного с событием приложения |
| data | String | Да | Данные события |
| dependencies | List[Dependency] | Да | Иные связанные с событием сущности Dependency |
case class Event(
id: UUID,
eventDate: Long,
storeDate: Long,
eventType: String,
userId: String,
eventCategory: String,
application: String,
data: String,
dependencies: List[Dependency])
Oberto-client: клиент для сервиса Oberto
Предоставляемые методы: - TBD
Ключевые сущности: - TBD
Oberto: сервис системы прав
Сервис отвечает за редактирование правил доступа к различным ресурсам и проверку доступности пользователей к ресурасам. Команды могут приходить только по HTTP.
Проект сервиса содержит несколько директорий:
commandsсодержит описание команд и их публикации.handlersсодержит обработчики команд (бизнес логика), которые определяют правила взаимодействия компонентов между собой.httpсодержит HTTP роуты, описания форматов входных данных и исходящих ошибок.bootсодержит зависимости и описание для сборки и запуска сервиса.utilсодержит вспомогательные объекты.
В сервисе реализованы следующие команды (все команды "синхронные"):
- Список добавленных политик (V1)
- Список добавленных политик (V1)
- Получение данных отдельной политики (V1)
- Получение данных отдельной политики (V2)
- Удаление отдельной версии политики (V1)
- Удаление отдельной версии политики (V2)
- Удаление всех версий политики (V1)
- Удаление всех версий политики (V2)
- Добавление или обновление политики (V1)
- Добавление или обновление политики (V2)
- Авторизация списка действий для выбранного ресурса (V1)
- Авторизация списка действий для выбранного ресурса (V2)
- Авторизация списка действий для выбранного ресурса (V2)
- Авторизация списка действий для выбранного ресурса (V2)
Локальный запуск сервиса Oberto
При запуске сервиса ожидается, что уже развернута необходимая инфраструктура:
- AuthZforce server по адресу
http://localhost:8080/authzforce-ce - Kafka по адресу
localhost:9092 - Consul по адресу
localhost:8500 - В Consul будет добавлены следующие ключи или они указаны в
application.conf:- адрес Kafka по ключу
kafka_bootstrap_server - адрес AuthZforce по ключу
authzforce_server_address- В рантайме, исходя из наличия адреса AuthZforce, будет попытка получить доступный DomainID для AuthZforce (
также его можно указать в
application.conf)
- В рантайме, исходя из наличия адреса AuthZforce, будет попытка получить доступный DomainID для AuthZforce (
также его можно указать в
- адрес Kafka по ключу
Запуск Oberto из консоли с помощью SBT
sbt run
Список всех переменных окружения для сервиса Oberto
| Переменная | Описание | Значение по-умолчанию |
|---|---|---|
| OBERTO_HTTP_HOST | Хост для HttpListener | "0.0.0.0" |
| OBERTO_HTTP_PORT | Порт для HttpListener | 8192 |
| OBERTO_KAFKA_SERVERS | Адрес Kafka | "localhost:9092" |
| OBERTO_KAFKA_TOPIC | Название топика для входящих команд | "oberto_commands" |
| OBERTO_KAFKA_COMMANDEVENT_TOPIC | Название топика для входящих/исходящих событий | "commandevents" |
| OBERTO_KAFKA_CONSUMER_GROUP | Название группы для чтения топика событий | |
| OBERTO_KAFKA_AUTH_USER | Название учетной записи Kafka. Если название не указано, то настройки авторизации не будут применены. | "" |
| OBERTO_KAFKA_AUTH_PASSWORD | Пароль учетной записи Kafka. | "" |
| OBERTO_KAFKA_AUTH_TRUSTSTORE_LOCATION | Путь до хранилища сертификатов (Java key store). Если путь не указан, то сертификат применяться не будет. | "" |
| OBERTO_KAFKA_AUTH_TRUSTSTORE_PASSWORD | Пароль к хранилищу сертификатов. | "" |
| OBERTO_KAFKA_AUTH_MODE | Режим аутентификации: static - одна учетная запись на все запросы, mapping - учетная запись зависит от запроса | "static" |
| OBERTO_KAFKA_AUTH_CONFIG | Настройки для аутентификации: если AuthMode = mapping, то необходим JSON с List[KafkaAuthConfig]. Переменная соответствует полю authConfig из KafkaAuthSettings. | "" |
| OBERTO_KAFKA_AUTH_CACHE_SIZE | Максимальный размер кеша для Kafka producer (количество активных соединений). | |
| OBERTO_KAFKA_AUTH_CACHE_TTL | Время жизни Kafka producer в кеше. | |
| OBERTO_CONSUL_ADDR | Адрес Сonsul. | "http://localhost:8500" |
| OBERTO_CONSUL_AUTH_USER | Название учетной записи Сonsul. Если название не указано, то настройки авторизации не будут применены. | "" |
| OBERTO_CONSUL_AUTH_PASSWORD | Пароль учетной записи Сonsul. | "" |
| OBERTO_TRACE_DURATION | Нужно ли логировать длительность выполнения команд | false |
| OBERTO_DISCOVERABLE_ID | ID данного сервиса | "another_oberto_instance" |
| OBERTO_DISCOVERABLE_NAME | Название группы сервисов к которой принадлежит данный | "oberto" |
| OBERTO_DISCOVERABLE_HOST | Адрес текущего инстанса сервиса, который будет виден через ServiceDiscovery | "localhost" |
| OBERTO_DISCOVERABLE_PORT | Адрес текущего инстанса сервиса, который будет виден через ServiceDiscovery | {OBERTO_HTTP_PORT} |
| OBERTO_DISCOVERABLE_LIVETIME | Время с последнего HeathCheck, в течении которого сервис считается "живым" | "2 minutes" |
| OBERTO_DISCOVERABLE_HEALTHPASS | Периодичность отправки HeathCheck | "1 minute" |
| OBERTO_INTERNALCMD_ALLOW | Возможно ли сервису отправлять "внутренние" команды | false |
| OBERTO_SENDERLIB_COMMANDS_CACHE_UPDATEPERIOD | Время на которое будут кешироваться данные о командах | "10 minutes" |
| OBERTO_SENDERLIB_SERVICES_CACHE_UPDATEPERIOD | Время на которое будут кешироваться данные о сервисах | "30 seconds" |
| OBERTO_AUTHZFORCE_ENABLE | Включены ли команды/функции связанные с AuthZforce server | true |
| OBERTO_AUTHZFORCE_ADDR | Адрес AuthZforce server | "http://localhost:8080/authzforce-ce" |
| OBERTO_AUTHZFORCE_DOMAIN | Доступный DomainID в AuthZforce server | |
| OBERTO_AUTHZFORCE_DEFAULT | Поведение контроля доступа по-умолчанию: deny - все запрещего, пока не будет получено разрешение; permit - все разрешено, пока не будет получен запрет | deny |
| OBERTO_AUTHZFORCE_PREFER_RULE | Выбор приоритета для правил: deny - при наличии двух и более применяемых правил для запроса авторизации, приоритет отдается первому запрещающему правилу; permit - в случае нескольких применяемых правил, приоритет отдается первому разрешающему правилу | deny |
| OBERTO_AUTHZFORCE_PROVIDER_TYPE | Тип AttributeProvider, который используется в Authzforce server (указан в его конфиге). Указывается в случае, если нужно работать с AttributeProvider из сервиса Oberto. | "" |
| OBERTO_AUTHZFORCE_PROVIDER_ID | Идентификатор AttributeProvider, который используется в Authzforce server (указан в его конфиге). Указывается в случае, если нужно работать с AttributeProvider из сервиса Oberto. | "" |
| OBERTO_AUTHZFORCE_DEBUG_RETURN_POLICY_ID | Добавлять ли в ответ от Authzforce список политик, которые учитывались при расчете ответа. | false |
| OBERTO_AUTHZFORCE_ATTRIBUTES_CACHE_SIZE | Максимальный размер кеша атрибутов для локального AttributeProvider, который используется в Oberto. | 1000 |
| OBERTO_AUTHZFORCE_ATTRIBUTES_CACHE_TTL | Максимальное время кеширования атрибутов для локального AttributeProvider, который используется в Oberto. | "5 minutes" |
| OBERTO_AUTHZFORCE_ATTRIBUTES_REQUEST_TIMEOUT | Максимальное время ожидания завершения запроса атрибутов из сервиса data-model для локального AttributeProvider, который используется в Oberto. | "30 seconds" |
| OBERTO_AUTHZFORCE_POLICIES_REQUEST_TIMEOUT | Максимальное время ожидания завершения запроса политики для локального PolicyProvider, который используется в Oberto. | "30 seconds" |
| OBERTO_AUTHZFORCE_POLICIES_REF_DEPTH | Максимальная глубина вложенных ссылок на политики | 10 |
| OBERTO_AUTHZFORCE_POLICIES_MAX_VERSIONS | Максимальное количество версий для одной политики | 10 |
| OBERTO_AUTHZFORCE_POLICIES_CLEAN_PERIOD | Период проверки списка политик для удаления версий, которые превышают лимит OBERTO_AUTHZFORCE_POLICIES_MAX_VERSIONS | "10 minutes" |
| OBERTO_AUTHZFORCE_POLICIES_DENY_BY_DEFAULT | Поведение по умолчанию: запрещено ли действие, если оно явно не регулируется | true |
| OBERTO_AUTHZFORCE_POLICIES_COMBINE_ALG | Идентификатор алгоритма для комбинации политик | "30 seconds" |
| OBERTO_DEBUGMODE | Режим отладки, в котором можно редактировать политики без проверки наличия прав | false |
| OBERTO_READABLE_NAME | Читаемое название этого сервиса | "Сервис системы прав" |
| OBERTO_DESCRIPTION | Читаемое описание этого сервиса | "Сервис системы прав" |
| OBERTO_LOG_OUTPUT | Параметры вывода логов: STDOUT - обычный лог в консоль, STDOUT_CEF - лог в формате CEF в консоль, FILE - обычный лог в файл, FILE_CEF - лог в формате CEF в файл. Может принимать несколько значений через любой разделитель (например, "STDOUT FILE_CEF"). Преобразования, если указано несколько значений: присутствуют "STDOUT" + "STDOUT_CEF" = учитывается только "STDOUT_CEF"; "FILE" + "FILE_CEF" = "FILE_CEF"; |
"STDOUT" |
| OBERTO_LOGGING_SRC_IP | Параметр SRC (источник/source, на который ссылается событие) для логов в формате CEF. Если не установлена, src=notFound. Требуемый формат: IPv4, например "192.168.10.1". |
|
| OBERTO_LOGGING_SRC_HOST | Параметр SHOST (источник/source, на который ссылается событие) для логов в формате CEF. Если не установлена, shost=notFound. Требуемый формат: fully qualified domain name (FQDN), например "host" или "host.domain.com". |
|
| OBERTO_LOGGING_DST_IP | Параметр DST (адрес назначения/destination, на который ссылается событие) для логов в формате CEF. Если не установлена, dst=notFound. Требуемый формат: IPv4, например "192.168.10.1". |
|
| OBERTO_LOGGING_CEF_VER | Версия формата CEF: либо 0, либо 1. Рекомендуется использовать 0. |
0 |
| OBERTO_DB_URL | Адрес базы данных. Можно использовать вместо DB_HOST, DB_PORT и DB_NAME | |
| OBERTO_DB_HOST | Хост сервера базы данных | |
| OBERTO_DB_PORT | Порт сервера базы данных | |
| OBERTO_DB_NAME | Название базы данных | |
| OBERTO_DB_USER | Пользователь базы данных | |
| OBERTO_DB_PASSWORD | Пароль для пользователя базы данных | |
| OBERTO_DB_THREADS | Количество потоков в пуле потоков для соединения с БД | 10 |
| OBERTO_DB_QUEUE_SIZE | Размер очереди для действий базы данных, которые не могут быть выполнены немедленно, когда все потоки заняты. За пределами этого значения новые действия немедленно завершаются неудачей | 300 |
| OBERTO_DB_CONN_MAX | Максимальное количество одновременных подключений к БД | 10 |
| OBERTO_DB_CONN_TIMEOUT | Максимальное время ожидания ответа для соединения к БД. Если это время превышено, а соединение не становится доступным, будет брошено исключение SQLException. 1000 мс — минимальное значение. | 20 second |
| OBERTO_DB_ISOLATION | Уровень изоляции транзакций для новых подключений. Допустимые значения: NONE, READ_COMMITTED, READ_UNCOMMITTED, REPEATABLE_READ, SERIALIZABLE. |
"READ_COMMITTED" |
| OBERTO_DB_READONLY | Read-only SQL транзакция может изменять только временные таблицы. Этот параметр управляет статусом «только для чтения» по умолчанию для каждой новой транзакции. | false |
| OBERTO_DB_CONN_MIN | Минимальное количество одновременных подключений к БД | = DB_THREADS |
| OBERTO_DB_VALIDATION_TIMEOUT | Максимальное время, в течение которого соединение будет проверяться на работоспособность. 1000 мс — минимальное значение. | 1 seconds |
| OBERTO_DB_IDLE_TIMEOUT | Максимальное время, в течение которого соединению разрешено простаивать в пуле. Значение 0 означает, что простаивающие соединения никогда не удаляются из пула. | 10 minutes |
| OBERTO_DB_MAX_LIFETIME | Максимальное время жизни соединения в пуле. Когда простаивающее соединение достигает этого времени ожидания, даже если оно недавно использовалось, оно будет удалено из пула. Значение 0 указывает на отсутствие максимального срока службы. | 30 minutes |
| OBERTO_DB_INITIALIZATION_FAIL_FAST | Определяет, будет ли пул «быстро выходить из строя», если пул не может быть успешно заполнен начальными соединениями. Если соединения не могут быть созданы во время запуска пула, будет выдано исключение RuntimeException. Это свойство не имеет никакого эффекта, если minConnections равно 0. | false |
| OBERTO_DB_LEAK_DETECTION_THRESHOLD | Время, в течение которого соединение может находиться вне пула, прежде чем будет зарегистрировано сообщение, указывающее на возможную утечку соединения. Значение 0 означает, что обнаружение утечек отключено. Наименьшее приемлемое значение для включения обнаружения утечек составляет 10 с. | 0 |
| OBERTO_DB_CONNECTION_TEST_QUERY | Выражение, которое будет выполнено непосредственно перед получением соединения из пула для проверки того, что соединение с базой данных все еще активно. Оно зависит от базы данных и должно представлять собой запрос, требующий минимальной обработки базой данных (например, «VALUES 1»). Если этот параметр не установлен, вместо него используется метод JDBC4 Connection.isValid(). |
"SELECT 1" |
| OBERTO_DB_REGISTER_MBEANS | Зарегистрированы ли JMX Management Beans («MBeans») | false |
| OBERTO_SENDERLIB_COMMANDS_HTTP_RETRY_ATTEMPTS | Поле attempts из RetrySettings | 5 |
| OBERTO_SENDERLIB_COMMANDS_HTTP_RETRY_DELAY | Поле delay из RetrySettings | "5 seconds" |
| OBERTO_SENDERLIB_COMMANDS_HTTP_RETRY_KIND | CommandResultRetryConditionKind | "OnSomeExceptions(ConnectException)" |
| OBERTO_LICENSE_PATH | Путь до файла с лицензией (только binary format). Данная переменная обязательна для работы. | "" |
| OBERTO_LICENSE_KEY | Путь до файла с публичным ключом лицензией. Данная переменная обязательна для работы. | "" |
Режим отладки (debug mode)
Режим отладки нужен для того, чтобы добавить/удалить/изменить политики без наличия на это прав.
Например, когда политики настроены неверно и пропал доступ к редактированию самих политик.
Также этот режим может быть полезен при первоначальной настройке.
При разворачивании сервиса, в нем присутствует политика, которая разрешает все действия "permit-all", но пока не будет добавлена какая-либо политика.
Этот режим поможет избежать ситуации, когда политика, разрешаюшая редактирвоание политик, не была добавленна первой.
Режим отладки включается установкой переменной окружения OBERTO_DEBUGMODE=true.
Политики по умолчанию
При каждом запуске сервис Oberto проверят доступные хранилища политик на возможность создать политики по умолчанию.
Политиками по умолчанию являются:
- Политика разрешающая любые действия (название: "default_AllowEverything", приоритет: 1)
- Политика разрешающая работу с политиками (PolicySet), правилами (HighLevelRule) и доступ в раздел админки (AdministrationSection) (название: "default_Control-Authorization", приоритет: 1000)
Такие политики создаются если:
- в хранилище нет ни одной политики (даже root)
- в хранилище только одна root политика (стандартный кейс №1)
- в хранилище только root политика и политика со стандартным поведением (DefaultBehaviorPolicySet) (стандартный кейс №2)
При добавлении политик также отправляется событие журнала "о добавлении политики".
Если в хранилище уже были добавлены какие-то нестандартные политики, то политики по умолчанию не добавляются.
Формат CEF для логов сервиса Oberto
У логов есть возможность включить формат CEF для логирования по следующему шаблону:
2022-12-14T16:56:31+0500 CEF:Version|Device Vendor|Device Product|Device Version|EventClassId|Message|Severity|src=? dst=? shost=? suid=? suser=? msg=? end=currentTimeMillis|.
Чтобы включить логирование в формате CEF, нужно задать переменную OBERTO_LOG_OUTPUT = STDOUT_CEF. Если логи пишутся в формате CEF, то необходимо задать следующие переменные, которые по умолчанию не заданы:
OBERTO_LOGGING_SRC_IP, для параметраsrcв логахOBERTO_LOGGING_SRC_HOST, для параметраshostв логахOBERTO_LOGGING_DST_IP, для параметраdstв логах
В файле boot/src/main/resources/logback.xml можно поменять class path для основного класса приложения (переменная projectMainClassPath), если это необходимо.
Список команд сервиса Oberto
В описании команд используется путь/route для отправки команды в сам сервис, а не в ApiGateway. В качестве Input-а для команд, сервис всегда ожидает CommandRequest (как и любой другой сервис, принимающий команды), так что в описании команды указано лишь описание поля payload для CommandRequest.
Информация по добавлению команд можно прочитать в описании шаблона
Информация по описанию правил для разграничения доступов: Формат описания правил
| Название команды | EntityType | Actions | UIAction |
|---|---|---|---|
| oberto_ListPolicyRecords | PolicySet | Edit | - |
| oberto_ListPolicyRecordsV2 | PolicySet | Edit | - |
| oberto_GetPolicyValue | PolicySet | Edit | - |
| oberto_GetPolicyValueV2 | PolicySet | Edit | - |
| oberto_DeletePolicy | PolicySet | Edit | - |
| oberto_DeletePolicyV2 | PolicySet | Edit | - |
| oberto_DeleteAllPolicyVersions | PolicySet | Edit | - |
| oberto_DeleteAllPolicyVersionsV2 | PolicySet | Edit | - |
| oberto_UpdatePolicy | PolicySet | Edit | - |
| oberto_UpdatePolicyV2 | PolicySet | Edit | - |
| oberto_OverridePoliciesFromSource | PolicySet | Edit | - |
| oberto_Authorize | - | - | - |
| oberto_AuthorizeV2 | - | - | - |
| oberto_AuthorizationRequest | - | - | - |
ListPolicyRecords (oberto)
Payload для команды
null
Результат выполнения команды
[
{
"id": "Any_Policy_Name",
"priority": 1000,
"versions": [
"0.1.0",
"0.1.1",
"0.1.2"
]
}
]
Возвращает список идентификаторов добавленных политик с указанием их версий. Поддерживается только синхронный вызов.
- Payload: null
- Result: List[PolicyRecord]
| Команда | Путь |
|---|---|
| oberto_ListPolicyRecords | HTTP POST "/v1/policy/list" |
ListPolicyRecordsV2 (oberto)
Payload для команды
null
Результат выполнения команды
[
{
"id": "Any_Policy_Name",
"priority": 1000,
"versions": [
"0.1.0",
"0.1.1",
"0.1.2"
]
}
]
Возвращает список идентификаторов добавленных политик с указанием их версий. Поддерживается только синхронный вызов.
- Payload: null
- Result: List[PolicyRecord]
| Команда | Путь |
|---|---|
| oberto_ListPolicyRecordsV2 | HTTP POST "/v1/policy/list_v2" |
GetPolicyValue (oberto)
Payload для команды
{
"id": "Any_Policy_Name",
"version": "0.1.1"
}
Результат выполнения команды
{
"policyId": "Any_Policy_Name",
"version": "0.1.1",
"priority": 1000,
"description": "Policy effects description",
"rules": [
{
"ruleId": "Policy_rule_ID",
"rule": "permit Actions(View_WebDoc, somesvc_ListItems ,other_UpdateEntity) if User.email endsWith '_employee@email.io'",
"priority": 1,
"filters": "ResourceType.innerObject.value >= '100'"
}
]
}
Возвращает данные указанной политики. Поддерживается только синхронный вызов.
- Payload: SinglePolicy
- Result: Option[PolicyUpdateDto]
| Команда | Путь |
|---|---|
| oberto_GetPolicyValue | HTTP POST "/v1/policy/single" |
GetPolicyValueV2 (oberto)
Payload для команды
{
"id": "Any_Policy_Name",
"version": "0.1.1"
}
Результат выполнения команды
{
"policyId": "Any_Policy_Name",
"version": "0.1.1",
"priority": 1000,
"description": "Policy effects description",
"rules": [
{
"ruleId": "Policy_rule_ID",
"rule": "permit Actions(View_WebDoc, somesvc_ListItems ,other_UpdateEntity) if User.email endsWith '_employee@email.io'",
"priority": 1,
"filters": "ResourceType.innerObject.value >= '100'"
}
]
}
Возвращает данные указанной политики. Поддерживается только синхронный вызов.
- Payload: SinglePolicy
- Result: Option[PolicyUpdateDto]
| Команда | Путь |
|---|---|
| oberto_GetPolicyValueV2 | HTTP POST "/v1/policy/single_v2" |
DeletePolicy (oberto)
Payload для команды
{
"id": "Any_Policy_Name",
"version": "0.1.1"
}
Результат выполнения команды
true
Удаляет указанную политику. Поддерживается только синхронный вызов.
- Payload: SinglePolicy
- Result: Boolean
| Команда | Путь |
|---|---|
| oberto_DeletePolicy | HTTP POST "/v1/policy/delete" |
DeletePolicyV2 (oberto)
Payload для команды
{
"id": "Any_Policy_Name",
"version": "0.1.1"
}
Результат выполнения команды
true
Удаляет указанную политику. Поддерживается только синхронный вызов.
- Payload: SinglePolicy
- Result: Boolean
| Команда | Путь |
|---|---|
| oberto_DeletePolicyV2 | HTTP POST "/v1/policy/delete_v2" |
DeleteAllPolicyVersions (oberto)
Payload для команды
"Any_Policy_Name"
Результат выполнения команды
true
Удаляет все версии для указанного идентификатора политики. Поддерживается только синхронный вызов.
- Payload: PolicyId
- Result: Boolean
| Команда | Путь |
|---|---|
| oberto_DeleteAllPolicyVersions | HTTP POST "/v1/policy/deleteAll" |
DeleteAllPolicyVersionsV2 (oberto)
Payload для команды
"Any_Policy_Name"
Результат выполнения команды
true
Удаляет все версии для указанного идентификатора политики. Поддерживается только синхронный вызов.
- Payload: PolicyId
- Result: Boolean
| Команда | Путь |
|---|---|
| oberto_DeleteAllPolicyVersionsV2 | HTTP POST "/v1/policy/deleteAll_v2" |
UpdatePolicy (oberto)
Payload для команды
{
"policyId": "Any_Policy_Name",
"version": "0.1.1",
"priority": 1000,
"description": "Policy effects description",
"rules": [
{
"ruleId": "Policy_rule_ID",
"rule": "permit Actions(View_WebDoc, somesvc_ListItems ,other_UpdateEntity) if User.email endsWith '_employee@email.io'",
"priority": 1,
"filters": "ResourceType.innerObject.value >= '100'"
}
]
}
Результат выполнения команды
true
Добавляет версию политики. Каждая версия должна иметь уникальное policyId и уникальную version внутри политики. Policy_rule_ID должны быть уникальными внутри текущей версии.
Поддерживается только синхронный вызов.
- Payload: PolicyUpdateDto
- Result: Boolean
| Команда | Путь |
|---|---|
| oberto_UpdatePolicy | HTTP POST "/v1/policy/update" |
UpdatePolicyV2 (oberto)
Payload для команды
{
"policyId": "Any_Policy_Name",
"version": "0.1.1",
"priority": 1000,
"description": "Policy effects description",
"rules": [
{
"ruleId": "Policy_rule_ID",
"rule": "permit Actions(View_WebDoc, somesvc_ListItems ,other_UpdateEntity) if User.email endsWith '_employee@email.io'",
"priority": 1,
"filters": "ResourceType.innerObject.value >= '100'"
}
]
}
Результат выполнения команды
true
Добавляет версию политики. Каждая версия должна иметь уникальное policyId и уникальную version внутри политики. Policy_rule_ID должны быть уникальными внутри текущей версии.
Поддерживается только синхронный вызов.
- Payload: PolicyUpdateDto
- Result: Boolean
| Команда | Путь |
|---|---|
| oberto_UpdatePolicyV2 | HTTP POST "/v1/policy/update_v2" |
OverridePoliciesFromSource (oberto)
Payload для команды
"Authzforce"
Результат выполнения команды
10
Удаляет все текущие версии политик и добавляет последние версии из указанного источника политик. Поддерживается только синхронный вызов.
- Payload: XacmlPolicySource
- Result: Int
| Команда | Путь |
|---|---|
| oberto_OverridePoliciesFromSource | HTTP POST "/v1/policy/overridePoliciesFromSource" |
Authorize (oberto)
Payload для команды
{
"resourceId": "123456",
"resourceType": "ProposalFile",
"actions": [
"DownloadFile",
"View_WebDoc"
],
"attributes": {
"my_custom_attribute": [
"value_for_this_attribute",
"another_value"
]
}
}
Результат выполнения команды
{
"DownloadFile": true,
"View_WebDoc": true
}
Проверяет доступность действий для конкретного ресурса, указанных во входных параметрах.
Поддерживается только синхронный вызов.
- Payload: CommandsAuthorization
- Result: Map[String, Boolean]
| Команда | Путь |
|---|---|
| oberto_Authorize | HTTP POST "/v1/authorize" |
AuthorizeV2 (oberto)
Payload для команды
{
"resourceId": "123456",
"resourceType": "ProposalFile",
"actions": [
"DownloadFile",
"View_WebDoc"
],
"attributes": {
"my_custom_attribute": [
"value_for_this_attribute",
"another_value"
]
}
}
Результат выполнения команды
{
"DownloadFile": true,
"View_WebDoc": true
}
Проверяет доступность действий для конкретного ресурса, указанных во входных параметрах. Отличается от "первой версии" тем, что не делает вызов в Authzforce-server (локальный расчет политик).
Поддерживается только синхронный вызов.
- Payload: CommandsAuthorization
- Result: Map[String, Boolean]
| Команда | Путь |
|---|---|
| oberto_AuthorizeV2 | HTTP POST "/v1/authorize_v2" |
AuthorizationRequest (oberto)
Payload для команды
{
"attributes": [
{
"id": "MyDocument.id",
"cat": "urn:oasis:names:tc:xacml:3.0:attribute-category:resource",
"values": [
"123",
"987"
]
},
{
"id": "User.departmentCode",
"cat": "urn:oasis:names:tc:xacml:1.0:subject-category:access-subject",
"values": [
"FirstDepartmentCode",
"SecondDepartmentCode"
]
}
],
"userContext": {
"id": "67287d83-25dd-4d3c-9559-e5cc02860a3f",
"emailOpt": "test_user_email@embedika.ru",
"groupIds": [
"group_for_regular_users",
"group_for_admins"
]
}
}
Результат выполнения команды
{
"allow": true,
"entityFilters": "MyResource.name contains 'Test resource'"
}
Выполняет запрос авторизации с указанными параметрами. Не делает вызов в Authzforce-server (локальный расчет политик). Если во входных данных не указано поле "userContext", то будут использованы данные пользователя, который вызвал данную команду.
Поддерживается только синхронный вызов.
- Payload: AuthorizationRequestDTO
- Result: AuthorizationResult
| Команда | Путь |
|---|---|
| oberto_AuthorizationRequest | HTTP POST "/v1/authorizationRequest" |
Модели сервиса Oberto
PolicyId (oberto)
Идентификатор политики. Соответствует типу String.
XacmlPolicySource (oberto)
Идентификатор источника политик. Поддерживаемые значения: Authzforce.
CommandsAuthorization (oberto)
Данные для авторизации набора действий по отношению к заданному ресурсу.
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| resourceId | String | Нет | Идентификатор конкретного объекта с типом resourceType, для которого проверяются actions. |
| resourceType | String | Нет | Название типа ресурса, для которого проверяются actions. |
| actions | List[String] | Да | Список идентификаторов действий. Может быть пустым, но тогда и результат будет пустым (завершение команды без проверок доступа). |
| attributes | Map[String, List[String]] | Нет | Произвольный список аттрибутов ресурса (можно задавать атрибуты любого ресурса). Пример: '{ "MyDocument.authorId": ["123"] }'. |
PolicyRecord (oberto)
Запись с информацией о политике и набором ее версий.
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| id | PolicyId | Да | Идентификатор политики |
| priority | Int | Нет | Приоритет политики (чем меньше, тем раньше она будет проверена). null = после всех политик, у которых установлен приоритет. |
| versions | List[String] | Да | Список версий политик. Версия представленна в виде строки (semver) и должна соответствовать regexp "\d{1,16}.\d{1,16}.\d{1,16}", где доступны только цифры и точки: "0.1.0". |
SinglePolicy (oberto)
Идентификатор конкретной версии политики.
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| id | PolicyId | Да | Идентификатор политики |
| version | String | Да | Версия политики. В данной структуре, версия не должна соответствовать какому-либо формату. |
PolicyUpdateDto (oberto)
Данные новой версии политики.
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| policyId | PolicyId | Да | Идентификатор политики. Должен быть уникальным значением в рамках всей системы. |
| version | String | Да | Версия политики, представленная в виде строки (semver). Должна быть уникальной в рамках политики и соответствовать regexp "\d{1,16}.\d{1,16}.\d{1,16}". |
| priority | Int | Нет | Приоритет политики (чем меньше, тем раньше она будет проверена). null = после всех политик, у которых установлен приоритет. |
| description | String | Да | Описание политики. |
| rules | PolicyRuleDto | Да | Список правил. |
PolicyRuleDto (oberto)
Описание правила применяемого в политике.
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| ruleId | String | Да | Идентификатор правила. Должен быть уникальным значением в рамках политики. |
| rule | String | Да | Правило применения эффекта (permit/deny). Формат описания правил. |
| priority | Int | Да | Приоритет правила (чем меньше, тем раньше оно будет проверена). Обязательное поле. |
| filters | Condition | Нет | Фильтры, которые нужно применить при обращении к списку. |
AuthorizationRequestDTO (oberto)
Описание правила применяемого в политике.
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| attributes | List[AttributeWithValues] | Да | Список атрибутов и их значений для запроса. |
| userContext | UserContext | Нет | Данные о пользователе, для которого будет проверятся доступность. Нужно в том случае, если проверяемый пользователь отличается от пользователя, который вызвал запрос авторизации. |
Формат описания правил
RuleFormat (oberto)
Формат описания правил.
Шаблон для описания правил
{permit|deny} Actions(name, name, ...) if CONDITION
Варианты описания сранения значений:
1) {User|name}.field.path Operation 'Value'
2) {anyOf|allOf} {User|name}.field.path Operation {anyOf|allOf} ['Value', 'Value', 'Value', ...]
Примеры правил:
1) разрешает редактирования политик (тип "PolicySet") если email пользователя начинается с "manager":
permit Actions(Edit) if (resource.entityType == 'PolicySet' and User.email startsWith 'manager')
P.S. Условие на конкретный тип ресурса ("resource.entityType == 'PolicySet'") может быть записано, как только название ресурса без дополнительных условий ("PolicySet")
2) разрешает просмотр журнала событий (тип отсутствует, т.е. ограничивается только действием) если группа пользователя одна из списка или доступ требуется для определенного пользователя:
permit Actions(ViewEventsJournal) if (User.groupId == anyOf ['manager', 'admin', 'business_admin'] or User.id = '123456789')
Описание правила можно разделить на несколько частей "Effect Actions IfClause"
- Effect может содержать одно из двух ключевых слов:
permin,deny - Actions содержит список действий, на которые будет распространяться правило. Список правил начинается со
слова
Actions, заключен в скобки и разделен запятыми.
Для списка A,B,C =>Actions(A, B, C). - IfClause содержит условия для пользоваетеля и целевого ресурса, при выполнении которых будет применяться
правило.
Условия начинаются со словаifи разделены на группы. Внутри каждой группы могут быть другие группы условий или само условие.
Более подробно, структура описана на странице Condition.
Доступные операции для сравнения значений:
==два значения равны
>=левое значение больше или равно правому
<=левое значение меньше или равно правому
containsлевое значение содержит подстроку, равную правой
startsWithлевое значение начинается со строки, что справа
endsWithлевое значение заканчивается строкой, что справа
Sagas: библиотека для описания Саг
Сага/Saga представляет собой цепочку переходов между состояниями (шагов)..
Каждый переход имеет две операции: переход к следующему состоянию (переход впепед) или откат изменений вызваных переходом (переход назад).
Под операцией перехода понимается любая операция, которая соответстует List[A] => A. Где, A это базовый класс для всех результатов операций.
Результаты выполненых операций складываются в один список, таким образом List[A] представляет собой состояние Саги, которое содержит результаты выполненых переходов.
Для описания состояния Саги используется класс SagaState. Для описания переходов есть несколько интерфесов, но все они расширяют базовый Transition. Для описания Саги, как списка переходов используется SagaDefinition.
Описание Саги в коде
Базовый тип для результатов выполнения переходов Саги
sealed trait StagesForSagaName
Результаты переходов
object StagesForSagaName {
// init or empty stage
case class FirstStage(args: SagaArgumentClass) extends StagesForSagaName
case class SecondStage(firstStageRes: Option[FirstOperationResult]) extends StagesForSagaName
case class ThirdStage() extends StagesForSagaName
case class FourthStage(thirdStageRes: ThirdOperationResult) extends StagesForSagaName
}
Реализация переходов
case class FirstWithoutCommandsOp_SagaName(dispatcher: Dispatcher,
sagaLogger: Logger)
(implicit ec: ExecutionContext)
extends SyncSagaStageTransition[Future, StagesForSagaName] {
override protected type OperationContext = Unit
override protected def transitionImpl(previousStages: List[StagesForSagaName], context: Unit): Future[StagesForSagaName] = {
sagaLogger.info("Execute FirstOperation in SagaName")
val operationArguments = convertToForwardArgs(previousStages)
val opResult = FirstOperationResult(operationArguments.toList.mkString(" => "))
val resultStage = StagesForSagaName.SecondStage(Some(opResult))
Future.successful(resultStage)
}
override protected def rollbackImpl(previousStages: List[StagesForSagaName], context: Unit): Future[Unit] = {
sagaLogger.info("Execute compensation for FirstOperation in SagaName: Nothing to do")
Future.successful(())
}
override protected def transitionResultByOperationId(operationIdentifier: Option[OperationId]): Future[Option[Future[StagesForSagaName]]] = Future.successful(None)
override protected def rollbackResultByOperationId(operationIdentifier: Option[OperationId]): Future[Option[Future[Unit]]] = Future.successful(None)
override protected def generateOperationContext(isRollback: Boolean, previousStages: List[StagesForSagaName]): Future[Unit] = Future.successful(())
override protected def extractOperationId(context: Unit): Option[OperationId] = None
private def convertToForwardArgs(s: List[StagesForSagaName]): SagaArgumentClass = ???
}
case class SecondSyncOp_SagaName(durationTracingEnable: Boolean,
dispatcher: Dispatcher,
sagaLogger: Logger)
(implicit ec: ExecutionContext)
extends SyncSagaStageTransition[Future, StagesForSagaName]
with CommandSendingSupport
with OperationIdAsCommandIdHelper {
override protected def transitionImpl(previousStages: List[StagesForSagaName], context: RequestContext): Future[StagesForSagaName] = {
sagaLogger.info("Execute SecondOperation")
val commandArgs = convertToForwardArgs(previousStages)
dispatcher.sendCommandSync(CommandsForSagaName.CommandToCallOn_SecondStage, commandArgs)(context)
.flatMap {
case Right(commandResult) =>
commandResult.value.flatMap(_.as[SecondOperationResult].toOption) match {
case Some(secondStageRes) if secondStageRes.someInnerResult > 100 =>
val resultStage = StagesForSagaName.ThirdStage()
Future.successful(resultStage)
case _ =>
// rollback
Future.failed(new RuntimeException("Second operation in SagaName ended unexpectedly"))
}
case Left(_) =>
Future.failed(new RuntimeException("Failed to send command for Second operation in SagaName"))
}
}
override protected def rollbackImpl(previousStages: List[StagesForSagaName], context: RequestContext): Future[Unit] = {
sagaLogger.info("Execute compensation for SecondOperation")
val compensationArgs = convertToRollbackArgs(previousStages)
dispatcher.sendCommandSync(CommandsForSagaName.CommandToCallOn_SecondStage_Compensation, compensationArgs)(context)
.flatMap {
case Right(result) => // ignore compensation result
val resultStage = StagesForSagaName.SecondStage(None)
Future.successful(resultStage)
case Left(_) =>
Future.failed(new RuntimeException("Failed to sent command"))
}
}
override protected def transitionResultByOperationId(operationIdentifier: Option[OperationId]): Future[Option[Future[StagesForSagaName]]] = {
sagaLogger.info(s"SecondOperation result have be restored from operationIdentifier '$operationIdentifier'")
commandResultByOperationId[SecondOperationResult](operationIdentifier).map { resultOpt =>
resultOpt.map(_ => Future.successful(StagesForSagaName.ThirdStage()))
}
}
override protected def rollbackResultByOperationId(operationIdentifier: Option[OperationId]): Future[Option[Future[Unit]]] = {
sagaLogger.info("SecondOperation compensation result have be restored from operationIdentifier '$operationIdentifier'")
commandResultByOperationId[Json](operationIdentifier).map { resultOpt =>
resultOpt.map(_ => Future.successful(Some()))
}
}
override protected def generateOperationContext(isRollback: Boolean, previousStages: List[StagesForSagaName]): Future[RequestContext] = {
val cr = if (!isRollback)
makeRequestContext("Second operation in SagaName")
else
makeRequestContext("Compensation for Second operation in SagaName")
Future.successful(cr)
}
private def convertToForwardArgs(s: List[StagesForSagaName]): Json = ???
private def convertToRollbackArgs(s: List[StagesForSagaName]): Json = ???
}
case class ThirdAsyncOp_SagaName(durationTracingEnable: Boolean,
dispatcher: Dispatcher,
sagaLogger: Logger)
(implicit ec: ExecutionContext)
extends AsyncSagaStageTransition[Future, StagesForSagaName]
with FutureRollbackCallbackDefaultImpl {
override protected type OperationContext = RequestContext
override protected implicit val applicativeInstance: Applicative[Future] = cats.implicits.catsStdInstancesForFuture(ec)
override protected def transitionImpl(previousStages: List[StagesForSagaName], context: RequestContext): Future[OperationCallback[Future, StagesForSagaName]] = {
sagaLogger.info("Execute ThirdOperation")
implicit val cr: RequestContext = makeRequestContext("???")
val commandArgs = convertToForwardArgs(previousStages)
dispatcher.sendCommandAsync(CommandsForSagaName.CommandToCallOn_ThirdStage, commandArgs)
.flatMap {
case Right(commandId) =>
val cb = getTransitionCallback(commandId)
Future.successful(cb)
case Left(_) =>
Future.failed(new RuntimeException("Failed to sent command"))
}
}
override protected def rollbackImpl(previousStages: List[StagesForSagaName], context: RequestContext): Future[OperationCallback[Future, Unit]] = {
sagaLogger.info("Execute compensation for ThirdOperation")
implicit val cr: RequestContext = makeRequestContext("???")
val compensationArgs = convertToRollbackArgs(previousStages)
dispatcher.sendCommandAsync(CommandsForSagaName.CommandToCallOn_ThirdStage_Compensation, compensationArgs)
.flatMap {
case Right(commandId) =>
val cb = getRollbackCallback(commandId)
Future.successful(cb)
case Left(_) =>
Future.failed(new RuntimeException("Failed to sent command"))
}
}
override def getTransitionCallback(commandId: CommandId): OperationCallback[Future, StagesForSagaName] = new ThirdOpTransitCallback(commandId)
override protected def generateOperationContext(isRollback: Boolean, previousStages: List[StagesForSagaName]): Future[RequestContext] = {
val cr = if (!isRollback)
makeRequestContext("Third operation in SagaName")
else
makeRequestContext("Compensation for third operation in SagaName")
Future.successful(cr)
}
override protected def extractOperationId(context: RequestContext): Option[OperationId] =
Some(OperationId.fromCommandId(context.commandId))
private def convertToForwardArgs(s: List[StagesForSagaName]): Json = ???
private def convertToRollbackArgs(s: List[StagesForSagaName]): Json = ???
}
final class ThirdOpTransitCallback(val commandId: CommandId)
extends OperationCallback[Future, StagesForSagaName] {
override def callback(commandAggregatedStatus: CommandAggregatedStatus): Future[StagesForSagaName.FourthStage] = {
commandAggregatedStatus.value.flatMap(_.as[ThirdOperationResult].toOption) match {
case Some(thirdStageRes) =>
val stageResult = StagesForSagaName.FourthStage(thirdStageRes)
Future.successful(stageResult)
case None =>
Future.failed(new RuntimeException("Third operation in SagaName ended unexpectedly"))
}
}
}
case class SagaNameDefinition(sagaStageService: SagaStatePersistService[Future, StagesForSagaName],
firstOp: FirstWithoutCommandsOp_SagaName,
secondOp: SecondSyncOp_SagaName,
thirdOp: ThirdAsyncOp_SagaName)
extends SagaDefinition[Future, SagaArgumentClass, StagesForSagaName] {
override val steps: StageList[Future, StagesForSagaName] =
StepsList(
firstOp,
secondOp,
thirdOp
)
override protected def initStage(arguments: SagaArgumentClass): StagesForSagaName =
StagesForSagaName.FirstStage(arguments)
override def sendSagaEvent(message: SagaEventMessage): Future[Boolean] =
// Например, отправка в Kafka topic
Future.successful(true)
override def wholeSagaResult(sagaId: SagaId, sagaState: SagaState[TestStage]): Future[Option[Json]] =
// Создание результата всей саги на основе состояния саги после завершения
Future.successful(None)
}
Чтобы описать Сагу как цепочку переходов нужно:
- Создать базовый тип (очень желательно
sealed trait), к которому можно будет привести все результаты переходов (для полученияList[A]) - Добавить 1+ классов для результатов переходов, которые будут расширять базовый тип
- Добавить переходы, расширяя
SyncSagaStageTransitionилиAsyncSagaStageTransitiontrait-ы. Расширяя данные trait-ы, необходимо описать две операцииtransitionиrollback. - Создать реализацию
SagaDefinition, которая будет содержатьStageListописывающий список переходов.
Используемые структуры
SagaDefinition
Описание саги в виде списка шагов/переходов. Это описание запускается с помощью SagaRunner
SagaRunner
Раннер саг, который контролирует выполнение саги на основе ее описания.
SagaState
Состояние саги, которое включает общий статус саги, результаты выполненных шагов и идентификатор операции предыдущей попытки выполнить шаг.
Идентификатор в поле relatedOperationId относится к идентификатору в поле index.
StepsList
Список шагов/переходов Саги, который позволяет получить следующий шаг на основе состояния Саги. Для этого у каждого элемента в списке есть свой идентификатор.
StepsListElement
Обертка над SagaTransition, которая выбирает нужный метод для вызова.
SagaTransition
Описывает шаг Саги. Переход между двумя состояниями/стадиями. Каждый переход имеет две операции: прямой переход и откат.
Условия "успеха или не успеха", необходимость повторных попыток и сложности параллельного выполнения/отката операций (в разных Сагах) не лежат в зоне отвественности Саги. Т.е. все аспекты выполнения операции должны быть описаны при реализации трейта.
SyncSagaTransition
Описывает синхронный переход между состояниями саги.
AsyncSagaTransition
Описывает асинхронный переход между состояниями саги.
В данной реализации, результат операции - OperationCallback, который завязан на команды Verdi.
OperationCallback
Callback, который будет вызван при завершении команды с заданным CommandId.
SagaManager
Менеджер callback-ов саг, который вызывает необходимый callback при завершении команды или удаляет его если время ожидания превышено.
SagaStatus
Статус Саги.
| Значение | Описание |
|---|---|
| Initiated | Сага была иницирована, т.е. был присвоен Идентификатор и созданно состояние |
| Running | Сага запущенна, т.е. первая операция уже была запущенна и Сага в процессе прямого выполнения |
| Rollback | Сага откатывается, т.е. был начат процесс отката |
| Success | Сага успешно завершенна, т.е. Сага была запущенна и прямое выполнение завершилось без вызова отката. |
| Failure | Сага неудачно завершенна, т.е. был вызван процесс отката и Сага завершила откат. |
SagaEvent
Тип события саги.
| Значение | Описание |
|---|---|
| Initiated | Сага иницирована, т.е. ей присвоен SagaId и первоначальное состояние. |
| Started | Начался процесс прямого выполнения саги |
| RollbackStarted | Начался процесс отката саги |
| FinishedSuccess | Вся сага завершена успешно |
| FinishedFailure | Вся сага завершена. Т.к. был процесс отката, то завершение считается неуспешным. |
| StepStarted | Началось выполнение шага саги |
| StepCompleted | Шаг саги завершен успешно |
| StepFailed | Шаг саги завершен неуспешно/с ошибкой |
SagaEventMessage
Сообщение описывающее событие саги
SagaStateService
Интерфейс для получения состояния саги из хранилища. Хранилище может быть любое: in-memory, Redis, PostgreSQL и т.п.
SagaStatePersistService
Интерфейс для записи состояния саги в хранилище. Хранилище может быть любое: in-memory, Redis, PostgreSQL и т.п.
Пример описания Саги
Полный пример описания Саги
package com.embedika.verdi.sagas.example
import cats.Applicative
import com.embedika.verdi.models.RequestContext
import com.embedika.verdi.models.command.{CommandAggregatedStatus, CommandId, CommandName, CommandStatus}
import com.embedika.verdi.sagas._
import com.embedika.verdi.senderlib.Dispatcher
import io.circe.{Decoder, Json}
import io.circe.generic.semiauto.deriveDecoder
import org.slf4j.Logger
import scala.concurrent.{ExecutionContext, Future}
import scala.util.Success
//
// stages description
//
case class SagaArgumentClass() {
def toList: List[String] = ???
}
object SagaArgumentClass {
implicit val jsonDecoder: Decoder[SagaArgumentClass] = deriveDecoder
}
case class FirstOperationResult(data: String)
object FirstOperationResult {
implicit val jsonDecoder: Decoder[FirstOperationResult] = deriveDecoder
}
case class SecondOperationResult(someInnerResult: Int)
object SecondOperationResult {
implicit val jsonDecoder: Decoder[SecondOperationResult] = deriveDecoder
}
case class ThirdOperationResult(data: Json, nedata: Json)
object ThirdOperationResult {
implicit val jsonDecoder: Decoder[ThirdOperationResult] = deriveDecoder
}
sealed trait StagesForSagaName
object StagesForSagaName {
// init or empty state
case class FirstStage(args: SagaArgumentClass) extends StagesForSagaName
case class SecondStage(firstStageRes: Option[FirstOperationResult]) extends StagesForSagaName
case class ThirdStage() extends StagesForSagaName
case class FourthStage(thirdStageRes: ThirdOperationResult) extends StagesForSagaName
}
object CommandsForSagaName {
val CommandToCallOn_SecondStage = CommandName("second_stage_command")
val CommandToCallOn_ThirdStage = CommandName("third_stage_command")
val CommandToCallOn_FourthStage = CommandName("fourth_stage_command")
val CommandToCallOn_SecondStage_Compensation = CommandName("second_stage_command_Compensation")
val CommandToCallOn_ThirdStage_Compensation = CommandName("third_stage_command_Compensation")
val CommandToCallOn_FourthStage_Compensation = CommandName("fourth_stage_command_Compensation")
}
//
// stages implementation
//
case class SagaNameDefinition(sagaStageService: SagaStatePersistService[Future, StagesForSagaName],
firstOp: FirstWithoutCommandsOp_SagaName,
secondOp: SecondSyncOp_SagaName,
thirdOp: ThirdAsyncOp_SagaName)
extends SagaDefinition[Future, SagaArgumentClass, StagesForSagaName] {
override val steps: StepsList[Future, StagesForSagaName] =
StepsList(
firstOp,
secondOp,
thirdOp
)
override protected def initStage(arguments: SagaArgumentClass): StagesForSagaName =
StagesForSagaName.FirstStage(arguments)
override def sendSagaEvent(message: SagaEventMessage): Future[Boolean] = Future.successful(true)
override def wholeSagaResult(sagaId: SagaId, sagaState: SagaState[TestStage]): Future[Option[Json]] =
Future.successful(None)
}
case class FirstWithoutCommandsOp_SagaName(dispatcher: Dispatcher,
sagaLogger: Logger)
(implicit ec: ExecutionContext)
extends SyncSagaTransition[Future, StagesForSagaName] {
override protected type OperationContext = Unit
override protected def transitionImpl(previousStages: List[StagesForSagaName], context: Unit): Future[StagesForSagaName] = {
sagaLogger.info("Execute FirstOperation in SagaName")
val operationArguments = convertToForwardArgs(previousStages)
val opResult = FirstOperationResult(operationArguments.toList.mkString(" => "))
val resultStage = StagesForSagaName.SecondStage(Some(opResult))
Future.successful(resultStage)
}
override protected def rollbackImpl(previousStages: List[StagesForSagaName], context: Unit): Future[Unit] = {
sagaLogger.info("Execute compensation for FirstOperation in SagaName: Nothing to do")
Future.successful(())
}
override protected def transitionResultByOperationId(operationIdentifier: Option[OperationId]): Future[Option[Future[StagesForSagaName]]] = Future.successful(None)
override protected def rollbackResultByOperationId(operationIdentifier: Option[OperationId]): Future[Option[Future[Unit]]] = Future.successful(None)
override protected def generateOperationContext(isRollback: Boolean, previousStages: List[StagesForSagaName]): Future[Unit] = Future.successful(())
override protected def extractOperationId(context: Unit): Option[OperationId] = None
private def convertToForwardArgs(s: List[StagesForSagaName]): SagaArgumentClass = ???
}
case class SecondSyncOp_SagaName(durationTracingEnable: Boolean,
dispatcher: Dispatcher,
sagaLogger: Logger)
(implicit ec: ExecutionContext)
extends SyncSagaTransition[Future, StagesForSagaName]
with CommandSendingSupport
with OperationIdAsCommandIdHelper {
override protected def transitionImpl(previousStages: List[StagesForSagaName], context: RequestContext): Future[StagesForSagaName] = {
sagaLogger.info("Execute SecondOperation")
val commandArgs = convertToForwardArgs(previousStages)
dispatcher.sendCommandSync(CommandsForSagaName.CommandToCallOn_SecondStage, commandArgs)(context)
.flatMap {
case Right(commandResult) =>
commandResult.value.flatMap(_.as[SecondOperationResult].toOption) match {
case Some(secondStageRes) if secondStageRes.someInnerResult > 100 =>
val resultStage = StagesForSagaName.ThirdStage()
Future.successful(resultStage)
case _ =>
// rollback
Future.failed(new RuntimeException("Second operation in SagaName ended unexpectedly"))
}
case Left(_) =>
Future.failed(new RuntimeException("Failed to send command for Second operation in SagaName"))
}
}
override protected def rollbackImpl(previousStages: List[StagesForSagaName], context: RequestContext): Future[Unit] = {
sagaLogger.info("Execute compensation for SecondOperation")
val compensationArgs = convertToRollbackArgs(previousStages)
dispatcher.sendCommandSync(CommandsForSagaName.CommandToCallOn_SecondStage_Compensation, compensationArgs)(context)
.flatMap {
case Right(result) => // ignore compensation result
val resultStage = StagesForSagaName.SecondStage(None)
Future.successful(resultStage)
case Left(_) =>
Future.failed(new RuntimeException("Failed to sent command"))
}
}
override protected def transitionResultByOperationId(operationIdentifier: Option[OperationId]): Future[Option[Future[StagesForSagaName]]] = {
sagaLogger.info(s"SecondOperation result have be restored from operationIdentifier '$operationIdentifier'")
commandResultByOperationId[SecondOperationResult](operationIdentifier).map { resultOpt =>
resultOpt.map(_ => Future.successful(StagesForSagaName.ThirdStage()))
}
}
override protected def rollbackResultByOperationId(operationIdentifier: Option[OperationId]): Future[Option[Future[Unit]]] = {
sagaLogger.info("SecondOperation compensation result have be restored from operationIdentifier '$operationIdentifier'")
commandResultByOperationId[Json](operationIdentifier).map { resultOpt =>
resultOpt.map(_ => Future.successful(Some()))
}
}
override protected def generateOperationContext(isRollback: Boolean, previousStages: List[StagesForSagaName]): Future[RequestContext] = {
val cr = if (!isRollback)
makeRequestContext("Second operation in SagaName")
else
makeRequestContext("Compensation for Second operation in SagaName")
Future.successful(cr)
}
private def convertToForwardArgs(s: List[StagesForSagaName]): Json = ???
private def convertToRollbackArgs(s: List[StagesForSagaName]): Json = ???
}
case class ThirdAsyncOp_SagaName(durationTracingEnable: Boolean,
dispatcher: Dispatcher,
sagaLogger: Logger)
(implicit ec: ExecutionContext)
extends AsyncSagaTransition[Future, StagesForSagaName]
with FutureRollbackCallbackDefaultImpl {
override protected type OperationContext = RequestContext
override protected implicit val applicativeInstance: Applicative[Future] = cats.implicits.catsStdInstancesForFuture(ec)
override protected def transitionImpl(previousStages: List[StagesForSagaName], context: RequestContext): Future[OperationCallback[Future, StagesForSagaName]] = {
sagaLogger.info("Execute ThirdOperation")
implicit val cr: RequestContext = makeRequestContext("???")
val commandArgs = convertToForwardArgs(previousStages)
dispatcher.sendCommandAsync(CommandsForSagaName.CommandToCallOn_ThirdStage, commandArgs)
.flatMap {
case Right(commandId) =>
val cb = getTransitionCallback(commandId)
Future.successful(cb)
case Left(_) =>
Future.failed(new RuntimeException("Failed to sent command"))
}
}
override protected def rollbackImpl(previousStages: List[StagesForSagaName], context: RequestContext): Future[OperationCallback[Future, Unit]] = {
sagaLogger.info("Execute compensation for ThirdOperation")
implicit val cr: RequestContext = makeRequestContext("???")
val compensationArgs = convertToRollbackArgs(previousStages)
dispatcher.sendCommandAsync(CommandsForSagaName.CommandToCallOn_ThirdStage_Compensation, compensationArgs)
.flatMap {
case Right(commandId) =>
val cb = getRollbackCallback(commandId)
Future.successful(cb)
case Left(_) =>
Future.failed(new RuntimeException("Failed to sent command"))
}
}
override def getTransitionCallback(commandId: CommandId): OperationCallback[Future, StagesForSagaName] = new ThirdOpTransitCallback(commandId)
override protected def generateOperationContext(isRollback: Boolean, previousStages: List[StagesForSagaName]): Future[RequestContext] = {
val cr = if (!isRollback)
makeRequestContext("Third operation in SagaName")
else
makeRequestContext("Compensation for third operation in SagaName")
Future.successful(cr)
}
override protected def extractOperationId(context: RequestContext): Option[OperationId] =
Some(OperationId.fromCommandId(context.commandId))
private def convertToForwardArgs(s: List[StagesForSagaName]): Json = ???
private def convertToRollbackArgs(s: List[StagesForSagaName]): Json = ???
}
final class ThirdOpTransitCallback(val commandId: CommandId)
extends OperationCallback[Future, StagesForSagaName] {
override def callback(commandAggregatedStatus: CommandAggregatedStatus): Future[StagesForSagaName.FourthStage] = {
commandAggregatedStatus.value.flatMap(_.as[ThirdOperationResult].toOption) match {
case Some(thirdStageRes) =>
val stageResult = StagesForSagaName.FourthStage(thirdStageRes)
Future.successful(stageResult)
case None =>
// rollback
Future.failed(new RuntimeException("Third operation in SagaName ended unexpectedly"))
}
}
}
Преобразование поисковой строки в DSL поиска
Поисковая строка может содержать ключи фильтров, скобки, операторы || и && Для поисковой строки должен существовать контекст - словарь ключей фильтров и значений для фильтрации
Пример реализации и использования QueryEvaluator
Пример поисковый строки
scala
val query = "date && (eventType || user)"
Пример контекста
scala
val context = Map("date" -> "1188583200000_", "type" -> "type1,type2", "user"->"user1")
Цель получить например sql
... eventDate >= 1188583200000 AND (eventType IN ('type1', 'type2') AND userId = 'user1')
В качестве DSL поиска будем использовать Fragment из doobie.util.fragment.Fragment
Нам нужно определить type class для Fragment, который должен уметь создавать нейтральный элемент
(в sql например это будет TRUE, т.к. TRUE может быть скомбинирован с любыми другими выражениями, не изменяя
результат этих выражения, и выражение только с WHERE TRUE будет корректным sql) и реализовывать конъюнкцию и
дизъюнкцию (AND и OR)
```scala
import com.embedika.verdi.search.MultiMonoid
import doobie.util.fragment.Fragment
import doobie.util.fragments.{ whereAnd, whereOr }
implicit val doobieAlg: MultiMonoid[Fragment] = new MultiMonoid[Fragment] { override def multiply(x: Fragment, y: Fragment): Fragment = whereAnd(x, y)
override def empty: Fragment = Fragment.empty
override def combine(x: Fragment, y: Fragment): Fragment = whereOr(x, y)
}
Пример реализации фильтров
scala
import doobie.Fragments
import doobie.implicits._
import cats.data.{ Kleisli, NonEmptyList }
val filters: Map[String, Kleisli[Option, String, Fragment]] = Map( "date" -> Kleisli(data => Some(fr""""MyTable".date >= $data""")), "type" -> Kleisli(type => Some(Fragments.in(fr""""MyTable.type""", NonEmptyList("type1", List("type2"))))), "user" -> Kleisli(user => Some(fr""""MyTable".user = $user""")), ) ``` В фильтрах должны описываться алгоритмы преобразования "1188583200000" в "eventDate >= 1188583200000", "type1,type2" в "eventType IN ('type1', 'type2')" и т.д. В конце получаем финальный Fragment ```scala import com.embedika.verdi.search.QueryEvaluator
val evaluator = QueryEvaluator.createFragment val evaluated = evaluator.evaluate(query) ```
SenderLib: библиотека для работы с командами
Библиотека предоставляет API для отправки команд, получения их статуса, регистрации команд и регистрации сервиса обрабатывающего команды.
API библиотеки можно разделить на 3 секции:
- Регистрация сервисов: ServiceDiscovery
- Регистрация команд: CommandDiscovery
- Отправка команд: Dispatcher
Используемые структуры поделены на отдельные пакеты по сущностям.
Структуры models.command
- CommandId
- CommandName
- CommandAggregatedStatus
- CommandAggregatedStatusJson
- CommandEvent
- CommandInfo
- CommandStatus
Структуры models.dispatch
Структуры models.service
Общие структуры
Структуры для настройки Kafka
ServiceDiscovery
Предоставляет API реестра сервисов, который хранит реальные адреса сервисов
Методы
RegisterServiceInstance
Регистрирует инстанс сервиса в реестре сервисов с указанными параметрами
Регистрация экземпляра сервиса
import com.embedika.verdi.models._
import com.embedika.verdi.models.service._
import com.embedika.verdi.senderlib.ServiceDiscovery
import scala.concurrent.ExecutionContext
import scala.concurrent.Future
import scala.concurrent.duration._
import scala.util.{Failure, Success}
def registration(serviceRegistry: ServiceDiscovery,
ec: ExecutionContext): Future[(ServiceId, AsyncStoppable)] = {
val serviceId = ServiceId("someid")
val serviceName = ServiceName("somename")
val healthCheckingAtConsulPeriod = 2.minutes
val healthCheckPassingToConsulPeriod = 1.minutes
val serviceInfo = DiscoverableService(
id = serviceId,
name = serviceName,
host = "localhost",
port = 8080,
version = "v1",
ttl = healthCheckingAtConsulPeriod
)
serviceRegistry.registerServiceInstance(serviceInfo)
.transform {
case Success(registerSuccess) if registerSuccess =>
val cancellation = serviceRegistry.startServiceHealthChecking(serviceId, healthCheckPassingToConsulPeriod)
Success((serviceId, cancellation))
case Success(_) =>
Failure(new Exception(s"Service instance registration failed:\n$serviceInfo"))
case Failure(ex) =>
Failure(ex)
}(ec)
}
DeregisterServiceInstance
Удаляет определенный эксземпляр сервиса из реестра сервисов, чтобы его нельзя было найти
Удаление зарегистрированного экземпляра сервиса
import com.embedika.verdi.models._
import com.embedika.verdi.models.service._
import com.embedika.verdi.senderlib.ServiceDiscovery
import scala.concurrent.ExecutionContext
import scala.concurrent.Future
val ec: ExecutionContext = ???
val serviceRegistry: ServiceDiscovery = ???
def registration(): Future[(ServiceId, AsyncStoppable)] = ???
registration().map { case (serviceId, healthCheckPassing) =>
/* wait for the instance to stop working and become needless */
healthCheckPassing.stop()
serviceRegistry.deregisterServiceInstance(serviceId)
}(ec)
StartServiceHealthChecking
Стартует процесс отправки состояния текущего экземпляра в реестр сервисов. Для отправки состояния создается задача, которая будет периодически запускаться. Результатом задачи является обновление статуса сервиса, чтобы сервис считался "активным" в реестре. Задача будет выполнятся в 0, T, 2T, 3T.
Регистрация экземпляра сервиса
import com.embedika.verdi.models.service._
import com.embedika.verdi.senderlib.ServiceDiscovery
import scala.concurrent.ExecutionContext
import scala.concurrent.duration._
import scala.util.{Failure, Success}
val ec: ExecutionContext = ???
val serviceRegistry: ServiceDiscovery = ???
val serviceId: ServiceId = ???
val serviceInfo: DiscoverableService = ???
val healthCheckPassingToRegistryPeriod: FiniteDuration = 1.minutes
serviceRegistry
.registerServiceInstance(serviceInfo)
.transform {
case Success(registerSuccess) if registerSuccess =>
val cancellation = serviceRegistry.startServiceHealthChecking(serviceId, healthCheckPassingToRegistryPeriod)
Success((serviceId, cancellation))
case Success(_) =>
Failure(new Exception(s"Service instance registration failed:\n$serviceInfo"))
case Failure(ex) =>
Failure(ex)
}(ec)
IsAlive
Проверяет есть ли живой экземпляр сервиса в реестре сервисов.
CommandDiscovery
Предоставляет API для работы с хранилищем команд.
Методы
CommandInfo
Ищет метаинформацию о команде по имени команды.
Получение информации о команде
import com.embedika.verdi.models.command._
import com.embedika.verdi.senderlib.CommandDiscovery
import scala.concurrent.ExecutionContext
val ec: ExecutionContext = ???
val commandDiscovery: CommandDiscovery = ???
def handleCommandInfo[A](cmdInfo: CommandInfo): A = ???
val commandName = CommandName("somename")
commandDiscovery
.commandInfo(commandName)
.map { infoOpt =>
infoOpt.map(handleCommandInfo)
}(ec)
PublishCommands
Публикует метаинформацию о командах. Метаинформацию об отдельных командах после можно найти по имени команды.
Публикация команд
import com.embedika.verdi.models.command.{CommandInfo, CommandName}
import com.embedika.verdi.models.dispatch._
import com.embedika.verdi.models.service.ServiceName
import com.embedika.verdi.senderlib.CommandDiscovery
import io.circe.syntax._
import scala.concurrent.ExecutionContext
val ec: ExecutionContext = ???
val commandDiscovery: CommandDiscovery = ???
def handleCommandsPublishingResult[A](isPublished: Seq[Boolean]): A = ???
val serviceName = ServiceName("somename")
val dispatchParameters = HttpDispatchParameters(
method = HttpMethod.POST,
route = Some("/someroute")
)
val commandName = CommandName("somename")
val commandMeta = CommandInfo(
commandName = commandName,
dispatchType = DispatchType.Http,
serviceName = serviceName,
parameters = dispatchParameters.asJson,
permissions = Seq.empty,
entityType = "EntityToImpact",
description = Some("Http command that returns result when receives arguments"),
isInternalCommand = true,
withoutEvents = false
)
commandDiscovery
.publishCommands(Seq(commandMeta))
.map(isPublished => handleCommandsPublishingResult(isPublished))(ec)
ListCommandsInfo
Возвращает список всех команд, опубликованных в хранилище команд на данный момент.
Список команд
import com.embedika.verdi.models.command._
import com.embedika.verdi.senderlib.CommandDiscovery
import scala.concurrent.ExecutionContext
val ec: ExecutionContext = ???
val commandDiscovery: CommandDiscovery = ???
def handleCommandInfo[A](cmdInfo: Either[(CommandName, Exception), CommandInfo]): A = ???
commandDiscovery
.listCommandsInfo()
.map { infoList =>
infoList.map(handleCommandInfo)
}(ec)
UnpublishCommand
Удаляет команду по ее имени из хранилища команд.
Dispatcher
Предоставляет API для отправки комманд
Методы
SendCommandSync
Отправляет команду и дожидается завершения ее выполнения. Команда считается завершенной, если ее статус изменился на один из "конечных".
Возвращает CommandId и CommandAggregatedStatusJson отправленной команды. В случае ошибки, возвращает DispatchError.
Синхронное выполнение команды
import com.embedika.verdi.models.debug.TracingContext
import com.embedika.verdi.models._
import com.embedika.verdi.models.command._
import com.embedika.verdi.senderlib.Dispatcher
import scala.concurrent.ExecutionContext
import scala.concurrent.Future
import scala.util.{Failure, Success}
import io.circe.{Decoder, Json}
import io.circe.syntax._
val ec: ExecutionContext = ???
val dispatcher: Dispatcher = ???
val commandArgumentsAsJson: Json = ???
val commandArgumentsAsString = commandArgumentsAsJson.noSpaces
val commandName = CommandName("someAction")
case class CommandResult(res: Int)
object CommandResult {
implicit val decoder: Decoder[CommandResult] = ???
}
val durationTracingEnable: Boolean = ???
val initBuilder = RequestContext.builder()
val reqCtxBuilder = if (durationTracingEnable) {
initBuilder.withTracing("Doing '$commandName' synchronously")
} else {
initBuilder
}
implicit val reqCtx: RequestContext = reqCtxBuilder.build()
def handleCommandResult[A](r: CommandResult): A = ???
dispatcher.sendCommandSync(commandName, commandArgumentsAsString)
.transform {
case Success(Right(commandAggregatedStatus)) =>
val handledResult: Option[_] = for {
commandResultAsJson <- commandAggregatedStatus.value
commandResult <- commandResultAsJson.as[CommandResult].toOption
} yield {
handleCommandResult(commandResult)
}
Success(handledResult)
case _ =>
Success(None)
}(ec)
SendCommandAsync
Отправляет команду без ожидания завершения ее выполнения.
Возвращает CommandId отправленной команды. В случае ошибки, возвращает DispatchError.
Асинхронное выполнение команды
import com.embedika.verdi.models.debug.TracingContext
import com.embedika.verdi.models._
import com.embedika.verdi.models.command._
import com.embedika.verdi.senderlib.Dispatcher
import scala.concurrent.ExecutionContext
import scala.concurrent.Future
import scala.util.{Failure, Success}
import io.circe.{Decoder, Json}
import io.circe.syntax._
val ec: ExecutionContext = ???
val dispatcher: Dispatcher = ???
val commandArgumentsAsJson: Json = ???
val commandArgumentsAsString = commandArgumentsAsJson.noSpaces
val commandName = CommandName("someAction")
case class CommandResult(res: Int)
object CommandResult {
implicit val decoder: Decoder[CommandResult] = ???
}
val durationTracingEnable: Boolean = ???
val initBuilder = RequestContext.builder()
val reqCtxBuilder = if (durationTracingEnable) {
initBuilder.withTracing("Doing '$commandName' asynchronously")
} else {
initBuilder
}
implicit val reqCtx: RequestContext = reqCtxBuilder.build()
dispatcher.sendCommandAsync(commandName, commandArgumentsAsString)
.transform {
case Success(Right(commandId)) =>
Success(Some(commandId))
case _ =>
Success(None)
}(ec)
SendCommandEvent
Отправляет событие CommandEvent о процессе выполнения команды, которое будет учитываться в CommandAggregatedStatus
Возвращает Right(()) при успешной отправке события, иначе Left(errorResponse), где errorResponse - DispatchError, произошедшая в процессе.
Асинхронное выполнение команды
import com.embedika.verdi.models.command._
import com.embedika.verdi.senderlib.Dispatcher
import scala.concurrent.ExecutionContext
import scala.concurrent.Future
import io.circe.Encoder
import io.circe.syntax._
case class CommandResult(res: Int)
object CommandResult {
implicit val encoder: Encoder[CommandResult] = ???
}
trait CommandStateSwitcher {
protected val ec: ExecutionContext = ???
protected val dispatcher: Dispatcher = ???
def completeCommand(commandId: CommandId,
result: CommandResult): Future[Either[ErrorResponse, Unit]] = {
val eventPayload: Option[String] = Some(result.asJson.noSpaces)
dispatcher.sendCommandEvent(commandId = commandId,
newState = CommandStatus.Completed,
payload = eventPayload)
}
}
Status
Возвращает текущий статус CommandAggregatedStatusJson команды по ее CommandId, если он существует.
Получение текущего статуса команды
import com.embedika.verdi.models.debug.TracingContext
import com.embedika.verdi.models.{RequestContext, RequestContextBuilder}
import com.embedika.verdi.models.command._
import com.embedika.verdi.senderlib.Dispatcher
import scala.concurrent.ExecutionContext
import scala.concurrent.Future
import io.circe.Decoder
import io.circe.syntax._
import scala.util.{Failure, Success}
case class CommandResult(res: Int)
object CommandResult {
implicit val decoder: Decoder[CommandResult] = ???
}
val ec: ExecutionContext = ???
val dispatcher: Dispatcher = ???
val commandId: CommandId = ???
def handleCommandResult[A](r: CommandResult): A = ???
def handleCommandError[A](error: ErrorResponse): A = ???
val durationTracingEnable: Boolean = ???
val initBuilder = RequestContext.builder()
val reqCtxBuilder = if (durationTracingEnable) {
initBuilder.withTracing("Doing '$commandName' asynchronously")
} else {
initBuilder
}
implicit val reqCtx: RequestContext = reqCtxBuilder.build()
dispatcher
.status(commandId)
.transform {
case Success(Right(Some(commandAggregatedStatus))) =>
val handledResult: Option[_] = for {
commandResultAsJson <- commandAggregatedStatus.value
commandResult <- commandResultAsJson.as[CommandResult].toOption
} yield {
handleCommandResult(commandResult)
}
Success(handledResult)
case Success(Left(error)) =>
handleCommandError(error)
Success(None)
case _ =>
Success(None)
}(ec)
PollStatus
Возвращает статус CommandAggregatedStatusJson команды по ее CommandId с задержкой
Поведение команды и ее результат зависят от ситуации:
* Если найден результат команды, то сразу же без задержки возвращает его.
* Если результат не найден, то ждет его в течении timeout. Если по его прошествию результат все еще не найден, то возвращает последний статус, если он есть.
Получение результата или статуса команды
import com.embedika.verdi.models.debug.TracingContext
import com.embedika.verdi.models.{RequestContext, RequestContextBuilder}
import com.embedika.verdi.models.command._
import com.embedika.verdi.senderlib.Dispatcher
import scala.concurrent.ExecutionContext
import scala.concurrent.Future
import scala.concurrent.duration._
import io.circe.Decoder
import io.circe.syntax._
import scala.util.{Failure, Success}
case class CommandResult(res: Int)
object CommandResult {
implicit val decoder: Decoder[CommandResult] = ???
}
val ec: ExecutionContext = ???
val dispatcher: Dispatcher = ???
val commandId: CommandId = ???
def handleCommandResult[A](r: CommandResult): A = ???
def handleCommandError[A](error: ErrorResponse): A = ???
val durationTracingEnable: Boolean = ???
val initBuilder = RequestContext.builder()
val reqCtxBuilder = if (durationTracingEnable) {
initBuilder.withTracing("Doing '$commandName' asynchronously")
} else {
initBuilder
}
implicit val reqCtx: RequestContext = reqCtxBuilder.build()
dispatcher
.pollStatus(commandId, 5.seconds)
.transform {
case Success(Right(Some(commandAggregatedStatus))) if commandAggregatedStatus.status == CommandStatus.Completed =>
val handledResult: Option[_] = for {
commandResultAsJson <- commandAggregatedStatus.value
commandResult <- commandResultAsJson.as[CommandResult].toOption
} yield handleCommandResult(commandResult)
Success(handledResult)
case Success(Left(error)) =>
handleCommandError(error)
Success(None)
case _ =>
Success(None)
}(ec)
models.command
Структуры, которые описывают команды.
CommandId
Содержит идентификатор команды.
| Название поля | Описание |
|---|---|
| raw | ID в виде строки |
CommandName
Содержит наименование команды.
| Название поля | Описание |
|---|---|
| raw | Имя в виде строки |
CommandAggregatedStatus
Содержит последний известный результат выполнения команды.
| Название поля | Описание |
|---|---|
| status | Статус выполнения команды |
| timestamp | Время получения результата команды (кол-во миллисекунд с 1970-01-01T00:00:00Z). Для HTTP команд это время получения ответа, а для Kafka - время события завершения команды. |
| value | Опциональный результат выполнения команды. Может быть как JSON строкой, так и JSON объектом. |
| httpHeaders | Заголовки HTTP ответа команды, которые нужно передавать клиенту также в виде заголовков HTTP. На APIGateway данное поле переносится из тела ответа в заголовки. |
CommandAggregatedStatusJson
Содержит наименование команды.
| Название поля | Описание |
|---|---|
| raw | JSON представление CommandAggregatedStatus в виде строки. |
CommandEvent
Содержит описание события по команде.
| Название поля | Описание |
|---|---|
| id | ID события |
| status | Новый статус выполнения команды |
| timestamp | Время изменения статуса команды |
| value | Опциональный результат выполнения команды, которы соответствует статусу. Может быть как JSON строкой, так и JSON объектом. |
CommandInfo
Содержит описание команды.
| Название поля | Описание |
|---|---|
| незаполненная | таблица |
CommandStatus
Описывает статус выполнения команды.
| Возможные значения | Описание | Дополнительно |
|---|---|---|
| Created | Команда создана | Обязательный начальный статус |
| InProcess | Началась обработка команды | Необязательный промежуточный статус |
| Completed | Команда завершена | Конечный статус. Считается успешным. |
| Failed | Команда завершена с ошибкой | Конечный статус. Считается неуспешным. |
| TimedOut | Команда не завершилась за заданное время. Выполнение прекращено. | Конечный статус. Считается неуспешным. |
ErrorResponse
Ответ команды с ошибкой в качестве результата.
| Название поля | Тип | Обязательное | Описание |
|---|---|---|---|
| code | ErrorResponseCode | да | Тип ошибки |
| message | String | нет | Сообщение об ошибке (см. https://confluence.dev.embedika.ru/pages/viewpage.action?pageId=889782432) |
| stackTrace | String | нет | Информация о стеке вызовов во время ошибки |
| businessError | Json | нет | Константа описывающая бизнес-ошибку (контекстно-специфичный код ошибки) |
| data | Json | нет | Данные, которые необходимо передавать клиенту вместе с ошибкой |
| cause | Throwable | нет | Поле не участвует в сериализации. Содержит исключение, которое является причиной ошибки. |
ErrorResponseCode
Код обозначающий тип ошибки в ответе.
| Возможные значения | Тип | Описание |
|---|---|---|
| InvalidParameters | String | Для команды переданы неверные параметры. |
| NotAvailableDestinations | String | Нет ни одного доступного получателя для данной команды. |
| NotValidResponse | String | Некорректный HTTP ответ по команде. |
| CannotWaitCommandCompletion | String | Async команда не успела завершится за определенное время. |
| CannotSend | String | Невозможно отправить команду или результат ее работы. Содержит описание причины. |
| Exception | String | Необработанный exception. Может содержать Throwable и его сообщение, являющийся причиной ошибки. |
| HttpError(statusCode: Int) | Json | Результат команды следует интерпретировать как HttpResponse с определенным статусом. |
models.dispatch
Структуры, которые описывают способ отправки команд.
DispatchError
Содержит идентификатор команды.
| Возможные значения | Описание |
|---|---|
| InvalidParameters | Для команды переданы неверные параметры |
| NotAvailableDestinations | Нет ни одного доступного получателя для данной команды |
| NotValidResponse | Некорректный HTTP ответ по команде |
| CannotWaitCommandCompletion | Команда не завершена |
| CannotSend | Невозможно отправить команду или результат ее работы. Содержит описание причины. |
| Exception | Необработанный exception. Содержит Throwable, являющийся причиной ошибки. |
DispatchType
Описывает тип отправки команды
| Возможные значения | Описание |
|---|---|
| Http | Отправка команды с помощью HTTP (HTTP request) |
| Kafka | Отправка команды с помощью Kafka (Kafka message) |
HttpDispatchParameters
Содержит параметры отправки команды по HTTP.
| Возможные значения | Описание |
|---|---|
| незаполненная | таблица |
HttpMethod
Описывает используемый метод HTTP
| Возможные значения | Описание |
|---|---|
| GET | GET запрос |
| POST | POST запрос |
KafkaDispatchParameters
Содержит параметры отправки команды через Apache Kafka.
| Название поля | Описание |
|---|---|
| незаполненная | таблица |
models.service
Структуры, которые описывают принимающий команды сервис.
ServiceId
Содержит идентификатор сервиса. Например, "documents-service-instance-1".
| Название поля | Описание |
|---|---|
| raw | ID в виде строки |
ServiceName
Содержит наименование сервиса. Например, "documents-service".
| Название поля | Описание |
|---|---|
| raw | Имя в виде строки |
ServiceAddress
Содержит адрес сервиса. Например, "192.168.0.1".
| Название поля | Описание |
|---|---|
| raw | Адрес в виде строки |
DiscoverableService
Описание сервиса, который можно найти в реестре сервисов.
| Название поля | Описание |
|---|---|
| незаполненная | таблица |
GeneralStructs
Общие структуры, не привязанные к какой-либо сущности.
RequestContext
Контекст запроса выполнения команды.
| Название поля | Описание |
|---|---|
| незаполненная | таблица |
AsyncStoppable
Некоторая продолжительная операция или непрерывная обработка, которую можно явным образом прервать. Прерывание является асинхронным.
Имеет метод def stop(): Future[Boolean], который останавливает текущую операцию.
SyncStoppable
Интерфейс описывающий процесс, который можно синхронно прервать
Senderlib-kafka
KafkaAuthConfig (senderlib)
Параметры для авторизации kafka-клиента (доступ к топикам).
| Поле | Тип | Обязательное | Описание | Ограничения |
|---|---|---|---|---|
| user | string | Да | Имя пользователя | |
| password | string | Да | Пароль пользователя | |
| truststore | TrustStoreConfig | Нет | Параметры для хранилища сертификатов | |
| topic | string | Нет | Название топика, к которому привязаны данные | |
| kind | string | Нет | Тип сущности, к которой привязаны данные | "producer" или "consumer" или "both"(равнозначно параметрам, валидным для "producer" и "consumer") или "admin"(не входит в "both") |
TrustStoreConfig (senderlib)
Параметры для хранилища сертификатов (Java key store)
| Поле | Тип | Обязательное | Описание | Ограничения |
|---|---|---|---|---|
| location | string | Да | Путь до хранилища сертификатов | |
| password | string | Нет | Пароль к хранилищу сертификатов |
KafkaAuthSettings (senderlib)
Параметры для авторизации kafka-клиента (доступ к топикам).
| Поле | Тип | Обязательное | Описание | Ограничения |
|---|---|---|---|---|
| staticAuth | KafkaAuthConfig | Нет | Данные для статической аутентификации (не зависит от топика) | |
| authMode | string | Нет | Режим аутентификации | "Static"(одна учетная запись на все запросы) или "Mapping"(учетная запись зависит от запроса) |
| authConfig | string | Нет | Данные для аутентификации в указанном режиме, если он отличается от KafkaAuthMode.Static | Строка в формате JSON с типом List[KafkaAuthConfig] |
| clientsCache | VerdiCacheConfig | Нет | Настройки кеширования Kafka-клиентов |
RetrySettings (senderlib)
Настойки для повтороного вызова функций
| Поле | Тип | Обязательное | Описание | Ограничения |
|---|---|---|---|---|
| attempts | string | Да | Максимальное количество повторных вызовов. Чтобы исключить возможность повторного вызова, нужно установить значение 0. | |
| delay | duration string | Да | Константная задержка перед повторным вызовом |
CommandResultRetryConditionKind (senderlib)
Вид условия, по которому результат команды будет считаться неуспешным и команду следует вызвать повторно. Соответствует типу String.
| Возможные значения | Описание |
|---|---|
| OnFailStatuses | Если результат команды ErrorResponse или результат имеет CommandStatus, который является неуспешным |
| OnAllExceptions | Если результат команды ErrorResponse и имеет ErrorResponseCode = Exception |
| OnSomeExceptions(Name1, Name2, ..) | Содержит список значений с разделителем , (запятая). Если результат команды ErrorResponse, имеет ErrorResponseCode = Exception. Значения в полях message или cause должны содержать одно из значений. |
SenderLibSettings
Настойки для создания senderlib-объектов через VerdiFactory. Для создания SenderLibSettings используется конфиг com.typesafe.config.Config.
| Поле | Тип | Обязательное | Значение по умолчанию | Описание | Ограничения |
|---|---|---|---|---|---|
| allowInternalCommands | bool | да | Возможность отправлять внутрение команды | ||
| commandsCacheUpdatePeriod | duration string | да | "0 ns" | Период обновления кеша с информацией о командах | |
| servicesCacheUpdatePeriod | duration string | да | "0 ns" | Период обновления кеша с информацией о сервисах | |
| commandsPrefix | string | нет | "commands" | Префикс в kv хранилище для команд | |
| consulConnectionMaxRetry | int | нет | 5 | Максимальное кол-во попыток подключения к Consul | |
| consulConnectionRetryDelay | duration string | нет | "1 seconds" | Фиксированные интервалы между попытками подключения к Consul | |
| kafkaSyncPollingTimeout | duration string | нет | "10 seconds" | Время ожидания завершения kafka-команд, которые клиент отправил "синхронно" | |
| httpCommandsRetryAttempts | int | нет | 5 | Максимальное количество повторных вызовов для HTTP команд | |
| httpCommandsRetryDelay | duration string | нет | "5 seconds" | Константная задержка перед повторным вызовом HTTP команды | |
| httpCommandsRetryKind | int | нет | "OnSomeExceptions(ConnectException)" | Вид условия, по которому результат команды будет считаться неуспешным и команду следует вызвать повторно |
Сервис технического справочника
Сервис технического справочника позволяет другим сервисам, а также пользователям, регистрировать различные технические константы,
такие как, например, человекочитаемое название вида события в журнале или описание события в разделе уведомлений.
После регистрации, константы могут быть получены на фронтенде для отображения необходимой информации.
В сервисе реализованы следующие команды:
- Регистрация сервиса
- Получение списка сервисов
- Удаление сервиса
- Регистрация типа констант
- Получение списка типов констант
- Удаление типа констант
- Автоматическая регистрация константы
- Ручная регистрация константы
- Получение информации по одной константе
- Получение списка констант
- Получение списка всех констант определенного типа
- Удаление ручного значения константы
Информация по добавлению команд можно прочитать в описании шаблона
Общая документация по сервису находится в соответствующем разделе Confluence
Локальный запуск сервиса технического справочника
При запуске сервиса ожидается, что уже развернута необходимая инфраструктура:
- PostgreSQL база по адресу, указанному в конфиге (см. ниже)
- Kafka по адресу
localhost:9092 - Consul по адресу
localhost:8500 - В Consul будет добавлен адрес Kafka по ключу
kafka_bootstrap_serverили адрес будет указан вapplication.conf. Добавлены необходимые переменные окружения:
- TECH_CONSTANTS_DB_HOST
- TECH_CONSTANTS_DB_PORT
- TECH_CONSTANTS_DB_NAME
- TECH_CONSTANTS_DB_USER
- TECH_CONSTANTS_DB_PASSWORD
Запуск из консоли с помощью SBT
TECH_CONSTANTS_DB_HOST=localhost TECH_CONSTANTS_DB_PORT=5432 TECH_CONSTANTS_DB_NAME=techConstants_db TECH_CONSTANTS_DB_USER=postgres TECH_CONSTANTS_DB_PASSWORD=12345 sbt boot/run
Список переменных окружения сервиса технического справочника
| Переменная | Описание | Значение по умолчанию |
|---|---|---|
| TECH_CONSTANTS_HTTP_HOST | Хост для HttpListener | "0.0.0.0" |
| TECH_CONSTANTS_HTTP_PORT | Порт для HttpListener | 8192 |
| TECH_CONSTANTS_KAFKA_SERVERS | Адрес Kafka | "localhost:9092" |
| TECH_CONSTANTS_KAFKA_TOPIC | Название топика для команд | "tech_constants_commands" |
| TECH_CONSTANTS_KAFKA_TOPIC_PARTITIONS | Количество партиций в топике команд | 10 |
| TECH_CONSTANTS_KAFKA_CONSUMER_GROUP | Название consumer-группы для чтения команд | "tech_constants_consumer_group" |
| TECH_CONSTANTS_KAFKA_COMMANDEVENT_TOPIC | Название топика для отправки сообщений со статусом команд | "commandevents" |
| TECH_CONSTANTS_KAFKA_CONSUMER_RESTART_MIN_BACKOFF | Минимальная задержка перед перезапуском. | 1 second |
| TECH_CONSTANTS_KAFKA_CONSUMER_RESTART_MAX_BACKOFF | Максимально возможная задержка перед перезапуском. | 30 seconds |
| TECH_CONSTANTS_KAFKA_CONSUMER_RESTART_RANDOM_FACTOR | Величина дополнительной случайной задержки: 0.0 - без случайно задержки, 1.0 - до 100% задрежки. | 0.2 |
| TECH_CONSTANTS_KAFKA_CONSUMER_RESTART_MAX_RESTARTS | Максимальное количество перезапусков в заданный период времени. | 5 |
| TECH_CONSTANTS_KAFKA_CONSUMER_RESTART_MAX_RESTARTS_WITHIN | Период времени для ограничения перезапусков. | 5 minutes |
| TECH_CONSTANTS_KAFKA_AUTH_USER | Название учетной записи Kafka. Если название не указано, то настройки авторизации не будут применены. | "" |
| TECH_CONSTANTS_KAFKA_AUTH_PASSWORD | Пароль учетной записи Kafka. | "" |
| TECH_CONSTANTS_KAFKA_AUTH_TRUSTSTORE_LOCATION | Путь до хранилища сертификатов (Java key store). Если путь не указан, то сертификат применяться не будет. | "" |
| TECH_CONSTANTS_KAFKA_AUTH_TRUSTSTORE_PASSWORD | Пароль к хранилищу сертификатов. | "" |
| TECH_CONSTANTS_KAFKA_AUTH_MODE | Режим аутентификации: static - одна учетная запись на все запросы, mapping - учетная запись зависит от запроса | "static" |
| TECH_CONSTANTS_KAFKA_AUTH_CONFIG | Настройки для аутентификации: если AuthMode = mapping, то необходим JSON с List[KafkaAuthConfig]. Переменная соответствует полю authConfig из KafkaAuthSettings. | "" |
| TECH_CONSTANTS_KAFKA_AUTH_CACHE_SIZE | Максимальный размер кеша для Kafka producer (количество активных соединений). | |
| TECH_CONSTANTS_KAFKA_AUTH_CACHE_TTL | Время жизни Kafka producer в кеше. | |
| TECH_CONSTANTS_CONSUL_ADDR | Адрес Сonsul. | "http://localhost:8500" |
| TECH_CONSTANTS_CONSUL_AUTH_USER | Название учетной записи Сonsul. Если название не указано, то настройки авторизации не будут применены. | "" |
| TECH_CONSTANTS_CONSUL_AUTH_PASSWORD | Пароль учетной записи Сonsul. | "" |
| TECH_CONSTANTS_TRACE_DURATION | Признак необходимости трассировки выполнения команд | false |
| TECH_CONSTANTS_DISCOVERABLE_ID | Id сервиса в ServiceDiscovery | "another_tech_constants_instance" |
| TECH_CONSTANTS_DISCOVERABLE_NAME | Имя сервиса в ServiceDiscovery | "techConstants" |
| TECH_CONSTANTS_DISCOVERABLE_HOST | Хост, публикуемый в ServiceDiscovery | "localhost" |
| TECH_CONSTANTS_DISCOVERABLE_PORT | Порт, публикуемый в ServiceDiscovery | 8192 |
| TECH_CONSTANTS_DISCOVERABLE_LIVETIME | Период после отправки health check, в течение которого ServiceDiscovery считает сервис живым | 2 minutes |
| TECH_CONSTANTS_DISCOVERABLE_HEALTHPASS | Периодичность отправки health check в ServiceDiscovery | 1 minute |
| TECH_CONSTANTS_AKKA_HTTP_CLIENT_MAXCON | Максимальное число одновременных исходящих HTTP-соединений | 512 |
| TECH_CONSTANTS_AKKA_HTTP_CLIENT_MAXREQ | Максимальное число одновременных исходящих HTTP-запросов | 1024 |
| TECH_CONSTANTS_AKKA_HTTP_SERVER_MAXCON | Максимальное число одновременных входящих HTTP-соединений | 1024 |
| TECH_CONSTANTS_INTERNALCMD_ALLOW | Можно ли сервису отправлять внутрисистемные команды | false |
| TECH_CONSTANTS_SENDERLIB_COMMANDS_CACHE_UPDATEPERIOD | Время кэширования данных по командам из CommandDiscovery | 10 minutes |
| TECH_CONSTANTS_SENDERLIB_SERVICES_CACHE_UPDATEPERIOD | Время кэширования данных по сервисам из ServiceDiscovery | 30 seconds |
| TECH_CONSTANTS_DB_URL | Адрес базы данных. Можно использовать вместо DB_HOST, DB_PORT и DB_NAME | |
| TECH_CONSTANTS_DB_HOST | Хост сервера базы данных | |
| TECH_CONSTANTS_DB_PORT | Порт сервера базы данных | |
| TECH_CONSTANTS_DB_NAME | Название базы данных | |
| TECH_CONSTANTS_DB_USER | Пользователь базы данных | |
| TECH_CONSTANTS_DB_PASSWORD | Пароль для пользователя базы данных | |
| TECH_CONSTANTS_DB_THREADS | Количество потоков в пуле потоков для соединения с БД | 10 |
| TECH_CONSTANTS_DB_QUEUE_SIZE | Размер очереди для действий базы данных, которые не могут быть выполнены немедленно, когда все потоки заняты. За пределами этого значения новые действия немедленно завершаются неудачей | 300 |
| TECH_CONSTANTS_DB_CONN_MAX | Максимальное количество одновременных подключений к БД | 10 |
| TECH_CONSTANTS_DB_CONN_TIMEOUT | Максимальное время ожидания ответа для соединения к БД. Если это время превышено, а соединение не становится доступным, будет брошено исключение SQLException. 1000 мс — минимальное значение. | 20 second |
| TECH_CONSTANTS_DB_ISOLATION | Уровень изоляции транзакций для новых подключений. Допустимые значения: NONE, READ_COMMITTED, READ_UNCOMMITTED, REPEATABLE_READ, SERIALIZABLE. |
"READ_COMMITTED" |
| TECH_CONSTANTS_DB_READONLY | Read-only SQL транзакция может изменять только временные таблицы. Этот параметр управляет статусом «только для чтения» по умолчанию для каждой новой транзакции. | false |
| TECH_CONSTANTS_DB_CONN_MIN | Минимальное количество одновременных подключений к БД | = DB_THREADS |
| TECH_CONSTANTS_DB_VALIDATION_TIMEOUT | Максимальное время, в течение которого соединение будет проверяться на работоспособность. 1000 мс — минимальное значение. | 1 seconds |
| TECH_CONSTANTS_DB_IDLE_TIMEOUT | Максимальное время, в течение которого соединению разрешено простаивать в пуле. Значение 0 означает, что простаивающие соединения никогда не удаляются из пула. | 10 minutes |
| TECH_CONSTANTS_DB_MAX_LIFETIME | Максимальное время жизни соединения в пуле. Когда простаивающее соединение достигает этого времени ожидания, даже если оно недавно использовалось, оно будет удалено из пула. Значение 0 указывает на отсутствие максимального срока службы. | 30 minutes |
| TECH_CONSTANTS_DB_INITIALIZATION_FAIL_FAST | Определяет, будет ли пул «быстро выходить из строя», если пул не может быть успешно заполнен начальными соединениями. Если соединения не могут быть созданы во время запуска пула, будет выдано исключение RuntimeException. Это свойство не имеет никакого эффекта, если minConnections равно 0. | false |
| TECH_CONSTANTS_DB_LEAK_DETECTION_THRESHOLD | Время, в течение которого соединение может находиться вне пула, прежде чем будет зарегистрировано сообщение, указывающее на возможную утечку соединения. Значение 0 означает, что обнаружение утечек отключено. Наименьшее приемлемое значение для включения обнаружения утечек составляет 10 с. | 0 |
| TECH_CONSTANTS_DB_CONNECTION_TEST_QUERY | Выражение, которое будет выполнено непосредственно перед получением соединения из пула для проверки того, что соединение с базой данных все еще активно. Оно зависит от базы данных и должно представлять собой запрос, требующий минимальной обработки базой данных (например, «VALUES 1»). Если этот параметр не установлен, вместо него используется метод JDBC4 Connection.isValid(). |
"SELECT 1" |
| TECH_CONSTANTS_DB_REGISTER_MBEANS | Зарегистрированы ли JMX Management Beans («MBeans») | false |
| TECH_CONSTANTS_LOG_OUTPUT | Параметры вывода логов: STDOUT - обычный лог в консоль, STDOUT_CEF - лог в формате CEF в консоль, FILE - обычный лог в файл, FILE_CEF - лог в формате CEF в файл. Может принимать несколько значений через любой разделитель (например, "STDOUT FILE_CEF"). Преобразования, если указано несколько значений: присутствуют "STDOUT" + "STDOUT_CEF" = учитывается только "STDOUT_CEF"; "FILE" + "FILE_CEF" = "FILE_CEF"; |
"STDOUT" |
| TECH_CONSTANTS_LOGGING_SRC_IP | Параметр SRC (источник/source, на который ссылается событие) для логов в формате CEF. Если не установлена, src=notFound. Требуемый формат: IPv4, например "192.168.10.1". |
|
| TECH_CONSTANTS_LOGGING_SRC_HOST | Параметр SHOST (источник/source, на который ссылается событие) для логов в формате CEF. Если не установлена, shost=notFound. Требуемый формат: fully qualified domain name (FQDN), например "host" или "host.domain.com". |
|
| TECH_CONSTANTS_LOGGING_DST_IP | Параметр DST (получатель/destination, на который ссылается событие) для логов в формате CEF. Если не установлена, dst=notFound. Требуемый формат: IPv4, например "192.168.10.1". |
|
| TECH_CONSTANTS_LOGGING_CEF_VER | Версия формата CEF: либо 0, либо 1. Рекомендуется использовать 0. |
0 |
| TECH_CONSTANTS_SENDERLIB_COMMANDS_HTTP_RETRY_ATTEMPTS | Поле attempts из RetrySettings | 5 |
| TECH_CONSTANTS_SENDERLIB_COMMANDS_HTTP_RETRY_DELAY | Поле delay из RetrySettings | "5 seconds" |
| TECH_CONSTANTS_SENDERLIB_COMMANDS_HTTP_RETRY_KIND | CommandResultRetryConditionKind | "OnSomeExceptions(ConnectException)" |
Формат CEF для логов сервиса Tech constants
У логов есть возможность включить формат CEF для логирования по следующему шаблону:
2022-12-14T16:56:31+0500 CEF:Version|Device Vendor|Device Product|Device Version|EventClassId|Message|Severity|src=? dst=? shost=? suid=? suser=? msg=? end=currentTimeMillis|.
Чтобы включить логирование в формате CEF, нужно задать переменную TECH_CONSTANTS_LOG_OUTPUT = STDOUT_CEF. Если логи пишутся в формате CEF, то необходимо задать следующие переменные, которые по умолчанию не заданы:
TECH_CONSTANTS_LOGGING_SRC_IP, для параметраsrcв логахTECH_CONSTANTS_LOGGING_SRC_HOST, для параметраshostв логахTECH_CONSTANTS_LOGGING_DST_IP, для параметраdstв логах
В файле boot/src/main/resources/logback.xml можно поменять class path для основного класса приложения (переменная projectMainClassPath), если это необходимо.
Команды сервиса технического справочника
UpsertService
На входе свойства регистрируемого или обновляемого сервиса
{
"code": "techConstants",
"name": "Технический справочник",
"description": "Сервис технического справочника"
}
На выходе Boolean (признак успешности исполнения)
true
Регистрирует или обновляет сервис, для которого будут указываться константы.
- Input: ServiceUpsertDTO
- Output: Boolean
| Команда | Путь |
|---|---|
| techConstants_UpsertService | Kafka Topic "tech_constants_commands" |
ListServices
Входные данные отсутствуют
На выходе перечень зарегистрированных сервисов
[
{
"code": "techReferences",
"name": "Технический справочник",
"description": "Сервис технического справочника"
}
]
Возвращает перечень зарегистрированных сервисов.
- Input: -
- Output: List[Service]
| Команда | Путь |
|---|---|
| techConstants_ListServices | HTTP POST /v1/service/list |
DeleteService
На входе код удаляемого сервиса
"techConstants"
На выходе Boolean (признак успешности исполнения)
true
Удаляет зарегистрированный сервис.
- Input: String
- Output: Boolean
| Команда | Путь |
|---|---|
| techConstants_DeleteService | Kafka Topic "tech_constants_commands" |
UpsertConstantType
На входе свойства регистрируемого или обновляемого типа констант
{
"code": "journal",
"name": "Журнал",
"description": "Описание типов событий журнала"
}
На выходе Boolean (признак успешности исполнения)
true
Регистрирует или обновляет тип констант.
- Input: ConstantTypeUpsertDTO
- Output: Boolean
| Команда | Путь |
|---|---|
| techConstants_UpsertConstantType | Kafka Topic "tech_constants_commands" |
ListConstantTypes
Входные данные отсутствуют
На выходе перечень зарегистрированных типов констант
[
{
"code": "journal",
"name": "Журнал",
"description": "Описание типов событий журнала"
}
]
Возвращает перечень зарегистрированных типов констант.
- Input: -
- Output: List[ConstantType]
| Команда | Путь |
|---|---|
| techConstants_ListConstantType | HTTP POST /v1/constantType/list |
DeleteConstantType
На входе код удаляемого типа констант
"journal"
На выходе Boolean (признак успешности исполнения)
true
Удаляет зарегистрированный тип констант.
- Input: String
- Output: Boolean
| Команда | Путь |
|---|---|
| techConstants_DeleteConstantType | Kafka Topic "tech_constants_commands" |
UpsertAutoConstant
На входе свойства регистрируемой константы
{
"code": "testConstant",
"constantType": "testConstantType",
"service": "testServiceType",
"name": "Тестовая константа",
"description": "Описание тестовой константы",
"settings": {"test": "auto"},
"serviceVersion": 1
}
На выходе Boolean (признак успешности исполнения)
true
Регистрирует или обновляет константу. Должно вызываться сервисом.
Константа будет обновлена, если значение serviceVersion, указанное в запросе,
не ниже значения того же поля у сохраненной в справочнике константы.
При этом в ином случае все равно будет возвращено true, как признак того, что запрос отработал корректно.
- Input: ConstantUpsertAutoDTO
- Output: Boolean
| Команда | Путь |
|---|---|
| techConstants_UpsertAutoConstant | Kafka Topic "tech_constants_commands" |
UpsertManualConstant
На входе свойства регистрируемой константы
{
"code": "testConstant",
"constantType": "testConstantType",
"service": "testServiceType",
"name": "Тестовая константа - переопределение вручную",
"description": "Описание тестовой константы - переопределение вручную",
"settings": {"test": "manual"}
}
На выходе Boolean (признак успешности исполнения)
true
Регистрирует или обновляет константу вручную. Должно вызываться человеком.
Если уже зарегистрирована автоматическая константа с таким же кодом и типом, то при запросе данных констант(ы), приоритет будет отдаваться ручной.
- Input: ConstantUpsertManualDTO
- Output: Boolean
| Команда | Путь |
|---|---|
| techConstants_UpsertManualConstant | Kafka Topic "tech_constants_commands" |
GetConstant
На входе код и тип константы
{
"code": "testConstant",
"constantType": "testConstantType"
}
На выходе данные константы
{
"code": "testConstant",
"constantType": "testConstantType",
"service": "testService",
"name": "Тестовая константа - переопределение вручную",
"description": "Описание тестовой константы - переопределение вручную",
"settings": {"test": "manual"},
"source": "Auto"
}
Получает актуальную версию данных по константе.
Если константа была зарегистрирована и автоматически и вручную, получит ручную версию.
- Input: ConstantIdentityDTO
- Output: Constant
| Команда | Путь |
|---|---|
| techConstants_GetConstant | HTTP POST /v1/constant/get |
ListAllConstants
На входе поисковой запрос
{
"query": "source",
"context": {"source": "auto"},
"sorting": {"fieldName": "constanttype.name", "order": "Asc"},
"paging": {"page": 1, "count": 5}
}
На выходе страница со списком констант
{
"items": [
{
"code": "ViewDataModel",
"constantType": "uiActionType",
"service": "dataModel",
"name": "Просмотр модели данных в виде схемы",
"description": null,
"settings": null,
"source": "Auto"
},
{
"code": "ViewDataModelSection",
"constantType": "uiActionType",
"service": "dataModel",
"name": "Просмотр раздела \"Модель данных\"",
"description": null,
"settings": null,
"source": "Auto"
},
{
"code": "UpdateObject",
"constantType": "uiActionType",
"service": "dataModel",
"name": "Редактирование объекта",
"description": null,
"settings": null,
"source": "Auto"
},
{
"code": "DocumentSearch",
"constantType": "uiActionType",
"service": "dataModel",
"name": "Работа с разделом \"Поиск с использованием документа\"",
"description": null,
"settings": null,
"source": "Auto"
},
{
"code": "ViewObjectCard",
"constantType": "uiActionType",
"service": "dataModel",
"name": "Просмотр карточки объекта",
"description": null,
"settings": null,
"source": "Manual"
}
],
"total": 27
}
Получает список констант по поисковому запросу.
Доступные поля для фильтрации и виды фильтров по ним:
| Поле | Виды фильтров |
|---|---|
| id | InSetQuery |
| code | InSetQuery, LikeQuery |
| name | InSetQuery, LikeQuery |
| constantType | InSetQuery |
| source | InSetQuery |
Доступные поля для сортировки:
| Поле | Примечание |
|---|---|
| code | - |
| name | - |
| constantType.name | По человекочитаемому названию типа константы |
| source | В порядке приоритета, т.е. сначала Manual (приоритет = 1), потом Auto (приоритет = 2) |
| Команда | Путь |
|---|---|
| techConstants_ListAllConstants | HTTP POST /v1/constant/list |
ListConstantsByType
На входе код и тип константы
{
"codes": ["testConstant"],
"constantType": "testConstantType"
}
На выходе данные по списку констант
[
{
"code": "testConstant",
"constantType": "testConstantType",
"service": "testService",
"name": "Тестовая константа - переопределение вручную",
"description": "Описание тестовой константы - переопределение вручную",
"settings": {"test": "manual"},
"source": "Auto"
}
]
Получает список актуальных версий констант по типу констант, и, опционально, перечню кодов констант.
- Input: ConstantsByTypeDTO
- Output: List[Constant]
DeleteManualConstant
На входе код и тип константы
{
"code": "testConstant",
"constantType": "testConstantType"
}
На выходе Boolean (признак успешности исполнения)
true
Удаляет ручную версию данных константы; т.о. если была зарегистрирована также автоматическая версия той же константы, произойдет откат до нее; если не было - то константа будет удалена полностью.
- Input: ConstantIdentityDTO
- Output: Boolean
| Команда | Путь |
|---|---|
| techConstants_DeleteManualConstant | Kafka Topic "tech_constants_commands" |
Объекты сервиса технического справочника
Service
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| code | string | Да | Код сервиса |
| name | string | Да | Наименование сервиса |
| description | string | Нет | Описание сервиса |
ServiceUpsertDTO
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| code | string | Да | Код сервиса |
| name | string | Да | Наименование сервиса |
| description | string | Нет | Описание сервиса |
ConstantType
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| code | string | Да | Код типа констант |
| name | string | Да | Наименование типа констант |
| description | string | Нет | Описание типа констант |
ConstantTypeUpsertDTO
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| code | string | Да | Код типа констант |
| name | string | Да | Наименование типа констант |
| description | string | Нет | Описание типа констант |
Constant
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| code | string | Да | Код константы |
| constantType | string | Да | Код типа константы |
| service | string | Да | Код сервиса константы |
| name | string | Да | Наименование типа констант |
| description | string | Нет | Описание типа констант |
| settings | json | Нет | Дополнительные произвольные настройки константы |
| source | string | Да | Источник текущей версии константы, Auto или Manual (автоматический или ручной) |
ConstantIdentityDTO
DTO для запросов, связанных с одной константой, например на удаление или поиск.
Поскольку несколько констант могут иметь один и тот же код, но разный тип, требуется указать оба соответствующих поля.
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| code | string | Да | Код константы |
| constantType | string | Да | Код типа константы |
ConstantUpsertAutoDTO
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| code | string | Да | Код константы. Константы разного типа могут иметь одинаковый код. |
| constantType | string | Да | Код типа константы |
| service | string | Да | Код сервиса константы |
| name | string | Да | Наименование типа констант |
| description | string | Нет | Описание типа констант |
| settings | json | Нет | Дополнительные произвольные настройки константы |
| serviceVersion | integer | Да | Версия константы внутри сервиса |
ConstantUpsertManualDTO
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| code | string | Да | Код константы. Константы разного типа могут иметь одинаковый код. |
| constantType | string | Да | Код типа константы |
| service | string | Да | Код сервиса константы |
| name | string | Да | Наименование типа констант |
| description | string | Нет | Описание типа констант |
| settings | json | Нет | Дополнительные произвольные настройки константы |
ConstantsByTypeDTO
DTO для запроса на список констант определенного типа
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
| constantType | string | Да | Код типа константы |
| codes | array[string] | Нет | Перечень кодов констант, которые надо получить. Если не указано - получит все константы указанного типа. |
Регистрация данных в сервисе технического справочника
Сервис технического справочника позволяет другим сервисам регистрировать различные технические константы,
такие как, например, человекочитаемое название вида события в журнале или описание события в разделе уведомлений.
После регистрации, константы могут быть использованы на фронтенде для отображения необходимой информации.
Предполагается, что (пере)регистрация будет происходить при каждом старте инстанса сервиса,
примерно там же, где, например, происходит регистрация команд в консуле.
Константа, по сути, является маппингом из "код константы + код типа константы" в "наименование + описание + дополнительные произвольные настройки". Константы разного типа могут иметь одинаковый собственный код.
Типы констант, на текущем этапе, являются предзаданными в сервисе техсправочника значениями
и перечислены в объекте com.embedika.verdi.models.techConstant;
однако сервис допускает регистрацию иных типов вызовом соответствующего апи.
Ниже будет описан процесс использования клиента для регистрации констант. Более подробную информацию, описание моделей данных и пр. можно найти в документации сервиса технического справочника.
Процесс регистрации
Предварительно, перед регистрацией, в проекте необходимо описать перечень его констант.
Пример:
import com.embedika.verdi.models.techConstant.TechConstant
import com.embedika.verdi.models.techConstant.CommonConstantTypes._
object TechConstants {
object Journal {
val createForm: TechConstant = TechConstant(
code = ConstantCode("createForm"),
constantType = JournalEventType,
name = "Создана новая форма",
description = None,
settings = None,
serviceVersion = 1
)
val updateForm: TechConstant = TechConstant(
code = ConstantCode("updateForm"),
constantType = NotificationType,
name = "Создана новая форма",
description = None,
settings = None,
serviceVersion = 1
)
val all: Seq[TechConstant] = Seq(createForm, updateForm)
}
object Notifications {
//...
val all: Seq[TechConstant] = Seq(???)
}
val all = Journal.all + Notifications.all
}
Далее требуется создать объект клиента, например в DI
Пример для DI (izumi):
scala
import com.embedika.verdi.techConstants.client.TechConstantsClient
make[TechConstantsClient].from { (dispatcher: Dispatcher, ec: ExecutionContext @Id("ec-services")) =>
new LiveTechConstantsClient(dispatcher)(ec)
}
Имея клиент, можно начать сам процесс регистрации. Ожидается, что она будет происходить при рестарте сервиса, где-то на одном этапе с регистрацией команд.
Первым этапом должна вызываться регистрация текущего сервиса. В случае успешной регистрации сервиса, можно зарегистрировать его константы.
Пример вызова:
scala
val serviceName = ServiceName(config.getString("name"))
techConstantsClient.registerService(
serviceName = serviceName,
readableName = "Хранение UI",
description = Some("...описание сервиса")
).flatMap {
case Left(err) =>
// Обработать ошибку (логи, остальные обработки если нужны)
case Right(_) =>
// Зарегистрировать все константы, например через Future.traverse или иным способом:
Future.traverse(TechConstants.all)(techConstantsClient.registerConstant(serviceName, _)).flatMap { results =>
// Обработать ошибки если нужны
}
}
Validation: библиотека для работы с валидацией
Библиотека предоставляет DSL для:
- Интеграции валидации в
circe - Работы с ошибками от "вложенных" команд
Circe (validation)
Decoders (validation)
import com.embedika.verdi.models.NewTypeJsonSupport.deriveNewTypeDecoder
import com.embedika.verdi.validation.circe.Decoders
import Decoders.implicits.nonEmptyStringDecoder
import Decoders.syntax._
import io.circe.Decoder
import io.circe.parser._
import io.estatico.newtype.macros.newtype
object Example {
@newtype case class MyId(raw: String)
object MyId {
// "deriveNewTypeDecoder" использует
// "Decoders.implicits.nonEmptyStringDecoder", вместо стандартного,
// для создания изначального Decoder[MyId],
// который в последствии изменяется с помощью метода "withValidation"
implicit val myIdDecoder: Decoder[MyId] = deriveNewTypeDecoder[MyId, String].withValidation {
case notEmptyId if notEmptyId.raw.matches("\\w*") => Right(notEmptyId)
case _ => Left("Not my ID")
}
}
}
Пакет предоставляет DSL для создания декодеров с валидацией. Также содержит базовые реализации часто используемых
типов: String, Search.
Decoder для Search проверяет значение поля "query": корректность формата, наличие значений для фильтров из поля "
query" в поле "context".
ErrorResponseSyntax (validation)
import com.embedika.verdi.models.error.{ErrorResponse, ErrorResponseCode}
import com.embedika.verdi.validation.commands.ErrorResponseSyntax._
import io.circe.Json
object Example {
val internalInput = Json.obj(
"field1" -> Json.arr(
Json.fromInt(1),
Json.fromInt(2),
Json.fromInt(3),
Json.fromInt(4),
Json.fromInt(5),
Json.fromInt(6)
),
"field2" -> Json.fromString("field_2_value")
)
val internalError = ErrorResponse(
code = ErrorResponseCode.InvalidParameters,
message = Some("DecodingFailure at .field1: field1 supports only 3,4,5,6")
)
val externalError = internalError.asExternalError(internalInput) {
case str if str.startsWith(".field1") =>
List(
"[0].field1",
"[1].field1"
)
}
// externalError.message =
// "DecodingFailure at [0].field1 or [1].field1: field1 supports only 3,4,5,6: '1'"
}
Пакет предоставляет DSL для работы с ошибками:
asExternalError- преобразует ошибку от "внутренней" команды в ошибку "внешней" команды.
Verdi-cache
Предоставляет интерфейс и реализацию кеша.
Loggers config
<logger name="com.embedika.verdi.cache.CacheApi" level="DEBUG"/>
<logger name="scalacache.caffeine.CaffeineCache" level="DEBUG"/>
Структуры Verdi-cache
VerdiCacheConfig (Verdi-cache)
Настройки кеширования.
| Поле | Тип | Обязательное | Описание | Ограничения |
|---|---|---|---|---|
| maxSize | int | Нет | Максимальное количество элементов в кеше, при превышении которого элементы начнут удаляться (LRU). | |
| elementTTL | duration string | Нет | Максимальное время жизни элемента. |
XacmlDSL: библиотека для работы с XACML
Библиотека предоставляет DSL для создания и разбора XACML формата (XML).
Ключевые сущности: - TBD