Журнал технических изменений
Примечание
Ниже представлены технические изменения вышедших релизов. Краткий перечень изменений релизов отражен в новых функциях. Также см. информацию о важных изменениях.
Реализация вспомогательного запроса
Разработан REST API для получения данных по активам и связям для графа "Поток данных". В качестве входных данных используются данные по активу , для которого строится граф. В результат попадают все вложенные связи до заданного уровня, а также все связи с типом "Поток данных" дочерних элементов.
Получение активов и связей для графа поток данных
POST /universe-backend/api/v1/dg/data/catalog/traverse/children-data-lineage
Пример запроса:
Пример ответа
{
"typeNames": [ // Фильтр для дочерних типов активов (опционально)
"bdel100",
"v6",
"bp1",
"v8",
"dq_check",
"InformationSystemAsset",
"DG606",
"default_transformation",
"bdel_rel3",
"del1",
"dq_check_formit",
"PgColumn",
"default_transformation_container",
"default_transformation_port",
"AssetType",
"PgTable",
"bdel_rel1",
"sm1",
"bdel_rel2",
"aktiv1",
"PK",
"PgSchema",
"dq_rule"
],
"relationTypes": null, // Фильтр для дочерних связей (опционально)
"maxDepth": 2, // Глубина дерева вложенных связей графа
"sourceTypeName": "PgSchema", //Логическое наименование типа актива
"sourceId": "1b381321-4409-11f1-9e37-03c8e3731629" //etalonId актива
}
Пример ответа:
Пример ответа
{
"assets": [
{
"etalonId": "29c62e27-48da-11f1-b71a-03c8e3731629",
"typeName": "PgColumn",
"displayName": "name22",
"status": "ACTIVE"
},
{
"etalonId": "b0b18bb1-48d7-11f1-b71a-03c8e3731629",
"typeName": "PgColumn",
"displayName": "nam1",
"status": "ACTIVE"
},
{
"etalonId": "1b386145-4409-11f1-9e37-03c8e3731629",
"typeName": "PgColumn",
"displayName": "id",
"status": "ACTIVE"
},
{
"etalonId": "d93a1164-4fcc-11f1-bbe3-03c8e3731629",
"typeName": "v8",
"displayName": "1_2",
"status": "ACTIVE"
},
{
"etalonId": "1b381321-4409-11f1-9e37-03c8e3731629",
"typeName": "PgSchema",
"displayName": "public",
"status": "ACTIVE"
},
{
"etalonId": "29c5b8f2-48da-11f1-b71a-03c8e3731629",
"typeName": "PgColumn",
"displayName": "nameeeeeee",
"status": "ACTIVE"
},
{
"etalonId": "1b3e7bd0-4409-11f1-9e37-03c8e3731629",
"typeName": "PgColumn",
"displayName": "name",
"status": "ACTIVE"
},
{
"etalonId": "1b3db87b-4409-11f1-9e37-03c8e3731629",
"typeName": "PgTable",
"displayName": "test_sm",
"status": "ACTIVE"
},
{
"etalonId": "b0b3fcb6-48d7-11f1-b71a-03c8e3731629",
"typeName": "PgColumn",
"displayName": "name2",
"status": "ACTIVE"
}
],
"relations": [
{
"etalonId": "68b6316d-0ac3-4f72-b631-6d0ac31f721c",
"relationType": "Consist",
"fromEtalonId": "1b3db87b-4409-11f1-9e37-03c8e3731629",
"toEtalonId": "b0b18bb1-48d7-11f1-b71a-03c8e3731629",
"status": "ACTIVE"
},
{
"etalonId": "4a05c9d1-a24e-42a0-85c9-d1a24eb2a0d5",
"relationType": "vvv",
"fromEtalonId": "1b386145-4409-11f1-9e37-03c8e3731629",
"toEtalonId": "d93a1164-4fcc-11f1-bbe3-03c8e3731629",
"status": "ACTIVE"
},
{
"etalonId": "fe60f7b9-3591-418c-a0f7-b93591218c8c",
"relationType": "Consist",
"fromEtalonId": "1b3db87b-4409-11f1-9e37-03c8e3731629",
"toEtalonId": "1b386145-4409-11f1-9e37-03c8e3731629",
"status": "ACTIVE"
},
{
"etalonId": "c3980edc-0a98-4fbb-980e-dc0a987fbb3c",
"relationType": "vvv",
"fromEtalonId": "1b3e7bd0-4409-11f1-9e37-03c8e3731629",
"toEtalonId": "d93a1164-4fcc-11f1-bbe3-03c8e3731629",
"status": "ACTIVE"
},
{
"etalonId": "7a38371e-2ae0-4065-b837-1e2ae0f065e9",
"relationType": "Consist",
"fromEtalonId": "1b3db87b-4409-11f1-9e37-03c8e3731629",
"toEtalonId": "29c5b8f2-48da-11f1-b71a-03c8e3731629",
"status": "ACTIVE"
},
{
"etalonId": "82571ca5-ad20-46df-971c-a5ad2026dfce",
"relationType": "Consist",
"fromEtalonId": "1b381321-4409-11f1-9e37-03c8e3731629",
"toEtalonId": "1b3db87b-4409-11f1-9e37-03c8e3731629",
"status": "ACTIVE"
},
{
"etalonId": "ae498cb4-01c9-4140-898c-b401c9a140c5",
"relationType": "vvv",
"fromEtalonId": "29c5b8f2-48da-11f1-b71a-03c8e3731629",
"toEtalonId": "d93a1164-4fcc-11f1-bbe3-03c8e3731629",
"status": "ACTIVE"
},
{
"etalonId": "d2c3a365-8448-43a2-83a3-65844853a2a2",
"relationType": "vvv",
"fromEtalonId": "29c62e27-48da-11f1-b71a-03c8e3731629",
"toEtalonId": "d93a1164-4fcc-11f1-bbe3-03c8e3731629",
"status": "ACTIVE"
},
{
"etalonId": "f467090d-8545-44b8-a709-0d8545c4b886",
"relationType": "Consist",
"fromEtalonId": "1b3db87b-4409-11f1-9e37-03c8e3731629",
"toEtalonId": "b0b3fcb6-48d7-11f1-b71a-03c8e3731629",
"status": "ACTIVE"
},
{
"etalonId": "47800487-2ba5-4242-8004-872ba5a242c8",
"relationType": "Consist",
"fromEtalonId": "1b3db87b-4409-11f1-9e37-03c8e3731629",
"toEtalonId": "29c62e27-48da-11f1-b71a-03c8e3731629",
"status": "ACTIVE"
},
{
"etalonId": "e9ba0a73-cc43-4569-ba0a-73cc4305696e",
"relationType": "Consist",
"fromEtalonId": "1b3db87b-4409-11f1-9e37-03c8e3731629",
"toEtalonId": "1b3e7bd0-4409-11f1-9e37-03c8e3731629",
"status": "ACTIVE"
},
{
"etalonId": "94741397-5896-464e-b413-975896764e3a",
"relationType": "vvv",
"fromEtalonId": "b0b18bb1-48d7-11f1-b71a-03c8e3731629",
"toEtalonId": "d93a1164-4fcc-11f1-bbe3-03c8e3731629",
"status": "ACTIVE"
},
{
"etalonId": "103adb53-e5af-43ad-badb-53e5af03ade2",
"relationType": "vvv",
"fromEtalonId": "b0b3fcb6-48d7-11f1-b71a-03c8e3731629",
"toEtalonId": "d93a1164-4fcc-11f1-bbe3-03c8e3731629",
"status": "ACTIVE"
}
]
}
Запрет выбора неконечных записей в качестве значения по умолчанию для иерархического справочника
Backend:
Добавлена поддержка поиска записей в иерархическом справочнике:
только по конечным записям (без дочерних записей);
только по не конечным записям (с дочерними записями).
При поиске по черновикам не поддерживается. Логически удалённые записи НЕ считаются дочерними записями.
В запросе POST /v2/search добавлено поле hierarchicalDataSearchType, доступные значения:
LEAFS_ONLY - поиск только конечных записей (без дочерних записей);
NON_LEAFS_ONLY - поиск только не конечных записей (с дочерними записями).
{
"payload": {
"org.unidata.mdm.rest.v2.data": {
"hierarchicalDataSearchType": null,
...
}
}
Авторекомендация
Реализация:
Поиск меток по метаданным реализован как FUZZY поиск по отображаемому наименованию среди объектов данного типа актива. Среди найденных записей процент соответствия вычисляется по формуле (maxLen - distance) / maxLen * 100, где maxLen - максимум из длин исходного наименования и найденного, а distance - расстояние Левенштейна между ними. Поиск меток по данным реализован исходя из получения результатов профилирования актива - значения из примера профилирования проверяются правилами данных меток системы. Процент соответствия вычисляется по формуле: количество значений успешно прошедших проверку правила метки * 100 /общее количество значений из профилирования. Поиск меток по данным работает только в случае если в системе доступен сервис профилирования (добавлен модуль в систему).
REST
Получение рекомендаций
POST /universe-backend/api/v1/dg/data-labeling/recommendations
Пример запроса:
Пример ответа
{
"typeName": "PgColumn", //Логическое наименование типа актива физического слоя
"etalonId": "etalonId", //etalonId актива
"recalculate": true, //Признак пересчета рекомендаций. В случае false рекомендации будут браться из последнего вычисления сохраненного в БД
"dataLabelingType": "BOTH", //Определяет каким способом будет проходить подбор меток, допустимые значения "BOTH", "META", "DATA"
"minMatchPercent": 10, // Минимальный процент соответствия для подбора меток
}
Пример ответа:
Пример ответа
{
"details": {
"info": [],
"warning": [],
"error": []
},
"typeName": "PgColumn",
"etalonId": "1b3e7bd0-4409-11f1-9e37-03c8e3731629",
"createDate": "2026-05-15T12:18:27.024Z",
"labels": [ //Список рекомендованных меток
{
"name": "SM_REF2",
"displayName": "SM_REF2",
"type": "META", // Способ получения рекомендованной метки - "По метаданным"
"matchPercent": 80 // Процент соответствия
},
{
"name": "smart_1104179468747997185",
"displayName": "sm_test",
"type": "META",
"matchPercent": 75
},
{
"name": "smart_1104187018461708289",
"displayName": "ddr",
"type": "META",
"matchPercent": 40
}
]
}
Массовое назначение меток
POST /universe-backend/api/v1/dg/data-labeling/catalog/assign/multiple
Пример запроса:
Пример ответа
{
"typeName": "PgColumn",
"etalonId": "1b3e7bd0-4409-11f1-9e37-03c8e3731629",
"dataLabels": ["SM_MARK", "SM_MARK2"]
}
Пример ответа:
Пример ответа
{
"details": {
"info": [],
"warning": [],
"error": []
},
"dataLabels": {
"typeName": "PgColumn",
"etalonId": "1b3e7bd0-4409-11f1-9e37-03c8e3731629",
"terms": [],
"dataLabels": [
{
"lsn": 3,
"id": "0f82be01-a331-41d0-82be-01a33111d01a",
"typeName": "PgColumn",
"etalonId": "1b3e7bd0-4409-11f1-9e37-03c8e3731629",
"labelName": "smart_1104179468747997185",
"labelDisplayName": "sm_test",
"smart": true,
"type": "MANUAL",
"status": "REJECTED",
"active": true,
"labelingType": null,
"labelingScore": 0,
"createDate": "2026-05-05T22:56:36.216Z",
"createdBy": "admin",
"updateDate": "2026-05-05T23:12:27.688Z",
"updatedBy": "admin"
},
{
"lsn": 13,
"id": "f65c9559-65a9-4e87-9c95-5965a97e87f7",
"typeName": "PgColumn",
"etalonId": "1b3e7bd0-4409-11f1-9e37-03c8e3731629",
"labelName": "SM_MARK2",
"labelDisplayName": "SM_MARK2",
"smart": false,
"type": "MANUAL",
"status": "APPROVED",
"active": true,
"labelingType": null,
"labelingScore": 0,
"createDate": "2026-05-06T09:58:04.668Z",
"createdBy": "admin",
"updateDate": null,
"updatedBy": null
},
{
"lsn": 14,
"id": "742c070c-8a14-4957-ac07-0c8a14195791",
"typeName": "PgColumn",
"etalonId": "1b3e7bd0-4409-11f1-9e37-03c8e3731629",
"labelName": "SM_MARK",
"labelDisplayName": "SM_MARK",
"smart": false,
"type": "MANUAL",
"status": "APPROVED",
"active": true,
"labelingType": null,
"labelingScore": 0,
"createDate": "2026-05-06T09:58:04.668Z",
"createdBy": "admin",
"updateDate": null,
"updatedBy": null
}
]
}
}
Детализация событий оценки в журнале (значение и комментарий)
В журнале событий уточнены два события по оценкам активов: при удалении оценки фиксируется ее значение, а при выставлении оценки - текст комментария. Разбивка по критериям ("Базовый критерий") выводится всегда.
Удаление оценки (score-delete)
Ранее событие отражало только факт удаления, без значения. Теперь добавлено значение удалённой оценки в том же формате, что и при выставлении (имя критерия и значение; поддерживается несколько критериев).
Было |
[Иван Петров] удалил(-а) свою оценку у актива [Asset A] типа [Таблица] |
Стало |
[Иван Петров] удалил(-а) свою оценку Базовый критерий: 1 у актива [Asset A] типа [Таблица] |
Выставление оценки (score-insert)
В событие добавлен текст комментария к оценке. Комментарий хранится как форматированный документ (Quill Delta), в журнал выводится читаемый текст; упоминания - как @Имя.
Было |
[Иван Петров] выставил(-а) оценку Базовый критерий: 4, активу [Asset A] типа [Таблица] |
Стало |
[Иван Петров] выставил(-а) оценку Базовый критерий: 4, и комментарий: согласовано, активу [Asset A] типа [Таблица] |
Для нескольких критериев значение выводится списком, например: Базовый критерий: 1, Качество: 3.
Область применения
События журнала "удаление оценки" (score-delete) и "выставление оценки" (score-insert), namespace asset.
Разбивка по критериям выводится всегда (лицензия на категории не проверяется).
Бизнес-поиск. Быстрые фильтры. Группировка по типам актива с настройкой условий
Логика построения запросов
Например имеется следующая иерархия типов активов, где в скобках указаны их поисковые атрибуты:
asset_1 (attr_1, attr_2)
│
└─ nested_assest_1 (attr_3)
│
└─ nested_nested_asset_1 (attr_4)
asset_2 (attr_1, attr_2)
│
└─ nested_asset_2 (attr_3)
Раньше критерии всех атрибутов объединялись только через оператор OR:
asset_1#attr_1
OR
asset_1#attr_2
OR
nested_asset_1#attr3
OR
nested_nested_asset_1#attr4
OR
asset_2#attr_1
OR
asset_2#attr_2
OR
nested_asset_2#attr_3
Теперь для каждого типа актива формируется отдельная группа с полным набором быстрых фильтров, включающим критерии атрибутов родительских типов активов:
(asset_1#attr_1 AND/OR asset_1#attr_2)
OR
(asset_1#attr_1 AND/OR asset_1#attr_2 AND/OR nested_asset_1#attr3)
OR
(asset_1#attr_1 AND/OR asset_1#attr_2 AND/OR nested_asset_1#attr3 AND/OR nested_nested_asset_1#attr4)
OR
(asset_2#attr_1 AND/OR asset_2#attr_2)
OR
(asset_2#attr_1 AND/OR asset_2#attr_2 AND/OR nested_asset_2#attr_3)
Между собой группы все так же объединяются через OR. Но внутри всей группы наследования можно задавать OR или AND, позволяя фильтровать актив не только по собственным быстрым фильтрам но и по быстрым фильтрам родительского типа актива. Внутри группы {+}оператор OR/AND задаётся на ВСЮ группу наследования типов актива{+}, а не на какой-то отдельный тип актива или связку его атрибутов.
Т.е. например для asset_2 и nested_asset_2 можно сформировать либо только такую структуру логических связей через OR:
(asset_2#attr_1 OR asset_2#attr_2)
OR
(asset_2#attr_1 OR asset_2#attr_2 OR nested_asset_2#attr_3)
Либо только такую через AND:
(asset_2#attr_1 AND asset_2#attr_2)
OR
(asset_2#attr_1 AND asset_2#attr_2 AND nested_asset_2#attr_3)
Расширение workflow API для получения задач БП
Backend:
Расширена функциональность Workflow API для получения задач бизнес-процессов:
Добавлен эндпоинт Получение элементов BPMN с фильтрацией по типам (GET universe-backend/api/v2/workflow/bpmn/model-elements/{имя_бизнесс_процесса}?types={список_типов}):
Ответ
{
"details": {
"info": [],
"warning": [],
"error": []
},
"elements": [
{
"id": "Activity_1xlu8m9", /* ID элемента */
"name": "Опубликовать черновик", /* Имя элемента */
"type": "userTask" /* Тип элемента */
},
{
"id": "Activity_117en0g",
"name": "Исправление замечаний",
"type": "userTask"
},
{
"id": "Activity_064p8qe",
"name": "Публикация черновика",
"type": "serviceTask"
}
]
}
Желаемые типы задаются через query-параметр types. Значения параметра - список типов через запятую. Параметр не обязательный, по умолчанию выберутся все элементы BPMN (тип BASE_ELEMENT).
Добавлен эндпоинт Получение типов элементов BPMN (GET universe-backend/api/v2/workflow/bpmn/model-element-types):
{
"details": {
"info": [],
"warning": [],
"error": []
},
"types": [
"BASE_ELEMENT", /* Любой элемент (включая выражения) */
"FLOW_ELEMENT", /* Любой flow-элемент (узлы и ребра графа)
"EVENT", /* Любое событие (стартовое, конечное и т.п.)
"ACTIVITY", /* Любая активность*/
"TASK", /* Любая задача */
"RECEIVE_TASK", /* Получение сообщения */
"SEND_TASK", /* Отправка сообщения */
"USER_TASK", /* Задача пользователя */
"BUSINESS_RULE_TASK", /* Бизнес-правило */
"SERVICE_TASK", /* Сервисная задача */
"SCRIPT_TASK", /* Задача-сценарий */
"MANUAL_TASK" /* Ручная задача */
]
}
Подробное описание типов элементов BPMN можно посмотреть в документации Camunda.
Уведомление участников бизнес-процессов
Backend:
Модули com.unidata.mdm.subscriptions и com.unidata.mdm.rest.v1.subscriptions добавлены в сборку.
Реализован механизм подписок для бизнес-процессов. Добавлены классы WorkflowSubscriptionProvider, WorkflowSubscriptionTypes, WorkflowNotificationTriggers.
Добавлен новый тип подписки WorkflowSubscriptionTypes.WORKFLOW_TASK и новый триггер WorkflowNotificationTriggers.TASK_ASSIGNMENT.
Добавлен новый endpoint GET v2/workflow/task/subscribers/{taskId} – позволяет получить список подписчиков для конкретной задачи при помощи нового класса TaskAssignmentSubscriptionsResolver.
Класс TaskAssignNotificationListener, отвечавший за отправку уведомлений исполнителю и инициатору о назначениях задач, помечен как deprecated.
Теперь исполнитель и инициатор получают уведомления при помощи виртуальных подписок. Отправка уведомлений подписчикам происходит в новом классе TaskAssignmentSubscriptionsNotifier.
Frontend:
Добавлен модуль @universe-ee/subscriptions.
В модуле @universe-ee/workflow добавлен утильный класс BpmnUtil.
В модуле @universe-ee/workflow добавлен стор TaskSubscriptionStore, управляющий подпиской на задачу.
Доработка быстрых фильтров
Frontend:
Максимальное доступное количество быстрых фильтров изменено с 5 до 15 штук, добавлена возможность скрывать атрибут, добавленный в список быстрых фильтров (quick_filters) при помощи установки значения дополнительному параметру атрибута quick_filters=only_filters.
Выполнение поиска по нажатию клавиши Enter
На UI значительно переработан механизм обработки событий нажатия на клавиши в рамках всего приложения.
Новая логика:
Когда на обработку нажатия на клавишу назначается более одного UI-элемента, то всегда выполняется обработчик последнего появившегося UI-элемента. При скрытии этого UI-элемента обработка события передается к предыдущему появившемуся элементу, если он задан.
Отображение краткого описание объектов модели данных
Backend:
Backend добавляет поле description для узлов дерева (группы/сущности), чтобы Frontend мог показать краткое описание рядом с названием. При формировании дерева model/entity-groups сервер собирает description из модели (для register/lookup), добавляет в маппинг и прокидывает в DTO; фильтр безопасности удаляет описания для недоступных сущностей.
В REST v2 дерево узлов теперь содержит description (на уровне TreeNodeRO).
В DTO дерева групп добавлен маппинг описаний сущностей, которые используются в ответе сервиса.
GET /api/v2/data/model/entity-groups` (по EntitiesGroupRestService): в ответе у узлов дерева добавлено поле description.
Пример:
{
"name": "ROOT",
"displayName": "Корневая группа",
"description": null,
"parentName": null,
"type": "group",
"children": [
{
"name": "reg",
"displayName": "reg",
"description": "reg",
"parentName": "ROOT",
"type": "register",
"hierarchical": false,
"leaf": true
}
],
"leaf": false
}
Фильтрация и сортировка правил качества и сопоставления
В разделах "Правила сопоставления" и "Правила качества" добавлен функционал сортировки, фильтрации и пагинации для различных столбцов таблиц.
Была отключена сортировка для следующих столбцов:
Раздел "Правила качества" - вкладка "Назначения": отключена сортировка по всем фазам.
Раздел "Правила сопоставления" - вкладка "Назначения": отключена сортировка столбцов "Таблицы" и "Наборы правил".
Добавлены 3 новых столбца в разделе "Правила качества": режим правила, критичность, система источник.
В разделе "Правила качества" добавлен новый пагинированный селектор для фильтрации по наборам правил. Также пагинированный селектор добавлен в редактор набора правил при выборе правила.
Изменения на Backend
Проиндексированы модели сопоставления и правил качества - их индекс добавлен к индексу мета-модели $[model] и [model] .
Доработан endpoint /v2/search/. Добавлена возможность обрабатывать запрос поиска мета-модели. Для этого используется объект
org.unidata.mdm.rest.v2.core.В операции переиндексации созданы пункты очистки маппинга метамодели, индексации модели сопоставления, индексации модели правил качества.
Разделены маппинг модели данных и метамодели.
По аналогии с
DataSearchRestRenderingComponentв рендерингSearchRestRenderingTypes#SEARCH_ATOMICдобавлен обработчикMetaModelRestRenderingComponent.Для проверки прав доступа пользователя к чтению запрашиваемой модели создан рендеринг
CoreRestRenderingTypes#METAMODEL_RIGHTS_RENDERING, который накапливает имена моделей, к которым у пользователя есть доступ.Для локализации полей индекса добавлен
MetaModelSearchResultHitModifier, использующийdisplayNameResolver.В индексе $[model] и [model] в поле entry_details добавлено поле display_version. Если данное поле присутствует, значит value данного details имеет локализованную версию.
Таблицы, правила, наборы правил и назначения модели сопоставления вынесены в индекс
default_default_[model].Добавлен шаг в операцию переиндексации для переиндексации модели сопоставления.
Добавлен поток выполнения удаления черновика модели сопоставления, где удаляются значения из индекса черновика метамодели.
Правила, наборы правил и назначения модели правил качества вынесены в индекс
default_default_[model].Добавлен шаг в операцию переиндексации для переиндексации модели правил качества.
Добавлен поток выполнения удаления черновика модели правил качества, где удаляются значения из индекса черновика метамодели.
Пример поиска таблицы сопоставления по имени колонки
"org.unidata.mdm.rest.v2.core": {
"countOnly": false,
"formFields": [],
"formGroups": [
{
"groupType": "AND",
"formFields": [
{
"name": "model_type",
"type": "STRING",
"searchType": "EXACT",
"inverted": false,
"value": "MATCHING"
},
{
"name": "subject_type",
"type": "STRING",
"searchType": "EXACT",
"inverted": false,
"value": "TABLE"
},
{
"name": "columns",
"type": "STRING",
"searchType": "EXACT",
"inverted": false,
"value": "columName"
}
],
"childGroups": []
}
],
"searchFields": [],
"returnFields": [
"model_type",
"subject_type",
"subject_name",
"entry_type",
"entry_name",
"entry_description",
"entry_display_name",
"columns",
"$draft_id"
],
"fetchAll": false,
"totalCount": true,
"supplementary": [],
"page": 1,
"count": 20,
"start": 0,
"sortFields": []
}
Пример поиска правила сопоставления по используемому алгоритму
"org.unidata.mdm.rest.v2.core": {
"countOnly": false,
"formFields": [],
"formGroups": [
{
"groupType": "AND",
"formFields": [
{
"name": "model_type",
"type": "STRING",
"searchType": "EXACT",
"inverted": false,
"value": "MATCHING"
},
{
"name": "subject_type",
"type": "STRING",
"searchType": "EXACT",
"inverted": false,
"value": "RULE"
},
{
"name": "matching_algorithms",
"type": "STRING",
"searchType": "EXACT",
"inverted": false,
"value": "ExactAlgorithm"
}
],
"childGroups": []
}
],
"searchFields": [],
"returnFields": [
"model_type",
"subject_type",
"subject_name",
"entry_type",
"entry_name",
"entry_description",
"entry_display_name",
"matching_algorithms",
"$draft_id"
],
"fetchAll": false,
"totalCount": true,
"supplementary": [],
"page": 1,
"count": 20,
"start": 0,
"sortFields": []
}
Пример поиска набора правил сопоставления по имени правила
"org.unidata.mdm.rest.v2.core": {
"countOnly": false,
"formFields": [],
"formGroups": [
{
"groupType": "AND",
"formFields": [
{
"name": "model_type",
"type": "STRING",
"searchType": "EXACT",
"inverted": false,
"value": "MATCHING"
},
{
"name": "subject_type",
"type": "STRING",
"searchType": "EXACT",
"inverted": false,
"value": "SET"
},
{
"name": "matching_rules",
"type": "STRING",
"searchType": "EXACT",
"inverted": false,
"value": "ruleName"
}
],
"childGroups": []
}
],
"searchFields": [],
"returnFields": [
"model_type",
"subject_type",
"subject_name",
"entry_type",
"entry_name",
"entry_description",
"entry_display_name",
"matching_rules",
"$draft_id"
],
"fetchAll": false,
"totalCount": true,
"supplementary": [],
"page": 1,
"count": 20,
"start": 0,
"sortFields": []
}
Пример поиска назначений наборов правил сопоставления
"org.unidata.mdm.rest.v2.core":{
"countOnly": false,
"formFields": [],
"formGroups": [
{
"groupType": "AND",
"formFields": [],
"childGroups": [
{
"groupType": "AND",
"formFields": [
{
"name": "model_type",
"type": "STRING",
"searchType": "EXACT",
"inverted": false,
"value": "MATCHING"
},
{
"name": "subject_type",
"type": "STRING",
"searchType": "EXACT",
"inverted": false,
"value": "ASSIGNMENT"
},
{
"name": "subject_name",
"type": "STRING",
"searchType": "EXACT",
"inverted": false,
"value": "register:registerName"
}
],
"childGroups": []
}
]
}
],
"searchFields": [
],
"returnFields": [
"$t",
"model_type",
"subject_type",
"subject_name",
"entry_type",
"entry_name",
"entry_description",
"entry_display_name",
"namespace",
"tables",
"matching_rule_sets",
"$draft_id"
],
"fetchAll": false,
"totalCount": true,
"supplementary": [],
"page": 1,
"count": 20,
"start": 0,
"sortFields": []
}
Пример поиска правила качества по условию запуска
"org.unidata.mdm.rest.v2.core": {
"countOnly": false,
"formFields": [],
"formGroups": [
{
"groupType": "AND",
"formFields": [
{
"name": "model_type",
"type": "STRING",
"searchType": "EXACT",
"inverted": false,
"value": "DATA_QUALITY"
},
{
"name": "subject_type",
"type": "STRING",
"searchType": "EXACT",
"inverted": false,
"value": "RULE"
},
{
"name": "run_condition",
"type": "STRING",
"searchType": "EXACT",
"inverted": false,
"value": "RUN_ALWAYS"
}
],
"childGroups": []
}
],
"searchFields": [],
"returnFields": [
"model_type",
"subject_type",
"subject_name",
"entry_type",
"entry_name",
"entry_description",
"entry_display_name",
"entry_description",
"dq_function_name",
"run_condition",
"severity",
"dq_function_type",
"applied_source_systems",
"$draft_id"
],
"fetchAll": false,
"totalCount": true,
"supplementary": [],
"page": 1,
"count": 20,
"start": 0,
"sortFields": []
}
Пример поиска наборов правил качества по имени правила качества
"org.unidata.mdm.rest.v2.core": {
"countOnly": false,
"formFields": [],
"formGroups": [
{
"groupType": "AND",
"formFields": [
{
"name": "model_type",
"type": "STRING",
"searchType": "EXACT",
"inverted": false,
"value": "DATA_QUALITY"
},
{
"name": "subject_type",
"type": "STRING",
"searchType": "EXACT",
"inverted": false,
"value": "SET"
},
{
"name": "dq_rules",
"type": "STRING",
"searchType": "EXACT",
"inverted": false,
"value": "ruleName"
}
],
"childGroups": []
}
],
"searchFields": [],
"returnFields": [
"model_type",
"subject_type",
"subject_name",
"entry_type",
"entry_name",
"entry_description",
"entry_display_name",
"dq_rules",
"$draft_id"
],
"fetchAll": false,
"totalCount": true,
"supplementary": [],
"page": 1,
"count": 20,
"start": 0,
"sortFields": []
}
Пример поиска назначений правил качества по набору правил в фазе
"org.unidata.mdm.rest.v2.core": {
"countOnly": false,
"formFields": [],
"formGroups": [
{
"groupType": "AND",
"formFields": [
{
"name": "model_type",
"type": "STRING",
"searchType": "EXACT",
"inverted": false,
"value": "DATA_QUALITY"
},
{
"name": "subject_type",
"type": "STRING",
"searchType": "EXACT",
"inverted": false,
"value": "ASSIGNMENT"
},
{
"name": "namespace",
"type": "STRING",
"searchType": "EXACT",
"inverted": false,
"value": "register"
}
],
"childGroups": []
},
{
"groupType": "AND",
"formFields": [
{
"name": "entry_details.key",
"type": "STRING",
"searchType": "EXACT",
"inverted": false,
"value": "PROCESS"
},
{
"name": "entry_details.value",
"type": "STRING",
"searchType": "EXACT",
"inverted": false,
"value": "ruleSetName"
}
],
"childGroups": []
}
],
"searchFields": [],
"returnFields": [
"model_type",
"subject_type",
"subject_name",
"entry_type",
"entry_name",
"entry_description",
"entry_display_name",
"namespace",
"entry_details",
"$draft_id"
],
"fetchAll": false,
"totalCount": true,
"supplementary": [],
"page": 1,
"count": 20,
"start": 0,
"sortFields": []
}
Алгоритмы модели сопоставления и функции правил качества локализуются при запросе.
Новые endpoint-ы обновления и удаления назначений DQ
Обновление назначения: PUT /v2/data-quality/quality-rules/type-assignments?draftId=162
draftId - опциональный параметр, id черновика модели правил качества, в который производится вставка. Если параметр не указан или равен 0, то вставка производится в чистовую версию.
Если назначения не существует - оно будет добавлено в соответствующий namespace. Если назначение существует, то его фазы будут заменены на фазы из тела запроса (удаление фаз, которых нет в теле запроса).
Тело запроса:
{
"assignment": {
"nameSpace": "lookup", // lookup/register
"assignments": [
{
"entityName": "lookupName", // Имя сущности на которую назначаются наборы
"assignment": [
{
"phase": "DEFAULT", // Фаза
"sets": [
"testSet" // Массив имен наборов правил качества
]
}
]
}
]
}
}
Удаление назначения: POST /v2/data-quality/quality-rules/type-assignments/delete?draftId=162
draftId - опциональный параметр, id черновика модели правил качества, из который удаляется назначение. Если параметр не указан или равен 0, то удаление производится из чистовой версии.
Тело запроса:
{
"assignment": {
"nameSpace": "lookup", // lookup/register
"entities": ["SS32", "SS1"] // Массив имен удаляемых сущностей
}
}
Указанные сущности будут удалены из модели правил качества.
Поля фильтрации в DQ
Для фильтрации по системам источникам правил в DQ в индекс модели правил качества вынесен источник "Для каких систем-источников применяется" в поле
applied_source_systems.
Индекс правила модели DQ:
{
"$t": "undefined",
"model_type": "DATA_QUALITY", // Модель DQ
"subject_type": "RULE", // Вкладка правила
"subject_name": "upperRule",
"entry_type": "RULE",
"entry_name": "upperRule",
"entry_description": null,
"entry_display_name": "верхнее правило",
"dq_function_name": "UpperCase",
"run_condition": "RUN_ALWAYS",
"dq_function_type": "enriching",
"applied_source_systems": "universe" // Источник по которому можно осуществлять фильтрацию
}
Для фильтрации по системам источникам правил в DQ в индекс модели правил качества вынесена критичность правила валидации в поле
severity.
{
...
"severity": "GREEN", // Критичность, по которой можно осуществлять фильтрацию
...
}
Для фильтрации по кодовому имени функции в DQ в индекс модели правил качества вынесено кодовое имя функции в поле
dq_function_name.
{
...
"dq_function_name": "UpperCase", // Имя функции, по которой можно осуществлять фильтрацию
...
}
Для фильтрации по режиму (валидация/обогащения) в DQ в индекс модели правил качества вынесен режим в поле
dq_function_type:
{
...
"dq_function_type": "enriching", // Режим, по которому можно осуществлять фильтрацию
...
}
Фильтрацию, используется ли она в наборе или нет, необходимо осуществлять на Frontend при помощи комбинируемого запроса или нескольких запросов.
Сортировка списков
Работа сортировки в OpenSearch, если указан не конкретный элемент, а массив.
Допустим, у нас есть 2 элемента: - strA: ["1", "2"] и strA: ["2", "3"]. Мы заказываем сортировку по убыванию. В этом случае сортировка идет по максимальному элементу массива, т.е. сравниваться будут "2" и "3", и в результате отсортированный результат будет таким: - strA: ["2", "3"] - strA: ["1", "2"], т.к. "3" >"2".
Если мы заказываем сортировку по возрастанию, то сортировка идет по минимальном элементу массива, т.е. сравниваться будут "1" и "2", и в результате отсортированный результат будет таким:
strA: ["1", "2"]
strA: ["2", "3"], т.к. "1" <"2".
Порядок элементов массива не важен.
Пример: связи с строковым массивом атрибутов:
["!", "1", "Aen", "aen", "Ару", "ару"] (min: "!"; max: "ару")
["1", "Aen", "aen", "Ару", "ару"] (min: "1"; max: "ару")
["Aen", "aen", "Ару", "ару"] (min: "Aen"; max: "ару")
["aen", "Ару", "ару"] (min: "aen"; max: "ару")
["Ару", "ару"] (min: "Ару"; max: "ару")
["ару"] (min: "ару"; max: "ару")
["!", "1", "Aen", "aen", "Ару", "ару"] (min: "!"; max: "ару")
["!", "1", "Aen", "aen", "Ару"] (min: "!"; max: "Ару")
["!", "1", "Aen", "aen"] (min: "!"; max: "aen")
["!", "1", "Aen"] (min: "!"; max: "Aen")
["!", "1"] (min: "!"; max: "1")
["!"] (min: "!"; max: "!")
Изменения на Frontend
Произведен рефакторинг модулей матчинга и DQ:
DQ - были изменены сторы: MappingStore, AssignmentStore, QualityRuleStore.
Matching - были изменены сторы: MatchingTableStore, MatchingRuleStore, MatchingRuleSetStore, MatchingAssignmentsStore.
В обоих модулях был использован стор AbstractEntityStore. Созданы соотвествующие сторы, наследуемые от AbstractEntityEditorStore и AbstractEntityListStore.
Функционал не изменился, был расширен поддержкой загрузкой данных при помощи нового поискового запроса org.unidata.mdm.rest.v2.core.
Изменение отображаемого наименования и статуса виджетов
Backend:
Добавлен новый API для работы с админскими и пользовательскими настройками виджетов:
v2/core/widgets.Созданы 2 новые таблицы в БД:
postgres.org_unidata_mdm_core.widget_settingиpostgres.org_unidata_mdm_core.widget_layout.Настройки расположения виджетов мигрированы из таблицы
postgres.org_unidata_mdm_core.s_custom_storageв новую таблицуpostgres.org_unidata_mdm_core.widget_layout.Добавлена точка расширения BE для подгрузки кастомных виджетов в систему.
Описание API:
Для обеспечения возможности настройки имени, описания и статуса виджетов был реализован новый API v2/core/widgets. Данный API состоит из двух частей - админских настроек виджетов (v2/core/widgets/settings) и пользовательской раскладки виджетов на главной странице (v2/core/widgets/layout).
Endpoint: GET v2/core/widgets/settings Описание: Предназначен для получения всех зарегистрированных виджетов с их настройками.
code - системный идентификатор виджета.
defaultName - имя виджета по умолчанию.
name - заданное админом имя виджета.
description - заданное админом описание виджета.
isSystem - флаг, показывающий, является ли виджет системным, или был загружен как кастомизация.
isActive - статус видимости виджета (также регулируется админом).
Если дефолтные имена виджетов, описание и статусы не изменялись, то будут заполнены только код виджета и его имя по умолчанию.
Запрос: Тело и параметры отсутствуют.
Ответ
"widgets": [
{
"code": "OBJECT_USAGE_METRIC_WIDGET",
"defaultName": "Статистика использования объектов",
"name": "",
"description": null,
"isSystem": true,
"isActive": true
},
{
"code": "SEARCH_WIDGET",
"defaultName": "Сквозной поиск",
"name": null,
"description": null,
"isSystem": true,
"isActive": false
}
]
Endpoint: PUT v2/core/widgets/settings/{code} Описание: Предназначен для обновления имени виджета, задания описания, или статуса активности. Эндпоинт доступен пользователям с правом "Управление настройками виджетов".
Запрос: параметр запроса code - идентификатор виджета
Тело запроса
{
"name": "Виджет для отображения статистики использования объектов",
"description": null,
"isActive": false
}
Обновленный виджет
{
"code": "OBJECT_USAGE_METRIC_WIDGET",
"defaultName": "Статистика использования объектов",
"name": "Виджет для отображения статистики использования объектов",
"description": null,
"isSystem": true,
"isActive":false
}
Endpoint: GET v2/core/widgets/layout Описание: Предназначен для получения настроек раскладки виджетов для текущего пользователя. Возвращает информацию о раскладке только для тех виджетов, которые доступны для размещения данному пользователю - он имеет права на них, и данный виджет активен.
code - системный идентификатор виджета
isHidden - флаг, показывающий, выбран ли пользователем данный виджет для размещения
layout - JSON-объект с фронтенд-специфичными данными о положении виджета на главной странице, может быть любой структуры
Для виджетов, у которых isHidden=true, layout может быть не заполнен.
Запрос: Тело и параметры отсутствуют.
Ответ
"widgets": [
{
"code": "CUSTOM_SUPER_WIDGET",
"isHidden": false,
"layout": {
"h": 5,
"i": "CUSTOM_SUPER_WIDGET",
"w": 5,
"x": 5,
"y": 5,
"moved": false,
"static": false
}
},
{
"code": "DATA_QUALITY_WITH_ERRORS_WIDGET",
"isHidden": true,
"layout": null
},
]
}
Endpoint: POST v2/core/widgets/layout Описание: Предназначен для перезаписывания настроек раскладки виджетов для определенного пользователя. Сохранение раскладки/видимости виджетов, которые неактивны, либо на которые у пользователя отсутствуют права, будет пропущено.
Запрос
{
"widgets": [
{
"code": "CUSTOM_SUPER_WIDGET",
"isHidden": false,
"layout": {
"w": 2,
"h": 2,
"x": 0,
"y": 0
}
},
{
"code": "DRAFT_LIST_WIDGET",
"isHidden": true,
"layout": null
}
]
}
Тело запроса
{
"name": "Виджет для отображения статистики использования объектов",
"description": null,
"isActive": false
}
Обновленный виджет
{
"code": "OBJECT_USAGE_METRIC_WIDGET",
"defaultName": "Статистика использования объектов",
"name": "Виджет для отображения статистики использования объектов",
"description": null,
"isSystem": true,
"isActive":false
}
Endpoint: GET v2/core/widgets/layout Описание: Предназначен для получения настроек раскладки виджетов для текущего пользователя. Возвращает информацию о раскладке только для тех виджетов, которые доступны для размещения данному пользователю - он имеет права на них, и данный виджет активен.
code - системный идентификатор виджета
isHidden - флаг, показывающий, выбран ли пользователем данный виджет для размещения
layout - JSON-объект с фронтенд-специфичными данными о положении виджета на главной странице, может быть любой структуры
Для виджетов, у которых isHidden=true, layout может быть не заполнен.
Запрос: Тело и параметры отсутствуют.
Ответ
"widgets": [
{
"code": "CUSTOM_SUPER_WIDGET",
"isHidden": false,
"layout": {
"h": 5,
"i": "CUSTOM_SUPER_WIDGET",
"w": 5,
"x": 5,
"y": 5,
"moved": false,
"static": false
}
},
{
"code": "DATA_QUALITY_WITH_ERRORS_WIDGET",
"isHidden": true,
"layout": null
},
]
}
Endpoint: POST v2/core/widgets/layout Описание: Предназначен для перезаписывания настроек раскладки виджетов для определенного пользователя. Сохранение раскладки/видимости виджетов, которые неактивны, либо на которые у пользователя отсутствуют права, будет пропущено.
Запрос
{
"widgets": [
{
"code": "CUSTOM_SUPER_WIDGET",
"isHidden": false,
"layout": {
"w": 2,
"h": 2,
"x": 0,
"y": 0
}
},
{
"code": "DRAFT_LIST_WIDGET",
"isHidden": true,
"layout": null
}
]
}
Ответ
{
"widgets": [
{
"code": "CUSTOM_SUPER_WIDGET",
"isHidden": false,
"layout": {
"w": 2,
"h": 2,
"x": 0,
"y": 0
}
},
{
"code": "DRAFT_LIST_WIDGET",
"isHidden": true,
"layout": null
}
]
}
Миграция
Для переноса старых сохраненных пользователями раскладок виджетов добавлена миграция, переносящая старые настройки в новое хранилище. Соответственно для пользователя расположение его виджетов на главной странице должно сохраниться.
Возможность установки перечня и/или диапазона допустимых значений для атрибутов
Модель данных
Для поддержания механизма одновременной валидации атрибутов на FE и BE, в модели данных для простых и кодовых атрибутов было поддержано новое поле hints. Данное поле представляет собой массив объектов-ограничителей значений атрибута. Ограничитель (hint) в свою очередь состоит из 2 полей - type, и params. Поддержанные на текущий момент типы ограничителей - RANGE (диапазон) и MASK (маска). Набор параметров params у каждого типа свой. Пример json конфигурации ограничителей каждого типа приведен ниже.
RANGE
"hints": [
{
"type": "RANGE",
"params": {
"min": 1,
"max": 115
}
}
],
MASK
"hints": [
{
"type": "MASK",
"params": {
"mask": "+7-(999)-999-99-99"
}
}
],
Ограничитель-диапазон может быть назначен на простом или кодовом атрибуте типа число, целое число, дата, время, дата/время.
Ограничитель-маска может быть назначен на простом или кодовом атрибуте типа строка.
Попытка назначения ограничителя на атрибут неподходящего типа данных приведет к ошибке валидации модели.
Ограничители поддержаны для атрибутов реестров, справочников, связей и узлов классификатора.
Проверка значений атрибутов, на которых назначены ограничители, производятся на BE при любом способе вставки записей.
Frontend
В компоненты Field и FieldLabel добавлена возможность кастомизировать цвет иконки подсказки.
В модели SimpleAttribute, CodeAttribute и AliasCodeAttribute добавлено поле hint, содержащее набор ограничителей данного атрибута.
Backend
В тело запроса POST /v2/data/model/upsert и всех других методов, содержащих в себе read-object SimpleAttributeDefRO добавлено необязательное поле hints.
В процесс вставки записей добавлены валидации атрибутов на соответствие ограничителям.
Возможность переключения оператора ИЛИ/И в поисковых критериях атрибутов
В абстрактный класс AbstractAttributeST, вынесенный в SDK, добавлен параметр canBeConvertedToSupplementary, определяющий, может ли поисковый терм атрибута иметь supplementaryGroup и, следовательно, иметь возможность смены оператора с ИЛИ на И.
Отображение времени обработки поискового запроса в модуле "Поиск по данным"
Добавлено измерение и возврат времени обработки поискового запроса в модуле "Поиск по данным".
Теперь ответ поиска содержит два новых поля:
totalProcessingTimeMillis - общее время обработки запроса на стороне сервера (мс), от начала обработки REST-запроса до формирования ответа.
searchProcessingTimeMillis - суммарное время работы поисковой подсистемы (OpenSearch took, мс), суммируется по всем ответам (включая scroll/scan).
Алгоритм работы:
В начале REST-запроса стартует захват таймингов (SearchTimingContext.startCapture() + отметка System.nanoTime()).
Во время выполнения поиска сервис накапливает took из OpenSearch (в т.ч. для scroll/scan).
Перед отдачей ответа выставляются поля totalProcessingTimeMillis и searchProcessingTimeMillis.
В finally контекст очищается.
Backend:
В ответ POST /api/v2/search добавлены поля totalProcessingTimeMillis и searchProcessingTimeMillis (мс).
Введен служебный контекст SearchTimingContext для накопления took из OpenSearch.
Логика сервиса поиска теперь суммирует took по всем ответам (включая scroll/scan).
Доработка экрана операций
Frontend:
Доработан компонент ItemsList для работы отдельно от Editor.Items;
SelectPaginated - переработан метод кастомного рендера отображаемого значения;
JobDefinitionStore теперь используется AbstractEntityStore вместо устаревшего AbstractCrudStore;
Добавлена новая операция ReadJobDefinitionByIds для получения операций по их ID.
Backend:
Изменен запрос POST /v2/core/jobs/definitions/search.
В тело запроса добавлено поле "displayName" (раньше его не было). Данное поле используется для регистронезависимого поиска имен операций содержащих указанный текст. Игнорируется если указаны точные имена в поле "definitionNames".
Поле "name" объявлено deprecated, вместо него стоит указывать список искомых полных имен операций в поле "definitionNames". Имена в списке должны точно совпадать с именами операций (для неточного совпадения используется поле "displayName"). Если данное поле заполнено, то поиск вернет те операции, имя которых точно совпадает с указанными, поле "displayName" будет проигнорировано.
Поле "jobName" объявлено deprecated, вместо него стоит указывать список искомых типов операций в поле "jobNames"
Тело запроса с использованием поиска по имени
{
"displayName": "def", // Часть имени искомых операций
// Типы искомых операций
"jobNames": [
"reindexDataJob",
"statisticJob"
],
"activeOnly": false, // Искать только активные операции
"inactiveOnly": false, // Искать только не активные операции (игнорируется при activeOnly: true)
"lastExecutionStatus": "COMPLETED", // Искать только операции, последний запуск которых был успешным
"tags": [],
"createdBy": "admin", // Искать только операции созданные пользователем admin
"count": 20, // Вернуть максимум 20 операций
"start": 0, // Начинать отсчет возвращаемых операций с 0-й
"sortField": "ID", // Отсортировать операции в результате по ID
"sortOrder": "ASC" // Порядок сортировки по возрастанию
}
Тело запроса с указанием конкретных имен
{
// Полные имена операций
"definitionNames": [
"fullDefinitionName",
"myFavoriteOp"
],
// Типы искомых операций
"jobNames": [
"reindexDataJob",
"statisticJob"
],
"activeOnly": false, // Искать только активные операции
"inactiveOnly": false, // Искать только не активные операции (игнорируется при activeOnly: true)
"lastExecutionStatus": null, // Искать операции не учитывая статус их последнего выполнения
"tags": [],
"createdBy": null, // Искать операции не учитывая создателя операции
"count": 20, // Вернуть максимум 20 операций
"start": 0, // Начинать отсчет возвращаемых операций с 0-й
"sortField": "ID", // Отсортировать операции в результате по ID
"sortOrder": "ASC" // Порядок сортировки по возрастанию
}
В результат запроса было добавлено поле "totalFilteredCount", хранящее в себе количество записей, удовлетворяющих поисковым фильтрам, без учета "count" и "start".
В результате запроса была изменена логика подсчета "totalCount". Теперь данное поле хранит количество записей доступных данному пользователю с учетом его прав на операции.
Результат запроса
{
"definitions": [
// Список найденных операций
],
"totalFilteredCount": 35, // Количество операций найденных с учетом фильтра
"totalCount": 60 // Общее количество операций доступных данному пользователю
}
Создан новый endpoint получения операций по их ID: POST /v2/core/jobs/definitions/by-ids
Endpoint принимает список id операций.
Результат запроса
{
"jobDefinitionsIds": [
1, 6 // id операций
]
}
Если список пуст, то вернет все операции, на которые у пользователя есть права.
Результат запроса
{
"definitions": [
// Список найденных операций
],
"totalFilteredCount": 35, // Количество операций найденных с учетом фильтра
"totalCount": 60 // Общее количество операций доступных данному пользователю
}
Уведомление пользователей о завершении / отклонении задачи
Backend
Часть
TaskAssignmentSubscriptionsNotifierвынесена в абстрактный классAbstractSubscriptionsNotifier.TaskAssignmentSubscriptionsResolverпеределан в общийTaskSubscriptionsResolver. Триггеры для которых происходит resolve теперь передаются внутри контекстаTaskSubscriptionsResolveContext. Если триггеры не указаны, то resolve ищет все триггеры.Созданы
TaskCompletionSubscriptionsNotifierиспользующий триггерWorkflowNotificationTriggers#TASK_COMPLETION. Для триггера созданы шаблоны нотификацииTaskCompletionPanelTemplateи отправки EmailTaskCompletionEmailTemplate.
Возможность удаления лишних пробелов
Добавлена новая системная cleanse-функция RemoveExtraWhitespaces.
Функция удаления первых и последних символов
Добавлена новая системная строковая функция УдалитьПервыеИПоследниеСимволы (RemoveLeadingAndTrailingCharacters).
Требования к ролевой модели
Добавлено гранулярное управление доступом на чтение на уровне раздела «Операции».
Добавлено гранулярное управление доступом на чтение на уровне раздела «Параметры системы».
Раздел «Параметры системы» перенесен в отдельную вкладку.
Некорректная работа API custom-storage
Разграничение прав доступа к данным custom-storage разделено на:
Пользовательское хранилище (данные конкретного пользователя, недоступные другим пользователям для чтения/изменения/удаления).
Общее хранилище (данные, доступные всем пользователям для чтения/изменения/удаления).
На Frontend при сохранении пользовательских запросов через /universe-backend/api/v2/core/custom-storage для общих запросов теперь отправляется флаг common.
Изменение выполнено для платформы и МДМ. В ДГ для пользовательских запросов используется другой REST. В ДГ это влияет только на справочники.
Ручная разметка и пакетная операция
Реализована ручная разметка активов и пакетная операция установки меток на активы.
Возможность делиться сохраненными запросами в табличном поиске
Возможность сохранить запрос как общий теперь управляется ресурсом безопасности SYSTEM: org.unidata.mdm.core.security.saved.queries.
На Backend endpoint DELETE /v2/search/query разделен на три endpoint'а:
Полное удаление сохраненного запроса по id:
DELETE /v2/search/query?id=queryId.Удаление текущего пользователя из sharedUsers сохраненного запроса:
DELETE /v2/search/query/shared?id=queryId.Удаление additionalInfo, доступных текущему пользователю, из сохраненного запроса.
Дополнительно:
В параметре additionalInfo можно указать, какие именно элементы удалить (если у пользователя нет доступа к указанным additionalInfo, удаление не произойдет).
Можно указать флаг deleteFirst — тогда будет удален только первый найденный элемент:
DELETE /v2/search/query/additional?id=queryId&additionalInfo=groupId1&additionalInfo=groupId2&deleteFirst=false
Для восстановления сохраненных пользовательских запросов (пользовательские поисковые критерии) необходимо использовать точку расширения SearchQueryToTermMapFunc (заменяет SearchTermModelDG), которая восстанавливает AbstractSearchTerm на основе Query.
При сохранении поисковых запросов для активов/объектов (бизнес- и табличный поиск в Data Governance) теперь используется формат Query вместо JsonData.
Старый тип точки расширения (SearchTermModelDG) продолжает использоваться в ограниченном количестве случаев (обычно для внутренних технических преобразований/конвертаций).
На Backend реализована миграция сохраненных запросов из custom-storage в сервис сохраненных запросов.
Права доступа: список сущностей при назначении прав по умолчанию свернут
Frontend:
Поддержка нового поля depthOfExpansion, получаемого с Backend, отвечающего за уровень раскрытия дерева категорий в ролях.
Ранее на Frontend использовалась константа:
const LOAD_DATA_DEPTH = 5.
Теперь:
поле добавлено в модель Category;
значение передается в VirtualCategoryViewStore.ts вместе с активной категорией;
используется при формировании запроса на загрузку дерева.
Если depthOfExpansion === undefined, используется значение по умолчанию (5).
Backend:
Добавлено поле depthOfExpansion, отвечающее за количество уровней раскрытия дерева прав.
Значения задаются для категорий через константы.
Права доступа: поиск и сортировка ресурсов
Frontend
Активирован поиск по дереву ролей во вкладке «Данные».
Основные изменения в VirtualCategoryViewStore.ts:
Переопределен searchStore на новый VirtualCategoryTreeSearchStore.
Реализован метод doSearch.
TreeSearchStore:
Логика добавления найденных ключей вынесена в метод processFoundSearchKeys для возможности переопределения.
VirtualCategoryTreeSearchStore:
Переопределен метод processFoundSearchKeys.
Реализован поиск по всему дереву и добавление найденных ключей в searchResult для корректной подсветки.
platform/security - Category.ts:
Добавлен флаг, получаемый с Backend, для управления доступностью поиска по вкладкам.
platform/security - ReadCategoryResourcePaginatedListOp.ts:
Добавлен параметр
searchTextдля передачи строки поиска.
platform/tree - VirtualTreeController.tsx:
При отображении найденной ноды добавлен проброс defaultRenderer, чтобы не нарушать кастомное отображение узлов.
Backend
Обновлен endpoint получения дерева ресурсов безопасности категории:
GET /v2/core/security/secured-resources/resources-paginated/{category}.
Добавлен query-параметр searchText:
Поиск выполняется по именам и отображаемым названиям ресурсов;
Регистронезависимый поиск;
Если строка содержит несколько слов, каждое слово должно быть найдено;
Найденные ресурсы возвращаются вместе со всеми дочерними ресурсами (с учетом параметров depth, maxNestedChildren);
Также возвращаются все родительские ресурсы вверх по иерархии (до корневого узла, не включая его).
В запросы получения категорий добавлен параметр searchable — флаг, обозначающий возможность поиска по категории.
Для категории «Данные» (DATA) включена алфавитная сортировка (регистронезависимая) по отображаемому имени на всех уровнях дерева.
Сортировка может быть включена для любой категории соответствующим флагом;
Корректно работает только для категорий без локализованных отображаемых имен (в противном случае требуется доработка Backend).
Согласование изменений в рамках БП пользователем без прав на изменение записи
Операция: v2/workflow/process-definitions
Формат прежний, но дополнен флагом publishByInitiatorIdentity:
...
{
"name": "Process_6ce4a730-cc36-11f0-a266-733c22457379",
"displayName": "test1",
"description": "",
"manualStart": false,
"variables": [],
"assignmentMappings": [],
"customProperties": [
{
"name": "singleFlowRestriction",
"value": "true"
},
{
"name": "publishByInitiatorIdentity",
"value": "true"
}
],
"singleFlowRestriction": true,
"publishByInitiatorIdentity": true, // Публикация от имени инициатора. Если true — при финальном согласовании запись публикуется от имени пользователя, запустившего процесс, а не от имени текущего согласующего
"executable": false
}
Если флаг publishByInitiatorIdentity включен и в процессе сохранен логин инициатора, на этапе финальной публикации черновика система автоматически выполняет публикацию от имени инициатора (в отдельном потоке). Это позволяет пользователям без прав на запись в реестр/справочник успешно завершать согласование — изменения корректно записываются от имени реального автора.
Backend:
Добавлено поле в REST API:
publishByInitiatorIdentity(в корне)Добавлена системная переменная процесса:
StandardWorkflowVariables.VAR_PUBLICATION_BY_INITIATORХранится в
customPropertiesкакpublishByInitiatorIdentity = true/falseProcessHeaderField.FIELD_PUBLICATION_BY_INITIATOR— поле в поисковом индексеДобавлено новое исключение:
WorkflowExceptionIds.EX_WF_PROCESS_INITIATOR_NOT_AVAILABLEДобавлена локализация:
app.wf.process.initiator.not.availableДобавлены методы: -
ProcessInstance#isPublicationByInitiator()иProcessInstance#setPublicationByInitiator(Boolean)-ProcessIndexInfo#isPublicationByInitiator()иProcessIndexInfo#setPublicationByInitiator(boolean)-ProcessDefinitionSource#withPublishByInitiatorIdentity(boolean)
Версионирование бизнес-процессов
Frontend:
@universe-ee/workflow:
Для отображения списка бизнес-процессов (БП) используется компонент Table вместо Tree, так как Tree использует неподходящую логику отображения rowActions.
Добавлен компонент VersionsExtraItem — дропдаун выбора версий БП.
Добавлен компонент VersionsListDrawer — список всех версий БП.
Добавлен компонент VersionsCompareModal — сравнение версий.
Изменены параметры операций получения модели БП: теперь используется один объект вместо нескольких параметров (draftId, версия, ревизия).
В AbstractProcessDifferenceStore вынесена общая логика сравнения моделей БП, созданы два наследника для сравнения ревизий и версий.
В таблицу поиска задач/процессов добавлена колонка с версией БП.
В карточку задачи/процесса добавлен вывод версии БП, схемы и индексируемых переменных конкретной версии БП.
@universe-platform/meta:
Верхняя часть модального окна сравнения ревизий (с двумя селекторами и кнопкой «Сравнить») вынесена в отдельный компонент RevisionComparisonSelector.
Backend:
Изменен deploy в Camunda:
BpmnModelComponent#deployProcessDefinitions(String storageId, List processNames)деплоит новую BPMN только если она отличается от старой. - Метод с флагомforce:BpmnModelComponent#deployProcessDefinitions(String storageId, List processNames, boolean force)деплоит все BPMN приforce == true. - ВWorkflowModelComponent#deployProcessDefinitionsпередаются только измененные BPMN. - В Camunda хранятся только отличающиеся диаграммы, а вbpmn_models— все ревизии.Добавлен флаг
force, позволяющий деплоить все переданные процессы.В
act_ge_bytearrayдобавлен столбецprocess_key_, хранящийprocess_idиз столбцаbytes_.Добавлен триггер для автоматического заполнения
process_key_при вставке записи вact_ge_bytearray.В
WorkflowModelInstanceImplдобавленаprocessVersions— содержит все версии и ревизии модели Workflow (только метаинформация, без содержимого).В XML-файл ревизии модели добавлен список существующих версий БП; при удалении БП из XML удаляется информация о его версиях.
В endpoints
WorkflowBpmnRestService#getиWorkflowProcessDefinitionRestService#getProcessDefinitionдобавлен параметрversion(приоритет над lud, но draftId и revision имеют приоритет над version).В OpenSearch добавлено поле
$definition_versionдля задач, сохраняющее ревизию BPMN модели.Добавлен endpoint получения всех версий БП:
WorkflowProcessDefinitionRestService#getProcessDefinitionVersions.
Лимит черновиков записи
В реестры и справочники добавлено поле maxOneRecordDrafts, отвечающее за максимальное количество черновиков для одной записи.
Значение <=0 — ограничение отсутствует.
Значение >0 — максимальное количество одновременно существующих черновиков.
Пример ответа на запрос сущностей
{
"registerEntity": {
"simpleAttributes": [],
"arrayAttributes": [],
"entityDependency": null,
"customProperties": [],
"name": "rst1",
"displayName": "rst11",
"description": "",
"order": 0,
"version": "5",
"hasData": true,
"complexAttributes": [],
"modelName": null,
"outgoingRelations": [],
"incomingRelations": [],
"mergeSettings": null,
"attributeGroups": [],
"relationGroups": [],
"flyweight": false,
"validityPeriod": null,
"externalIdGenerationStrategy": null,
"hierarchical": false,
"maxHierarchyLevel": 0,
"globalSearchEnabled": false,
"hideValidityPeriods": false,
"maxOneRecordDrafts": -1, // Максимальное количество черновиков для одной записи
"dashboardVisible": false
}
}
{
"lookupEntity": {
"simpleAttributes": [],
"arrayAttributes": [],
"entityDependency": null,
"customProperties": [],
"name": "testLookup",
"displayName": "testLookup1",
"description": "",
"order": 0,
"version": "5",
"hasData": true,
"modelName": null,
"codeAttribute": {},
"aliasCodeAttributes": [],
"attributeGroups": [],
"mergeSettings": null,
"validityPeriod": null,
"externalIdGenerationStrategy": null,
"dashboardVisible": false,
"flyweight": false,
"hierarchical": false,
"maxHierarchyLevel": 0,
"hideValidityPeriods": false,
"maxOneRecordDrafts": -1, // Максимальное количество черновиков для одной записи
"globalSearchEnabled": false
}
}
Если реестр/справочник имеет ограничение, вставка черновика блокирует поток для других операций создания черновика той же записи до завершения текущей вставки.
При создании черновика записи с maxOneRecordDrafts > 0 проводится проверка количества существующих черновиков. Если >= maxOneRecordDrafts, возникает ошибка.
FE валидации обычно предотвращают превышение лимита, но ошибка возможна при прямом POST-запросе на вставку черновика.
Обработка логического удаления записи
При логическом удалении записи создается скрытый черновик на удаление. Пользователь в обычной ситуации к нему не имеет доступа.
Для обработки исключения введен флаг draft-for-delete:
Если флаг присутствует и на реестр/справочник НЕ назначены бизнес-процессы на удаление, черновик создается без проверки ограничения
maxOneRecordDrafts.
/api/v2/draft/upsert
{
"type": "record",
"subjectId": "a891140b-f146-11f0-ae22-b35a117995b3",
"description": "Версия от 14.01.2026 15:44:22",
"tags": [
"entity-name:testRst",
"namespace:register"
],
"parameters": {
"entity-name": "testRst",
"draft-for-delete": true // Флаг помечает черновик как черновик для удаления
}
}
Предупреждение
Флаг используется только для обработки исключения и требует осторожности. Для системы такой черновик не отличается от других. Корректность использования полностью возлагается на пользователя.
Frontend:
MainSettings.tsx — поля расширенных настроек возвращаются отсортированным массивом по order.
UEMetaModelAdvancedSetting.ts — поддержка поля order.
Модели реестра и справочника получили новое поле maxOneRecordDrafts, по умолчанию с BE: -1 (неограниченное количество).
DraftStore.ts — добавлено поле класса maxDraftCount.
DraftWrapper.tsx — модалка черновика не открывается, если черновик уже существует и maxDraftCount = 1; запись сразу переходит в режим черновика.
Backend:
В RegisterEntity и LookupEntity добавлено поле maxOneRecordDrafts.
В пайплайне вставки черновика добавлен RECORD_DRAFT_UPSERT_VALIDATION, проверяющий количество существующих черновиков.
Добавлен lock по subjectID при вставке черновика для предотвращения race condition.
Добавлена валидация соответствия subjectID указанному entityName.
Отслеживание статуса запущенных пакетных операций
Обновлен формат выдачи информации о запущенных и завершенных задачах в API мониторинга задач (core/task-execution). Изменения были необходимы для внедрения нового механизма отслеживания статуса пакетных операций, который использует Snowflake ID для генерации идентификаторов отслеживания.
Причина изменений:
JavaScript некорректно обрабатывает очень большие целые числа (Snowflake ID), поэтому формат ответа API был изменен для обеспечения совместимости и гармонизации с bulk API.
Изменения в формате ответа:
В объекте, описывающем задачу, были переименованы и изменены следующие поля:
Старое название поля |
Новое название поля |
Описание |
id |
trackingId (String) |
Идентификатор отслеживания. Большое целое число, сгенерированное Snowflake генератором. |
task |
taskId (String) |
Внутреннее имя задачи (например, 'reindexJob'). Может использоваться для фильтрации задач одного типа. |
taskType (TaskTypeRO) |
Тип задачи: читаемое имя и отображаемое имя. |
|
initiator |
taskInitiator (UserRO) |
Объект пользователя, инициировавшего выполнение. |
initiatorUsername |
taskInitiatorUsername (String) |
Человеко-читаемый идентификатор инициатора. |
externalExecutionId |
taskExecutionId (String) |
Внутренний идентификатор выполнения задачи (информационный). |
tags |
taskTags (Set<String>) |
Теги, связанные с этим выполнением. Содержимое может различаться в зависимости от типа задачи. |
Поля taskInstanceId и taskExecutionId остались без изменений по названию, но теперь являются частью новой согласованной структуры.
Пример измененной структуры объекта задачи:
{
"trackingId": "1627925810373197824",
"taskId": "reindexJob",
"taskInstanceId": "Reindex Job #12",
"taskExecutionId": "ext-abc-123",
"taskType": {
"name": "REINDEX",
"displayName": "Операция переиндексации"
},
"taskInitiator": { ... },
"taskInitiatorUsername": "ivanov_ii",
"taskTags": ["BULK", "ENTITY:customer"]
}
Рекомендация:
Разработчикам, использующим API core/task-execution для интеграций, необходимо обновить клиентский код в соответствии с новой структурой полей.
Механизм системных кнопок
Backend:
Созданы новые модули:
com.universe.mdm.buttonsиcom.universe.mdm.rest.v1.buttons.Реализованы методы API: GET, POST, PUT, DELETE (
api/v1/buttons).Создана новая схема БД
com_universe_mdm_buttonsс таблицейbuttons.
Frontend:
Добавлена страница редактора системных кнопок.
Добавлено отображение системных кнопок в шапке приложения.
UE RightHeaderItem заменен точкой расширения HeaderItem с возможностью настройки позиции.
В RouterStore добавлен метод нахождения роута по pathname.
В Field.Select добавлена поддержка generic-типа значения.
Описание API
Поддержан новый API: api/v1/buttons.
Метод: POST
Endpoint: api/v1/buttons
Описание: Создание системной кнопки.
Обязательные параметры: name, displayName, type, enabled, configuration.
Поле type принимает одно из значений:
MODULE— ссылка на модуль системыNETWORK_FOLDER— ссылка на сетевую папкуENTITY_RECORD— ссылка на запись в системеEXTERNAL_URL— внешняя ссылка
Поле roles содержит массив системных имен ролей, которым доступна кнопка.
Поле configuration — JSON-подструктура произвольного состава (корректный JSON).
Вызов доступен при наличии права «Администрирование системных кнопок».
Параметры/тело запроса
POST api/v1/buttons
{
"button": {
"name": "myButton1",
"displayName": "Моя кнопка",
"description": "Кнопка, содержащая ссылку на внешний ресурс",
"type": "EXTERNAL_URL",
"roles": ["ADMIN", "MANAGER"],
"enabled": true,
"configuration": {
"link": "https://stackoverflow.com/questions/5717093"
}
}
}
Параметры/тело ответа
{
"button": {
"id": 1,
"name": "myButton1",
"displayName": "Моя кнопка",
"description": "Кнопка, содержащая ссылку на внешний ресурс",
"type": "EXTERNAL_URL",
"roles": ["ADMIN", "MANAGER"],
"enabled": true,
"configuration": {
"link": "https://stackoverflow.com/questions/5717093"
}
}
}
Метод: PUT
Endpoint: api/v1/buttons/{id}
Описание: Редактирование системной кнопки.
Обязательный параметр: id. Тело запроса аналогично POST.
Параметры/тело запроса
PUT api/v1/buttons/1
{
"button": {
"name": "myButton1",
"displayName": "Моя измененная кнопка",
"description": "Кнопка, содержащая ссылку на внешний ресурс",
"type": "EXTERNAL_URL",
"roles": ["ADMIN", "MANAGER"],
"enabled": true,
"configuration": {
"link": "https://stackoverflow.com/questions/5717093"
}
}
}
Параметры/тело ответа
{
"button": {
"id": 1,
"name": "myButton1",
"displayName": "Моя измененная кнопка",
"description": "Кнопка, содержащая ссылку на внешний ресурс",
"type": "EXTERNAL_URL",
"roles": ["ADMIN", "MANAGER"],
"enabled": true,
"configuration": {
"link": "https://stackoverflow.com/questions/5717093"
}
}
}
Метод: DELETE
Endpoint: api/v1/buttons/{id}
Описание: Удаление системной кнопки.
Обязательный параметр: id.
Параметры/тело запроса
DELETE api/v1/buttons/1
Параметры/тело ответа
{
"success": true
}
Метод: GET
Endpoint: api/v1/buttons
Описание: Получение всех системных кнопок, упорядоченных по времени создания (от новой к старой).
Параметры/тело запроса
GET api/v1/buttons
Параметры/тело ответа
{
"buttons": [
{
"id": 2,
"name": "myButton2",
"displayName": "Моя любимая кнопка",
"description": "Кнопка, содержащая ссылку на модуль системы",
"type": "MODULE",
"roles": ["ADMIN"],
"enabled": true,
"configuration": {
"link": "http://localhost:8082/#/metamodel"
}
}
]
}
Метод: GET
Endpoint: api/v1/buttons/filtered-by-user
Описание: Получение системных кнопок, доступных текущему пользователю по его ролям.
Кнопка возвращается, если хотя бы одна роль пользователя совпадает с ролями в поле roles.
Параметры/тело запроса
GET api/v1/buttons/filtered-by-user
Параметры/тело ответа
Идентично GET запросу для получения всех кнопок.
Отображаемое имя операции в уведомлении о завершении
В
generateGeneralMessageдобавлено получение и выводdisplayNameчерезJobExecutionsComponent.Обратная совместимость сохранена, дополнительных действий не требуется.
API метод пакетного создания комментариев
Метод обрабатывает список комментариев, выполняет валидацию (subject, parentId), сохраняет их одной batch-операцией и вызывает lifecycle-обработчики до и после вставки. При ошибке выполнение останавливается, транзакция откатывается, а сообщение указывает проблемный комментарий по id или индексу.
Добавлен REST-эндпоинт POST /v1/marks/comments/multi-upsert для массового сохранения комментариев.
Добавлен метод multiUpsert в CommentsService и реализация в DAO.
Структура RO: - MultiUpsertCommentRequestRO: список комментариев List<CommentRO> comments. - MultiUpsertCommentResultRO: наследуется от AbstractSuccessfulResultRO, содержит boolean success.
Функция замены всех вхождений подстроки
Добавлена системная функция "Замена", выполняющая замену подстроки или шаблона регулярного выражения в строке.
Поддержка префикса
regex:для использования регулярных выражений.
ИзвлечьДату, ИзвлечьВремя из значения с типом DateTime
Добавлены функции
ИзвлечьDateиИзвлечьTimeдля получения даты и времени из значения с типом DateTime.
Выбор только конечных записей в иерархическом справочнике
Добавлен флаг
linkByLeafOnlyдля атрибутов типа «ссылка на справочник».Добавлена проверка выбора только конечных элементов иерархического справочника для атрибутов с признаком
linkByLeafOnly.
Changelog вне эпиков
Добавлен параметр в backend.properties: org.unidata.mdm.core.property.strict.security=${STRICT_SECURITY:false}
При значении true: при восстановлении пароля:
Добавляется задержка по времени при указании пользователя, которого не существует (что бы время ответа сервера при существующем пользователе и несуществующем было примерно одинаковым).
Ошибка для неверно введенной почты игнорируется.
Ошибка для внешнего пользователя игнорируется.
При значении false: поведение не изменяется.
Добавлен параметр backend.properties: org.unidata.mdm.core.property.widgets
Используется для фильтрации виджетов. Добавлены на BE виджеты (отключены для MDM), которые есть в DG и потенциально будут полезны и MDM.