Журнал технических изменений

Версия 6.13

Ограничение для настройки ширины лейбла и значения атрибутов

Создан отдельный UE MetaModelLayoutSetting, в котором можно переопределить список настроек ширины и состав атрибутов, для которых эти настройки будут доступны. Точка расширения работает и для атрибутов в составе комплексного. Синхронизированы глобальный и групповой список настроек.

Изоляция (архивация) невидимых ревизий

Под невидимыми ревизиями понимаются ревизии записи/связи/классификации, которые не влияют на вычисляемый эталон на текущую дату (на текущую дату на таймлайне обновления записи, но на любую дату на таймлайне периодов актуальности).

Изоляция (архивация) ревизий выполняется фоновой операцией, запускаемой по расписанию, заданным CRON-выражением:

  • Операция опциональна, она может быть отключена соответствующим параметром (отключена по умолчанию);

  • Она не будет запускаться, если хранилище для архивированных ревизий не сконфигурировано и при этом опция "Удалять ревизии вместо архивирования" отключена.

Предупреждение

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

Параметры операции архивации ревизий

  • Отключение операции (по умолчанию включено, доступно редактирование через UI). Если операция не отключена, то операция будет запускаться только в том случае,если узлы хранения архивированных ревизий сконфигурированы или включена опция "Удалять ревизии вместо архивирования" com.universe.mdm.hpe.job.archive.vistory.data.disabled = ${JOB_ARCHIVE_VISTORY_DISABLED:true}.

  • CRON-выражение, по которому запускается операция (по умолчанию каждый день в полночь, нельзя редактировать через UI, но можно увидеть значение) com.universe.mdm.hpe.job.archive.vistory.data.cronex = ${JOB_ARCHIVE_VISTORY_CRONEX:0 0 0 * * ?}.

  • Количество потоков, которыми выполняется операция на одном узле (по умолчанию 1 поток, недоступно в UI) com.universe.mdm.hpe.job.archive.vistory.data.threads = ${JOB_ARCHIVE_VISTORY_THREADS:1}.

  • Количество ревизий, записываемых одним коммитом в БД в архив и удаляемых из таблиц актуальных ревизий (по умолчанию 1024, недоступно в UI) com.universe.mdm.hpe.job.archive.vistory.data.commit.interval = ${JOB_ARCHIVE_VISTORY_COMMIT_INTERVAL:1024}.

  • Размер блока операции - количество записей/связей/классификаций (по умолчанию 4000, доступно редактирование через UI) com.universe.mdm.hpe.job.archive.vistory.data.block.size = ${JOB_ARCHIVE_VISTORY_BLOCK_SIZE:4000}.

  • Максимальное количество ревизий, оставляемых в таблицах актуальных ревизий (по умолчанию 3, доступно редактирование через UI), сли у записи несколько периодов, то количество ревизий может быть оставлено более заданного. Ревизии остаются для каждого отдельного ориджина (исходной записи/связи/классификации), а не для эталона com.universe.mdm.hpe.job.archive.vistory.data.max.revisions = ${JOB_ARCHIVE_VISTORY_MAX_REVISIONS:3}.

  • Удалять ревизии вместо архивирования (по умолчанию false, доступно редактирование через UI) com.universe.mdm.hpe.job.archive.vistory.data.truncate = ${JOB_ARCHIVE_VISTORY_TRUNCATE:false}.

Параметры подключения к архиву ревизий

  • Аналог org.unidata.mdm.data.nodes, но для архива ревизий этот параметр необязателен - если он не задан, то подключение к архиву ревизий не будет выполнено, фоновая операция не будет запускаться.

  • Архив ревизий так же шардирован, как и "горячее" хранилище - для этого используется параметр org.unidata.mdm.data.shards.

  • Если "горячее" хранилище уже сконфигурировано, конфигурация хранится в БД, а org.unidata.mdm.system.developer.mode равен false, то при первом запуске с указанием архива ревизий org.unidata.mdm.data.shards должно быть равно тому, что хранится в конфигурации "горячего" хранилища в БД (колонка shards_number в таблице org_unidata_mdm_data.cluster_info) com.universe.mdm.hpe.job.archive.vistory.data.nodes = 0:node0:${POSTGRES_USERNAME}@#[org.unidata.mdm.system.password.postgres]:${DATABASE_NAME:postgres}@${POSTGRES_ADDRESS}.

Остальные параметры подключения для записей и связей

  • com.universe.mdm.hpe.job.archive.vistory.data.nodes.minPoolSize = ${ARCHIVE_VISTORY_DATA_NODES_DATASOURCE_MINPOOLSIZE:1}

  • com.universe.mdm.hpe.job.archive.vistory.data.nodes.maxPoolSize = ${ARCHIVE_VISTORY_DATA_NODES_DATASOURCE_MAXPOOLSIZE:4}

  • com.universe.mdm.hpe.job.archive.vistory.data.nodes.appNamePrefix = Universe-Archive-Vistory-Data

  • com.universe.mdm.hpe.job.archive.vistory.data.nodes.driverProperties = currentSchema=com_universe_mdm_hpe_job_archive_vistory_data,#[org.unidata.mdm.system.extension.schema]&reWriteBatchedInserts=true&preparedStatementCacheQueries=4096&databaseMetadataCacheFieldsMiB=8&preparedStatementCacheSizeMiB=16

Остальные параметры подключения для классификаций

  • com.universe.mdm.hpe.job.archive.vistory.classifiers.nodes.minPoolSize = ${ARCHIVE_VISTORY_CLASSIFIERS_NODES_DATASOURCE_MINPOOLSIZE:1}

  • com.universe.mdm.hpe.job.archive.vistory.classifiers.nodes.maxPoolSize = ${ARCHIVE_VISTORY_CLASSIFIERS_NODES_DATASOURCE_MAXPOOLSIZE:4}

  • com.universe.mdm.hpe.job.archive.vistory.classifiers.nodes.appNamePrefix = Universe-Archive-Vistory-Classifiers

  • com.universe.mdm.hpe.job.archive.vistory.classifiers.nodes.driverProperties = currentSchema=com_universe_mdm_hpe_job_archive_vistory_classifiers,#[org.unidata.mdm.system.extension.schema]&reWriteBatchedInserts=true&preparedStatementCacheQueries=4096&databaseMetadataCacheFieldsMiB=8&preparedStatementCacheSizeMiB=16

Примечания

  • Физическое удаление записи должно удалять соответствующие данные из архива ревизий (ревизии записи, её связей и классификаций);

  • Получение истории записи (т.к. ревизии записи/связи/классификации являются элементами истории записи);

  • Получение исторического вида записи (если исторический вид записи требует архивированные ревизии для вычисления эталона, то они получаются из архива ревизий).

Проверка вхождения даты в диапазон

Добавлена системная функция "ПроверкаДатыВДиапазоне".

Поддержка параметра kind в операциях

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

Добавлена поддержка типа Collection путем переиспользования компонента EditableValuesListWrapper

  • ParameterCollection - сам компонент отвечающий за рендер EditableValuesListWrapper.

Изменен компонент ParameterItem.

  • Рендер инпутов для типа Single вынесен в отдельный компонент ParameterInput, который возвращает только инпуты без лейбла. Удален <Field>.

  • Добавлен рендер типа Collection.

  • Компоненты Select и groupSelect вынесены в отдельные компоненты соответственно ParameterSelect и ParameterGroupSelect.

Изменения в EditableValuesListWrapper

  • Компонент перенесен из пакета meta в новый созданный пакет для высокоуровневых компонентов composite-components.

  • Удалены два пропса itemCmp и itemCmpProps для удобного использования абстрактных компонентов.

  • Добавлен новый пропс itemNodeRenderer: (itemNodeProps: ItemNodeProps) => React.ReactNode, параметр которого это бывшие обязательные пропсы каждого itemCmp.

Экспорт записей с классификацией в Excel

Технический эпик для решения проблемы низкой скорости работы выгрузки классификаций при пакетной выгруке записей в XLSX. Нового функционала добавлено не было.

  • Добавлен ренедринг вызов в старте пакетной выгрузки записей.

  • Добавлена обработка рендеринг вызова при старте пакетной выгрузки записей для классификаций с целью предподсчета контекстов получения каждой классификации.

  • Добавлен прогрев кэша узлов классификаторов в рамках рендеринг вызова.

Импорт/экспорт ролей

Backend:

  • Создан API RoleRestService#export для экспорта ролей и свойств ролей в виде json объекта.

  • Создан API RoleRestService#source для импорта ролей и свойств ролей из json объекта.

  • Создан класс обертка RestReportBuilder для создания отчета по импорту множества сущностей. Создан объект класс обертка RestComplexReportBuilder, для объединения множества RestReportBuilder в один отчет.

  • Ошибка о не соответствии файла json формату вынесена в FileUtils.

  • В RoleServiceImpl аннотации @Transactional заменены на использование transactionTemplate.

Экспорт ролей запускает параллельный процесс выгрузки ролей в JSON, включая их метки безопасности и атрибуты. Результат отправляется через нотификации.

Импорт ролей использует политику Merge: данные из JSON полностью заменяют системные (права, имя, параметры), включая метки безопасности. Отсутствующие в файле параметры обнуляются.

Метки безопасности:

  • Несуществующие в системе метки из файла игнорируются.

  • Атрибуты меток импортируются только при совпадении с системными; отсутствующие в файле атрибуты очищаются.

Статус операций (экспорт/импорт) возвращается API сразу, окончательные результаты приходят в нотификациях.

Импорт/экспорт меток безопасности

BE:

  • Добавлен endpoint экспорта меток безопасности SecurityLabelRestService#export.

Конфликты с существующими в системе назначенными метками разрешаются следующим образом:

  • Если в импортируемом файле присутствует метка, но у нее отсутствуют атрибуты, которые заполнены в назначениях данной метки безопасности, то нужно удалить эти атрибуты из назначений.

  • Если в импортируемом файле присутствует метка, но ее объект модели не совпадает с объектом модели в системе, то удаляем все назначения данной метки.

  • При поднятом флаге recreate всегда удаляем все назначения метки.

  • Добавлен endpoint импорта меток безопасности SecurityLabelRestService#source.

  • В JsonUtils добавлены методы сериализации объекта с использованием PrettyPrint.

  • В FileUtils добавлены методы создания имени xml и json файлов.

  • В RoleDAO добавлены метод удаления всех меток безопасности для всех ролей RoleDAO#deleteAllLabels, метод удаления назначенных атрибутов указанной метки безопасности RoleDAO#removeLabelAttributes.

  • В UserDAO добавлены метод удаления назначенной метки безопасности со всех пользователей UserDAO#deleteLabel, метод удаления всех назначенных меток безопасности UserDAO#deleteAllLabels, метод удаления назначенных атрибутов указанной метки безопасности UserDAO#removeLabelAttributes.

При импорте меток безопасности для назначений меток безопасности(на роли и пользователей) осуществляется следующая политика:

  • Если в импортируемом файле присутствует метка безопасности, но у нее отсутствуют атрибуты, которые заполнены в назначениях данной метки безопасности, атрибуты будут удалены из назначений, при этом сами назначения останутся и совпадающие атрибуты сохранят свое значение.

  • Если в импортируемом файле присутствует метка, но ее объект модели не совпадает с объектом модели в системе, ВСЕ назначения данной метки безопасности будут УДАЛЕНЫ. (Не касается меток безопасности без атрибутов - через ui такие не создать)

  • При поднятом флаге recreate все назначения всех меток безопасности будут удалены.

Аутентификация через OAuth 2.0

BE

Добавлен запрос GET universe-backend/api/v2/core/authentication/login

  • Этот запрос не получает логин и пароль, опционально принимает в query string параметры source (имя источника безопасности, например, oauth2) и locale (локаль пользователя).

  • Этот запрос нужен для аутентификации через OAuth 2.0, сервер авторизации должен перенаправлять пользователя на него, добавляя в query string параметры code и state.

Добавлен запрос GET universe-backend/api/v1/oauth2/provider/all

  • Этот запрос получает список пар ID, под которыми зарегистрированы провайдеры OAuth 2.0, и ID, который возвращает метод getId() провайдера

  • Добавлен для информационных целей

Добавлен опциональный параметр com.universe.mdm.rest.v1.oauth2.redirect.address

  • Используется, если сервер авторизации обязательно требует указывать redirect URI в запросе авторизации

  • Должен быть указан адрес, который в итоге перенаправит запрос пользователя на GET http://your-universe-mdm.org/universe-backend/api/v2/core/authentication/login?source=oauth2.

  • При запуске через Docker значение этого параметра можно указать через переменную OAUTH2_REDIRECT_ADDRESS.

  • Этот параметр также можно редактировать через UI в разделе "Параметры системы".

FE

Добавлены реализации UE SsoAuthTypeManager:

  • DefaultSsoAuthManager (ssoType отсутствует или пустой) — авторизация и пустыми строками в качестве логина и пароля.

  • OAuth2SsoAuthManager (ssoType=oauth2) — авторизация по OAuth 2.0.

Операция профилирования данных

  • Добавлены 2 новых модуля: com.universe.mdm.hpe.job.profile.data и com.universe.mdm.hpe.job.profile.classifiers.

  • Создано две новых схемы БД: com_universe_mdm_hpe_job_profile_data и com_universe_mdm_hpe_job_profile_classifiers.

Операция деперсонализации данных

Добавлены 3 новых модуля:

  • com.universe.mdm.hpe.core - основной модуль HPE, который содержит общую логику, например, реализации обфускаторов;

  • com.universe.mdm.hpe.job.dp.classifiers - модуль операции деперсонализации классификаторов;

  • com.universe.mdm.hpe.job.dp.data - модуль операции деперсонализации данных, который содержит в том числе общую логику операции;

Добавлена новая операция "Деперсонализации данных" в списке операций.

Добавлены параметры для простых атрибутов во всех сущностях, которые их поддерживают:

  • Конфиденциальный - флаг, который показывает, что атрибут хранит конфиденциальную информацию.

  • Способ обезличивания данных - селектор выбора обфускатора, который будет деперсонализировать данные при запуске операции; отображается только если отмечен флаг "Конфиденциальный".

Расширение REST API

Добавлен endpoint: - GET /v2/core/obfuscation-functions/suggest. Возвращает список обфусцирующих функций, находящихся в заданной библиотеке с заданной версией, которые обрабатывают простые атрибуты, имеющие тип из заданного списка.

Параметры:

  • library - имя библиотеки, в которой искать;

  • version - версия библиотек;

  • processedSimpleDataTypes - список простых типов, которые функция может обработать.

Возвращает:

  • Список найденных функций, сгруппированных по обрабатываемому типу.

Пример

Запрос:

  • http://localhost:8080/universe-backend/api/v2/core/obfuscation-functions/suggest?libraryType=basic-obfuscating-functions.jar&version=v1.0.0&processedSimpleDataTypes=String

Ответ:

   {
 "details": {
   "info": [],
   "warning": [],
   "error": []
 },
 "simpleDataFunctions": {
   "String": [
     {
       "name": "com.universe.mdm.hpe.core.service.impl.function.ShuffleValuesInBatch",
       "displayName": "Хэшировать с солью",
       "description": "Создаёт уникальный хэш строки с использованием bcrypt и случайной соли.",
       "libraryName": "basic-obfuscating-functions.jar",
       "libraryVersion": "v1.0.0"
     },
     {
       "name": "com.universe.mdm.hpe.core.service.impl.function.ShuffleValuesInBatch",
       "displayName": "Случайное значение из других записей",
       "description": "Взять случайное значение из значений атрибутов других записей в этом же обрабатываемом списке",
       "libraryName": "basic-obfuscating-functions.jar",
       "libraryVersion": "v1.0.0"
     }
   ]
 }
}

Конфигурирование имени схемы БД

Схема БД, в которую будут устанавливаться расширения, задается настройкой в backend.properties: org.unidata.mdm.system.extension.schema. Значение настройки по умолчанию - public, соответственно текущее поведение системы никак не изменяется. Передать альтернативное имя схемы можно через переменную среды EXTENSION_SCHEMA.

Т.к. исключается жестко заданное имя схемы public, то оно было заменено на плейсхолдеры вида #[org.unidata.mdm.system.extension.schema] во всех местах, где было задано:

  • Все URL подключений к базе данных в backend.properties: org.unidata.mdm.system.datasource.url=jdbc:postgresql://${POSTGRES_ADDRESS}/${DATABASE_NAME:postgres}?user=${POSTGRES_USERNAME}&password=#[org.unidata.mdm.system.password.postgres]&currentSchema=org_unidata_mdm_system, #[org.unidata.mdm.system.extension.schema]&ApplicationName=Unidata-System

  • Все миграции, где создаются extensions, и другие места, где была явно указана схема public

Плейсхолдеры заменяются на значение проперти централизованно при старте системы через PropertyResolverService.

Примечания:

  • Создание/существование кастомной схемы в базе данных, в которую предполагается установка расширений - ответственность указывающего эту схему. Бэкенд не занимается проверкой существования указанной схемы, или ее созданием.

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

Виджет "Задачи" на главной странице

SearchLinksList.tsx

  • Добавлен пропс customCounterRenderer для изменения рендера каунтера.

SearchLinksWidget.tsx

Добавлены пропсы:

  • disableTabBar?: boolean - для отключения табов

  • overflowType?: OVERFLOW_TYPE - для управления overflow в cardPanel

DateTimeUtil

  • Добавлен новый метод diff для высчитывает количества время которое осталось от времени А до времени Б.

Подсветка записи при открытии из кластера

В таблице записей в кластере, а так же в предпросмотре объединения записей добавлена визуальная индикация записи из которой было произведено открытие кластера.

Для того, чтобы запись была отмечена таким образом, необходимо открыть ее из виджета кластеров в карточке записи (реализация DataCardSidePanelItem с moduleId: 'dataRecordClusters').

При переходе в таблицу кластеров из виджета к запросу в адресной строке браузера добавится параметр clusterTargetSubjectId=${etalonId_записи_из_карточки_которой_был_открыт_кластер}.

Колонка с наименованием реестра / справочника в разделе задач и процессов

@module-ee/workflow

  • Добавлены новый столбец в ProcessSearchColumnStore / TaskSearchColumnStore и обработка отображаемого значения в cellRenderer в компонентах Processes / TaskList.

Изменение отображения атрибутов в карточке записи

Заменена CustomProperty с ключом business_search_description на description_field, сохранив свое назначение - указание атрибута описания.

Точки расширения валидаторов карточки задачи

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

Валидаторы запускаются при смене статуса задачи (при взятии задачи в работу или её выполнении). Если результат валидации возвращает ошибку - смена статуса не будет произведена.

Также добавлена кнопка "Детали ошибки" в уведомлении об ошибке валидации.

Предпросмотр вложений

  • Было добавлено API LargeObjectsService#batchFetch для пакетного получения данных об объектах-файлах по списку id объектов и контекст для этой операции FetchLargeObjectsContext. Также в контексты fetch операций LargeObjectsService был добавлен флаг onlyMetadata. Для старых endpoint'ов пока не используется.

  • Для получения метаданных о файлах было добавлено API LargeObjectRestService#fetchLobsMetadata(FetchLargeObjectsRequestRO), endpoint: POST /v2/core/lob/metadata. В теле принимает объект FetchLargeObjectsRequestRO с полем List<String> largeObjectsIds, в котором необходимо передать список id файлов. Также есть флаг onlyMetadata, отвечающий за получение только метаданных, но на данный момент он не используется, т.к. был реализован отдельный endpoint только для них. В ответ на endpoint приходит объект FetchLobsResultRO, в котором хранится список FetchLargeObjectResult. Он был расширен полями createDate, updateDate и containerId для полноты метаданных.

  • Добавлена новая точка расширения FileViewerExtensionRenderer.

Переработка виджета черновиков

Был расширен UE DraftWidgetParams:

  • Поддерживает кастомный компонент.

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

  • extraItems, которые будут находиться справа от таблицы. В МДМ это каунтеры количества черновиков в работе и всего у реестров/справочников.

  • getRouteDescriptor - для корректного перехода к черновику по клику на его ID.

  • fetchDrafts кастомный запрос на получение черновиков.

DraftListWidget.tsx и DraftList.tsx

Вырезано все что работало не корректно, поддержка UE.

Сортировка, фильтрация и пагинация связей-ссылок

В endpoint filtered-relations добавлена корректная обработка флага isReduceReferences для пагинации, фильтрации и сортировки связей-ссылок.

Флаг isReduceReferences отвечает за возвращение только активных (неподавленных) периодов связи-ссылки. Если данный флаг включен, то фильтрация, пагинация и сортировка для связей ссылок работают. В противном случае бэкенд просто возвращает все периоды всех связей-ссылок, что есть в БД, не игнорируя параметры фильтрации и пагинации.

UX индикации параметров операций и параметров системы

Добавлены механизмы по санитизации HTML-сообщений:

  • класс HTMLSanitizer в пакет @universe-platform/util

  • компонент SanitizedHTML в пакет @universe-platform/uikit

UE для табов окна "О системе"

UELicenseInfo отмечен как deprecated.

Логика поиска по входящим связям

CommonTermView.tsx

  • Изменен тип у label?: React.ReactNode;

  • <RenderTermPart/>

  • Добавлен пропс isLabel?: boolean

  • Для label поряд элементов отображается по другому:

    <>
    {value}
    {divider}
    {helper}
    </>
    

Виджет "Задачи" на главной странице

  • SearchLinksList.tsx: добавлен пропс customCounterRenderer для изменения рендера каунтера.

  • SearchLinksWidget.tsx: добавлены пропсы:

    • disableTabBar?: boolean - для отключения вкладок;

    • overflowType?: OVERFLOW_TYPE - для управления overflow в cardPanel.

  • DateTimeUtil: добавлен новый метод diff для высчитывания количества времени, которое осталось от времени А до времени Б.

Наименования листов XLSX

Изменилась логика генерации имен листов XLSX:

  • Добавлен класс XlsxMetadata для хранения метаданных документа (на текущий момент содержит только информацию о маппингах);

  • Добавлен класс MetadataSheetGenerator для генерации служебного листа с метаданными/маппингами;

  • Обратная совместимость не нарушена, старые файлы должны успешно импортироваться/экспортироваться.

Изменения в meta-data-driven

Новая группа параметров

Добавлена новая группа параметров com.universe.mdm.metadriven.core.group "Параметры MetaDriven".

Также добавлен новый параметр com.universe.mdm.metadriven.core.mapper.fetchEnumIdValue "Получать идентификатор и его значение для перечислений при загрузке записи" (по умолчанию - false).

При его включении во всех ответах meta-data-driven (в ответах на запросы meta-data-driven/v1/data/, а так же для UPSERT, MERGE, UNMERGE нотификаций в точках [SEND_UPSERT_NOTIFICATION] для RECORD_UPSERT_START, [SEND_MERGE_NOTIFICATION] и [SEND_MERGE_LOSER_NOTIFICATION] для RECORD_MERGE_START, [SEND_UNMERGE _NOTIFICATION] для RECORD_UNMERGE _START, (т.к. в них записи загружаются через API meta-data-driven) , при загрузке записи с атрибутом типа перечисление, он будет представлено в новом виде:

"attributes": {
        "asd": {
                "id": "1", //идентификатор
                "value": "qwe" //отображаемое значение
        }
}

Удаление исходящей связи по toEtalonID

В MetaDriven добавлена возможность удалять исходящие связи по эталону связанной записи (toEtalonId).

В RelationDelete и RelationDeleteRO добавлено поле toEtalonId.

Удаление эталона связи по toEtalonId:

{
   "entityName": "from",
   "externalId": "952a61c0-319b-11f0-a983-0510b710939a",
   "sourceSystem": "universe",
   "attributes": {
       "str": "abc"
   },
   "relationsDeletes": {
       "m2m": [
           {
               "toEtalonId": "9cd19cc4-319b-11f0-89cc-99adaf6d7cd1"
           }
       ]
   }
}

Удаление периода связи по toEtalonId:

  {
   "entityName": "from",
   "externalId": "952a61c0-319b-11f0-a983-0510b710939a",
   "sourceSystem": "universe",
   "attributes": {
       "str": "abc"
   },
   "relationsDeletes": {
       "m2m": [
           {
               "toEtalonId": "9cd19cc4-319b-11f0-89cc-99adaf6d7cd1",
               "validFrom": "1900-01-01T00:00:00.000",
               "validTo": "2000-12-31T23:59:59.999"
           }
       ]
   }
}

Особенности

Приоритет способов удаления связей в MetaDriven:

  1. По эталону связи (etalonId);

  2. По эталону связанной записи (toEtalonId);

  3. По внешнему ключу связанной записи (toExternalId + toSourceSystem).

Удаление периода актуальности связи по toEtalonID/toExternalID

Ничего не изменилось. Функционал удаления периодов связей присутствовал в MetaDriven.

Удаление периода по эталону связи (etalonId):

  {
   "entityName": "from",
   "externalId": "952a61c0-319b-11f0-a983-0510b710939a",
   "sourceSystem": "universe",
   "attributes": {
       "str": "abc"
   },
   "relationsDeletes": {
       "m2m": [
           {
               "etalonId": "d9c88134-eaec-457b-98b3-3b99cb54e3ca",
               "validFrom": "1900-01-01T00:00:00.000",
               "validTo": "2000-12-31T23:59:59.999"
           }
       ]
   }
}

Удаление периода по эталону связанной записи (toEtalonId):

  {
   "entityName": "from",
   "externalId": "952a61c0-319b-11f0-a983-0510b710939a",
   "sourceSystem": "universe",
   "attributes": {
       "str": "abc"
   },
   "relationsDeletes": {
       "m2m": [
           {
               "toEtalonId": "b24b7e1e-7813-42cd-b26f-0880949a1524",
               "validFrom": "1900-01-01T00:00:00.000",
               "validTo": "2000-12-31T23:59:59.999"
           }
       ]
   }
}

Удаление периода по внешнему ключу связанной записи (toExternalId + toSourceSystem):

  {
   "entityName": "from",
   "externalId": "952a61c0-319b-11f0-a983-0510b710939a",
   "sourceSystem": "universe",
   "attributes": {
       "str": "abc"
   },
   "relationsDeletes": {
       "m2m": [
           {
               "toExternalId": "b1",
                               "toSourceSystem": "bit",
               "validFrom": "1900-01-01T00:00:00.000",
               "validTo": "2000-12-31T23:59:59.999"
           }
       ]
   }
}

Приоритет способов удаления связей в MetaDriven:

  1. По эталону связи (etalonId);

  2. По эталону связанной записи (toEtalonId);

  3. По внешнему ключу связанной записи (toExternalId + toSourceSystem).

Нотификации Kafka

  • В тело события добавлен вывод секции externalKeys со списком ключей записи, по которой отправлено уведомление. Присылается список объектов ExternalKey, содержащих поля "id" - id внешнего ключа, "sourceSystem" - система источник внешнего ключа, "initialOwner" - id эталона-владельца записи.

  • В нотификации, в которых загружаются исходящие связи (UPSERT, MERGE, UNMERGE), в секцию relName был добавлен список externalKeys связанной записи. Также было добавлено поле initialOwner в классе ExternalKey, отображающее etalonId записи-владельца.

    • Для отображения блока со связью необходимо включить параметр com.universe.mdm.notifications.notifications.fetchResult "Загружать запись события".

  • Добавлена возможность указывать атрибуты записи для отображения во входящей связи в блоке incomingRelations. Для использования функционала предварительно необходимо настроить отправку нотификаций:

    • Включить параметр com.universe.mdm.notifications.notifications.fetchIncomingRelations для отправки информации о входящих связях.

    • В дополнительных параметрах связи, для которой настраивается отображение атрибутов записи во входящей связи, необходимо указать доп.параметр с ключом addAttributesToIncomingRelation и в значении указать атрибуты реестра со связью, которые необходимо отображать без пробелов через запятую (например, testattribute1,test_attr2).

Нотификация удаления

В нотификацию удаления записи DeleteRecordEvent было добавлено состояние записи (атрибуты, связи, классификации) на момент ее удаления (поля attributes, lookupLinkAttributes, relations, classifications).

  • Заполнение этих полей регулируется параметром "Загружать запись события" (com.universe.mdm.notifications.notifications.fetchResult).

  • За отправку нотификаций удаления отвечает точка com.universe.mdm.notifications[SEND_DELETE_NOTIFICATION], которая должна встраиваться в поток выполнения org.unidata.mdm.data[RECORD_DELETE_START] перед точкой org.unidata.mdm.data[RECORD_DELETE_FINISH].

При отправке нотификации удаления теперь учитывается вид удаления (логический или физический).

Добавлены два новых типа нотификации: LOGICAL_DELETE и PHYSICAL_DELETE. Тип нотификации DELETE помечен как deprecated.

  • Если происходит логическое удаление, то будет отправлена нотификация с типом LOGICAL_DELETE.

  • Если происходит физическое удаление, то будет отправлена нотификация с типом PHYSICAL_DELETE.

Нотификации UPSERT, MERGE, UNMERGE

Для нотификаций с типом UPSERT, MERGE, UNMERGE была добавлена возможность при загрузке записи получать пустые null-значения атрибутов. Для массив-атрибутов должны отправляться символы пустого массива "[]". Для этого был добавлен параметр com.universe.mdm.notifications.notifications.fetchEmptyAttributes "Загружать пустые значения атрибутов".

  • Для получения уведомления с записью с null-значениями, помимо включения нового параметра, также необходимо включить параметр com.universe.mdm.notifications.notifications.fetchResult "Загружать запись события".

  • Функционал добавлен для UPSERT, MERGE, UNMERGE нотификаций в точках [SEND_UPSERT_NOTIFICATION] для RECORD_UPSERT_START, [SEND_MERGE_NOTIFICATION] и [SEND_MERGE_LOSER_NOTIFICATION] для потока RECORD_MERGE_START, [SEND_UNMERGE _NOTIFICATION] для потока RECORD_UNMERGE _START.

Также в нотификации был добавлен блок для информации о входящих связях и параметр com.universe.mdm.notifications.notifications.fetchIncomingRelations, включающий получение этой информации.

  • Для отображения этого блока необходимо включить параметр com.universe.mdm.notifications.notifications.fetchResult "Загружать запись события".

  • При включении параметра и наличии входящих связей у записи в нотификации приходит блок incomingRelations. При отсутствии входящих связей поле "incomingRelations" будет пустым. При отключенном параметре будет приходить null.

Нотификации при консолидации

Изменились типы событий в нотификациях при консолидации.

  • Нотификация с типом MERGE больше не отправляется, поскольку она избыточна.

  • Тип нотификации обновления консолидированной записи-победителя изменился на UPDATE_WINNER_MERGE.

  • За отправку нотификаций с типом UPDATE_WINNER_MERGE отвечает точка com.universe.mdm.notifications[SEND_MERGE_NOTIFICATION], которая должна встраиваться в поток org.unidata.mdm.data[RECORD_MERGE_START] перед точкой org.unidata.mdm.data[RECORD_MERGE_FINISH].

  • Добавлена отправка нотификаций с типом UPDATE_LOSER_MERGE. Нотификации содержат в себе информацию о проигравших записях до их консолидации.

  • За отправку нотификаций с типом UPDATE_LOSER_MERGE отвечает новая точка com.universe.mdm.notifications[SEND_MERGE_LOSER_NOTIFICATION], которая должна встраиваться в поток org.unidata.mdm.data[RECORD_MERGE_START] перед точкой org.unidata.mdm.data[RECORD_MERGE_PERSISTENCE] и коннекторами org.unidata.mdm.data[RELATION_MERGE_CONNECTOR], com.unidata.mdm.classifiers[CLASSIFICATION_MERGE_CONNECTOR] для обеспечения возможности получения корректного состояния проигравших записей, их связей и классификаций.

Нотификации при расконсолидации

Изменились типы событий в нотификациях при расконсолидации.

  • Нотификация с типом UNMERGE больше не отправляется, поскольку она избыточна. Тип нотификации UNMERGE помечен как deprecated.

  • Добавлены два новых типа нотификации: UPDATE_UNMERGE и CREATE_UNMERGE. Тип нотификации обновления консолидированной записи изменился на UPDATE_UNMERGE. Тип нотификации создания отсоединенной записи изменился на CREATE_UNMERGE.

  • За отправку нотификаций расконсолидации отвечает точка com.universe.mdm.notifications[SEND_UNMERGE_NOTIFICATION], которая должна встраиваться в поток org.unidata.mdm.data[RECORD_UNMERGE_START] перед точкой org.unidata.mdm.data[RECORD_UNMERGE_FINISH] для отправки финального состояния записей.

Нотификации об изменениях записей иерархических справочников

В Upsert нотификации для уведомлений об изменениях записей иерархических справочников добавлен блок "parent", содержащий информацию о родительской записи или отображающий, что запись является корневой.

  • Для отображения этого блока необходимо включить параметр com.universe.mdm.notifications.notifications.fetchResult "Загружать запись события".

  • Для записей регистров или не иерархических справочников блок не отправляется.

  • Блок добавлен для UPSERT нотификаций в точке [SEND_UPSERT_NOTIFICATION], который необходимо добавить в пайплайн RECORD_UPSERT_START.

Атрибуты связанной записи для нотификации

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

Необходимые атрибуты указываются в модели данных связи в доп. параметре с ключом "addAttributesToOutgoingRelation" и значением в виде списка их логических имен через запятую без пробела.

Пользовательские параметры

Добавлен новый ресурс безопасности: "Управление пользовательскими параметрами" (категория "Система", уровень прав "Полные").

Новая таблица в БД org_unidata_mdm_system.user_properties - хранит определения пользовательских параметров.

В лог теперь не пишутся старое и новое значения секретного параметра системы при изменении значения (теперь пишется только то, что изменился секретный параметр с указанным именем).

Рефакторинг GET API MetaDriven

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

Следующее API было помечено как @Deprecated(since = "6.13", forRemoval = true):

  • MetaDrivenManagerService#recordByEtalonKey(String entityName, String etalonKey)

  • MetaDrivenManagerService#recordByEtalonKeyWithDraftIdAndBoundary(String entityName, String etalonKey, Long draftId, Date forDate, Pair<Date, Date> dateFrame

  • MetaDrivenManagerService#recordByEtalonKeyAndBoundary(String entityName, String etalonKey, Date forDate, Pair<Date, Date> dateFrame, boolean includeDeletedPeriods)

  • MetaDrivenManagerService#recordByEtalonKeyWithDraft(String entityName, String etalonKey, Long draftId)

  • MetaDrivenManagerService#recordByExternalKey(String entityName, String sourceSystem, String externalId, Date forDate, Pair<Date, Date> dateFrame, boolean includeDeletedPeriods)

Вместо этого был добавлен метод MetaDrivenManagerService#getRecord(MetaDrivenGetRequestContext getCtx).

Для него были созданы следующие классы:

  • AbstractMetaDrivenRecordContext и AbstractMetaDrivenRecordContextBuilder - абстрактные классы для контекстов, отражающие контекст для работы с записью в MetaDriven.

  • MetaDrivenGetRequestContext и MetaDrivenGetRequestContextBuilder - наследники абстрактных классов контекстов записи, расширенные полями для использования в GET-запросах.

Изменения в REST API

Пользовательские параметры

Добавлены новые запросы:

  • Запрос POST /v2/core/user-defined-properties - создает пользовательские параметры, доступно только при наличии уровня прав "Полные" на ресурс "Управление пользовательскими параметрами".

  • Запрос GET /v2/core/user-defined-properties - получает все пользовательские параметры, доступно всем.

  • Запрос DELETE /v2/core/user-defined-properties?id[]=name1&id[]=name2 - удаляет пользовательские параметры, доступно только при наличии уровня прав "Полные" на ресурс "Управление пользовательскими параметрами".

ETL: восстановление записи

Добавлен новый REST API: POST api/meta-data-driven/v1/data/restore.

Было добавлено API MetaDrivenManagerService#restoreRecord.

Удаление эталона записи по одному из external ключей

Добавлен новый REST-запрос на удаление эталона записи по одному из external ключей: DELETE api/meta-data-driven/v1/data/{entity-name}/{source-system}/{external-id}

В интерфейсе MetaDrivenManagerService был добавлен метод deleteRecord(MetaDrivenDeleteRequestContext), использующий новый контекст для удаления записи MetaDrivenDeleteRequestContext.

  • Метод сервиса deleteByEtalonKey(String, String, boolean) был помечен как deprecated for removal в версии 6.13 из-за перехода на новый метод с контекстом.

  • Метод buildDeleteFragment(String, String, boolean) интерфейса ExtensibleObjectHandler помечен как deprecated for removal, поэтому необходимо использовать buildDeleteFragment(MetaDrivenDeleteRequestContext) вместо него.

  • Методы MetaDrivenManagerService#deleteByEtalonKey(String, String, boolean) и ExtensibleObjectHandler#buildDeleteFragment(String, String, boolean) были помечены deprecated for removal.

Бизнес-процессы. Добавление шаблона

Добавлены новые REST-запрос: POST v2/workflow/templates/upsert, DELETE v2/workflow/templates/delete, POST v2/workflow/templates/list.

Добавление нового шаблона

Метод POST v2/workflow/templates/upsert принимает 4 параметра:

  • name - системное имя шаблона;

  • displayName - отображаемое имя шаблона;

  • version - версия шаблона;

  • description - описание шаблона;

  • file - сам шаблон.

Параметры name, displayName, version, file являются обязательными к заполнению.

Этот метод позволяет изменять уже созданные шаблоны.

Дополнительные изменения

Удалено Deprecated API:

  • DataRecordsRestService#deleteEntity(String, String, long, Boolean, Boolean, Boolean, String, String): DELETE /v2/data/records/delete/{name}/{id}.

Удаление Deprecated:

  • CE:

    • класс TokenTimestampRefresher;

    • поля класса UpsertRelationsRequestContext и методы их получения/установки: skipCleanse, bypassExtensionPoints, override;

    • DataRecordsService#detachOrigin(SplitRecordRequestContext), DataRecordsService#join(JoinRequestContext);

    • CommonRecordsComponent#join(JoinRequestContext);

    • PostgresMatchingRecordDAO#delete(String, List<MatchingRecordKeyPO>);

    • API MatchingModelRestService#source(Attachment);

    • OpenSearchClientWrapper#clusterStats();

    • поле TermsAggregationContext#path и связанные с ним методы;

    • поле RangeAggregationContext#path и связанные с ним методы;

    • поле SearchRequestContext#must и связанные с ним методы;

    • TagRefreshContext#builder(String).

  • ЕЕ:

    • поле TableDataConverterImpl#RECORD_SYSTEM_HEADERS;

    • ClassifierVersionElement#source(ClassifierModelInstance, String, String, String, String);

    • SubscriptionsRestService#deprecatedDelete(DeleteSubscriptionRequestRO), endpoint: DELETE /v1/subscriptions;

    • поля ClassificationImportXlsxRenderingComponent#TO, ClassificationImportXlsxRenderingComponent#FROM.

Добавлен новый endpoint (POST v2/data/records-history/historic-record) для получения исторической записи.

Структура запроса похожа на atomic/get, но включает в себя только необходимые для получения исторической записи поля:

Запрос:

   {
 "payload": {
   "org.unidata.mdm.rest.v2.data": {
     "entityName": "entity", // Имя реестра или справочника
     "etalonId": "630c672d-08c2-11f0-a89d-352f4888a979", // Etalon ID записи
     "timelineDate": "1900-01-01T00:00:00.000Z", // Время периода актуальности записи (если не указано, то текущее)
     "lastUpdateDate": "2025-03-24T15:12:23.793Z" // Время последнего обновления записи (если указано, то все изменения после игнорируются)
   },
   "com.unidata.mdm.rest.v1.classifiers": {
     "entityName": "from", // Имя реестра или справочника
     "etalonId": "630c672d-08c2-11f0-a89d-352f4888a979", // Etalon ID записи
     "lastUpdateDate": "2025-03-24T15:12:23.793Z" // Время последнего обновления записи (если указано, то все изменения после игнорируются)
   }
 }
}

Структура ответа аналогична atomic/get:

Ответ:

   {
   "details": {
       "info": [],
       "warning": [],
       "error": []
   },
   "payload": {
       "org.unidata.mdm.rest.v2.matching.data": {
           "results": []
       },
       "org.unidata.mdm.rest.v2.data": {
           "details": {
               "info": [],
               "warning": [],
               "error": []
           },
           "recordKeys": {
               "etalonKey": {
                   "id": "630c672d-08c2-11f0-a89d-352f4888a979",
                   "status": "ACTIVE"
               },
               "originKeys": [
                   {
                       "externalId": {
                           "externalId": "5d1d3c00-08c2-11f0-b671-a9859d730e5e",
                           "sourceSystem": "universe"
                       },
                       "enrichment": false,
                       "revision": 1,
                       "status": "ACTIVE",
                       "createDate": "2025-03-24T15:12:23.79Z",
                       "updateDate": null,
                       "createdBy": "admin",
                       "updatedBy": null
                   }
               ],
               "entityName": "entity",
               "lsn": {
                   "lsn": 5,
                   "shard": 0
               },
               "node": 0,
               "createDate": "2025-03-24T15:12:23.79Z",
               "updateDate": "2025-03-24T15:12:23.791Z",
               "lastEventUpdateDate": null,
               "createdBy": "admin",
               "updatedBy": "admin",
               "active": true,
               "published": true,
               "parent": null,
               "children": []
           },
           "etalon": {
               "simpleAttributes": [
                   {
                       "name": "str",
                       "type": "String",
                       "displayValue": null,
                       "targetEtalonId": null,
                       "valueId": null,
                       "unitId": null,
                       "value": "test"
                   }
               ],
               "arrayAttributes": [],
               "complexAttributes": [],
               "codeAttributes": [],
               "etalonId": "630c672d-08c2-11f0-a89d-352f4888a979",
               "modified": false,
               "version": 0,
               "entityName": "entity",
               "namespace": "register",
               "validFrom": "1900-01-01T00:00:00Z",
               "validTo": "2500-12-31T23:59:59.999Z",
               "status": "ACTIVE",
               "lsn": {
                   "lsn": 5,
                   "shard": 0
               },
               "createDate": "2025-03-24T15:12:23.790Z",
               "createdBy": "admin",
               "updateDate": "2025-03-24T15:12:23.791Z",
               "updatedBy": "admin",
               "rights": null,
               "operationType": "DIRECT",
               "diffToDraft": null,
               "published": true
           }
       },
               "com.unidata.mdm.rest.v1.classifiers": {
           "details": {
               "info": [],
               "warning": [],
               "error": []
           },
           "classifications": [
               {
                   "etalon": {
                       "simpleAttributes": [
                           {
                               "name": "attr",
                               "type": "String",
                               "displayValue": null,
                               "targetEtalonId": null,
                               "valueId": null,
                               "unitId": null,
                               "value": "test"
                           }
                       ],
                       "arrayAttributes": [],
                       "complexAttributes": [],
                       "classificationEtalonId": "631010b1-08c2-11f0-a89d-352f4888a979",
                       "lsn": {
                           "lsn": 3,
                           "shard": 0
                       },
                       "classifierName": "clsf",
                       "classifierVersion": "clsf",
                       "classifierNodeId": "node",
                       "status": "ACTIVE",
                       "operationType": "DIRECT",
                       "createDate": "2025-03-24T15:12:23.797Z",
                       "createdBy": "admin",
                       "updateDate": "2025-03-24T15:12:23.797Z",
                       "updatedBy": "admin",
                       "rights": null,
                       "namespace": "classification"
                   },
                   "origins": [
                       {
                           "simpleAttributes": [
                               {
                                   "name": "attr",
                                   "type": "String",
                                   "displayValue": null,
                                   "targetEtalonId": null,
                                   "valueId": null,
                                   "unitId": null,
                                   "value": "asdasd"
                               }
                           ],
                           "arrayAttributes": [],
                           "complexAttributes": [],
                           "classificationOriginId": "63b50346-08c2-11f0-a89d-352f4888a979",
                           "externalId": "5d1d3c00-08c2-11f0-b671-a9859d730e5e",
                           "sourceSystem": "universe",
                           "entityName": "from",
                           "classifierName": "clsf",
                           "classifierVersion": "clsf",
                           "classifierNodeId": "node",
                           "status": "ACTIVE",
                           "operationType": "DIRECT",
                           "createDate": "2025-03-24T15:12:23.797Z",
                           "createdBy": "admin",
                           "updateDate": "2025-03-24T15:12:23.793Z",
                           "updatedBy": "admin",
                           "enriched": false,
                           "revision": 1,
                           "operationId": null
                       }
                   ],
                   "draftId": 0
               }
           ]
       },
       "org.unidata.mdm.rest.v2.dq.data": {
           "results": []
       }
   }
}

Так же было добавлено событие RECORD_MERGE_LOSER в истории записи. Оно указывает на то, что запись проиграла в процессе консолидации и содержит в себе информацию о победившем эталоне.

Endpoint вызывает новый метод RecordsHistoryService#getHistoricRecord, в котором модифицируется таймлайн исторической записи и запускается пайплайн RECORD_GET_START с этим таймлайном.

На данный момент модификация затрагивает консолидированные записи. Для победивших и проигравших записей выполняется поиск событий RECORD_MERGE и RECORD_MERGE_LOSER и в таймлайн добавляются только те источники, которые были в записи до консолидации.

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

При неизвестном значении будет выдана ошибка JOB_DEFINITION_UNKNOWN_VALUE_IN_PARAMETER. Пример текста сообщения об ошибке:

"message": "Неизвестное значение [product] для параметра [Проиндексировать объекты модели]",
"internalMessage": "Unknown value [product] for parameter [Проиндексировать объекты модели].",
"nested": []