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

Примечание

Ниже представлены технические изменения вышедших релизов. Краткий перечень изменений релизов отражен в новых функциях. Также см. информацию о важных изменениях.

Реализация вспомогательного запроса

Разработан 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 для получения задач бизнес-процессов:

  1. Добавлен эндпоинт Получение элементов 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).

  1. Добавлен эндпоинт Получение типов элементов 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 и отправки Email TaskCompletionEmailTemplate.

Возможность удаления лишних пробелов

  • Добавлена новая системная 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

Активирован поиск по дереву ролей во вкладке «Данные».

  1. Основные изменения в VirtualCategoryViewStore.ts:

  • Переопределен searchStore на новый VirtualCategoryTreeSearchStore.

  • Реализован метод doSearch.

  1. TreeSearchStore:

  • Логика добавления найденных ключей вынесена в метод processFoundSearchKeys для возможности переопределения.

  1. VirtualCategoryTreeSearchStore:

  • Переопределен метод processFoundSearchKeys.

  • Реализован поиск по всему дереву и добавление найденных ключей в searchResult для корректной подсветки.

  1. platform/security - Category.ts:

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

  1. platform/security - ReadCategoryResourcePaginatedListOp.ts:

  • Добавлен параметр searchText для передачи строки поиска.

  1. 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/false

  • ProcessHeaderField.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:

  1. MainSettings.tsx — поля расширенных настроек возвращаются отсортированным массивом по order.

  2. UEMetaModelAdvancedSetting.ts — поддержка поля order.

  3. Модели реестра и справочника получили новое поле maxOneRecordDrafts, по умолчанию с BE: -1 (неограниченное количество).

  4. DraftStore.ts — добавлено поле класса maxDraftCount.

  5. 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.