NAV
json scala

Verdi

Описание команд всех сервисов, которые входят в состав Verdi.

Список версий

v1.8

Важные изменения

v1.7.1

Важные изменения

v1.7

Важные изменения

v1.6.1

Важные изменения

v1.6

Важные изменения

v1.5.3

v1.5.2

Технический хотфикс

v1.5.1

Версия от 27.06.2023. Список изменений:

v1.5

Версия от 06.06.2023. Список изменений:

v1.4.1

Версия от 03.05.2023. Список изменений:

v1.4

Версия от 22.03.2023. Список изменений:

Общие типы данных

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" может быть использована любая строка (нет ограничений), но в данный момент мы используем только следующие значения:

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-е можно описать несколько видов проверок для полей:

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"
  }
}

Сортировка определяется двумя полями:

Alexandrina

Сервис справочников.

Bibliotheca Alexandrina (лат.) - Александрийская библиотека. Команды могут приходить как по HTTP, так и через Kafka в топик alexandrina_commands.

В сервисе реализовано 19 команд:

  1. Создать справочник
  2. Изменить справочник
  3. Удалить справочник
  4. Список справочников
  5. Получить данные справочника
  6. Получить справочник по коду
  7. Экспорт справочников
  8. Парсинг справочников
  9. Импорт справочников
  10. Создать элемент справочника
  11. Изменить элемент справочника
  12. Архивировать элемент справочника
  13. Удалить элемент справочника
  14. Удалить все элементы из справочника
  15. Список элементов справочника
  16. Список элементов справочника по коду
  17. Получить данные элемента справочника
  18. Получить элемент справочника по его коду
  19. Получить данные элементов справочников по списку id

Optimistic Lock

У многих команд реализован механизм оптимистичных блокировок. Для этого в данных присутствует поле 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, то необходимо задать следующие переменные, которые по умолчанию не заданы:

В файле boot/src/main/resources/logback.xml можно поменять class path для основного класса приложения (переменная projectMainClassPath), если это необходимо.

Команды для сервиса Alexandrina

CreateCatalog (Alexandrina)

На входе данные справочника

{
  "code": "cities",
  "title": "Города",
  "metadata": {}
}

На выходе строка с id созданного справочника

"0effa436-06c2-42cc-befe-8ebec417fc5a"

Создание справочника. Отправляет событие о создании справочника.

Имя команды для вызова: createCatalog. Поддерживается асинхронный и синхронный вызов.

UpdateCatalog (Alexandrina)

На входе данные справочника

{
  "id": "0effa436-06c2-42cc-befe-8ebec417fc5a",
  "code": "cities",
  "title": "Города",
  "metadata": {},
  "version": 2
}

На выходе количество обновленных записей

0

Изменение данных справочника. Использует оптимистичную блокировку. Если изменяемый справочник был изменен параллельно, тогда вернется 0, показывающий, что не произошло обновления из-за некорректной версии. Отправляет событие об обновлении справочника.

Имя команды для вызова: updateCatalog. Поддерживается асинхронный и синхронный вызов.

RemoveCatalog (Alexandrina)

На входе ID справочника

"0effa436-06c2-42cc-befe-8ebec417fc5a"

На выходе признак успешности

true

Удаление справочника и всех его элементов. Отправляет событие об удалении справочника и всех его элементов.

Имя команды для вызова: removeCatalog. Поддерживается асинхронный и синхронный вызов.

ListCatalogs (Alexandrina)

Входных данных нет

На выходе список справочников

[
  {
    "id": "0effa436-06c2-42cc-befe-8ebec417fc5a",
    "code": "cities",
    "title": "Города",
    "metadata": {},
    "modified": 1632978484446,
    "version": 1,
    "items": 4
  }
]

Получение списка всех справочников. Пока без пейджинга, сортировки, и т.п.

Имя команды для вызова: listCatalogs. Поддерживается только синхронный вызов.

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. Поддерживается только синхронный вызов.

GetCatalogByCode (Alexandrina)

На входе код справочника

"cities"

На выходе данные справочника

{
  "id": "0effa436-06c2-42cc-befe-8ebec417fc5a",
  "code": "cities",
  "title": "Города",
  "metadata": {},
  "modified": 1632978484446,
  "version": 1,
  "items": 4
}

Получение данных справочника по коду.

Имя команды для вызова: getCatalogByCode. Поддерживается только синхронный вызов.

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. Поддерживается только синхронный вызов.

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. Поддерживается только синхронный вызов.

ImportCatalogs (Alexandrina)

На входе массив справочников с элементами

[
  {
    "code": "countries",
    "title": "Страны",
    "metadata": {},
    "items": [
      {
        "code": "Russia",
        "title": "Россия",
        "archived": false,
        "metadata": {}
      }
    ]
  }
]

На выходе количество обновленных/добавленных справочников

1

Импорт массива справочников и массивов их элементов. Если справочник или элемент с таким кодом существует, он будет обновлен, если нет - будет создан новый. Допускается наличие посторонних полей, при импорте они будут игнорироваться. Отправляет события о созданных/обновленных справочниках и элементах.

Имя команды для вызова: importCatalogs. Поддерживается асинхронный и синхронный вызов.

CreateCatalogItem (Alexandrina)

На входе данные элемента справочника

{
  "catalogId": "0effa436-06c2-42cc-befe-8ebec417fc5a",
  "code": "moscow",
  "title": "Москва",
  "metadata": {}
}

На выходе строка с ID созданного элемента справочника

"cee05b97-ca41-4355-8565-d667c6807cbd"

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

Имя команды для вызова: createCatalogItem. Поддерживается асинхронный и синхронный вызов.

UpdateCatalogItem (Alexandrina)

На входе данные элемента справочника

{
  "id": "cee05b97-ca41-4355-8565-d667c6807cbd",
  "code": "moscow",
  "title": "Москва",
  "metadata": {},
  "version": 2
}

На выходе количество измененных записей

0

Изменение элемента справочника. Использует оптимистичную блокировку. Если изменяемый элемент был изменен параллельно, тогда вернется 0, показывающий, что не произошло обновления из-за некорректной версии. Отправляет событие об обновлении элемента справочника.

Имя команды для вызова: updateCatalogItem. Поддерживается асинхронный и синхронный вызов.

ArchiveCatalogItem (Alexandrina)

На входе ID элемента справочника и признак архивности

{
  "id": "cee05b97-ca41-4355-8565-d667c6807cbd",
  "archived": true
}

На выходе признак успешности

true

Архивирование элемента справочника. Отправляет событие об обновлении элемента справочника.

Имя команды для вызова: archiveCatalogItem. Поддерживается асинхронный и синхронный вызов.

RemoveCatalogItem (Alexandrina)

На входе ID элемента справочника

"cee05b97-ca41-4355-8565-d667c6807cbd"

На выходе признак успешности

true

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

Имя команды для вызова: removeCatalogItem. Поддерживается асинхронный и синхронный вызов.

RemoveItemsFromCatalog (Alexandrina)

На входе ID справочника

"0effa436-06c2-42cc-befe-8ebec417fc5a"

На выходе количество удаленных элементов справочника

15

Удаление всех элементов из справочника.

Имя команды для вызова: removeItemsFromCatalog. Поддерживается асинхронный и синхронный вызов.

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. Поддерживается только синхронный вызов.

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. Поддерживается только синхронный вызов.

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. Поддерживается только синхронный вызов.

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. Поддерживается только синхронный вызов.

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. Поддерживается только синхронный вызов.

Публикуемые события

События публикуются в ALEXANDRINA_CATALOG_EVENTS_TOPIC.

eventType payloadType payloadId Описание
catalogUpsert catalog Id каталога Каталог создан или обновлен
catalogItemUpsert catalogItem Id элемента каталога Элемент каталога создан или обновлен
catalogRemoval removeCatalog Id каталога Каталог удален
catalogItemRemoval removeCatalogItem Id элемента каталога Элемент каталога удален

Модели сервиса Alexandrina

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

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 для взаимодействия с системой и командами:

Конфигурирование 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, то необходимо задать следующие переменные, которые по умолчанию не заданы:

В файле 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 проектов:

В сервисе реализованны следующие команды:

Локальный запуск сервиса Attila

При запуске сервиса ожидается, что уже развернута необходимая инфраструктура:

Запуск 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, то необходимо задать следующие переменные, которые по умолчанию не заданы:

В файле 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
}

Возвращает список названий команд, которые удовлетворяют заданным фильтрам. Поддерживается только синхронный вызов.

Команда Путь
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
}

Возвращает список названий ресурсов, которые удовлетворяют заданным фильтрам. Поддерживается только синхронный вызов.

Команда Путь
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
}

Возвращает список действий для низкоуровневых правил, которые удовлетворяют заданным фильтрам. Поддерживается только синхронный вызов.

Команда Путь
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), не требует аутентификации.

Команда Путь
attila_AddAuthorizationInfo kafka topic "attila_commands"

DeleteAuthorizationInfo (attila)

Payload для команды

[
  "Other_Command"
]

Результат выполнения команды

1

Возвращает количество удаленных записей с информацией для авторизации. Одна запись это картеж из (CommandName, ResourceName, RuleAction, UiAction), т.е. количество записей не всегда равно количеству добавленных команд. Поддерживается асинхронный и синхронный вызов.

Команда только внутренняя (нельзя вызвать через Api Gateway), не требует аутентификации.

Команда Путь
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. Поддерживается только синхронный вызов.

Команда Путь
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".
Поддерживается только синхронный вызов.

Команда Путь
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

Возвращает индикатор успеха добавления новой версии правила. Поддерживается асинхронный и синхронный вызов.

Команда Путь
attila_UpdateHighLevelRule kafka topic "attila_commands"

DeleteHighLevelRule (attila)

Payload для команды

"Rule_AccessTo_ResourceA"

Результат выполнения команды

true

Возвращает индикатор успеха удаления указанной версии правила. Поддерживается асинхронный и синхронный вызов.

Команда Путь
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%
Команда Путь
attila_GetResourceTypeFields HTTP POST "/v1/resources/fields/list"

AddResourceTypeFields (attila)

Payload для команды

"Rule_AccessTo_ResourceA"

Результат выполнения команды

true

Добавление набора полей для определенного типа ресурсов. Поддерживается асинхронный и синхронный вызов.

Команда только внутренняя (нельзя вызвать через Api Gateway), не требует аутентификации.

Команда Путь
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):

Предоставляемые атрибуты

Атрибуты предоставляются для объектов сервиса 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, то необходимо задать следующие переменные, которые по умолчанию не заданы:

В файле 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

Добавление конфига для AttributeValueProvider в необходимый домен:

Пример тела запроса на изменение конфигурации 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 уведомления и отправляет их на электронную почту. Уведомления отправляются через рассылки по определенному расписанию. Каждая рассылка описывает шаблон отображения уведомления в письме. Уведомления могут группироваться в одном письме. Уведомления, рассылки и группы связываются через настройки.

Сервис предоставляет следующие команды:

Конфигурирование сервиса отправки уведомлений на электронную почту

Требования к запуску сервиса отправки уведомлений на электронную почту

Запуск сервиса осуществляется локально через 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, то необходимо задать следующие переменные, которые по умолчанию не заданы:

В файле 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"

Создание настройки. Поддерживается только синхронный вызов.

Команда Путь
CreateSettingEmail HTTP POST "/createSetting"

GetSettingEmail (Buzzer-Email)

Payload для команды

"testCode"

Результат выполнения команды

{
 "id": "testCode",
 "deliveryId": "testDelivery",
 "groupId": "testGroup",
 "version": 1
}

Получение настройки. Поддерживается только синхронный вызов.

Команда Путь
GetSettingEmail HTTP POST "/getSetting"

DeleteSettingEmail (Buzzer-Email)

Payload для команды

"testCode"

Результат выполнения команды

1

Удаление настройки. Поддерживается только синхронный вызов.

Команда Путь
DeleteSettingEmail HTTP POST "/deleteSetting"

UpdateSettingEmail (Buzzer-Email)

Payload для команды

{
 "id": "testCode",
 "deliveryId": "another-delivery",
 "groupId": "testGroup",
 "version": 2
}

Результат выполнения команды

1

Обновление настройки. Поддерживается только синхронный вызов.

Команда Путь
UpdateSettingEmail HTTP POST "/updateSetting"

ListSettingEmail (Buzzer-Email)

Payload для команды

null

Результат выполнения команды

[
 {
  "id": "testCode",
  "deliveryId": "another-delivery",
  "groupId": "testGroup",
  "version": 2
 }
]

Получение списка настроек. Поддерживается только синхронный вызов.

Команда Путь
ListSettingEmail HTTP POST "/listSetting"

CreateGroupEmail (Buzzer-Email)

Payload для команды

{
 "id": "testGroup",
 "name": "test group",
 "version": 1
}

Результат выполнения команды

"testCode"

Создание группы. Поддерживается только синхронный вызов.

Команда Путь
CreateGroupEmail HTTP POST "/createGroup"

GetGroupEmail (Buzzer-Email)

Payload для команды

"testCode"

Результат выполнения команды

{
  "id": "testGroup",
  "name": "test group",
  "version": 1
}

Получение группы. Поддерживается только синхронный вызов.

Команда Путь
GetGroupEmail HTTP POST "/getGroup"

DeleteGroupEmail (Buzzer-Email)

Payload для команды

"testCode"

Результат выполнения команды

1

Удаление группы. Поддерживается только синхронный вызов.

Команда Путь
DeleteGroupEmail HTTP POST "/deleteGroup"

UpdateGroupEmail (Buzzer-Email)

Payload для команды

{
 "id": "testGroup",
 "name": "test group for notification delivery",
 "version": 2
}

Результат выполнения команды

1

Обновление группы. Поддерживается только синхронный вызов.

Команда Путь
UpdateGroupEmail HTTP POST "/updateGroup"

ListGroupEmail (Buzzer-Email)

Payload для команды

null

Результат выполнения команды

[
 {
  "id": "testGroup",
  "name": "test group for notification delivery",
  "version": 2
 }
]

Получение списка групп. Поддерживается только синхронный вызов.

Команда Путь
ListGroupEmail HTTP POST "/listGroup"

CreateTemplateEmail (Buzzer-Email)

Payload для команды

{
 "id": "testTemplate",
 "title": "some title",
 "content": "some content",
 "version": 1
}

Результат выполнения команды

"testCode"

Создание шаблона. Поддерживается только синхронный вызов.

Команда Путь
CreateTemplateEmail HTTP POST "/createTemplate"

GetTemplateEmail (Buzzer-Email)

Payload для команды

"testCode"

Результат выполнения команды

{
 "id": "testTemplate",
 "title": "some title",
 "content": "some content",
 "version": 1
}

Получение шаблона. Поддерживается только синхронный вызов.

Команда Путь
GetTemplateEmail HTTP POST "/getTemplate"

DeleteTemplateEmail (Buzzer-Email)

Payload для команды

"testCode"

Результат выполнения команды

1

Удаление шаблона. Поддерживается только синхронный вызов.

Команда Путь
DeleteTemplateEmail HTTP POST "/deleteTemplate"

UpdateTemplateEmail (Buzzer-Email)

Payload для команды

{
 "id": "testTemplate",
 "title": "Object deletion",
 "content": "Объект {{data.title}} удален",
 "version": 2
}

Результат выполнения команды

1

Обновление шаблона. Поддерживается только синхронный вызов.

Команда Путь
UpdateTemplateEmail HTTP POST "/updateTemplate"

ListTemplateEmail (Buzzer-Email)

Payload для команды

null

Результат выполнения команды

[
 {
  "id": "testTemplate",
  "title": "Object deletion",
  "content": "Объект {{data.title}} удален",
  "version": 2
 }
]

Получение списка шаблонов. Поддерживается только синхронный вызов.

Команда Путь
ListTemplateEmail HTTP POST "/listTemplate"

CreateDeliveryEmail (Buzzer-Email)

Payload для команды

{
 "id": "testDelivery",
 "schedule": "0 12 * * *"
}

Результат выполнения команды

"testCode"

Создание рассылки. Поддерживается только синхронный вызов.

Команда Путь
CreateDeliveryEmail HTTP POST "/createDelivery"

GetDeliveryEmail (Buzzer-Email)

Payload для команды

"testDelivery"

Результат выполнения команды

{
 "id": "testDelivery",
 "schedule": "0 12 * * *",
 "version": 1
}

Получение рассылки. Поддерживается только синхронный вызов.

Команда Путь
GetDeliveryEmail HTTP POST "/getDelivery"

DeleteDeliveryEmail (Buzzer-Email)

Payload для команды

"testDelivery"

Результат выполнения команды

1

Удаление рассылки. Поддерживается только синхронный вызов.

Команда Путь
DeleteDeliveryEmail HTTP POST "/deleteDelivery"

UpdateDeliveryEmail (Buzzer-Email)

Payload для команды

{
 "id": "testDelivery",
 "schedule": "00 14 1 * *"
}

Результат выполнения команды

1

Обновление рассылки. Поддерживается только синхронный вызов.

Команда Путь
UpdateDeliveryEmail HTTP POST "/updateDelivery"

ListDeliveryEmail (Buzzer-Email)

Payload для команды

null

Результат выполнения команды

[
  {
    "id": "testDelivery",
    "schedule": "00 14 1 * *",
    "version": 2
  }
]

Получение списка рассылок. Поддерживается только синхронный вызов.

Команда Путь
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
    }
  ]
}

Список ошибок отправки уведомлений. Поддерживается только синхронный вызов.

Команда Путь
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

Связывание рассылки и шаблона. Поддерживается только синхронный вызов.

Команда Путь
CreateDeliveryTemplate HTTP POST "/createDeliveryTemplate"

DeliveryTemplatesEmail (buzzer-email)

Payload для команды

"testDelivery"

Результат выполнения команды

[
 {
  "id": "testTemplate",
  "title": "Object deletion",
  "content": "Объект {{data.title}} удален",
  "version": 2
 }
]

Получения списка шаблонов привязанных к рассылке. Поддерживается только синхронный вызов.

Команда Путь
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 Да Данные уведомления
email 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 уведомления, сохраняет и затем по запросу возвращает для личного кабинета пользователя. Уведомления могут быть поделены по разделам личного кабинета. Уведомления отображаются в личном кабинете по определенным шаблонам. Уведомления, разделы и шаблоны связываются через настройки.

Сервис предоставляет следующие команды:

Список переменных окружения сервиса

Все доступные переменные окружения для настройки сервиса уведомлений в личном кабинете.

Переменная Тип Обяза-тельная Значение по умолчанию Описание
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, то необходимо задать следующие переменные, которые по умолчанию не заданы:

В файле 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", то он будет переписан на текущего пользователя.
Поддерживается только синхронный вызов.

Команда Путь
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". Поддерживается только синхронный вызов.

Команда Путь
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

Поиск уведомлений в личном кабинете (ЛК). Поддерживается только синхронный вызов.

Команда Путь
changeNotificationStatus HTTP POST "/changeNotificationStatus"

createSectionPp (Buzzer-personal-page)

Payload для команды

{
  "id": "section1",
  "name": "section name",
  "version": 1
}

Результат выполнения команды

"section1"

Создание раздела. Поддерживается только синхронный вызов.

Команда Путь
createSectionPp HTTP POST "/createSection"

getSectionPp (Buzzer-personal-page)

Payload для команды

"section1"

Результат выполнения команды

{
  "id": "section1",
  "name": "section name",
  "version": 1
}

Получение раздела. Поддерживается только синхронный вызов.

Команда Путь
getSectionPp HTTP POST "/getSection"

deleteSectionPp (Buzzer-personal-page)

Payload для команды

"section1"

Результат выполнения команды

1

Удаление раздела. Поддерживается только синхронный вызов.

Команда Путь
deleteSectionPp HTTP POST "/deleteSection"

updateSectionPp (Buzzer-personal-page)

Payload для команды

{
  "id": "section1",
  "name": "other section name",
  "version": 2
}

Результат выполнения команды

1

Обновление раздела. Поддерживается только синхронный вызов.

Команда Путь
updateSectionPp HTTP POST "/updateSection"

listSectionPp (Buzzer-personal-page)

Payload для команды

null

Результат выполнения команды

[
  {
    "id": "section1",
    "name": "other section name",
    "version": 2
  }
]

Получение списка разделов. Поддерживается только синхронный вызов.

Команда Путь
listSectionPp HTTP POST "/listSection"

createSettingPp (Buzzer-personal-page)

Payload для команды

{
  "id": "testCode",
  "sectionId": "section1",
  "version": 1
}

Результат выполнения команды

"testCode"

Создание настройки. Поддерживается только синхронный вызов.

Команда Путь
createSettingPp HTTP POST "/createSetting"

getSettingPp (Buzzer-personal-page)

Payload для команды

"testCode"

Результат выполнения команды

{
  "id": "testCode",
  "sectionId": "section1",
  "version": 1
}

Получение настройки. Поддерживается только синхронный вызов.

Команда Путь
getSettingPp HTTP POST "/getSetting"

deleteSettingPp (Buzzer-personal-page)

Payload для команды

"testCode"

Результат выполнения команды

1

Удаление настройки. Поддерживается только синхронный вызов.

Команда Путь
deleteSettingPp HTTP POST "/deleteSetting"

updateSettingPp (Buzzer-personal-page)

Payload для команды

{
  "id": "testCode",
  "sectionId": "section2",
  "version": 2
}

Результат выполнения команды

1

Обновление настройки. Поддерживается только синхронный вызов.

Команда Путь
updateSettingPp HTTP POST "/updateSetting"

listSettingPp (Buzzer-personal-page)

Payload для команды

null

Результат выполнения команды

[
  {
    "id": "testCode",
    "sectionId": "section2",
    "version": 2
  }
]

Получение списка настроек. Поддерживается только синхронный вызов.

Команда Путь
listSettingPp HTTP POST "/listSetting"

createTemplatePp (Buzzer-personal-page)

Payload для команды

{
  "id": "template1",
  "settingId": "testCode",
  "title": "",
  "content": "",
  "version": 1
}

Результат выполнения команды

"template1"

Создание шаблона. Поддерживается только синхронный вызов.

Команда Путь
createTemplatePp HTTP POST "/createTemplate"

getTemplatePp (Buzzer-personal-page)

Payload для команды

"template1"

Результат выполнения команды

{
  "id": "template1",
  "settingId": "testCode",
  "title": "",
  "content": "",
  "version": 1
}

Получение шаблона. Поддерживается только синхронный вызов.

Команда Путь
getTemplatePp HTTP POST "/getTemplate"

deleteTemplatePp (Buzzer-personal-page)

Payload для команды

"template1"

Результат выполнения команды

1

Удаление шаблона. Поддерживается только синхронный вызов.

Команда Путь
deleteTemplatePp HTTP POST "/deleteTemplate"

updateTemplatePp (Buzzer-personal-page)

Payload для команды

{
  "id": "template1",
  "settingId": "testCode",
  "title": "Template name",
  "content": "",
  "version": 1
}

Результат выполнения команды

1

Обновление шаблона. Поддерживается только синхронный вызов.

Команда Путь
updateTemplatePp HTTP POST "/updateTemplate"

listTemplatePp (Buzzer-personal-page)

Payload для команды

null

Результат выполнения команды

[
  {
    "id": "template1",
    "settingId": "testCode",
    "title": "Template code",
    "content": "",
    "version": 1
  }
]

Получение списка шаблонов. Поддерживается только синхронный вызов.

Команда Путь
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 информацию о настройках подписки для событий и подписках пользователя.

Локальный запуск сервиса подписок

При запуске сервиса ожидается, что уже развернута необходимая инфраструктура:

Запуск сервиса подписок из консоли с помощью 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, то необходимо задать следующие переменные, которые по умолчанию не заданы:

В файле src/main/resources/log4j.xml можно поменять class path для основного класса приложения (переменная projectMainClassPath), если это необходимо.

Команды сервиса подписок

В качестве тела команд сервис всегда ожидает CommandRequest. В описании команд указано описание поля payload для CommandRequest и путь для отправки команды в сам сервис (не в ApiGateway).

В сервисе реализованы следующие команды:

CreateEventSettings (Buzzer-subscription)

Передаем JSON с данными для создания настроек события

{
    "entityType": "Document",
    "eventType": "Create",
    "enabled": true,
    "notificationCode": "DocumentCreated"
}

Получаем Id настроек события

"4345011c-c579-4e1b-b84b-94e4eed2f2da"

Создать настройки события из данных EventSettingsDTO. Гарантируется уникальность для набора полей: entityType, eventType.

В ответ приходит Id настроек события.

Команда Путь
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.

Команда Путь
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 формы увеличивается на единицу.

Команда Путь
updateEventSettingsBuzzer-subscription HTTP POST "/updateEventSettings"

DeleteEventSettings (Buzzer-subscription)

Передаем Id настроек события

"0ccd05ac-50c0-4e7e-906b-6893eaa8d9de"

Получаем результат

1

Удалить существующие настройки события по Id.

Команда Путь
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
    }
]

Возвращает список всех существующих настроек событий.

Команда Путь
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
}

Возвращает отфильтрованный список настроек событий.

Доступные для сортировки поля:

Доступные для фильтрации поля и виды фильтров по ним:

Поле Виды фильтров
id InSetQuery
eventType InSetQuery, LikeQuery
notificationCode InSetQuery, LikeQuery
entityType InSetQuery, LikeQuery
Команда Путь
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 подписки.

Команда Путь
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.

Команда Путь
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 формы увеличивается на единицу.

Команда Путь
updateSubscriptionBuzzer-subscription HTTP POST "/updateSubscription"

DeleteSubscription (Buzzer-subscription)

Передаем Id подписки

"954855b6-5390-45ca-9280-2746563e6905"

Получаем результат

1

Удалить существующую подписку по Id.

Команда Путь
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
    }
]

Возвращает список всех существующих настроек событий.

Команда Путь
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, то необходимо задать следующие переменные, которые по умолчанию не заданы:

В файле src/main/resources/log4j.xml можно поменять class path для основного класса приложения (переменная projectMainClassPath), если это необходимо.

Список команд сервиса уведомлений

В сервисе реализованы следующие команды:

CreateSendingBuzzer

Передаем JSON с описанием сервиса отправки

{
  "id": "profile",
  "topic": "personal-page",
  "version": 1
}

Получаем код сервиса

"profile"

Зарегистрировать сервис отправки.

Имя команды для вызова: createSendingBuzzer. Поддерживается только синхронный вызов.

GetSendingBuzzer

Передаем код сервиса отправки

"profile"

Получаем JSON с описанием сервиса отправки

{
  "id": "profile",
  "topic": "personal-page",
  "version": 1
}

Получить описание сервиса отправки.

Имя команды для вызова: getSendingBuzzer. Поддерживается только синхронный вызов.

ListSendingBuzzer

Получаем JSON со списком всех существующих сервисов отправки

[
  {
    "id": "profile",
    "topic": "personal-page",
    "version": 1
  },
  {
    "id": "email",
    "topic": "email",
    "version": 1
  }
]

Возвращает список всех зарегистрированных сервисов отправки

Имя команды для вызова: listSendingBuzzer. Поддерживается только синхронный вызов.

UpdateSendingBuzzer

Передаем JSON с описанием сервиса отправки

{
  "id": "profile",
  "topic": "personal-page",
  "version": 1
}

Получаем результат

1

Обновить существующий сервис отправки. Если значение, передаваемое в version, отличается от текущего, обновление не происходит. При успешном обновлении version формы увеличивается на единицу.

Имя команды для вызова: updateSendingBuzzer. Поддерживается только синхронный вызов.

DeleteSendingBuzzer

Передаем код сервиса отправки

"email"

Получаем результат

1

Отменить регистрацию сервиса отправки.

Имя команды для вызова: deleteSendingBuzzer. Поддерживается только синхронный вызов.

CreateSettingBuzzer

Передаем JSON с данными для создания пользовательских настроек уведомления

{
  "code": "buzzerNotification",
  "sendingIds": ["profile"]
}

Получаем ID новой настройки

"4345011c-c579-4e1b-b84b-94e4eed2f2da"

Создать пользовательские настройки уведомления.

Имя команды для вызова: createSettingBuzzer. Поддерживается только синхронный вызов.

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. Поддерживается только синхронный вызов.

ListSettingBuzzer

Получаем JSON со списком всех пользовательских настроек уведомлений

[
  {
    "id": "e3901923-21dc-4f95-98d0-cb2ea29abe1e",
    "code": "buzzerNotification",
    "sendingIds": [
      "profile",
      "email"
    ],
    "userId": "016d04f4-fc1a-4629-808b-e5e1c35bb348",
    "version": 1
  }
]

Возвращает список всех существующих пользовательских настроек уведомлений.

Имя команды для вызова: listSettingBuzzer. Поддерживается только синхронный вызов.

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

Доступные для сортировки поля:

Имя команды для вызова: searchSettingBuzzer. Поддерживается только синхронный вызов.

UpdateSettingBuzzer

Передаем JSON с пользовательскими настройками уведомления

{
  "id": "e3901923-21dc-4f95-98d0-cb2ea29abe1e",
  "code": "buzzerNotification",
  "sendingIds": [
    "profile",
    "email"
  ],
  "version": 1
}

Получаем результат

1

Обновить существующие пользовательские настройки уведомления. Если значение, передаваемое в version, отличается от текущего, обновление не происходит. При успешном обновлении version формы увеличивается на единицу.

Имя команды для вызова: updateSettingBuzzer. Поддерживается только синхронный вызов.

DeleteSettingBuzzer

Передаем ID пользовательских настроек уведомления

"0ccd05ac-50c0-4e7e-906b-6893eaa8d9de"

Получаем результат

1

Удалить существующие пользовательские настройки события по их ID.

Имя команды для вызова: deleteSettingBuzzer. Поддерживается только синхронный вызов.

CreateSystemSettingBuzzer

Передаем JSON с данными для создания системных настроек

{
  "code": "buzzerNotification",
  "sendingIds": ["profile", "email"]
}

Получаем ID новой системной настройки

"4345011c-c579-4e1b-b84b-94e4eed2f2da"

Создать системные настройки уведомления.

Имя команды для вызова: createSystemSettingBuzzer. Поддерживается только синхронный вызов.

GetSystemSettingBuzzer

Передаем ID системных настроек

"ed2de80e-0289-43c2-a944-70e9a4277d36"

Получаем JSON с системными настройками уведомления

{
  "id": "e3901923-21dc-4f95-98d0-cb2ea29abe1e",
  "code": "buzzerNotification",
  "sendingIds": [
    "profile",
    "email"
  ],
  "userId": null,
  "version": 1
}

Получить системные настройки уведомления.

Имя команды для вызова: getSystemSettingBuzzer. Поддерживается только синхронный вызов.

ListSystemSettingBuzzer

Получаем JSON со списком всех системных настроек уведомлений

[
  {
    "id": "e3901923-21dc-4f95-98d0-cb2ea29abe1e",
    "code": "buzzerNotification",
    "sendingIds": [
      "profile",
      "email"
    ],
    "userId": null,
    "version": 1
  }
]

Возвращает список всех существующих системных настроек уведомлений.

Имя команды для вызова: listSystemSettingBuzzer. Поддерживается только синхронный вызов.

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

Доступные для сортировки поля:

Имя команды для вызова: searchSystemSettingBuzzer. Поддерживается только синхронный вызов.

UpdateSystemSettingBuzzer

Передаем JSON с системными настройками уведомления

{
  "id": "e3901923-21dc-4f95-98d0-cb2ea29abe1e",
  "code": "buzzerNotification",
  "sendingIds": [
    "profile",
    "email"
  ],
  "version": 1
}

Получаем результат

1

Обновить существующие системные настройки уведомления. Если значение, передаваемое в version, отличается от текущего, обновление не происходит. При успешном обновлении version формы увеличивается на единицу.

Имя команды для вызова: updateSystemSettingBuzzer. Поддерживается только синхронный вызов.

DeleteSystemSettingBuzzer

Передаем ID системных настроек уведомления

"0ccd05ac-50c0-4e7e-906b-6893eaa8d9de"

Получаем результат

1

Удалить существующие системные настройки события по ID.

Имя команды для вызова: deleteSystemSettingBuzzer. Поддерживается только синхронный вызов.

SendNotification

Передаем данные отправляемого уведомления

{
  "code": "buzzerNotification",
  "userId": "016d04f4-fc1a-4629-808b-e5e1c35bb348",
  "data": {"field": "value"}
}

Получаем результат

true

Отправка уведомления пользователю.

Имя команды для вызова: sendNotification. Поддерживается синхронный и асинхронный вызовы.

Модели сервиса уведомлений

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, то необходимо задать следующие переменные, которые по умолчанию не заданы:

В файле 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 Название бакета в файловом хранилище для хранения 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, то необходимо задать следующие переменные, которые по умолчанию не заданы:

В файле 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"

Поддерживается только синхронный вызов.

extractVars

Извлечение переменных

На входе ссылка на шаблона отчета

"/tmp/55497df9-bf03-4364-bada-881bbd5531f8"

В результате переменные из шаблона

[
  "key1",
  "key2"
]

Поддерживается только синхронный вызов.

convertToPdf

На входе ссылка на файл для конвертации

"temp/55497df9-bf03-4364-bada-881bbd5531f8"

В результате ссылка на pdf версию

"pdf/55497df9-bf03-4364-bada-881bbd5531f8"

Конвертация в pdf. Поддерживаются файлы с расширениями .txt, .doc и .docx.

Имя команды для вызова: convertToPdf. Поддерживается только синхронный вызов.

Event Status: сервис информации о событиях

Сервис принимает запросы для работы со статусом обработки событий. На данный момент все состояние сервиса хранится в памяти. Команды могут приходить как по HTTP, так и через Kafka в топик event_status_commands.

В сервисе реализованно 2 команды:

Сервис разбит на несколько модулей, в виде sbt проектов:

Информация по добавлению команд можно прочитать в описании шаблона

Локальный запуск сервиса Event Status

При запуске сервиса ожидается, что уже развернута необходимая инфраструктура:

Запуск из консоли с помощью 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

Поддерживает только синхронный вызов.

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

waitForMultipleEvents

На входе описание событий и timeout

{
    "timeoutSeconds": 5,
    "eventsToWait": [{
        "eventType": "test",
        "payloadType": "test",
        "payloadId": "test",
        "serviceName": "test",
        "payloadVersion": 5
    }]
}

На выходе список Boolean, аналогично waitForEvent

{
  "status": "Completed",
  "timestamp": 1683184345008,
  "value": [true]
}

Дожидается публикации информации о всех событиях в сервисе. По истечению timeout возвращает результат по каждому событию.

Объекты сервиса 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)

Формат правила для инвалидации

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 проектов:

Особенности 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

При запуске сервиса ожидается, что уже развернута необходимая инфраструктура:

Запуск 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, то необходимо задать следующие переменные, которые по умолчанию не заданы:

В файле boot/src/main/resources/logback.xml можно поменять class path для основного класса приложения (переменная projectMainClassPath), если это необходимо.

Список команд сервиса Mon

В описании команд используется путь/route для отправки команды в сам сервис, а не в ApiGateway. В качестве Input-а для команд, сервис всегда ожидает CommandRequest (как и любой другой сервис, принимающий команды), так что в описании команды указано лишь описание поля payload для CommandRequest. Все команды в сервисе - "синхронные".

В сервисе реализованы следующие команды:

Информация по добавлению команд можно прочитать в описании шаблона

Название команды 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. Отправляет событие о регистрации нового пользователя. Поддерживается только синхронный вызов.

Команда Путь
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". Отправляет событие о подтверждении нового пользователя. Поддерживается только синхронный вызов.

Команда Путь
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

Осуществляет вход пользователя в систему по логину и паролю. Поддерживается только синхронный вызов.

Команда Путь
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).
Результат выполнения команды журналируется.
Поддерживается только синхронный вызов.

Команда Путь
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.
Поддерживается только синхронный вызов.

Команда Путь
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).
Результат выполнения команды журналируется.
Поддерживается только синхронный вызов.

Команда Путь
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

Осуществляет выход текущего пользователя из системы. Поддерживается только синхронный вызов.

Команда Путь
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"
  }
}

Изменяет статус пользователя на тот, что указан во входных данных. Возможны любые переходы.
Результат выполнения команды журналируется.
Поддерживается только синхронный вызов.

Команда Путь
mon_http_ChangeUserStatus HTTP POST "/v1/user/changeStatus"

ChangeUserPassword (mon)

Payload для команды

{
  "oldPassword": "pass123",
  "newPassword": "new_Pa$$w0rd"
}

Результат выполнения команды

true

Изменяет пароль пользователя со "старого" на "новый", что указан во входных данных. "Старый" пароль должен совпадать с текущим паролем пользователя. В ином случае, вовращается ошибка неверного пользователя/пароля. Поддерживается только синхронный вызов.

Команда Путь
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
email InSetQuery, LikeQuery
lastName InSetQuery, LikeQuery
firstName InSetQuery, LikeQuery
middleName InSetQuery, LikeQuery
fio TsQuery

Доступные поля для сортировки:

Поле
userId
status
email
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"
  }
}

Возвращает информацию о текущем пользователе. Поддерживается только синхронный вызов.

Команда Путь
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. Поддерживается только синхронный вызов.

Команда Путь
mon_http_UserCommandContextData HTTP POST "v1/user/contextdata"

CloseAllUserSessions (mon)

Payload для команды

"a133f0c8-a516-4537-8f3f-915acb335e2c"

Результат выполнения команды

true

Завершает все активные сессии выбранного пользователя. Завершение заключается в добавлении правила для инвалидации JWT с UserId.
Результат выполнения команды журналируется.
Поддерживается только синхронный вызов.

Команда Путь
mon_http_CloseAllUserSessions HTTP POST "/v1/user/closeAllSessions"

DeleteJwtInvalidation (mon)

Payload для команды

"a133f0c8-a516-4537-8f3f-915acb335e2c"

Результат выполнения команды

1

Удаляет из БД правило для инвалидации JWT с указанным идентификатором.
Результат выполнения команды журналируется.
Поддерживается только синхронный вызов.

Команда Путь
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": "Описание группы"
}

Добавляет новую группу пользователей с заданными параметрами. Отправляет событие о создании группы. Поддерживается только синхронный вызов.

Команда Путь
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": "Измененное описание группы"
}

Обновляет данные о группе пользователей. Отправляет событие об обновлении группы. Поддерживается только синхронный вызов.

Команда Путь
mon_http_UpdateGroup HTTP POST "/v1/group/update"

DeleteGroup (mon)

Payload для команды

"id_of_my_first_group"

Результат выполнения команды

true

Удаляет группу пользователей с заданным идентифкатором. Отправляет событие об удалении группы. Поддерживается только синхронный вызов.

Команда Путь
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
    }
  ]
}

Возвращает список всех групп пользователей, в которых состоит указанный пользователь. Поддерживается только синхронный вызов.

Команда Путь
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 (с пустым списком групп) вернется в ответе

Команда Путь
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
    }
  ]
}

Возвращает список всех пользователей, которые состоят в указанной группе. Поддерживается только синхронный вызов.

Команда Путь
mon_http_ListUsersByGroupId HTTP POST "/v1/user/listByGroup"

Доступные поля для фильтрации и виды фильтров по ним:

Поле Виды фильтров
userId InSetQuery
status InSetQuery
email InSetQuery, LikeQuery
lastName InSetQuery, LikeQuery
firstName InSetQuery, LikeQuery
middleName InSetQuery, LikeQuery
count InSetQuery, LikeQuery, RangeQuery

Доступные поля для сортировки:

Поле
userId
status
email
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
    }
  ]
}

Возвращает список всех пользователей и кол-во групп, в которых они состоят. Поддерживается только синхронный вызов.

Команда Путь
mon_http_ListUsersWithGroupsCount HTTP POST "/v1/user/listWithGroupsCount"

Доступные поля для фильтрации и виды фильтров по ним:

Поле Виды фильтров
userId InSetQuery
status InSetQuery
email InSetQuery, LikeQuery
lastName InSetQuery, LikeQuery
firstName InSetQuery, LikeQuery
middleName InSetQuery, LikeQuery
fio TsQuery
count InSetQuery, LikeQuery, RangeQuery

Доступные поля для сортировки:

Поле
userId
status
email
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

Добавляет новых пользователей в существующую группу. Отправляет событие о добавлении пользователей в группу. Поддерживается только синхронный вызов.

Команда Путь
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

Удаляет пользователей, которые состоят в указанной группе. Отправляет событие об удалении пользователей из группы. Поддерживается только синхронный вызов.

Команда Путь
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 Да Идентификатор пользователя
email 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)

Данные необходимые для выполнения входа пользователя в систему

Поле Тип Обязательное Описание
email String Да Email пользователя
password String Да Пароль пользователя

SuccessAuthentication (mon)

Результат, указывающий на успешный вход пользователя в систему. Содержит JWT.

Поле Тип Обязательное Описание
accessToken String Да JWT в виде строки
expiresIn Int Да Время жизни JWT в секундах

UserRegistrationDto (mon)

Данные, необходимые для регистрации пользователя в системе.

Поле Тип Обязательное Описание
email 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.
email 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

Сервис журналирования

В сервисе реализованы следующие команды:

  1. Создание события
  2. Получение события
  3. Поиск событий

Общее описание архитектуры сервиса журналирования

Для работы с журналом необходимы действия: добавить одно событие, запросить событие по идентификатору, отфильтровать события. Для каждого из этих действий 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, то необходимо задать следующие переменные, которые по умолчанию не заданы:

В файле 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), не требует аутентификации.

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. Поддерживается только синхронный вызов.

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.

Проект сервиса содержит несколько директорий:

В сервисе реализованы следующие команды (все команды "синхронные"):

Локальный запуск сервиса Oberto

При запуске сервиса ожидается, что уже развернута необходимая инфраструктура:

Запуск 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, то необходимо задать следующие переменные, которые по умолчанию не заданы:

В файле 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"
    ]
  }
]

Возвращает список идентификаторов добавленных политик с указанием их версий. Поддерживается только синхронный вызов.

Команда Путь
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"
    ]
  }
]

Возвращает список идентификаторов добавленных политик с указанием их версий. Поддерживается только синхронный вызов.

Команда Путь
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'"
    }
  ]
}

Возвращает данные указанной политики. Поддерживается только синхронный вызов.

Команда Путь
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'"
    }
  ]
}

Возвращает данные указанной политики. Поддерживается только синхронный вызов.

Команда Путь
oberto_GetPolicyValueV2 HTTP POST "/v1/policy/single_v2"

DeletePolicy (oberto)

Payload для команды

{
  "id": "Any_Policy_Name",
  "version": "0.1.1"
}

Результат выполнения команды

true

Удаляет указанную политику. Поддерживается только синхронный вызов.

Команда Путь
oberto_DeletePolicy HTTP POST "/v1/policy/delete"

DeletePolicyV2 (oberto)

Payload для команды

{
  "id": "Any_Policy_Name",
  "version": "0.1.1"
}

Результат выполнения команды

true

Удаляет указанную политику. Поддерживается только синхронный вызов.

Команда Путь
oberto_DeletePolicyV2 HTTP POST "/v1/policy/delete_v2"

DeleteAllPolicyVersions (oberto)

Payload для команды

"Any_Policy_Name"

Результат выполнения команды

true

Удаляет все версии для указанного идентификатора политики. Поддерживается только синхронный вызов.

Команда Путь
oberto_DeleteAllPolicyVersions HTTP POST "/v1/policy/deleteAll"

DeleteAllPolicyVersionsV2 (oberto)

Payload для команды

"Any_Policy_Name"

Результат выполнения команды

true

Удаляет все версии для указанного идентификатора политики. Поддерживается только синхронный вызов.

Команда Путь
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 должны быть уникальными внутри текущей версии. Поддерживается только синхронный вызов.

Команда Путь
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 должны быть уникальными внутри текущей версии. Поддерживается только синхронный вызов.

Команда Путь
oberto_UpdatePolicyV2 HTTP POST "/v1/policy/update_v2"

OverridePoliciesFromSource (oberto)

Payload для команды

"Authzforce"

Результат выполнения команды

10

Удаляет все текущие версии политик и добавляет последние версии из указанного источника политик. Поддерживается только синхронный вызов.

Команда Путь
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
}

Проверяет доступность действий для конкретного ресурса, указанных во входных параметрах.

Поддерживается только синхронный вызов.

Команда Путь
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 (локальный расчет политик).

Поддерживается только синхронный вызов.

Команда Путь
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", то будут использованы данные пользователя, который вызвал данную команду.

Поддерживается только синхронный вызов.

Команда Путь
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"

  1. Effect может содержать одно из двух ключевых слов: permin, deny

  2. Actions содержит список действий, на которые будет распространяться правило. Список правил начинается со слова Actions, заключен в скобки и разделен запятыми.
    Для списка A,B,C => Actions(A, B, C).

  3. 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)
}

Чтобы описать Сагу как цепочку переходов нужно:

Используемые структуры

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 секции:

Используемые структуры поделены на отдельные пакеты по сущностям.

Структуры models.command

Структуры 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

Локальный запуск сервиса технического справочника

При запуске сервиса ожидается, что уже развернута необходимая инфраструктура:

Запуск из консоли с помощью 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, то необходимо задать следующие переменные, которые по умолчанию не заданы:

В файле boot/src/main/resources/logback.xml можно поменять class path для основного класса приложения (переменная projectMainClassPath), если это необходимо.

Команды сервиса технического справочника

UpsertService

На входе свойства регистрируемого или обновляемого сервиса

{
  "code": "techConstants",
  "name": "Технический справочник",
  "description": "Сервис технического справочника"
}

На выходе Boolean (признак успешности исполнения)

true

Регистрирует или обновляет сервис, для которого будут указываться константы.

Команда Путь
techConstants_UpsertService Kafka Topic "tech_constants_commands"

ListServices

Входные данные отсутствуют

На выходе перечень зарегистрированных сервисов

[
  {
    "code": "techReferences",
    "name": "Технический справочник",
    "description": "Сервис технического справочника"
  }
]

Возвращает перечень зарегистрированных сервисов.

Команда Путь
techConstants_ListServices HTTP POST /v1/service/list

DeleteService

На входе код удаляемого сервиса

"techConstants"

На выходе Boolean (признак успешности исполнения)

true

Удаляет зарегистрированный сервис.

Команда Путь
techConstants_DeleteService Kafka Topic "tech_constants_commands"

UpsertConstantType

На входе свойства регистрируемого или обновляемого типа констант

{
  "code": "journal",
  "name": "Журнал",
  "description": "Описание типов событий журнала"
}

На выходе Boolean (признак успешности исполнения)

true

Регистрирует или обновляет тип констант.

Команда Путь
techConstants_UpsertConstantType Kafka Topic "tech_constants_commands"

ListConstantTypes

Входные данные отсутствуют

На выходе перечень зарегистрированных типов констант

[
  {
    "code": "journal",
    "name": "Журнал",
    "description": "Описание типов событий журнала"
  }
]

Возвращает перечень зарегистрированных типов констант.

Команда Путь
techConstants_ListConstantType HTTP POST /v1/constantType/list

DeleteConstantType

На входе код удаляемого типа констант

"journal"

На выходе Boolean (признак успешности исполнения)

true

Удаляет зарегистрированный тип констант.

Команда Путь
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, как признак того, что запрос отработал корректно.

Команда Путь
techConstants_UpsertAutoConstant Kafka Topic "tech_constants_commands"

UpsertManualConstant

На входе свойства регистрируемой константы

{
  "code": "testConstant",
  "constantType": "testConstantType",
  "service": "testServiceType",
  "name": "Тестовая константа - переопределение вручную",
  "description": "Описание тестовой константы - переопределение вручную",
  "settings": {"test": "manual"}
}

На выходе Boolean (признак успешности исполнения)

true

Регистрирует или обновляет константу вручную. Должно вызываться человеком.
Если уже зарегистрирована автоматическая константа с таким же кодом и типом, то при запросе данных констант(ы), приоритет будет отдаваться ручной.

Команда Путь
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"
}

Получает актуальную версию данных по константе.
Если константа была зарегистрирована и автоматически и вручную, получит ручную версию.

Команда Путь
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"
  }
]

Получает список актуальных версий констант по типу констант, и, опционально, перечню кодов констант.

DeleteManualConstant

На входе код и тип константы

{
  "code": "testConstant",
  "constantType": "testConstantType"
}

На выходе Boolean (признак успешности исполнения)

true

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

Команда Путь
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 (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 для работы с ошибками:

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