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

Standard Edition

Версия 6.10.2

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

  • Добавлен новый параметр в backend.properties для операции экспорта com.unidata.mdm.bulk.export.records.max.count = ${BULK_EXPORT_RECORDS_MAX_COUNT:50000}, который ограничивает количество записей под выгрузку для настройки под выделенные мощности. В случае попытки экспорта большего кол-ва записей - отображается ошибка.

  • Добавлен новый блок параметров для настройки операции экспорта в XLSX через UI.

  • Количество выгружаемых записей ограничивается параметром.

Выбор единиц измерения в карточке записи

Frontend:

  • TextAttributeField.tsx теперь используется исключительно для текстовых атрибутов. Для числового атрибута создан NumberAttributeField.tsx.

  • В NumberAttributeField.tsx реализована обертка, выводящая селектор единицы измерения. simpleAttributeEditOrPreview используется внутри numberWithMeasurement во избежание потери фокуса на селекторе единицы измерения при смене isEditMode.

  • Использование TextAttributeField для отображения числовых атрибутов отмечено как deprecated. Такое использование будет ограничено в версии 6.11.

Атрибуты в карточке записи

  • Генерация сторов атрибутов происходит всегда независимо от их скрытия (конструктор DataAttributeValueFactory);

  • Удалены лишние вызовы addAttributeIfNotExists, так как теперь все атрибуты должны существовать сразу после инициации карточки;

  • При сохранении из dataEntity удаляются только скрытые атрибуты;

  • Пустые атрибуты, которые были изменены, при сохранении приводятся к единому виду и отправляются на BE;

  • В AbstractRecordEntityStore добавлена опция ignoreDefaultValues, так как дефолтные значения не должны отображаться в пакетной модификации;

  • В ModelCollection добавлен метод commitCollection. Используется при добавлении атрибута в коллекцию dataEntity. Изменение коллекции дата-атрибутов не должно отслеживаться, так как атрибуты могут динамически добавляться, и это не должно влиять на dirty у dataEntity.

Обратная совместимость:

  • Поле hidden в RecordAbstractAttribute отмечено как deprecated, вместо него необходимо использовать метод setHidden и геттер isHidden.

Переименование индексов OpenSearch

Исключена возможная коллизия имен системных индексов, индексов бизнес-процессов и индексов реестров/справочников OpenSearch.

Имена индексов имеют следующий вид: prefix_default_некоеИмя, где prefix – значение параметра org.unidata.mdm.search.index.prefix (по умолчанию default).

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

  • default_default_auditdefault_default_[audit] - индекс аудита;

  • default_default_modeldefault_default_[model] - индекс модели данных;

  • default_default_$modeldefault_default_$[model] - индекс черновиков модели данных (сейчас не используется);

  • default_default_draftdefault_default_[draft] - индекс всех черновиков всех типов (сейчас не имеет доступа ни через FE, ни через REST API BE);

  • default_default_clustersdefault_default_[clusters] - индекс кластеров дубликатов;

  • default_default_workflowdefault_default_[workflow] - общий индекс задач и процессов;

  • default_default_wf_имяПроцессаdefault_default_[wf]_имяПроцесса - индекс задач и процессов для каждого отдельного бизнес-процесса.

Имена реестров и справочников теперь не могут содержать в своем имени следующие символы (ограничения добавлены на BE, на FE ограничения на имя уже есть и более строгие):

  • :, ", *, +, /, \, |, ?, #, >, < - ограничения OpenSearch на имя индекса;

  • [, ] - зарезервированные символы для системных индексов,

  • $ - зарезервированный символ для индексов с черновыми элементами – например, $entityName хранит поисковые данные черновиков записей реестра с именем entityName,

  • . - точка уже была ранее запрещена.

Имена бизнес-процессов теперь не могут содержать в своем имени следующие символы (ограничения добавлены на BE, на FE ограничения на имя уже есть и более строгие):

  • :, ", *, +, /, \, |, ?, #, >, < - ограничения OpenSearch на имя индекса,

  • . - точка уже была ранее запрещена.

Изменения в операции reindexDataJob

  • Параметр "Проиндексировать черновики" переименован в "Проиндексировать черновики записей". Этот параметр индексирует черновики записей и их дочерних сущностей (связей и т.п.) в индексы черновиков выбранных реестров/справочников;

  • Добавлены новые параметры:
    • "Проиндексировать черновики" – выполняет индексацию черновиков в индекс default_default_[draft],

    • "Проиндексировать модель данных" – выполняет индексацию модели данных в индекс default_default_[model].

Инструкция по миграции данных в переименованных индексах

Все индексы (кроме аудита) можно перезаполнить поисковыми данными через операцию переиндексации данных со следующими настройками (Рисунок 1).

Выполните миграцию данных индекса аудита следующим образом (замените хост и порт своими):

  • Старый индекс аудита обновите следующим запросом: PUT http://localhost:9200/default_default_audit/_settings с телом:

    {
      "settings": {
        "index.blocks.write": true
      }
    }
    
  • Если BE после обновления не запускался или уже запускался, но вы не хотите сохранять новые данные в аудите (или их нет), то:

    • Удалите новый индекс, если BE после обновления уже запускался: DELETE http://localhost:9200/default_default_[audit]

    • Затем клонируйте индекс в новый: PUT http://localhost:9200/default_default_audit/_clone/default_default_[audit]

    • Обновите новый индекс следующим запросом: PUT http://localhost:9200/default_default_[audit]/_settings с телом:

      {
        "settings": {
          "index.blocks.write": false
        }
      }
      
  • Если BE после обновления уже запускался и вы хотите сохранить новые данные в аудите, то (этот способ может занять более длительное время при большом размере старого индекса):

    • Выполните запрос реиндекса записей старого индекса в новый: POST http://localhost:9200/_reindex с телом:

      {
          "source": {
              "index": "default_default_audit"
          },
          "dest": {
              "index": "default_default_[audit]"
          }
      }
      
  • Удалите старые индексы с помощью следующих запросов:

    • DELETE http://localhost:9200/default_default_audit

    • DELETE http://localhost:9200/default_default_model

    • DELETE http://localhost:9200/default_default_$model

    • DELETE http://localhost:9200/default_default_draft

    • DELETE http://localhost:9200/default_default_clusters

    • DELETE http://localhost:9200/default_default_workflow

    • DELETE http://localhost:9200/default_default_wf_*

Настройки операции переиндексации данных

Рисунок 1 – Настройки операции переиндексации данных

Нотификация пользователя об автоконсолидации

Нотификация пользователя об автоконсолидации записей перемещена в отдельную точку потока выполнения org.unidata.mdm.data[RECORD_MERGE_NOTIFICATION].

Необходимо добавить эту точку в потоки выполнения перед финишем:

{
    "segmentType":"CONNECTOR",
    "id":"org.unidata.mdm.matching.data[RECORD_MERGE_MATCHING_CONNECTOR]"
},
{
    "segmentType":"POINT",
    "id":"org.unidata.mdm.data[RECORD_MERGE_NOTIFICATION]"
},
{
  "segmentType":"FINISH",
   "id":"org.unidata.mdm.data[RECORD_MERGE_FINISH]"
}

Сопоставление дубликатов по связям

Добавлен алгоритм сопоставления "Сопоставление наборов значений". Опираясь на тип физических колонок hstore сопоставляет множества значений и возвращает true, если у множеств есть пересечение (оператор ?|).

  • Добавлена поддержка типа HSTORE в колонках для поддержки наборов строк. Добавлен SetAlgorithm для поддержки наборов строк.

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

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

  • Записи теперь поддерживают специальные поля, валидные для некоего пространства имен. Так, например, запись данных содержит автоматическое поле $etalon_id. Запись связи содержит автоматические поля $etalon_id, $from_etalon_id и $to_etalon_id.

Версия 6.10

Frontend SDK

В класс Dialog добавлены следующие методы:

  • static setMaxNestedItems(count: number) — позволяет изменять максимальное количество ошибок, которые отображаются во всплывающем сообщении (по умолчанию 3). Также во всплывающем сообщении теперь есть возможность просмотреть все ошибки, нажав на кнопку Показать все.

  • static getMaxNestedItems(): number — геттер для значения, устанавливаемого предыдущим методом.

Поиск задач по критерию "Инициатор"

На backend добавлено поле "Инициатор" для задач. Для старых задач поле будет отсутствовать в индексе - необходимо выполнить переиндексацию.

Переиндексация задач реализована в модуле com.unidata.mdm.workflow.data.

Проверки правил качества

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

В текущий реализации правила качества работают в режиме Beta версии (только для атрибутов записей реестра/справочника).

  • Добавлен эндпоинт /check-state/check - REST интерфейс, позволяющий проверить любую запись по идентификатору записи и черновика в модуле org.unidata.mdm.rest.v2.dq.data.

  • Добавлен новый модуль com.universe.mdm.dqw (новые проверки для черновиков + сервисная операция с проверками).

  • Обновлены потоки выполнения (добавлены новые сегменты).

Настройка отображения входящих связей в карточке записи

  1. Изменения в REST-сервисе RegisterEntityRestService:

RegisterEntityRO:

  • Поле relations, методы getRelations, setRelations помечены deprecated;

  • Добавлены поля outgoingRelations, incomingRelations.

RelationEntityRO:

  • Добавлены атрибуты incomingNameForDisplay, showIncoming, showIncomingAttributes.

Влияет на:

  • /v2/data/model/register-entities/{id}

  • /v2/data/model/register-entities/

  1. Изменения в REST-сервисе DataRelationsRestService:

fetchByToSide:

  • Добавлен query parameter fetchByToSide для получения информации о входящей связи.

  • Если fetchByToSide = true, то запрашивается входящая связь; если fetchByToSide = false, то запрашивается исходящая. По умолчанию значение остается false.

Влияет на:

  • GET /v2/data/relations/timelines/date/{etalonId}

  • GET /v2/data/relations/timelines/range/{etalonId}

  • GET /v2/data/relations/relation-bulk/{id}/{name}

Для GET /v2/data/relations/relation-bulk/{id}/{name}:

  • Добавлено поле etalonDisplayNameFromEtalonRelationToRO) для имени входящей связи;

  • Если showIncomingAttributes = false для входящей связи - аттрибуты связи не возвращаются;

  • Добавлен query parameter fetchByToSide для получения информации о входящей связи.

Настройка кластерной конфигурации

На стороне BE для поддержки кластерной конфигурации были внесены следующие изменения:

  • Параметризован ряд настроек в backend.properties.

  • Исключены гонки при одновременной инициализации нескольких узлов при помощи ConfigurationActionService, возникающие при параллельных запросах к БД:

    • Миграции БД;

    • Сохранение информации о кластере БД;

    • Инициализация планировщика операций;

    • Инициализация BPMN движка.

  • Обратная совместимость не нарушена.

Поток выполнения отправки сообщений в SmartETL

  1. В поток выполнения отправки сообщений в SmartETL о событиях объединения и отсоединения записей добавлены сегменты (не включены в pipelines.json стандартной сборки):

    • com.universe.mdm.notifications[SEND_MERGE_NOTIFICATION] - сегмент для события объединения записей.

    • com.universe.mdm.notifications[SEND_UNMERGE_NOTIFICATION] - сегмент для события отсоединения записи.

Для регистрации эндпоинта слушателя для получения сообщений необходимо в backend.properties указать его в параметре com.universe.mdm.notifications.messaging.endpoint. Например, com.universe.mdm.notifications.messaging.endpoint = smart-etl:80/consumer.

  • К адресу, указанному в параметре, для отправки уведомлений будет добавляться /consumer. В примере: smart-etl:80/consumer/consumer. Туда будут отправляться POST запросы, в теле которых содержится JSON с событием.

  1. Изменено имя параметра эндпоинта слушателя в backend.properties:

    • com.unidata.smartetl.mdm.notifications.messaging.endpoint изменено на com.universe.mdm.notifications.messaging.endpoint

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

  • Событие объединения записей

Для включения отправки событий сегмент com.universe.mdm.notifications[SEND_MERGE_NOTIFICATION] необходимо добавить в поток выполнения org.unidata.mdm.data[RECORD_MERGE_START] перед конечным сегментом org.unidata.mdm.data[RECORD_MERGE_FINISH] для обеспечения неизменности результата после отправки оповещения.

Формат тела запроса, получаемого слушателем:

{
  "id": "Идентификатор сообщения",
  "entityName": "Имя реестра/справочника",
  "etalonId": "Эталонный ключ записи победителя",
  "originId": "Оригинальный ключ записи победителя",
  "timestamp": "Метка времени события",
  "type": "MERGE",
 "winner": { //Ключи записи победителя
    "entityName": "Имя реестра/справочника",
    "etalonId": "Эталонный ключ записи",
    "externalKeys": [ //Внешние ключи записи
      "Внешний ключ записи"
    ]
  },
  "merged": [] //Ключи поглощенных записей
}

Пример запроса:

{
  "id": "5341c39f-749b-4136-b8e1-bac3d73aef36",
  "entityName": "regTest",
  "etalonId": "fcd26202-5947-11ee-b77a-f58a37793af4",
  "originId": "fcd26203-5947-11ee-b77a-f58a37793af4",
  "timestamp": "2023-09-22T16:00:39.082",
  "type": "MERGE",
  "winner": {
    "entityName": "regTest",
    "etalonId": "fcd26202-5947-11ee-b77a-f58a37793af4",
    "externalKeys": [
      "universe|test-doc-10"
    ]
  },
  "merged": [
    {
      "entityName": "regTest",
      "etalonId": "fcd23aef-5947-11ee-b77a-f58a37793af4",
      "externalKeys": [
        "universe|test-doc-9"
      ]
    },
    {
      "entityName": "regTest",
     "etalonId": "fcd06617-5947-11ee-b77a-f58a37793af4",
      "externalKeys": [
        "universe|test-doc-1"
      ]
   },
    {
      "entityName": "regTest",
      "etalonId": "fcd17796-5947-11ee-b77a-f58a37793af4",
      "externalKeys": [
        "universe|test-doc-6"
      ]
    },
    {
      "entityName": "regTest",
      "etalonId": "fcd1eccc-5947-11ee-b77a-f58a37793af4",
      "externalKeys": [
        "universe|test-doc-8"
      ]
    },
    {
      "entityName": "regTest",
      "etalonId": "fcd1025d-5947-11ee-b77a-f58a37793af4",
      "externalKeys": [
        "universe|test-doc-3"
      ]
    },
    {
      "entityName": "regTest",
      "etalonId": "fcd15083-5947-11ee-b77a-f58a37793af4",
      "externalKeys": [
        "universe|test-doc-5"
      ]
    },
    {
      "entityName": "regTest",
      "etalonId": "fcd12970-5947-11ee-b77a-f58a37793af4",
      "externalKeys": [
        "universe|test-doc-4"
      ]
    },
    {
      "entityName": "regTest",
      "etalonId": "fcd1c5b9-5947-11ee-b77a-f58a37793af4",
      "externalKeys": [
        "universe|test-doc-7"
      ]
    },
    {
      "entityName": "regTest",
      "etalonId": "fcd0db4a-5947-11ee-b77a-f58a37793af4",
      "externalKeys": [
        "universe|test-doc-2"
      ]
    }
  ]
}
  • Событие отсоединения записи

Для включения отправки событий необходимо добавить сегмент com.universe.mdm.notifications[SEND_UNMERGE_NOTIFICATION] в пайплайн org.unidata.mdm.data[RECORD_UNMERGE_START] перед конечным сегментом org.unidata.mdm.data[RECORD_UNMERGE_FINISH] для обеспечения неизменности результата после отправки оповещения.

Формат тела запроса, получаемого слушателем:

{
  "id": "Идентификатор сообщения",
  "entityName": "Имя реестра/справочника",
  "etalonId": "Эталонный ключ изначальной записи, от которой отсоединяли другую запись",
  "originId": "Оригинальный ключ записи победителя",
  "timestamp": "Метка времени события",
  "type": "UNMERGE",
  "from": { //Ключи изначальной записи, от которой отсоединяли другую запись
    "entityName": "Имя реестра/справочника",
    "etalonId": "Эталонный ключ записи",
    "externalKeys": [ //Внешние ключи записи
      "Внешний ключ записи"
    ]
  },
  "remaining": {}, //Ключи изначальной записи после отсоединения
  "excluded": {} //Ключи отсоединенной записи
}

Пример запроса:

{
  "id": "373427ba-8566-4429-8630-ba5b81f17b6b",
  "entityName": "regTest",
  "etalonId": "fcd26202-5947-11ee-b77a-f58a37793af4",
  "originId": "fcd26203-5947-11ee-b77a-f58a37793af4",
  "timestamp": "2023-09-22T16:06:55.881",
  "type": "UNMERGE",
  "from": {
    "entityName": "regTest",
    "etalonId": "fcd26202-5947-11ee-b77a-f58a37793af4",
    "externalKeys": [
      "universe|test-doc-9",
      "universe|test-doc-3",
      "universe|test-doc-2",
      "universe|test-doc-1",
      "universe|test-doc-8",
      "universe|test-doc-7",
      "universe|test-doc-6",
      "universe|test-doc-10",
      "universe|test-doc-5"
    ]
  },
  "remaining": {
    "entityName": "regTest",
    "etalonId": "fcd26202-5947-11ee-b77a-f58a37793af4",
    "externalKeys": [
      "universe|test-doc-9",
      "universe|test-doc-3",
      "universe|test-doc-2",
      "universe|test-doc-1",
      "universe|test-doc-8",
      "universe|test-doc-7",
      "universe|test-doc-6",
      "universe|test-doc-10",
      "universe|test-doc-5"
    ]
  },
  "excluded": {
   "entityName": "regTest",
    "etalonId": "fcd12970-5947-11ee-b77a-f58a37793af4",
    "externalKeys": [
      "universe|test-doc-4"
    ]
  }
}

REST для проверки на дубли

Появились два потока выполнения, необходимые для вызова онлайн матчинга в случае вставки/обновления и восстановления, соответственно:

  • RECORD_UPSERT_START, ${online-matching-upsert-pipeline}

  • RECORD_RESTORE_START, ${online-matching-restore-pipeline}

Выполнено только расширение API, обратная совместимость не нарушена.

Поддержка транслитерации в поиске

Для обновления необходимо установить плагин analysis-icu для OpenSearch.

  • При использовании Docker:

При запуске Universe MDM из репозитория Docker через Docker Compose плагин будет установлен автоматически.

  • Установка вручную:

Приведенные ниже команды выполняются из директории, где установлен OpenSearch.

Команда для проверки установленных плагинов:

bin/opensearch-plugin list

Команда для установки плагина analysis-icu:

bin/opensearch-plugin install analysis-icu

Пример команды для установки плагина оффлайн (необходимо заранее скачать zip-файл с плагином, например, по ссылке https://artifacts.opensearch.org/releases/plugins/analysis-icu/2.7.0/analysis-icu-2.7.0.zip):

bin/opensearch-plugin install file:C:/Downloads/analysis-icu-2.7.0.zip

Пример команды для установки плагина через указание URL на zip-файл с плагином:

bin/opensearch-plugin install https://artifacts.opensearch.org/releases/plugins/analysis-icu/2.7.0/analysis-icu-2.7.0.zip

Инструмент отсоединения записи (unmerge)

Добавлены потоки выполнения (связанные с классификацией коннектор com.unidata.mdm.classifiers[CLASSIFICATIONS_UNMERGE_CONNECTOR] и поток com.unidata.mdm.classifiers[CLASSIFICATION_UNMERGE_START] добавлены только в EE):

 {
 "startId": "org.unidata.mdm.data[RECORD_UNMERGE_START]",
 "subjectId": "",
 "description": "org.unidata.mdm.data.record.unmerge.start.description",
 "segments": [
   {
     "segmentType": "START",
     "id": "org.unidata.mdm.data[RECORD_UNMERGE_START]"
   },
   {
     "segmentType": "POINT",
     "id": "org.unidata.mdm.data[RECORD_UNMERGE_ACCESS]"
   },
   {
     "segmentType": "POINT",
     "id": "org.unidata.mdm.data[RECORD_UNMERGE_TIMELINE]"
   },
   {
     "segmentType": "CONNECTOR",
     "id": "org.unidata.mdm.dq.data[RECORD_UNMERGE_QUALITY_CONNECTOR]"
   },
   {
     "segmentType": "POINT",
     "id": "org.unidata.mdm.data[RECORD_UNMERGE_INDEXING]"
   },
   {
     "segmentType": "POINT",
     "id": "org.unidata.mdm.data[RECORD_UNMERGE_TRANSITION]"
   },
   {
     "segmentType": "CONNECTOR",
     "id": "org.unidata.mdm.data[RELATIONS_UNMERGE_CONNECTOR]"
   },
   {
     "segmentType": "CONNECTOR",
     "id": "com.unidata.mdm.classifiers[CLASSIFICATIONS_UNMERGE_CONNECTOR]"
   },
   {
     "segmentType": "POINT",
     "id": "org.unidata.mdm.data[RECORD_UNMERGE_PERSISTENCE]"
   },
   {
     "segmentType": "CONNECTOR",
     "id": "org.unidata.mdm.matching.data[RECORD_UNMERGE_MATCHING_CONNECTOR]"
   },
   {
     "segmentType": "POINT",
     "id": "org.unidata.mdm.data[RECORD_UNMERGE_AUDIT]"
   },
   {
     "segmentType": "FALLBACK",
     "id": "org.unidata.mdm.data[RECORD_UNMERGE_AUDIT_FALLBACK]"
   },
   {
     "segmentType": "FINISH",
     "id": "org.unidata.mdm.data[RECORD_UNMERGE_FINISH]"
   }

 ]
},
{
 "startId": "org.unidata.mdm.data[RELATION_UNMERGE_START]",
 "subjectId": "",
 "description": "org.unidata.mdm.data.relation.unmerge.start.description",
 "segments": [
   {
     "segmentType": "START",
     "id": "org.unidata.mdm.data[RELATION_UNMERGE_START]"
   },
   {
     "segmentType": "POINT",
     "id": "org.unidata.mdm.data[RELATION_UNMERGE_TIMELINE]"
   },
   {
     "segmentType": "POINT",
     "id": "org.unidata.mdm.data[RELATION_UNMERGE_INDEXING]"
   },
   {
     "segmentType": "POINT",
     "id": "org.unidata.mdm.data[RELATION_UNMERGE_PERSISTENCE]"
   },
   {
     "segmentType": "FINISH",
     "id": "org.unidata.mdm.data[RELATION_UNMERGE_FINISH]"
   }
 ]
},
{
 "startId": "com.unidata.mdm.classifiers[CLASSIFICATION_UNMERGE_START]",
 "subjectId": "",
 "description": "com.unidata.mdm.classifiers.classification.unmerge.start.description",
 "segments": [
   {
     "segmentType": "START",
     "id": "com.unidata.mdm.classifiers[CLASSIFICATION_UNMERGE_START]"
   },
   {
     "segmentType": "POINT",
     "id": "com.unidata.mdm.classifiers[CLASSIFICATION_UNMERGE_TIMELINE]"
   },
   {
     "segmentType": "POINT",
     "id": "com.unidata.mdm.classifiers[CLASSIFICATION_UNMERGE_INDEXING]"
   },
   {
     "segmentType": "POINT",
     "id": "com.unidata.mdm.classifiers[CLASSIFICATION_UNMERGE_PERSISTENCE]"
   },
   {
     "segmentType": "FINISH",
     "id": "com.unidata.mdm.classifiers[CLASSIFICATION_UNMERGE_FINISH]"
   }
 ]
}

Ресурс безопасности на пользовательские пакетные операции

В секцию "Менеджмент записей" вкладки "Права доступа" (раздел "Роли") добавлены ресурсы безопасности для пользовательских пакетных операций, которые включают в себя импорт, экспорт, модификацию и удаление записей.

При отсутствии прав - соответствующие действия ("Импорт", "Экспорт", "Модификация", "Удаление" ) будут неактивны, а пользователь получит сообщение: "У пользователя [user1] недостаточно прав для (импорта/экспорта/модификации/удалении - в зависимости от выбранной операции) записей. Доступ запрещен. ( The user [user1] has no rights to (import/export/modify/delete records). Access denied.)"

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

Добавлено поле complexValues в DTO и RO объекты результата поиска.

SE/EE Возможность прикреплять несколько файлов

Добавлена поддержка файловых массив-атрибутов:

  • org.unidata.mdm.rest.system.upload.attachment.directory

  • org.unidata.mdm.rest.system.upload.attachment.memory.threshold

  • org.unidata.mdm.rest.system.upload.attachment.max.size

Были изменены на:

  • org.unidata.mdm.core.upload.attachment.directory

  • org.unidata.mdm.core.upload.attachment.memory.threshold

  • org.unidata.mdm.core.upload.attachment.max.size

Было добавлено поле:

  • org.unidata.mdm.core.file.max.count

Оно задает максимальное количество файлов для атрибута (по умолчанию 10).

Модуль уведомлений org.universe.mdm.notifications

Изменился сегмент отправки уведомлений о вставке записи com.universe.mdm.notifications[SEND_UPSERT_NOTIFICATION] модуля уведомлений com.universe.mdm.notifications:

  • Добавлены параметры системы:

    • com.universe.mdm.notifications.notifications.upsert.version - используемая версия сегмента;

    • com.universe.mdm.notifications.notifications.fetchLookupLinkAttributesLookupKeys - заполнять/не заполнять поле lookupLinkAttributes в уведомлении;

    • com.universe.mdm.notifications.notifications.showAbsentLookupLinkAttributes - вносить/не вносить в lookupLinkAttributes отсутствующие атрибуты как null (в ChangeDiffResult не вносится);

    • com.universe.mdm.notifications.notifications.fetchRecordKeys - заполнять/не заполнять в атрибутах поля lookupLinkAttributes поля recordKeys.

  • Добавлены следующие объекты:

    • NotificationExternalKey - com.universe.mdm.notifications.types.NotificationExternalKey;

    • LookupLinkAttribute - com.universe.mdm.notifications.types.upsert.LookupLinkAttribute;

    • LookupLinkValueChanges - com.universe.mdm.notifications.types.upsert.LookupLinkValueChanges.

  • Изменены объекты:

    • RecordChanges - добавлено поле lookupLinkAttributes, в версии 1 всегда null - com.universe.mdm.notifications.types.upsert.RecordChanges;

    • RecordDiffResult - добавлено поле lookupLinkAttributes, в версии 1 всегда null - com.universe.mdm.notifications.types.upsert.RecordDiffResult;

    • UpsertRecordEvent - добавлено поле lookupLinkAttributes, в версии 1 всегда null - com.universe.mdm.notifications.types.upsert.UpsertRecordEvent;

    • NotificationRecordKeys - поле externalKeys теперь содержит список NotificationExternalKey, а не строк - com.universe.mdm.notifications.types.NotificationRecordKeys.

Примечание

После обновления для использования старой версии сегмента необходимо установить параметр com.universe.mdm.notifications.notifications.upsert.version=1.

Версия 6.9

История консолидации записей

  • Изменения для хранения структуры дерева история консолидации:

    • В таблицу org_unidata_mdm_data.transitions добавлены столбцы parent (bigint) и children (bigint[]) для transition, участвующих в дереве истории консолидации.

  • Изменен формат transition с типом RECORD_MERGE (класс RecordMergeTransitionData):

    • Вместо списка duplicateEtalonIds сохраняется список всех эталонов, участвующих в мерже, с displayName и списком origins в эталоне (набор пар source system + external id);

    • Для событий консолидации до обновления у узлов не будут указаны displayName и дочерние события консолидации, а только записи, которые были объединены (тоже без displayName, а также без списка origins).

  • Добавлены запросы для просмотра дерева истории консолидации (проверяется право "История консолидации"):

    • Загрузка вниз - GET /v2/data/merge/history/tree/{etalonId}?depth=1&rootId=1;

    • Загрузка вверх - GET /v2/data/merge/history/tree-upward/{etalonId}?height=1&nodeId=1. Подгружаются родители на height уровней вверх, у родителей подгружаются прямые потомки (на один уровень вниз).

  • Добавлен запрос POST /v2/data/merge/history/record-preview для просмотра записи из набора ориджинов:

    • Указываются entity name и список пар source system + external id;

    • Опционально можно указать timeline date (дата на таймлайне актуальности) и last update date (дата на таймлайне обновлений записи).

Нечеткое сопоставление дубликатов

В хранилище PostgreSQL добавлен алгоритм нечеткого сопоставления org.unidata.mdm.matching.storage.postgres.service.impl.algorithm.InexactAlgorithm. Подробнее см. описание работы алгоритма Неточное соответствие. Не предоставляет непосредственного доступа. Используется org.unidata.mdm.matching.core.service.MatchingService.

Изменения в JoinableInputFragmentCollector

Изменения в интерфейсе JoinableInputFragmentCollector:

  • Метод fragment(JoinableInputFragment) переименован в joinFragment(JoinableInputFragment).

  • Вызов fragment(JoinableInputFragment) вызовет метод fragment(InputFragment) из интерфейса InputFragmentCollector, от которого наследуется JoinableInputFragmentCollector, поэтому ошибок компиляции не будет, но возможно изменение поведения, если для использованного инстанса JoinableInputFragment метод isJoined() возвращает true.

Новые параметры в backend.properties

  • В Workflow теперь можно включить Job Executor для работы таймеров, Multi-instance (элементы BPMN) и т.п. в бизнес-процессах.

  • Осуществляется через настройку com.unidata.mdm.workflow.core.job.executor.activate (доступные значения: false/true, по умолчанию false). При запуске через Docker передать настройку можно через переменную WORKFLOW_JOB_EXECUTOR_ACTIVATE.

Подписка на уведомления

Для уведомлений, приходящих с backend, был реализован механизм подписок по типу уведомлений. Теперь помимо запроса количества уведомлений BackendNotificationsPoller запрашивает список уведомлений, сгенерированных с момента прошлого запроса. В связи с этим интервал запросов уменьшен с 60 до 30 секунд.

Подписка на уведомлений реализуется методом BackendNotificationsSubscribes.subscribe.

Параметры:

  • type - тип уведомления для подписки;

  • handler - функция, выполняющаяся в случае, если нужный тип уведомления поступил с backend.

Типы уведомлений перечислены в enum NotificationModelType. На описанной выше логике реализуется модальное окно NeedReloadModal.tsx.

Поскольку теперь пользователь может изменить собственные права доступа без сброса сессии - во всех местах, где возможно обновление прав пользователя, был добавлен метод UserManager.updateUserData, подтягивающий новые права и обновляющий пути в RouterStore.

Также была изменена логика регистрации путей, приходящих из подключаемых модулей. Теперь все пути регистрируются в RouterStore вне зависимости от выполнения их резолверов. В самом RouterStore был добавлен registeredRoutesMap, хранящий все пути. Зарезолвленные пути также хранятся в routesMap.

Операция очистки паролей

Добавлена операция очистки паролей org.unidata.mdm.core.service.impl.job.CleanInactivePasswordsJob. Параметры операции описаны по ссылке.

Конфигурация операции находится в org.unidata.mdm.core.configuration.CoreConfiguration: cleanInactivePasswordsJobDetail и cleanInactivePasswordsJobTrigger.

  • В реализацию org.unidata.mdm.core.service.impl.SecurityServiceImpl.login(AuthenticationRequestContext ctx) включено разрешение на аутентификацию через сервис org.unidata.mdm.core.service.impl.security.AuthenticationAttemptAllowComponent.

  • В реализацию org.unidata.mdm.core.service.impl.SecurityServiceImpl добавлена зависимость org.unidata.mdm.core.service.AuthenticationAttemptCheckService.

Операция объединения данных

  • Добавлен модуль org.universe.mdm.job.duplicate

  • Обратная совместимость не нарушена

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

  • Добавлен модуль org.universe.mdm.job.matching

  • Добавлены потоки выполнения для обновления таблиц: [BATCH_RECORD_UPSERT_START]${matching-table-bulk-pipeline} (общий массовый поток) и [RECORD_UPSERT_START]${matching-table-worker-pipeline} (поток, определяющий действия для каждой записи отдельно)

  • Обратная совместимость не нарушена

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

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

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

  • В классах org.unidata.mdm.core/src/main/java/org/unidata/mdm/core/context/security/RoleDeleteContext.java и org.unidata.mdm.core/src/main/java/org/unidata/mdm/core/context/security/RoleUpsertContext.java метод public boolean isLogout() объявлен deprecated, т.к. выход из системы не предполагается при обновлении роли.

  • В классе org.unidata.mdm.core/src/main/java/org/unidata/mdm/core/service/SecurityService.java удалены методы:

    • void updateInnerToken(String login) заменен на void updateToken(String login) - обновляет все токены пользователя с таким логином.

    • void updateInnerTokensWithRole(String roleName) заменен на void updateTokensWithRole(String roleName) - обновляет все токены пользователей с такой ролью.

Сервис проверки разрешения на аутентификацию

org.unidata.mdm.core.service.impl.security.AuthenticationAttemptAllowComponent является частью реализации org.unidata.mdm.core.service.impl.SecurityServiceImpl. Не является публичным.

Компонент org.unidata.mdm.core.service.impl.SecurityServiceImpl предназначен для проверки разрешения на аутентификацию.

Содержит дополнительные методы для обработки успешных и неуспешных попыток аутентификации.

Принимает для обработки org.unidata.mdm.core.context.AuthenticationRequestContext и использует данные из контекста о решении о допуске аутентификации и выполнения дополнтельных действий при успешной или неуспешной аутентификации.

Реализация объединяет и вызывает все зарегистрированные компоненты, которые реализуют конкретные проверки: по имени пользователя, по IP адресу и т.д.

Реализации проверок регистрируются в org.unidata.mdm.core.service.impl.security.AuthenticationAttemptCheckComponent.

Методы:

  • boolean isAllowedToLogin(AuthenticationRequestContext ctx) - разрешать (true) или не разрешать (false) аутентификацию.

  • boolean isNotAllowedToLogin(AuthenticationRequestContext ctx) - обратный методу выше. Имеет реализацию по умолчанию: вызывает и обращает результат isAllowedToLogin(AuthenticationRequestContext ctx).

  • void fail(AuthenticationRequestContext ctx, Throwable cause) - действие при неудачной аутентификации на основе контекста с указанием причины в виде Throwable.

  • void fail(AuthenticationRequestContext ctx) - повторяет метод выше без указания причины, имеет реализацию по умолчанию: вызывает и обращает результат fail``(AuthenticationRequestContext ctx, Throwable cause)`` с cause = null.

  • void success(AuthenticationRequestContext ctx) - действие при удачной аутентификации на основе контекста.

Ошибка о блокировке доступа

Ошибка: exception org.unidata.mdm.core.exception BlockedAuthenticationAttemptException extends PlatformBusinessException.

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

Примечание

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

Класс для конкретных проверок

Класс abstract class org.unidata.mdm.core.service.impl.security.AuthenticationAttemptCheckComponent implements AfterModuleStartup реализует конкретную проверку и должен быть зарегистрирован в реестре, чтобы участвовать при проверке разрешения на аутентификацию.

После старта модуля регистрируется в реестре AuthenticationAttemptAllowComponent автоматически, если включен (по методу isEnabled()).

Текущие реализации:

  • org.unidata.mdm.core.service.impl.security.UsernameAuthenticationAttemptCheckComponent

  • org.unidata.mdm.core.service.impl.security.ClientIpAuthenticationAttemptCheckComponent

Методы:

  • @Override public final void afterModuleStartup() - регистрирует компонент в реестре после старта модуля, если компонент включен.

  • protected final void register() - регистрирует компонент в реестре.

  • protected final void unregister() - снимает регистрацию в реестре.

  • protected boolean isEnabled() - включение компонента. По умолчанию возвращает true.

  • public String getInternalComponentName() - внутреннее имя компонента. По умолчанию возвращает полное имя класса. В текущей реализации используется только в методе getExternalComponentName по умолчанию.

  • public String getExternalComponentName() - возвращает внешнее имя компонента, например, более читаемое для лога. По умолчанию вызывает getInternalComponentName(). Например, "Username authentication attempt count check component" для UsernameAuthenticationAttemptCheckComponent.

  • protected Locale locale(@NonNull AuthenticationRequestContext ctx) - достает из контекста локаль, присылаемую пользователем при попытке аутентификации, чтобы использовать в локализации текстов. Например, ошибки о превышении лимита неудачных попыток аутентификации для IP адреса. Поскольку пользователь еще не авторизовался - TextUtils возвращает локаль системы иначе.

  • public abstract boolean isAllowedToLogin(@NonNull AuthenticationRequestContext ctx) - проверяет разрешение на аутентификацию согласно правилам компонента.

  • public abstract void fail(@NonNull AuthenticationRequestContext ctx, Throwable cause) throws BlockedAuthenticationAttemptException - действие при неудачной попытке аутентификации с указанием причины. Например, увеличить счетчик неудачных попыток. Может бросать BlockedAuthenticationAttemptException. Может не бросать исключение, если нет необходимости передавать наружу событие. Чтобы локализовать текст BlockedAuthenticationAttemptException можно использовать метод locale выше для выборки из контекста.

  • public final void fail(@NonNull AuthenticationRequestContext ctx) throws BlockedAuthenticationAttemptException - вызывает метод выше с cause = null.

  • public abstract void success(@NonNull AuthenticationRequestContext ctx) - действие при успешной аутентификации. Например, сбросить счетчики неудачных попыток.

  • protected void auditAuthenticationBlock(@NonNull AuthenticationRequestContext ctx, String details) - метод для отправки в аудит стандартного события блокировки аутентификации, берет из контекста IP адреса сервера и клиента и details - детальное сообщение для журнала. В заголовок COMPONENT (компонент, который заблокировал аутентификацию) будет выводиться название из getExternalComponentName().

Пример задачи: необходимо добавить список разрешенных для аутентификации IP адресов.

  1. Создайте реализацию AuthenticationAttemptCheckComponent.

  2. Реализуйте String getExternalComponentName() - например, пусть возвращает "IP address white list authentication attempt check component".

  3. Добавьте туда список разрешенных адресов.

  4. Реализуйте isEnabled(), если он не должен быть всегда включен.

  • Если включается/выключается в рантайме, то необходимо сделать метод, вызывающий register()/unregister().

  1. Реализуйте boolean isAllowedToLogin(@NonNull AuthenticationRequestContext ctx) - проверьте, находится ли CLIENT_IP из AuthenticationRequestContext в списке разрешенных адресов.

  2. Реализуйте fail(@NonNull AuthenticationRequestContext ctx, Throwable cause) - в этом случае можно ничего не делать или написать в лог.

  3. Реализуйте void success(@NonNull AuthenticationRequestContext ctx) - в этом случае можно ничего не делать.

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

Класс class org.unidata.mdm.core.type.security.AuthenticationAttempt implements Serializable хранит информацию о количестве неудачных попыток и времени последней неудачной попытки.

Используется для компонентов проверки, которые реализуют счетчики попыток, основанные, например, на Map<Object, AuthenticationAttempt>.

Поля класса:

  • public static final AuthenticationAttempt EMPTY = new AuthenticationAttempt(0, LocalDateTime.MIN) - возвращать, если в истории нет неудачных попыток аутентификации, чтобы не возвращался null.

Поля объекта:

  • private final int tries - количество неудачных попыток.

  • private final LocalDateTime lastTime - время последней попытки.

Методы:

  • public AuthenticationAttempt(int tries, @NonNull LocalDateTime last) - конструктор.

  • public int getTries() - возвращает tries .

  • @NonNull public LocalDateTime getLastTime() - возвращает lastTime.

  • @Override public String toString() - возвращает "LoginAttempt{" + "tries=" + tries + ", lastTime=" + lastTime + '}'.

Автоконсолидация записей при назначении правил сопоставления

Frontend

Установка автоконсолидации реализована через custom properties назначений. Для работы с custom properties назначений был создана новая точка расширения (UE):

Создан новый модуль @universe-se/matching, в котором реализована точка расширения автоконсолидации (autoConsolidationMatchingProperty).

Backend

  • Добавлен новый сегмент RECORD_UPSERT_MATCHING_AUTO_MERGE типа Point в поток выполнения [RECORD_UPSERT_MATCHING_START].

  • В потоке выполнения [BATCH_RECORD_UPSERT_MATCHING_START] изменен порядок расположения сегментов. Сегмент типа Point - BATCH_RECORD_UPSERT_MATCHING_CONNECTOR должен располагаться после сегмента BATCH_RECORD_UPSERT_PERSISTENCE.

Удаление модулей REST API и рефакторинг механизма рендеринга

В версии 6.9 были удалены следующие модули (REST API V2 остается единственным действующим API для этих модулей):

  • org.unidata.mdm.rest.v1.search,

  • org.unidata.mdm.rest.v1.meta,

  • org.unidata.mdm.rest.v1.data,

  • org.unidata.mdm.rest.v1.draft,

  • org.unidata.mdm.rest.v1.dq.core,

  • org.unidata.mdm.rest.v1.dq.data,

  • org.unidata.mdm.rest.v1.bulk.core,

  • org.unidata.mdm.rest.v1.matching.core,

  • org.unidata.mdm.rest.v1.matching.data,

  • com.unidata.mdm.rest.v1.bulk.remove.records,

  • com.unidata.mdm.rest.v1.workflow.core,

  • org.unidata.mdm.rest.core.

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

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

  • org.unidata.mdm.system.service.RenderingService - реестр для типов рендеринга и постпроцессор для аннотированных полей @RenderingRef, который обычно не используется напрямую.

  • org.unidata.mdm.system.type.rendering.RenderingExtension - новый тип, реализующий FunctionalExtension, группирующий несколько типов рендеринга.

  • org.unidata.mdm.system.type.rendering.RenderingInstance - представление RenderingExtension во время выполнения. Используется для вставки @RenderingRef.

  • org.unidata.mdm.system.type.rendering.RenderingType<X, Y> - дескриптор действий рендеринга.

  • org.unidata.mdm.system.type.rendering.RenderingFragment - дескриптор фрагмента, зарегистрированный в типе рендеринга.

  • org.unidata.mdm.system.context.RenderingContext - тип носителя содержимого фрагментов.

  • org.unidata.mdm.system.dto.RenderingResult - носитель результатов рендеринга.

Запуск конкретных правил качества на реестр

В pilelines.json добавлены потоки, необходимые для работы функции: reapply-records-bulk-pipeline (общий, массовый поток) и reapply-records-worker-pipeline (поток, определяющий, что происходит с каждой записью).

Если настроены иные фазы правил качества, поток reapply-records-worker-pipeline необходимо доработать.

Управление журналом аудита

Добавлены 3 новые фоновые операции. Настройки по умолчанию не требуются. Размещаются в модуле com.universe.mdm.core.

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

Реализована возможность запуска новой операции после удачного/неуспешного завершения другой операции.

Запуск новой операции реализован с помощью TriggerExecutionListener.

В JobDefinitionStore имплементирован JobTriggersStore, реализующий настройку цепочек операций.

Добавлены API модули:

  • PUT /v2/core/jobs/triggers/{jobDefinitionId} - создание новой цепочки; сохранение идентификаторов связанных операций в таблице job_trigger.

  • GET /v2/core/jobs/triggers/{jobDefinitionId} - получение всех цепочек операций.

  • DELETE /v2/core/jobs/{jobDefinitionId}/triggers/{triggerId} - удаление цепочки операций.