Версия 6.12
Валидация спецсимволов в External Id
Изменен парсинг external key. Разрешено использование пробелов в External Id.
Уведомление о сроке окончания лицензии
@universe/license - в
LicenseManager
добавлен методgetIsSoonExpire
, проверяющий, скоро ли окончится действие лицензии.@universe-ee/base-app -
loginHandler
после успешной авторизации запускает проверку срока действия лицензии и отображает уведомление о скором окончании.@universe-platform/app - создание
loginHandler
вынесено в отдельный методgetLoginHandler
, чтобы можно было переопределять его из дочернего класса.
Связи типа "Включение" удалены из uPath в "Качестве данных"
К uPath input в DQ был добавлен новый фильтр UPathMdmFilters.disallowRelationType(disallowRelType?:string)
, который удаляет из typeName возможность выбрать связь "Включение".
uPath в "Правилах сопоставления" остался без изменений.
Точка расширения InnerCompareStore
В релизе 6.12 удалена точка расширения InnerCompareStore.
Виджет "Черновики" на главной странице
FE:
Добавлен новый запрос POST ReadMultiDraftListOp.
В запрос ReadDraftListOp добавлено два ключа - owner и state.
BE:
Расширено API: новый endpoint v2/draft/multidrafts.
Режим вставки комплексного атрибута (PATCH)
org.unidata.mdm.rest.v2.data:
Добавлен флаг patch в UpsertRequestRO, отвечающий за частичное обновление атрибутов записи.
org.unidata.mdm.data:
Реализован функционал для частичного обновления комплексных атрибутов.
UI UX: Экрана настройки БП
@universe-platform/uikit
Поправлено поведение компонента Resizer, если он расположен с левой стороны от масштабируемого контейнера.
@universe-ee/workflow
Появилась функция масштабирования и сворачивания панели редактирования параметров бизнес-процесса.
Были поправлены некоторые визуальные дефекты на экране бизнес-процесса.
Запуск Tomcat в контейнере BE MDM от пользователя tomcat
Реализован запуск tomcat в контейнере от пользователя tomcat. Права "root" в контейнере BE MDM больше не требуются.
Импорт/экспорт потоков выполнения
Для поддержания возможности импорта/экспорта потоков выполнения в разделе "Импорт/Экспорт" был расширен API новыми эндпоинтами:
Эндпоинт для импорта потоков выполнения из файла: v2/core/system/pipeline/import. Формат файла JSON. Файл передается в теле запроса, также передается флаг
recreate
, который помечает, нужно ли пересоздание потоков.Эндпоинт для экспорта потоков выполнения: v2/core/system/pipeline/export. Формат файла JSON.
Удаление связей в meta-data-driven
В тело запроса на обновление записи было добавлено поле relationsDeletes
. В тело ответа для связи было добавлено поле etalonId
.
com.universe.mdm.metadriven.core:
В RecordInputRO добавлено поле
relationsDeletes
;В RelationOutputRO добавлено поле
etalonId
.
Эксплуатация приложения без прав superuser для БД
Права суперпользователя больше не требуются для установки приложения.
Удаленные API
Удалено Deprecated API:
TagsRestService#deprecatedDelete(DeleteTagsRequestRO):
DELETE /v2/core/tags/delete
DELETE /v2/search/tags/delete
DELETE /v1/marks/tags/delete
UserNotificationsRestService#getUserNotifications:
/v2/core/user/notifications/{p:/?}{date: (([0-9T-:.]{23})?)}
RoleRestService#getAllSecurityLabels (до этого был скопирован в SecurityLabelRestService):
/v2/core/security/role/get-all-security-labels
Удалены следующие методы и поля:
XLSXProcessor#RELATION_TITLE_LABEL
XLSXProcessor#ENTITY_TITLE_LABEL
XLSXProcessor#LOOKUP_ENTITY_TITLE_LABEL
XLSXProcessor#COMPLEX_TO_NESTED_DISPLAY_NAMES_DELIMITER
XLSXProcessor#buildSheetName
XLSXProcessor#identifySheet
XlsxImportState#withNested
ClassificationImportXlsxRenderingComponent#identifySheet
ClassificationImportXlsxRenderingComponent#isEmptyRow
ClassificationImportXlsxRenderingComponent#convertToString
ClassificationImportXlsxRenderingComponent#getExcelCoordinates
ClassificationImportXlsxRenderingComponent#normalizeTo
Покрытие тестами класса SearchFormFieldDeserializer
Добавление searchType по умолчанию для запросов без searchType. Для запроса имеющего узел value/values устанавливается searchType = EXACT, для запроса имеющего узел range устанавливается searchType = RANGE.
Запрет передачи значения null в качестве имени искомого атрибута name: null.
Замена npe при невалидном значении типа искомого атрибута type: someWrongType на читаемый JsonMappingException с сообщением "Types other then simple types [ someWrongType ] aren't supported.".
Замена IllegalArgumentException при передаче невалидного типа поиска searchType: someWrongType на читаемый JsonMappingException с сообщением "Unsupported search type [ someWrongType ]".
Обработка исключения отсутствия всех значений value, values и range. Если в запросе нет ни value, ни values, ни range, то будет брошен JsonMappingException с сообщением "Requires single 'value' or 'value_s_' or 'range' node.".
Запрет одновременного использования флагов like, startWith, fuzzy, morphological. Можно установить в true только один из данных флагов, т.к. каждый из флагов переписывает searchType.
Установка searchType == EXIST в случае когда запрос делается по values, без searchType и values is Null.
Понижение приоритета флага like. Если все значения запроса пусты, searchType устанавливается в EXIST, а значение like игнорируется.
При searchType == RANGE, если значение range отсутствует, бросается JsonMappingException с сообщением "Range search type must contain a range node".
Если range: null, бросается JsonMappingException с сообщением "Range node must be an array". range всегда должен быть массивом.
Если searchType == START_WITH или searchType == MORPHOLOGICAL или searchType == LIKE, но тип искомого значения не равен STRING, CLOB или BLOB, бросается JsonMappingException с сообщением *"START_WITH/MORPHOLOGICAL/LIKE search type cannot be applied to a value of type typename".
Прочие изменения:
AttributeUtils#toDate, AttributeUtils#toTimestamp, AttributeUtils#toTime, AttributeUtils#toInstant исправлено внутреннее сообщение сервера, в соответствии с сообщением из файлов properties.
Исправление срабатываний анализатора Svacer
Возвращаемый тип DataTableFormatter#zip изменен с InputStream на File, т.к. InputStream может быть не закрыт в дальнейшем.
Новая проверка состава Бизнес ключа в процессах
Убрана проверка, которая не позволяла хранить в ключе ничего кроме namespace и draftId.
Добавлена проверка на корректность, в случае, если у БП есть linkedSubjectId и linkedTypeName тогда ключ обязательно должен начинаться c namespace и draftId, далее может быть что угодно, главное присутствие namespace и draftId.
Пример: register:test:draftId:148:кастомноеполе1:кастомноеполе2
Переделана логика нахождения процессов по бизнес ключу: теперь процесс находится по draftId внутри бизнес ключа, так как в бизнес ключе теперь могут быть пользовательские поля.
Внутренние изменения логики сравнения записей
Реализован новый стор для сравнения записей - RecordsCompareStore.
В нем посредством user-exit-a UEInnerCompareStore подключаются внутренние сторы сравнения. Каждый внутренний стор реализует логику сравнения для своего пространства имен: генерирует колонки, строит diff-ы, определяет внешний вид колонок.
Колонки в сравнении делятся на два типа: мета-колонки и дата-колонки. Мета-колонки отображаются в панели настроек, а дата-колонки в самой таблице. На одну мета-колонку может приходиться несколько дата-колонок либо дата-колонок может не быть вовсе (пример: колонки классификатора).
Поскольку каждое окно сравнения записей может иметь уникальные методы загрузки данных и уникальное отображение ячеек таблиц, был реализован глобальный интерфейс UniverseCompare, который хранит следующие типы:
ICompareTypeArgsMap - Объект, объявляющий тип сравнения (ключ объекта) и параметры, использующиеся для загрузки данных этого типа сравнения;
INamespaceLoadResultMap - Объект, объявляющий тип возвращаемых после загрузки значений для каждого пространства имён.
CompareType - тип, содержащий все виды сравнения.
Тип сравнения - один из параметров конструктора RecordsCompareStore. Каждый тип сравнения имеет свои аргументы загрузки данных. В параметры конструктора стора сравнения попадают не уже загруженные записи, а массив параметров загрузки сравниваемых записей.
Типы из UniverseCompare используются в реализации менеджера методов сравнения CompareRecordMethodsManager. Данный менеджер регистрирует для каждого пространства имён каждого типа сравнения:
загрузочные функции;
рендеры ячеек;
функцию, определяющую может ли в таблице сравнения быть выбран какой-либо dataPath.
В результате, при инициации стор сравнения RecordsCompareStore проходит по всем зарезолвленным UE, создает вложенные сторы сравнения, и загружает данные посредством загрузочных методов, зарегистрированных в CompareRecordMethodsManager.
Пункт главного меню "Процессы"
Были разделены пути для "Процессов" и "Задач".
module-ee/workflow/src/index.ts
Для процессов - 'ProcessesPage';
Для задач - 'TasksPage';
routeFunc и routeAccessor, так же разделены.
Отображение системной информации при сравнении записей
RegisterCompareStore.ts, LookupCompareStore.ts - данные получаются при помощи getValueByPath()
INamespaceLoadResultMap - изменен тип register: IRecordEntity на:
register: {
dataRecord: IRecordEntity;
recordKeys?: RecordKeys
};
InnerCompareCtorData - добавлен флаг showSystemInfo.
SystemRow.ts - константы для колонки системной информации.
Отображение "Etalon ID" связанной записи в сравнении связей
RelationCompareStore.ts
Был добавлен метод получения EtalonIdTo - public getEtalonIdTo (relation: AbstractRelation): string | undefined.
InnerCompareStore.ts
Добавлен новый параметр в ColumnView - getColumnHeader?: (metaColumn:ISearchColumn,columnName:string)=>ReactNode;
AbstractRelationInnerCompareStore.ts
Добавлен метод getColumnHeader
Добавлен новый компонент RelationCompareColumnHeader для отображения подсказки.
Уведомление состояния записи
Изменился формат сообщения meta-driven, уведомления о вставке и удалении.
Сортировка списка значений перечисления
@universe-platform/model
В класс ModelCollection был добавлен метод sort для сортировки элементов коллекции.
@universe-platform/uikit
В компоненте HeaderCell иконки фильтрации и сортировки отображаются сразу после названия колонки, а не прижимаются к правой границе.
@universe-platform/meta
В компоненте EnumerationEditorForm добавлена возможность сортировки значений выбранного перечисления по id и отображаемому имени.
Доработка отображения cron-выражений на экране операций
@universe-platform/cron
Добавлен компонент ScheduleFieldInput, который отрисовывает уже присутствующий в этом пакете ScheduleInput вместе с названием поля.
@universe-platform/model-form
У компонентов ModelField и ModelForm добавлена возможность менять вид отображаемых полей при помощи свойства fieldRender.
Операция мониторинга расхождений в данных
Добавлена новая операция - "Операция мониторинга расхождения в данных".
Добавлен новый модуль com.universe.mdm.job.data.difference
.
Критерий поиска задач/процессов "Доступные мне"
В момент mount компонента WorkflowCard добавляется SearchTerm AvailableForMeST.
Также добавлена проверка на наличие SearchTerm, т.к. критерий "Доступные мне" не нужно добавлять, если поделить с кем-то URL.
Измененная группировка поисковых критериев
DropDownTreeStore.ts
Добавлен новый ключ needDivider?: boolean
в тип DropDownTreeNode.
Добавлен новый компонент для рендера кнопки "Избранные" - FavoriteRecordsButton.tsx.
mdm/data/src/page/search/store/search_panel_store/systemTerms.ts - изменен порядок вывода поисковых критериев.
Стилизация системы
Добавлен опциональный флаг ENABLE_EXPERIMENTAL_THEME_PAGE
в customer.json (по умолчанию false).
Добавлен опциональный конфигурационный файл theme.json (по умолчанию отсутствует, используется системная тема).
Выбор классификации записи
@universe-ee/data-classifier:
Изменился вид всплывающего окна для выбора узлов классификатора в закладке "Классификация" в карточке записи.
@universe-platform/confirm:
В компонент Confirm было добавлено свойство isFlexBody по аналогии с компонентом Modal.
@universe-platform/tree:
В VirtualTreeStore добавлена возможность получения детей корневого элемента дерева с помощью getNodeChildren.
В компонент VirtualTree добавлено свойство isDisabled, запрещающее выбирать какие-либо ноды.
Виджеты карточки задачи
Модуль @universe-ee/workflow:
Добавлена точка расширения UETaskCardDescriptionWidget для отображения в карточке задачи различных виджетов для секции "Описание". Можно задать виджету расположение - колонку (левая или правая) и порядок в колонке. Левая и правая колонка будут отсортированы отдельно.
Добавлен компонент WorkflowListsWidget, показывающий результаты заданного количества поисковых выдач по workflow в виде списков ссылок, внешний вид которых адаптирован под показ ссылок на задачи и процессы, которые расположены во вкладках виджета.
Также из карточки задачи удален компонент EmbeddedTasksList. Чтобы вернуть старый виджет на прежнее место необходимо:
Отключить точку расширения subjectWorkflowEntitiesList.
Реализовать точку UETaskCardDescriptionWidget одним из трех вариантов:
Использовав WorkflowListsWidget (Рекомендуется. Специализировано для workflow, можно настроить).
Использовав SearchLinksList/SearchLinksWidget (То же самое, но абстрактное, не специализировано под workflow. List отображает один список, Widget - набор списков во вкладках).
Вернув старый компонент EmbeddedTasksList (Не рекомендуется).
Указать созданной точке расширения meta.column = 1 и meta.order = 0 - таким образом она будет размещена там же, где ранее располагался EmbeddedTasksList.
Отображение связей записи в табличном виде
Добавлен новый endpoint POST v2/data/relations/filtered-relations/ и метод
getRelationsByRecordEtalonIdAndBoundary(GetRelationsRequestRO)
, получающий отфильтрованные связи на указанной странице с заданными сортировками. Связи сначала ищутся в openSearch, а затем для найденных relation загружается их актуальное состояние из БД.Ответ на запрос
GetRelationsResultRO
добавлено полеtotalCount
, необходимое для пагинации.В
getRelationsByRecordEtalonIdAndBoundary
запроса v2/data/relations/relation-bulk/ добавлены параметры page и count. page - страница в openSearch, count - количество получаемых на странице записей. Теперь обращение к POST v2/data/relations/filtered-relations/ происходит без указания фильтров.getRelationsByRecordEtalonIdAndBoundary
запроса v2/data/relations/relation-bulk/ объявлен Deprecated.В
CommonRelationsComponent#loadTimelinesExt
внесены изменения, позволяющие при наличии указания конкретных etalonKeys связей подгружать их также из черновиков.Класс
GetRelationsBulkRequest
переименован вGetRelationsData
и сделан внутренним статическим классом классаDataRelationsRestService
.Создан REST обогащения черновика
CheckStateRestService#applyEnrichment
. В данном методе вызываетсяEnrichmentService#applyEnrichment
. Внутри него через ренедеринг создается множество контекстов вставки текущего состояния указанной записи. Каждый контекст вставки записи соответствует периоду актуальности записи. В первый контекст вставки записи добавляются фрагменты вставки связей и классификаторов, затем осуществляется batchUpsert.В пайплайн вставки записи добавлен шаг DQ: если фаза вставки записи "DRAFT", то осуществляется DQ, а затем вставляется обогащенное состояние записи.
Поднят флаг
trackEnrichments
при включенном обогащении.Поля
DataQualitySpot
, отвечающие за идентификацию атрибута на FE, вынесены в абстрактный классAbstractNamedSpot
.DataQualityEnrichment
наследуется отAbstractNamedSpot
для подсветки обогащенных полей на FE.Переработан
CheckStateBlockConverter
для конвертации enrichments атрибутов.ClassificationUpsertTimelineExecutor
, если существует не пустойnextTimeline
, используется его в качествеmodificationBox
.
Классификация записей промежуточными узлами версии классификатора
Добавлена возможность устанавливать флаг для назначения классификатора на реестр/справочник, который запрещает создавать классификации записей промежуточными узлами версии классификатора.
Backend:
Ранее параметр leafOnly был в параметрах назначения классификатора, в том числе в запросе /v1/classifiers/model/classifier. При этом GET /v1/classifiers/model/classifier всегда возвращал false в поле leafOnly. В текущем релизе это исправлено.
Теперь у назначений полностью поддерживается параметр leafOnly, который показывает, можно ли создавать классификации по неконечным узлам. При попытке создать классификацию по неконечному узлу при условии, что leafOnly = true для назначения на эту сущность, возникает ошибка валидации.
Frontend:
platfom/uikit:
EditableCell.tsx - добавлена поддержка Checkbox;
CheckboxInput.tsx - соответственно, checkbox;
cell.tsx - изменены условия для рендера value и editableCell. Поскольку у чекбокса нет value, которое можно было отрендерить, нужно рендерить сам компонент. Поэтому добавлена проверка на
editComponentType === EditComponentType.checkbox
.
ClassifierClassificationDataStore.ts:
Добавлен новый метод isLeafOnly;
Новая переменная
classificationNodeError: ObservableSet<ClassifierNode>
, которая хранит добавленные ошибочные узлы классификации на карточку записи;setClassificationNodeError
- метод для расчетаclassificationNodeError
.
Движок JSON-правил
@universe-platform/parameters: в компонент ParameterItem добавлена поддержка скрытия параметра через дескриптор;
@universe/json-rules:
Добавлены классы JsonRuleFormStore, JsonRuleFormEventHandler, JsonRuleFormValueParser, позволяющие использовать функциональность JSON-правил;
Добавлена обертка над библиотечным правилом UdRule, позволяющая указывать событие, которое будет эмититься, если правило завершится с ошибкой;
@unidata-mdm/matching: в MatchingAlgorithmParameterList логика взаимодействия инпутов формы переписана на JSON-правила;
@universe-ee/data: в ImportWizardButtonStore и ExportExcelRecordsStore логика взаимодействия инпутов формы переписана на JSON-правила.
Обновление/удаление назначений правил качества
В UpsertQualityModelContext были добавлены новые поля для обновления/удаления назначений по связке namespace + typeName:
typeAssignmentsUpdates - обновление назначений по namespace + typeName,
typeAssignmentsMerges - объединение назначений по namespace + typeName,
typeAssignmentsDeletes - удаление назначений по namespace + typeName.
Существующий функционал объединения назначений правил качества (DataQualityModelComponent#merge) был дополнен с учетом новых полей.
Настройка контейнеров хранилищ
Добавлены новые таблицы;
Добавлена новая модель;
Добавлены два новых модуля
com.universe.mdm.los.s3
иcom.universe.mdm.job.los.migrate
;Добавлена операция миграции больших объектов largeObjectMigrateJob;
В API больших объектов добавлен новый параметр - имя контейнера конфигурации;
В API добавлен новый блок обслуживания модели хранения больших объектов.
Версия 6.11
Автопрокрутка к новому правилу в наборе правил качества
platform/model-form/ModelCollectionPanel.tsx
Добавлен новый пропс isAutoScrollToNewItem: boolean
для включения прокрутки.
Добавлена переменная cardRefs: ObservableMap<number, HTMLElement | null>
.
В конструкторе добавлена MobX реакция на изменения в cardRefs для прокрутки к последнему добавленному элементу.
Преобразования в единицах измерения
Для эндпоинта
/v2/meta/measurement
появился новый параметр "reverseConversionFunction": "String" вmeasurementUnits
.В экспортируемых файлах для каждого измеримого атрибута появилась колонка с единицами измерения.
Для импортируемых записей в карточку сохраняются единицы измерения, указанные в загруженном файле.
Переименование "Модель данных" на "Структура данных"
Frontend:
Изменено название категории "Управление моделью данных" на "Управление структурой данных".
platform/app/assets/locale
Переименование ресурсов безопасности
Ресурс безопасности
org.unidata.mdm.data.security.resource.model.management
переименован наorg.unidata.mdm.core.security.resource.model.management
.
Скрытие объемных строк в JSON-сравнении
В окне JSON-сравнения (например, в сравнении ревизий модели данных) реализован механизм сворачивания ячеек, если в ней содержится более 200 символов.
Если рядом расположены две свернутые ячейки, они сворачиваются и разворачиваются независимо друг от друга.
API получения списка классификаторов
Добавлен новый необязательный параметр сортировки sort [] в API получения списка классификаторов (GET api/v1/classifiers/model/classifier
).
Параметр имеет тип "Массив строк". Сортировка доступна по следующим полям классификатора: name, displayName, createDate.
Пример запроса с новым параметром:
api/v1/classifiers/model/classifier?sort[]=createDate:desc&sort[]=name:asc
Также добавлен необязательный параметр сортировки sort [] (GET api/v2/data/model/nested-entities
).
Параметр имеет тип "Массив строк". Сортировка доступна по двум полям классификатора: name, displayName.
Пример запроса с новым параметром:
api/v2/data/model/nested-entities?draftId=0&sort[]=displayName:asc&sort[]=name:desc
Валидация при объединении записи
Добавлена валидация при объединении записи на предмет прохождения записью бизнес-процесса.
При появлении системной ошибки о невозможности выполнить операцию объединения необходимо завершить бизнес-процесс, в котором участвует запись с данным etalon ID, и еще раз объединить записи.
Аудит событий для черновиков
Backend:
Новые события в журнале аудита:
Создание черновика модели данных;
Редактирование черновика модели данных;
Публикация черновика модели данных;
Удаление черновика модели данных.
Удаленные неактивные события из журнала аудита:
Вставка черновика;
Применение черновика;
Удаление черновика.
Измененные пайплайны:
org.unidata.mdm.data[MODEL_DRAFT_UPSERT_START]
org.unidata.mdm.data[MODEL_DRAFT_PUBLISH_START]
Теперь последовательность сегментов следующая:
MODEL_DRAFT_UPSERT_START → MODEL_DRAFT_AUDIT_EVENT → MODEL_DRAFT_UPSERT_FINISH → MODEL_DRAFT_AUDIT_FALLBACK
MODEL_DRAFT_PUBLISH_START → MODEL_DRAFT_AUDIT_EVENT → MODEL_DRAFT_PUBLISH_FINISH → MODEL_DRAFT_AUDIT_FALLBACK
Новый пайплайн:
org.unidata.mdm.data[MODEL_DRAFT_REMOVE_START]
MODEL_DRAFT_REMOVE_START → MODEL_DRAFT_AUDIT_EVENT → MODEL_DRAFT_REMOVE_FINISH → MODEL_DRAFT_AUDIT_FALLBACK
Операция сбора статистики по данным
Перед обновлением на новую версию Юниверс MDM необходимо переключиться на версию контейнера базы данных, содержащую timescaledb extension, иначе сборка не соберется. Есть версия установки с Docker и без.
Сборка образа
Содержимое dockerfile
#Используйте базовый образ PostgreSQL версии 16.3
FROM postgres:16.3
#Устанавливайте необходимые пакеты
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
wget \
gnupg \
lsb-release \
software-properties-common \
ca-certificates
#Добавляйте ключ GPG и репозиторий TimescaleDB без использования apt-key
RUN wget --quiet -O - https://packagecloud.io/timescale/timescaledb/gpgkey | gpg --dearmor -o /usr/share/keyrings/timescaledb-archive-keyring.gpg \
&& echo "deb [signed-by=/usr/share/keyrings/timescaledb-archive-keyring.gpg] https://packagecloud.io/timescale/timescaledb/debian/ $(lsb_release -cs) main" > /etc/apt/sources.list.d/timescaledb.list
#Устанавливайте TimescaleDB
RUN apt-get update \
&& apt-get install -y --no-install-recommends timescaledb-2-oss-postgresql-16
#Конфигурируйте PostgreSQL для загрузки расширения TimescaleDB
RUN echo "shared_preload_libraries = 'timescaledb'" >> /usr/share/postgresql/postgresql.conf.sample
#Очищайте кэш apt, чтобы уменьшить размер образа
RUN apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
Сборка самого образа:
docker build --no-cache -t docker.universe-data.ru/mirror/postgres16.3-tsdb2.15.2
.
Итоговый образ:
docker.universe-data.ru/mirror/postgres16.3-tsdb2.15.2
Docker version
Для этого запустите docker-compose с измененной конфигурацией контейнера postgres-mdm:
postgres-mdm:
image: docker.universe-data.ru/mirror/postgres16.3-tsdb2.15.2
restart: always
environment:
POSTGRES_DB: ${MDM_POSTGRES_DB_NAME}
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
MDM_POSTGRES_USER: ${MDM_POSTGRES_USER}
MDM_POSTGRES_PASSWORD: ${MDM_POSTGRES_PASSWORD}
TZ: ${TIMEZONE:-UTC}
ports:
- ${POSTGRES_OUTER_PORT}:5432
networks:
- mdm_network
volumes:
- ./init-db.sh:/docker-entrypoint-initdb.d/initdb.sh
- mdm-postgres-data:/var/lib/postgresql/data
command: postgres -c max_prepared_transactions=300 -c shared_preload_libraries='timescaledb'
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres -d $MDM_POSTGRES_DB_NAME"]
interval: 10s
timeout: 1s
retries: 20
No docker version
$ apt-get update \
&& apt-get install -y --no-install-recommends \
wget \
gnupg \
lsb-release \
software-properties-common \
ca-certificates
$ wget --quiet -O - https://packagecloud.io/timescale/timescaledb/gpgkey | gpg --dearmor -o /usr/share/keyrings/timescaledb-archive-keyring.gpg \
&& echo "deb [signed-by=/usr/share/keyrings/timescaledb-archive-keyring.gpg] https://packagecloud.io/timescale/timescaledb/debian/ $(lsb_release -cs) main" > /etc/apt/sources.list.d/timescaledb.list
$ apt-get update \
&& apt-get install -y --no-install-recommends timescaledb-2-oss-postgresql-16
$ echo "shared_preload_libraries = 'timescaledb'" >> /usr/share/postgresql/postgresql.conf.sample
После перезагрузите бд и выполните:
psql -U postgres -d postgres -c "CREATE EXTENSION IF NOT EXISTS timescaledb CASCADE;"
Примечания:
timescaledb-2-oss-postgresql-16 - версия для image: postgres:16.
Если версия image postgres отличается, необходимо установить соответствующую версию timescaledb.
Путь к postgresql.conf тоже может отличаться.
Отправка записи в системы-потребители
Новые REST:
GET /v2/data/records-send-notifications/domains - получает список доступных систем-потребителей с доступными типами.
- {
- "domains": [
- {
"id": "string", "displayName": "string", "description": "string", "types": [
- {
"id": "string", "displayName": "string", "description": "string", "headers": [
- {
"name": "string", "displayName": "string", "description": "string", "type": "string"
}
]
}
]
}
]
}
POST /v2/data/records-send-notifications/send - отправляет сообщение в выбранный домен.
- {
"etalonId": "string", "externalId": "string", "domains": [
- {
"id": "string", "types": [ "string" ]
}
]
}
Новый ресурс "Ручная отправка в системы-потребители" на вкладке Менеджмент записей - предоставляет права на:
GET /v2/data/records-send-notifications/domains
POST /v2/data/records-send-notifications/send
Новая система-потребитель:
Очередь доступна по адресу http://server_url:8161/admin/
.
Выбор формата импорта ФИО из AD
Имя и фамилия в режиме sgi читались из неверных атрибутов, исправлено (sn - фамилия, givenName - имя, Initials - Отчество).
Режим sgi заменен на gsi : название - первые буквы атрибутов-источников имени, изменено для ясности.
Режим displayName оставлен без изменений.
Добавлен режим gsDisplayMix - имя и фамилию берет из атрибутов, отчество вычисляет из атрибута displayName.
Требуется в фильтре "Формат имён в Active Directory" выбрать правильное сопоставление Фамилии Отчества Имени в displayName.
При режиме gsDisplayMix, если displayName не содержит в себе корректных атрибутов, отчество будет не заполненным, имя и фамилия будут взяты из атрибутов givenName и sn.
Прекращение сборки редакции SE
Редакции SE и EE определяются лицензией на продукт.
Frontend:
Все модули @universe-se были перемещены в каталог ЕЕ и переименованы в @universe-ee.
Соединены модули @universe-se/workflow и @universe-ee/workflow.
Файлы из @universe-ee/data перенесены в @universe-ee/data-classifier.
Backend:
Поле лицензии «Ограничить кластер операции "Переиндексация данных"» перенесено из редакции SE в EE (значение по умолчанию – "нет"), ранее это поле было только в SE редакции.
Возврат к исторической версии модели реестра/справочника
Множественная вставка
Создан новый API для вставки множества реестров, справочников, вложенных объектов.
POST:
/v2/data/model/upsert
В теле запроса передается список реестров, список справочников и список вложенных объектов для вставки.
{
"registerEntities": [/*Объекты вставляемых/обновляемых реестров*/],
"lookupEntities": [/*Объекты вставляемых/обновляемых справочников*/],
"nestedEntities": [/*Объекты вставляемых/обновляемых комплексных атрибутов*/]
}
Откат к указанной ревизии
Создан новый API для отката указанных реестров, справочников, связей, вложенных объектов к указанной ревизии.
POST:
/v2/data/model/rollback
Данный API откатывает указанные реестры, справочники, вложенные объекты к указанной ревизии. При необходимости восстанавливает состояние групп для указанных сущностей.
В теле запроса передается ревизия, имена откатываемых реестров, справочников, вложенных объектов, bool переменная указывающая нужно ли осуществлять откат групп для указанных сущностей.
{
"revision": 242, /*Номер ревизии*/
"registerEntitiesNames": ["versionRegister"], /*Список имен откатываемых реестров*/
"lookupEntitiesNames": ["versLookup"], /*Список имен откатываемых справочников*/
"nestedEntitiesNames": ["versComl"], /*Список имен откатываемых вложенных объектов*/
"rollbackGroups": true /*Нужно ли откатывать группы для указанных сущностей*/
}
Если rollbackGroups == true, то будет восстановлена полная иерархия до указанной сущности. Все группы, в которые была вложена сущность будут восстановлены и переименованы в то состояние в котором они были в ревизии к которой совершается откат. Так же если в данной ревизии были удалены группы и в последней ревизии они не содержат элементов - такие группы удаляются (удаление пустых групп).
Сортировка значений параметров операций
В platformParametersForm.tsx и platform/ParameterItem.tsx добавлен новый props для передачи функции сортировки:
props.onSortOptions?: (parameters: IParameterDescriptor) => IOption[]
.В module/JobForm.tsx реализована функция сортировки с сохранением элемента с ключом ALL на первом месте. Создана константа
DEFAULT_VALUE='ALL'
.
Отображение системного имени функции
В paltform/uikit в CardPanel.tsx добавлен новый props
headerAutoHeight?: boolean
для выставления высоты хедера = auto.
Разлогинивание по бездействию юзера (ФСТЭК)
Обратная совместимость не нарушена.
Для активации функции необходимо изменить параметра системы. Подробнее см Параметры системы.
Упорядоченность списка операций по алфавиту
В модуль module/job/PropertiesForm.tsx добавлена сортировка списка типов операций в метод formattedJobName.
Дополнительные кнопки в шапке карточке записи
Добавлена новая точка расширения DataCardButton для отображения кнопок в шапке карточки записи.
Подсветка автоматически обогащенных атрибутов
Изменены возвращаемые типы для метода enrich в AbstractRecordEntityStore.ts и IInnerRecordCardStore.ts с
undefined
наSet<string> | undefined
. Реализация сundefined
помечена как deprecated. Метод enrich должен возвращать сеты dataPath, которые были обогащены.Во всех input-компонентах добавлен параметр
isHighlighted
, подсвечивающий поле ввода.Добавлена новая точка расширения AttributeViewProperties, задающая параметры отображения полей ввода значений атрибутов. Данная точка используется в DataAttributeValueFactory.tsx и передается в компоненты атрибутов посредством контекста DataAttributeViewContext.
Удалены устаревшие API
Из сборки исключены следующие API-модули:
UserRestService#read;
UserRestService#readInfo;
UserRestService#readAll(GetUsersRequestRO);
UserRestService#readAll(GetUsersByLoginsRequestRO.
А также соответствующие им endpoint-ы (были помечены как deprecated в релизе 6.9):
/v2/core/security/user/{login}
/v2/core/security/user/logins
/v2/core/security/user/query
/v2/core/security/user/user-info/{login}
Пул соединений и менеджер транзакций в операциях
Вместо PlatformTransactionManager в операциях теперь используется ResourcelessTransactionManager, не запускающий реальных транзакций для пула соединений операций.
org.apache.tomcat.jdbc.pool.DataSource
заменен на HikariCP последней версии, что дает также возможность делать сборки системы на других сервлет-контейнерах, а не только на Tomcat.
В текущей реализации поддерживаются следующие конфигурационные свойства пула соединений операций (org.unidata.mdm.core.job.datasource.*):
"minPoolSize", дефолтное значение "10"
"maxPoolSize", дефолтное значение "30"
"maxIdleTime", дефолтное значение "600000" # миллисекунды
"maxLifeTime", дефолтное значение "1800000" # миллисекунды
"validationInterval", дефолтное значение "0" # не использовать
"suspectTimeout", дефолтное значение "0" # не использовать
"isolationLevel", дефолтное значение null # READ_COMMITED
"uniqueName" # будет использоваться как имя пула в JMX консоли
Добавлены параметры для внешней конфигурации:
org.unidata.mdm.core.job.datasource.minPoolSize=${CORE_JOB_DATASOURCE_MINPOOLSIZE:3} org.unidata.mdm.core.job.datasource.maxPoolSize=${CORE_JOB_DATASOURCE_MAXPOOLSIZE:10} org.unidata.mdm.core.job.datasource.maxIdleTime=${CORE_JOB_DATASOURCE_MAXIDLETIME:600000} org.unidata.mdm.core.job.datasource.maxLifeTime=${CORE_JOB_DATASOURCE_MAXLIFETIME:1800000} org.unidata.mdm.core.job.datasource.validationInterval=${CORE_JOB_DATASOURCE_VALIDATIONINTERVAL:0} org.unidata.mdm.core.job.datasource.suspectTimeout=${CORE_JOB_DATASOURCE_SUSPECTTIMEOUT:0} org.unidata.mdm.core.job.datasource.uniqueName=Unidata-Core-Jobs
Улучшение отображения ошибок правил качества
Изменены стили в:
platform/record/errorsBadge.module.scss
platform/record/errorsRow.module.scss
platform/uikit/icon.m.scss
В module/dq-base/PortConstantField.tsx для констант
<Input>
заменен на<Input.TextArea>
.
Поиск пользователя для назначения задачи
В module-ee/workflow/Reassign.tsx добавлен элемент
<Input>
, а также 2 методаonSearch
иget filteredAcceptedUserList
.
Сортировка типов операций
В module/job/PropertiesForm.tsx: в метод
formattedJobName
добавлена сортировка перечня типов операций.
Цветовая индикация связей на графе
RelationGraphStore.ts:
Добавлены новые переменные для доп. параметров.
colorIncomingProperty: string = 'clustergraph_incoming_color'
иcolorIncomingProperty: string = 'clustergraph_color'
.Добавлена проверка направления связи в метод getLinkDisplayConfig. Заданы цвета: исходящая связь - оранжевая, входящая - голубая.
Добавлен метод graphDisplayConfig, где задается вид графа displayConfig.layout = GraphLayouts.Tree.
GraphD3:
В displayConfig добавлен новый параметр
isLinkStraight
.В метод linkArc добавлена проверка
isLinkStraight
. Рекурсивные связи теперь отображаются как: исходящая связь - прямая линия, входящая - изогнутая.
Отображение информации об исполнителе в задачах и процессах
Добавлен текст локализации "Исполнитель" / "Assignee".
В окне просмотра задачи ProcessCard.tsx и в окне просмотра подробностей процесса TaskOverview.tsx добавлен элемент
<UserView/>
для отображения имени пользователя и всплывающая подсказка с подробной информацией о пользователе.
Раскрытие токена авторизации
GET-запросы, в которых имело место раскрытие токена в параметрах, заменены на POST-запросы с передачей токена в теле запроса.
Измененные POST-запросы теперь принимают данные только в формате
application/x-www-form-urlencoded
.Изменена логика проверки токена авторизации для поддержки передачи токена в теле запроса.
Убрана возможность передачи токена авторизации в параметрах запроса.
Новая логика обработки токена авторизации:
Изменена логика обработки токена в методе SystemSecurityDataSourceComponent#handleRequest
. Сначала заголовки запроса проверяются на наличие в запросе на наличие в запросе типа данных application/x-www-form-urlencoded
.
При наличии в запросе типа данных
application/x-www-form-urlencoded
производится попытка извлечь токен авторизации из строки тела запроса. Если токен не найден, то токен ищется в Authorization header запроса.При отсутствии в запросе типа данных
application/x-www-form-urlencoded
логика осталась прежней, токен ищется в Authorization header запроса.
Измененные методы:
LargeObjectRestService#fetchBlob
LargeObjectRestService#fetchClob
DataModelRestService#exportDataModel
QualityModelRestService#dump
MatchingModelRestService#dump
EnumerationRestService#export
MeasurementUnitsRestService#export
SourceSystemRestService#exportSourceSystems
ClassifiersModelRestService#dump
WorkflowModelRestService#dump
UtilityRestService#logs
В перечисленных методах теперь указан единственный разрешенный формат принимаемых данных: application/x-www-form-urlencoded
.
Также в указанных методах изменен тип с GET на POST.
Все параметры, которые передавались как параметры запроса, теперь передаются как параметры формы (тела запроса).
Пример измененного запроса:
Было:
GET /v1/classifiers/model/export/book_classifier?token=f3b61f0b-5ec4-4a9e-b61f-0b5ec43a9ea1
Стало:
POST /v1/classifiers/model/export/book_classifier Content-Type: application/x-www-form-urlencoded token=f3b61f0b-5ec4-4a9e-b61f-0b5ec43a9ea1
Соответствующие методам endpoint-ы:
/v2/core/lob/blob/{id}
/v2/core/lob/clob/{id}
/v2/data/model/export
/v2/data-quality/model/export
/v2/matching/model/export
/v2/meta/enumeratuions/export
/v2/meta/measurement/export
/v2/meta/source-system/export
/v1/classifiers/model/export/{classifierName}
/v2/workflow/model/export
/v2/core/utility/logs
Во всех этих эндпоинтах токен теперь ожидается в теле POST-запроса вместо GET.
Запуск бизнес-процессов при удалении записи
Backend:
В
WorkflowRecordDraftTriggers
добавлены триггеры DELETE_RECORD, DELETE_PERIOD;В OpenSearch добавлен индекс
$workflow_trigger
;В
WorkflowDataStorageIds
добавлена константаSID_PROCESS_TRIGGER
, при помощи которой в SystemStorage храниться триггер БП;В пайплайн [RECORD_DRAFT_PUBLISH_WORKFLOW_SELECTOR] добавлен новый point [RECORD_DRAFT_PUBLISH_BEFORE_START_WORKFLOW], в котором осуществляется проверка существования процесса на удаление записи. Проверка срабатывает при попытке опубликовать черновик на удаление записи. Если процесс уже существует, появляется ошибка PlatformValidationException, которая прерывает выполнение публикации черновика;
В
WorkflowValidationComponent
добавлена проверка на существование незавершенного БП, привязанного к данному черновику. В случае нахождения такого БП отобразится ошибка WorkflowRuntimeException.Добавлен новый API, возвращающий количество БП данной записи, сгруппированных по типу триггера БП.
WorkflowProcessInstanceRestService#getProcessCount endpoint: v2/workflow/process/count-by-trigger
.
Frontend:
mdm/DataCardStore.ts:
В методе
handleDelete
добавлено создание нового черновика.В методе
deleteData
:В
deleteRecordPayload
был добавленdraftId
;Добавлена публикация черновика для запуска бизнес-процесса. Если публикация не удалась, то отобразится ошибка валидации и удалится созданный черновик, т.к. невалидные черновики не хранятся.
platform/networkResponseException.ts:
Добавлен метод
getErrorWithDomain
для поиска поиска ошибку по необходимому domain
platform/record/AbstractCardStore.ts
Добавлен необязательный параметр
draftId
в следующие методыdeleteData
иhandleDelete
.
Изменение логики отключения route-ов
RouterStore:
routerStore.disableRoute
иrouterStore.undisableRoute
помечены как deprecated и будут удалены в universe-platform/router v2.0.0. Вместо устаревших методов добавленыrouterStore.addRouteDisableCondition
иrouterStore.removeRouteDisableCondition
.routerStore.addRouteDisableCondition
принимает имя роута и функцию, результат выполнения которой должен вернуть boolean значение, означающее отключен роут или нет.После добавления условий отключения через
routerStore.addRouteDisableCondition
необходимо выполнитьrouterStore.reloadRoutes
для перерасчета активных роутов.
Unidata.ts:
Метод
getDisabledRoutes
помечен как deprecated и будет удален в universe-platform/app v2.0.0. Вместо устаревшего метода добавленmanageDisabledRoutes
, внутри которого необходимо использовать методыrouterStore.addRouteDisableCondition
иrouterStore.removeRouteDisableCondition
.Дополнительно вызывать метод
routerStore.reloadRoutes
не требуется. Он будет вызван послеmanageDisabledRoutes
.
Проверка прав на rout-ы
В RouterStore реализован метод hasRouteAccess
:
public hasRouteAccess<T extends UniverseRouter.RouteKeys> (name: T, params: RouteDescriptorParams<T>)
Метод использует функцию routeAccessor
из IRouteMeta
и возвращает boolean значение доступности роута текущему пользователю. Если у роута не задан routeAccessor
- функция вернет true.
routeAccessor
принимает те же параметры, что и routeFunc
:
RouteAccessor extends ((...args: Parameters<RouteFunc>) => boolean) = ((...args: Parameters<RouteFunc>) => boolean)
В случае если у текущего пользователя недостаточно прав на открытый раздел, ему будет отображена заглушка "Просмотр ограничен".
Копирование данных из окна о системе
universe-platform/security - Добавлен экспорт из пакета типа BUILD_VERSION.
universe-platform/app
В зависимости добавлена библиотека copy-to-clipboard.
В модальное окно с информацией о системе добавлена кнопка "Скопировать текст".
Исправление срабатываний анализатора Svacer
Классы AbstractBulkOperationContext, RoleDeleteContext, RoleGetContext, RoleUpsertContext, UserGetContext, UserUpsertContext, AbstractImportContext, AbstractModelChangeContext, AbstractModelGetContext, AbstractModelRefreshContext, AbstractModelRemoveContext, AbstractModelSourceContext, AuthenticationRequestContext, DataImportInputContext, DataImportTemplateContext, DeleteLargeObjectContext, FetchLargeObjectContext, RefreshModelContext, UpsertLargeObjectContext, UpsertUserEventRequestContext, PasswordPO, RolePO, SecurityLabelAttributePO, SecurityLabelPO, UserPO, GetDataModelContext, SourceDataModelContext, UpsertDataModelContext, AbstractRecordIdentityContext, AbstractRelationIdentityContext, AbstractRelationsFromRequestContext, AbstractRelationsFromToRequestContext, CloneFromRelationRequestContext, CloneRecordContext, CloneRelationsContext, CloneToRelationRequestContext, DeleteRelationRequestContext, DeleteRelationsRequestContext, DeleteRequestContext, GetMultipleRequestContext, GetRecordTimelineRequestContext, GetRecordsTimelinesRequestContext, GetRelationRequestContext, GetRelationTimelineRequestContext, GetRelationsDigestRequestContext, GetRelationsRequestContext, GetRelationsTimelineRequestContext, GetRequestContext, JoinRequestContext, MergeFromRelationRequestContext, MergeRelationsRequestContext, MergeRequestContext, MergeToRelationRequestContext, PreviewRequestContext, RecordHistoryContext, RelationFromPreviewContext, RestoreFromRelationRequestContext, RestoreRecordRequestContext, RestoreRelationsRequestContext, RestoreToRelationRequestContext, SplitRecordRequestContext, UnmergeFromRelationContext, UnmergeRecordContext, UnmergeToRelationContext, UpsertRelationRequestContext, UpsertRelationsRequestContext, UpsertRequestContext, DataQualityContext, GetQualityModelContext, SourceQualityModelContext, UpsertQualityModelContext, RecordQualityContext, StateCheckContext, AbstractDraftDataContext, AbstractDraftFieldsContext, DraftGetContext, DraftPublishContext, DraftQueryContext, DraftRemoveContext, DraftUpsertContext, MatchingModelGetContext, MatchingModelUpsertContext, DataMatchingContext, RecordMatchingCloneContext, RecordMatchingDeleteContext, RecordMatchingMergeContext, RecordMatchingRestoreContext, RecordMatchingUnmergeContext, RecordMatchingUpsertContext, ApplyUploadedModelRequestContext, ExportModelRequestContext, GetEnumerationsContext, GetMeasurementUnitsContext, GetSourceSystemsContext, SourceEnumerationsContext, SourceMeasurementUnitsContext, SourceSourceSystemsContext, UpsertEnumerationsContext, UpsertMeasurementUnitsContext, UpsertSourceSystemsContext, AbstractCompositeRequestContext, CommonRequestContext, RenderingContext, TimelogDeleteContext, TimelogQueryContext, GetTransformModelContext, SourceTransformModelContext, UpsertTransformModelContext, TransformContext, AbstractCommentContext, AbstractScoreContext, CommentDeleteContext, CommentQueryContext, CommentUpsertContext, ScoreDeleteContext, ScoreQueryContext, ScoreUpsertContext, TagDeleteContext, TagQueryContext, TagSearchContext, TagUpsertContext, ExportRecordsXLSXBulkOperationContext, RemoveRecordsBulkOperationContext, AbstractClassificationIdentityContext, AbstractClassificationsContext, AbstractClassifierModelChangeContext, AbstractClassifierModelGetContext, AbstractClassifierModelRemoveContext, ClassificationCloneContext, ClassificationDeleteContext, ClassificationGetContext, ClassificationMergeContext, ClassificationPreviewContext, ClassificationRestoreContext, ClassificationTimelineQueryContext, ClassificationUnmergeContext, ClassificationUpsertContext, ClassificationsCloneContext, ClassificationsDeleteContext, ClassificationsGetContext, ClassificationsMergeContext, ClassificationsPreviewContext, ClassificationsRestoreContext, ClassificationsTimelineQueryContext, ClassificationsUpsertContext, GetClassifierModelContext, GetNodeModelContext, GetVersionModelContext, RemoveClassifierModelContext, RemoveNodeModelContext, RemoveVersionModelContext, SourceDataModelContext, UpsertClassifierModelContext, UpsertNodeModelContext, UpsertVersionModelContext, DataStatusModelGetContext, DataStatusModelSourceContext, DataStatusModelUpsertContext, WorkflowModelGetContext, WorkflowModelUpsertContext и ModifyRecordsBulkOperationContext больше не являются Serializable.
Переименование колонок таблиц сопоставления
Переименованы системные колонки в таблицах сопоставления на _$x
, _$id
, _$namespace
, _$type_name
, _$subject_id
, _$valid_from
, _$valid_to
для исключения конфликтов с пользовательскими колонками. Добавились новые индексы и изменились настройки нечеткого алгоритма, поэтому таблицы сопоставления необходимо пересоздать.
Чтение данных из AD
Поскольку имя и фамилия в режиме sgi читались из неверных атрибутов, исправлено (sn - фамилия, givenName - имя).
Режим sgi заменен на gsi: название - это первые буквы атрибутов-источников имени; режим displayName
оставлен без изменений.
Добавлен режим gsDisplayMix
- имя и фамилия берутся из атрибутов, отчество вычисляется из атрибута displayName
.
При режиме gsDisplayMix
если displayName
не содержит в себе корректных атрибутов, то отчество будет незаполненным, имя и фамилия будут взяты из атрибутов givenName
и sn
.
Валидация атрибутов с поисковыми параметрами
Добавлена валидация атрибутов с параметрами поиска, которые не являются поисковыми.
В случае появления ошибки необходимо либо сделать атрибут поисковым, либо убрать у него параметры поиска.
Из-за большого количества таких ошибок может не быть возможности сохранить модель через интерфейс. В таком случае необходимо:
Экспортировать модель данных.
Открыть файл модели .xml в любом редакторе.
Найти элемент в котором произошла ошибка (<register>, <lookup>, <nested>) по имени (атрибут name).
Найти атрибут в котором произошла ошибка (<simpleAttribute>, <arrayAttribute>) по имени (атрибут name).
Далее либо сделать атрибут поисковым (searchable="true");
Либо убрать у него параметры поиска (searchMorphologically="false" searchCaseInsensitive="false" searchWithTransliteration="false" searchWithSynonyms="false").
После изменений необходимо произвести реиндекс модели с обновлением маппингов.
Для классификаторов шаги аналогичны - отличается только структура .xml и шаг 3: необходимо найти узел (<node>) в котором произошла ошибка по имени (атрибут name).
Валидация отображаемых атрибутов связей
Добавлена валидация отображаемых атрибутов связей на backend; frontend не отображает в списке невалидные атрибуты.
Выбранные отображаемыми атрибуты связи должны быть отображаемыми или главными отображаемыми в связанном реестре, в противном случае появится ошибка системы.
Из-за большого количества таких ошибок может не быть возможности сохранить модель через интерфейс. В таком случае необходимо:
Экспортировать модель данных и открыть файл модели .xml в любом редакторе.
Найти связи с ошибками, стереть в них строки
<toEntityDefaultDisplayAttributes>*имя атрибута*</toEntityDefaultDisplayAttributes>
.Сохранить файл и импортировать модель.
Публикация черновика модели данных в последнюю ревизию
В
DataModelValidationComponent
добавлена валидация merge черновика и последней ревизии модели данных:DataModelValidationComponent#mergeValidation
. Эта валидация используется вDataModelComponent#upsert
.В
DataModelComponent
убрана ошибка, бросаемая, если версия модели данных черновика не равна следующей ревизии.В методе
DataModelComponent#merge
распаковка модели данных вынесена в методы классаDataModelUtils
.В методе
DataModelComponent#merge
при mergeEntitiesGroup
удаление несуществующих сущностей из дерева производится всегда, а не только при обновлении состояния дерева.В
DataModelDiffProvider
добавлены статические методы для получения значений входных параметров методаDataModelDiffProvider#compare
. МетодDataModelDiffProvider#revision
возвращает интерпретацию ревизии.В
ModelDraftPublishFinishExecutor#publish
в зависимости от флагаisForce
создаются разные контексты вставки модели данных.Если при публикации черновика
isForce == false
, то вызываетсяdiffService.compare
, который сравнивает состояние модели данных черновика и последней ревизии. На основе этих результатов формируется`UpsertDataModelContext`
, содержащий только измененные сущности. Если при публикации черновикаisForce == false
, тоUpsertDataModelContext
имеетupsertType == ModelChangeType.MERGE
.Если при публикации черновика
isForce == true`
, то контекст заполняется текущим состоянием черновика. Если при публикации черновикаisForce == true
, тоUpsertDataModelContext
имеетupsertType == ModelChangeType.FULL
.
Для удобства работы с результатом
DiffService#compare
создан вспомогательный классDiffElements
.Добавлено версионирование групп сущностей. Новая версия присваивается при создании, переименовании и перемещении.
Создана обертка над узлом дерева групп
MergedGroupedEntity
. Данная обертка помимо узла хранит имя родителя, расстояния до узлов находящихся левее/правее вливаемого узла и ссылки на обертки детей.В
UpsertDataModelContext
добавлено полеentitiesGroupsMerge
. В данном поле хранится список поддеревьев из обертокMergedGroupedEntity
. ВDataModelComponent
данное поддерево будет использоваться для merge групп сущностей в текущую модель данных.Создан метод слияния деревьев сущностей
EntitiesGroup#merge(EntitiesGroup root, List<MergedGroupedEntity> mergedEntities)
.
Поиск процессов и задач по полям статусов
Теперь у процессов и статусов задач на согласование есть поля статусов $process_status/$task_status
и $linked_subject_display_value
, по которым можно производить поиск и сортировку.
Значения полей не сохраняются в БД, а вычисляются перед индексацией. $task_status
вычисляется по $completed
и наличию $assignee
, $process_status
вычисляется по $finished
и наличию активных/завершенных задач-потомков, $linked_subject_display_value
— по $linked_type_name
и $linked_subject_id
.
$process_status/$task_status
хранятся в числовом виде для упрощения сортировки, в ответе на поисковый запрос REST API числовые значения дополняются человеко-читаемым displayValue
.
Примечание
Поскольку маппинг поля $linked_subject_display_value у задач/процессов бизнес-процессов был изменен - необходима переиндексация бизнес-процессов с ремаппингом.
Пример ответа с $process_status
{
"id": "Process_36181b30-edbf-11ee-9271-031d8add40d5-41864",
"score": 1.0,
"status": null,
"preview": [
{
"field": "$initiator_full_name",
"fieldDisplayName": null,
"fieldValueType": null,
"values": [
"Root2 Admin2"
],
"extendedValues": [
{
"value": "Root2 Admin2",
"displayValue": null,
"linkedEtalonId": null,
"linkedTypeName": null
}
],
"complexValues": null
},
{
"field": "$definition_id",
"fieldDisplayName": null,
"fieldValueType": null,
"values": [
"Process_36181b30-edbf-11ee-9271-031d8add40d5:1:41707"
],
"extendedValues": [
{
"value": "Process_36181b30-edbf-11ee-9271-031d8add40d5:1:41707",
"displayValue": null,
"linkedEtalonId": null,
"linkedTypeName": null
}
],
"complexValues": null
},
{
"field": "$definition_display_name",
"fieldDisplayName": null,
"fieldValueType": null,
"values": [
"test"
],
"extendedValues": [
{
"value": "test",
"displayValue": null,
"linkedEtalonId": null,
"linkedTypeName": null
}
],
"complexValues": null
},
{
"field": "$end_event_name",
"fieldDisplayName": null,
"fieldValueType": null,
"values": [],
"extendedValues": null,
"complexValues": null
},
{
"field": "$finished",
"fieldDisplayName": null,
"fieldValueType": null,
"values": [
false
],
"extendedValues": [
{
"value": false,
"displayValue": null,
"linkedEtalonId": null,
"linkedTypeName": null
}
],
"complexValues": null
},
{
"field": "$linked_subject_id",
"fieldDisplayName": null,
"fieldValueType": null,
"values": [
"676773a5-edc4-11ee-a295-a91ddb030bdd"
],
"extendedValues": [
{
"value": "676773a5-edc4-11ee-a295-a91ddb030bdd",
"displayValue": null,
"linkedEtalonId": null,
"linkedTypeName": null
}
],
"complexValues": null
},
{
"field": "$start_date",
"fieldDisplayName": null,
"fieldValueType": null,
"values": [
"2024-03-29T12:03:54.631Z"
],
"extendedValues": [
{
"value": "2024-03-29T12:03:54.631Z",
"displayValue": null,
"linkedEtalonId": null,
"linkedTypeName": null
}
],
"complexValues": null
},
{
"field": "$linked_type_name",
"fieldDisplayName": null,
"fieldValueType": null,
"values": [
"register:UN-25529-test"
],
"extendedValues": [
{
"value": "register:UN-25529-test",
"displayValue": null,
"linkedEtalonId": null,
"linkedTypeName": null
}
],
"complexValues": null
},
{
"field": "$process_status",
"fieldDisplayName": null,
"fieldValueType": null,
"values": [
"В РАБОТЕ"
],
"extendedValues": [
{
"value": 1,
"displayValue": "В РАБОТЕ",
"linkedEtalonId": null,
"linkedTypeName": null
}
],
"complexValues": null
},
{
"field": "$definition_key",
"fieldDisplayName": null,
"fieldValueType": null,
"values": [
"Process_36181b30-edbf-11ee-9271-031d8add40d5"
],
"extendedValues": [
{
"value": "Process_36181b30-edbf-11ee-9271-031d8add40d5",
"displayValue": null,
"linkedEtalonId": null,
"linkedTypeName": null
}
],
"complexValues": null
},
{
"field": "$initiator",
"fieldDisplayName": null,
"fieldValueType": null,
"values": [
"admin"
],
"extendedValues": [
{
"value": "admin",
"displayValue": null,
"linkedEtalonId": null,
"linkedTypeName": null
}
],
"complexValues": null
},
{
"field": "$end_event_id",
"fieldDisplayName": null,
"fieldValueType": null,
"values": [],
"extendedValues": null,
"complexValues": null
},
{
"field": "$business_key",
"fieldDisplayName": null,
"fieldValueType": null,
"values": [
"register:UN-25529-test:draftId:751"
],
"extendedValues": [
{
"value": "register:UN-25529-test:draftId:751",
"displayValue": null,
"linkedEtalonId": null,
"linkedTypeName": null
}
],
"complexValues": null
},
{
"field": "$finish_date",
"fieldDisplayName": null,
"fieldValueType": null,
"values": [],
"extendedValues": null,
"complexValues": null
},
{
"field": "$initiator_email",
"fieldDisplayName": null,
"fieldValueType": null,
"values": [
"mail@example.com"
],
"extendedValues": [
{
"value": "mail@example.com",
"displayValue": null,
"linkedEtalonId": null,
"linkedTypeName": null
}
],
"complexValues": null
},
{
"field": "$id",
"fieldDisplayName": null,
"fieldValueType": null,
"values": [
"41864"
],
"extendedValues": [
{
"value": "41864",
"displayValue": null,
"linkedEtalonId": null,
"linkedTypeName": null
}
],
"complexValues": null
},
{
"field": "$etalon_id",
"fieldDisplayName": null,
"fieldValueType": null,
"values": [],
"extendedValues": null,
"complexValues": null
}
],
"source": null
}
Поиск процессов по полям связанных задач
Добавлена возможность искать процессы по полям связанных задач на согласование (supplementary поисковые запросы).
Пример запроса, возвращающего все процессы
{
"payload": {
"com.unidata.mdm.rest.v2.workflow.core": {
"countOnly": false,
"formFields": [],
"formGroups": [],
"searchFields": [
"$id",
"$definition_display_name",
"$finished"
],
"returnFields": [
"$business_key",
"$id",
"$definition_id",
"$definition_key",
"$definition_display_name",
"$start_date",
"$finish_date",
"$initiator",
"$initiator_full_name",
"$initiator_email",
"$finished",
"$end_event_id",
"$end_event_name",
"$linked_type_name",
"$linked_subject_id"
],
"fetchAll": true,
"entity": "",
"typedVariablesSearch": false,
"searchWorkflowType": "PROCESS",
"totalCount": true,
"page": 1,
"count": 20,
"start": 0,
"sortFields": []
},
"org.unidata.mdm.rest.v2.dq.data": {
"setNames": [],
"ruleNames": [],
"functionNames": [],
"message": null,
"severity": null,
"category": null,
"totalScore": 0,
"matchAll": false,
"negate": false
},
"com.unidata.mdm.rest.v1.classifiers.multiple": null
}
}
Пример запроса, возвращающего задачи с исполнителем "admin"
{
"payload": {
"org.unidata.mdm.rest.v2.dq.data": {
"setNames": [],
"ruleNames": [],
"functionNames": [],
"message": null,
"severity": null,
"category": null,
"totalScore": 0,
"matchAll": false,
"negate": false
},
"com.unidata.mdm.rest.v1.classifiers.multiple": null,
"com.unidata.mdm.rest.v2.workflow.core": {
"countOnly": false,
"formFields": [
{
"name": "$assignee",
"type": "STRING",
"searchType": "EXACT",
"inverted": false,
"value": "admin"
}
],
"formGroups": [],
"searchFields": [
"$definition_display_name",
"$id",
"$assignee",
"$completed",
"$process_definition_display_name"
],
"returnFields": [
"$id",
"$definition_key",
"$definition_display_name",
"$create_date",
"$due_date",
"$complete_date",
"$completed",
"$assignee",
"$assignee_full_name",
"$candidate_groups.$name",
"$candidate_users.$login",
"$variables",
"$initiator",
"$linked_subject_id",
"$linked_type_name",
"$business_key",
"$process_instance_id",
"$process_definition_id",
"$process_definition_key",
"$process_definition_display_name",
"$process_start_date",
"$process_finish_date",
"$linked_type_name",
"$linked_subject_id"
],
"fetchAll": false,
"entity": "",
"typedVariablesSearch": false,
"searchWorkflowType": "TASK",
"totalCount": true,
"page": 1,
"count": 20,
"start": 0,
"sortFields": []
}
}
}
На основе примеров выше можно составить поисковой запрос, возвращающий все процессы, у которых связанные задачи удовлетворяют вышеупомянутому запросу.
Пример запроса поиска процессов по полям задач
{
"payload": {
"com.unidata.mdm.rest.v2.workflow.core": {
"countOnly": false,
"formFields": [],
"formGroups": [],
"searchFields": [
"$id",
"$definition_display_name",
"$finished"
],
"returnFields": [
"$business_key",
"$id",
"$definition_id",
"$definition_key",
"$definition_display_name",
"$start_date",
"$finish_date",
"$initiator",
"$initiator_full_name",
"$initiator_email",
"$finished",
"$end_event_id",
"$end_event_name",
"$linked_type_name",
"$linked_subject_id"
],
"fetchAll": true,
"entity": "",
"typedVariablesSearch": false,
"searchWorkflowType": "PROCESS",
"totalCount": true,
"page": 1,
"count": 20,
"start": 0,
"sortFields": [],
"supplementary": [
{
"countOnly": false,
"formFields": [
{
"name": "$assignee",
"type": "STRING",
"searchType": "EXACT",
"inverted": false,
"value": "admin"
}
],
"formGroups": [],
"searchFields": [
"$definition_display_name",
"$id",
"$assignee",
"$completed",
"$process_definition_display_name"
],
"returnFields": [
"$id",
"$definition_key",
"$definition_display_name",
"$create_date",
"$due_date",
"$complete_date",
"$completed",
"$assignee",
"$assignee_full_name",
"$candidate_groups.$name",
"$candidate_users.$login",
"$variables",
"$initiator",
"$linked_subject_id",
"$linked_type_name",
"$business_key",
"$process_instance_id",
"$process_definition_id",
"$process_definition_key",
"$process_definition_display_name",
"$process_start_date",
"$process_finish_date",
"$linked_type_name",
"$linked_subject_id"
],
"fetchAll": false,
"entity": "",
"typedVariablesSearch": false,
"searchWorkflowType": "TASK",
"totalCount": true,
"page": 1,
"count": 20,
"start": 0,
"sortFields": []
}
]
},
"org.unidata.mdm.rest.v2.dq.data": {
"setNames": [],
"ruleNames": [],
"functionNames": [],
"message": null,
"severity": null,
"category": null,
"totalScore": 0,
"matchAll": false,
"negate": false
},
"com.unidata.mdm.rest.v1.classifiers.multiple": null
}
}
Версия 6.10.2
Standard Edition
Параметр максимального количества выгружаемых записей
Добавлен новый параметр в 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_audit
➔default_default_[audit]
- индекс аудита;default_default_model
➔default_default_[model]
- индекс модели данных;default_default_$model
➔default_default_$[model]
- индекс черновиков модели данных (сейчас не используется);default_default_draft
➔default_default_[draft]
- индекс всех черновиков всех типов (сейчас не имеет доступа ни через FE, ни через REST API BE);default_default_clusters
➔default_default_[clusters]
- индекс кластеров дубликатов;default_default_workflow
➔default_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
.
Enterprise Edition
Настройки таймаута заброшенных подключений
В файл backend.properties добавлены параметры, отвечающие за настройки таймаута заброшенных подключений к БД:
org.unidata.mdm.core.job.datasource.removeAbandonedTimeout
иorg.unidata.mdm.core.job.datasource.suspectTimeout
.
История замещений пользователя
Переименован endpoint
/v1/commercial-core/user-replacement/list
с методом POST на/v1/commercial-core/user-replacement/active
.
Frontend SDK
В компоненте
OptionList
свойствоmaxHeight
помечено как deprecated в пользуDropDown.maxHeight
.
Шифрование паролей подключений LDAP
Класс org.unidata.mdm.core.util.CryptUtils
объявлен deprecated. Необходимо использовать класс org.unidata.mdm.system.utils.CryptUtils
.
Шифрование паролей подключений LDAP переведено на общий механизм платформы. Если шифрование в платформе выключено, подключения LDAP шифроваться не будут.
Старые настройки подключений LDAP после обновления станут неактуальны. Для существующих подключений необходимо заново ввести пароль.
Версия 6.10.1
Enterprise Edition
Замещение пользователей
Существующий эндпоинт
/v1/commercial-core/user-replacement/load
с методом GET помечен как deprecated. Вместо него необходимо использовать новый point/v1/commercial-core/user-replacement/list
с методом POST.Добавлены новые поля в модель Substitution: user и replacedUser.
Добавлен
SearchSubstitutionListOp
вSubstitutionService
для получения списка заместителей для определенного пользователя.Добавлен
SpecificSubstitutionsStore
для обработкиSearchSubstitutionListOp
, так как при использованииSubstitutionsStore
вместе с заместителями подгружается и список всех пользователей, что негативно влияет на скорость загрузки.Добавлены точки расширения:
UserViewTooltipExtraContent
иUserViewContent
. Для них реализованыsubstitutionUserViewTooltip
иsubstitutionUser
соответственно.Добавлен чекбокс для UserView
showExtraData
, который отключает отображение информации о заменяющем даже, если таковой имеется.
Версия 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
(новые проверки для черновиков + сервисная операция с проверками).Обновлены потоки выполнения (добавлены новые сегменты).
Настройка отображения входящих связей в карточке записи
Изменения в REST-сервисе RegisterEntityRestService:
RegisterEntityRO:
Поле
relations
, методыgetRelations
,setRelations
помечены deprecated;Добавлены поля
outgoingRelations
,incomingRelations
.
RelationEntityRO:
Добавлены атрибуты
incomingNameForDisplay
,showIncoming
,showIncomingAttributes
.
Влияет на:
/v2/data/model/register-entities/{id}
/v2/data/model/register-entities/
Изменения в 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}:
Добавлено поле
etalonDisplayNameFrom
(вEtalonRelationToRO
) для имени входящей связи;Если
showIncomingAttributes = false
для входящей связи - аттрибуты связи не возвращаются;Добавлен
query parameter fetchByToSide
для получения информации о входящей связи.
Настройка кластерной конфигурации
На стороне BE для поддержки кластерной конфигурации были внесены следующие изменения:
Параметризован ряд настроек в backend.properties.
Исключены гонки при одновременной инициализации нескольких узлов при помощи ConfigurationActionService, возникающие при параллельных запросах к БД:
Миграции БД;
Сохранение информации о кластере БД;
Инициализация планировщика операций;
Инициализация BPMN движка.
Обратная совместимость не нарушена.
Поток выполнения отправки сообщений в SmartETL
В поток выполнения отправки сообщений в 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 с событием.
Изменено имя параметра эндпоинта слушателя в 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
.
Enterprise Edition
Модуль com.unidata.mdm.rest.v1.classifiers
Добавлен REST эндпоинт:
POST /api/v1/classifiers/model/reindex
.
Отображение иерархии узлов классификатора в карточке записи
Добавлен новый компонент BreadCrumbs в пакете "@universe-platform/uikit", используемый для отображения иерархии узла классификатора в закладке "Классификация".
Расширенный поиск по классификации
В связи с реализацией расширенного поиска по наличию/отсутствию классификации был изменен рендер поискового критерия по атрибутам - отрицание (выбор элемента "НЕ") и тип поиска теперь представляют из себя один селектор. В связи с этим возможны проблемы с поисковым критерием по атрибутам.
Отображение данных классификации в поисковой выдаче
Для поиска классификаций полученных записей предполагается использование REST эндпоинта POST api/v1/classifiers/classification/search
. Точка была доработана для более удобного использования.
Значения атрибутов классификации хранятся в карте внутри документа. Ее необходимо перенести в поисковой ответ, тем самым обогатив отображаемыми значениями и другой необходимой информацией. Теперь атрибуты классификации добавляются в поисковую выдачу так же, как и поля записи.
Атрибуты классификации выдаются в том же порядке, в котором они определены в модели.
Добавлено поле
fieldDisplayName
для получения отображаемого имени атрибута без загрузки модели классификатора. Для однородности поле добавлено в общую поисковую выдачу (записи, связи).Добавлено поле
fieldValueType
для получения типа атрибута без загрузки модели классификатора. Для однородности поле добавлено в общую поисковую выдачу (записи, связи).Расширено API поиска (
v2/search
,v1/classifiers/classification/search
) за счет добавления дополнительных полей, обратная совместимость не нарушена.Выделена компонента для обогащения поисковой выдачи
SearchResultHitModifierCommonComponent
.
Выбор формата импорта ФИО из Active Directory
Добавлен REST эндпоинт
Запрос: GET http://{{server}}:{{port}}/universe-backend/api/v1/ldap/configuration/name-formats
Ответ (displayName локализован):
{"details":{"info":[],"warning":[],"error":[]},
"names":[
{"name":"LAST_FIRST_MIDDLE","displayName":"фамилия / имя / отчество"},
{"name":"LAST_MIDDLE_FIRST","displayName":"фамилия / отчество / имя"},
{"name":"FIRST_LAST_MIDDLE","displayName":"имя / фамилия / отчество"},
{"name":"FIRST_MIDDLE_LAST","displayName":"имя / отчество / фамилия"},
{"name":"FIRST_LAST","displayName":"имя / фамилия"},
{"name":"LAST_FIRST","displayName":"фамилия / имя"},
{"name":"MIDDLE_LAST_FIRST","displayName":"отчество / фамилия / имя"},
{"name":"MIDDLE_FIRST_LAST","displayName":"отчество / имя / фамилия"}]}
Изменен объект для GET/POST/PUT http://{{server}}:{{port}}/unidata-backend/api/v1/ldap/configuration/{{_int}}
В объект domains добавлено поле
adUserNameFormat
типа строка. Значение - одно из списка name-formats
Версия 6.9
Standard Edition
История консолидации записей
Изменения для хранения структуры дерева история консолидации:
В таблицу 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 адресов.
Создайте реализацию AuthenticationAttemptCheckComponent.
Реализуйте
String getExternalComponentName()
- например, пусть возвращает "IP address white list authentication attempt check component".Добавьте туда список разрешенных адресов.
Реализуйте
isEnabled()
, если он не должен быть всегда включен.
Если включается/выключается в рантайме, то необходимо сделать метод, вызывающий
register()/unregister()
.
Реализуйте boolean
isAllowedToLogin(@NonNull AuthenticationRequestContext ctx)
- проверьте, находится ли CLIENT_IP из AuthenticationRequestContext в списке разрешенных адресов.Реализуйте
fail(@NonNull AuthenticationRequestContext ctx, Throwable cause)
- в этом случае можно ничего не делать или написать в лог.Реализуйте
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}
- удаление цепочки операций.
Enterprise Edition
Пакетные операции
В дескрипторы пакетных операций добавлена структура для дополнительных параметров операции additional{}
. Обратная совместимость сохранена.
Добавлены модули:
com.universe.mdm.bulk.export.classification.data.xlsx
- экспорт классификации данных;com.universe.mdm.rest.v1.bulk.export.classifications.xlsx
- параметры экспорта для REST API;com.universe.mdm.bulk.imprt.classification.data.xlsx
- импорт классификации данных.
Пакетная модификация записей: классификация
В тело запроса для операции пакетной модификации добавлена возможность указывать фрагменты (в поле payload, например, аналогично запросу Atomic Get Record). Для классификации указывается фрагмент с именем modify-classifications-request-fragment-v1
.
Добавлен UserExit UEModifyRecords. Он возвращает компонент, принимающий как свойства ModifyRecordsOperationStore, который будет отображен в виджете модификации записи и хранилище, имплементирующее IModifyInnerStore, возвращающее фрагмент запроса на модификацию.
Получение групп пользователя
Добавлен новый запрос GET /v1/commercial-core/users-group/by-user-login/{user}
В качестве ответа приходит список групп, в котором состоит пользователь с указанным логином. В ответе содержится основная информация, без ролей и других учетных записей в группе, без дочерних групп.
Переиндексация данных бизнес-процессов
Добавлены новые классы и методы:
добавлены методы для массовой индексации в WorkflowIndexingComponent
добавлены методы для конвертации Camunda объектов в Workflow[Process|Task|Attachment|Comment]Converter
Права доступа
Добавлены ресурсы:
Группы пользователей / User groups (вкладка Безопасность / Security).
Каталог доступа/ Access directory (вкладка Система /System).
Администрирование модели классификаторов (вкладка Модель данных / Data model);
Классификаторы / Classifiers.
Дубликаты / Duplicates (вкладка Менеджмент записей).
Логаут заменен на возможность подтянуть обновления при обновлении групп пользователей.
Добавлен статус 401 Unauthorized:
В группе пользователей:
Если нет прав на чтение групп пользователей:
GET /v1/commercial-core/users-group/{groupName}/check-users
GET /v1/commercial-core/users-group/{groupName}
GET /v1/commercial-core/users-group/{groupName}/users
Если нет прав на редактирование групп пользователей:
POST /v1/commercial-core/users-group
PUT /v1/commercial-core/users-group/{groupName}
DELETE /v1/commercial-core/users-group/{groupName}
POST /v1/commercial-core/users-group/move
Каталог доступа:
Если нет прав к каталогу доступа:
POST /v1/ldap/configuration/sandbox
GET /v1/ldap/configuration
POST /v1/ldap/configuration
GET /v1/ldap/configuration/{id}
PUT /v1/ldap/configuration/{id}
DELETE /v1/ldap/configuration/{id}
POST /v1/ldap/configuration/preview
POST /v1/ldap/configuration/preview
Остались открытыми (чтобы у пользователя была возможность просмотреть свои группы в карточке):
GET /v1/commercial-core/users-group/by-user-login/{login}
GET /v1/commercial-core/users-group
Классификаторы:
DELETE /v1/classifiers/model/classifier/{name}
- если нет прав на редактирование модели классификаторов или нет прав на редактирование классификатора {name}POST /v1/classifiers/model/classifier
:при создании классификатора - если нет прав на редактирование модели классификаторов или нет прав на редактирование всех классификаторов
при изменении классификатора - если нет прав на редактирование модели классификаторов или нет прав на редактирование классификатора при изменении классификатора
Импорт / экспорт:
GET /v1/classifiers/model/export/{name}
- если нет прав на чтение модели классификаторов или нет прав на чтение классификатора {name}POST /v1/classifiers/model/import
:при импорте нового классификатора - если нет прав на редактирование модели классификаторов или нет прав на редактирование всех классификаторов
при импорте существующего классификатора - если нет прав на редактирование модели классификаторов или нет прав на редактирование классификатора при изменении классификатора
Nodes:
POST /v1/classifiers/model/node/{classifier}/{version}
- если нет прав на редактирование модели классификаторов или нет прав на редактирование классификатора {classifier}DELETE /v1/classifiers/model/node/{classifier}/{version}
- если нет прав на редактирование модели классификаторов или нет прав на редактирование классификатора {classifier}
Versions:
DELETE /v1/classifiers/model/version/{classifier}/{name}
- если нет прав на редактирование модели классификаторов или нет прав на редактирование классификатора {classifier}POST /v1/classifiers/model/version/{classifier}
:при создании версии классификатора - если нет прав на редактирование модели классификаторов или нет прав на редактирование всех классификаторов
при изменении версии классификатора - если нет прав на редактирование модели классификаторов или нет прав на редактирование классификатора при изменении классификатора
Дубликаты
POST /v2/data/merge/apply
POST /v2/data/merge/preview
Для остальных запросов GET возвращается код 200, но данные ответа фильтруются по правам на конкретный классификатор, т. е. если нет прав на чтение классификатора, то его не будет в ответе на следующие запросы:
Classifiers:
GET /v1/classifiers/model/assignments/{namespace}/{type}
GET /v1/classifiers/model/classifier/{name}
GET /v1/classifiers/model/classifier
Nodes:
GET /v1/classifiers/model/node/{classifier}/{version}/{name}
Search/Filtering
GET /v1/classifiers/model/search/{classifier}/{version}
Versions:
GET /v1/classifiers/model/version/{classifier}/{name}
GET /v1/classifiers/model/version/{classifier}
Изменения в модулях
org.unidata.mdm.data
Добавлена операция клонирования записей и связей.
Изменены классы батч-статистики. Теперь вместо счетчиков они содержат контексты, разложенные по по группам (inserted/updated/failed/skipped/etc.). Это нужно для более точной обработки балков например аудитом. Также, аудит индивидуальных контекстов отключен в батч режиме.
Добавлен новый point "org.unidata.mdm.data[RECORD_PREVIEW_ATTRIBUTES_POSTPROCESSING]" в поток выполнения "org.unidata.mdm.data[RECORD_PREVIEW_START]"
Добавлено новое событие аудита RECORD_RESTORE в DataTypes
Добавлена поддержка RestoreRecordRequestContext в сегмент RecordAuditEventExecutor
Добавлена поддержка RestoreRecordRequestContext в сегмент RecordAuditEventExecutor
Добавлена поддержка RestoreRecordRequestContext в сегмент RecordAuditEventFallback
В пайплайн восстановления записи добавлены сегменты POINT org.unidata.mdm.data[RECORD_AUDIT_EVENT] и FALLBACK org.unidata.mdm.data[RECORD_AUDIT_FALLBACK] после сегмента org.unidata.mdm.data[RECORD_RESTORE_PERSISTENCE]
{
"segmentType":"POINT",
"id":"org.unidata.mdm.data[RECORD_RESTORE_PERSISTENCE]"
},
{
"segmentType":"POINT",
"id":"org.unidata.mdm.data[RECORD_AUDIT_EVENT]"
},
{
"segmentType":"FALLBACK",
"id":"org.unidata.mdm.data[RECORD_AUDIT_FALLBACK]"
},
{
"segmentType":"POINT",
"id":"org.unidata.mdm.data[RECORD_RESTORE_REFRESH]"
},
Был изменен формат заголовков листов вложенных объектов в xlsx файлах some_nested_name -> some_complex_attribute_name>some_nested_name (разделитель ">") some_nested_display_name -> some_complex_attribute_display_name > some_nested_display_name (разделитель " > ") По этим заголовкам идентифицируется объект для импорта и экспорта комплексных атрибутов. Чтобы импортировать старые файлы, необходимо соответственно поменять заголовки на листах вложенных объектов или перенести данные в новый шаблон, который можно скачать на UI при импорте.
org.unidata.mdm.matching.data
Добавлена фракция пакетной операции сопоставления, отвечающая за обновление таблиц сопоставления.
Добавлена фракция пакетной операции консолидации, отвечающая за консолидацию записей.
org.unidata.mdm.matching.storage.postgres
Добавлена поддержка примитивного нечеткого матчинга с использованием tsquery/tsvector + триграмм.
org.unidata.mdm.rest.v2.bulk.core
Добавлен параметр 'owner' в operationOptions дескрипторов bulk-operations.
"descriptors": [
{
"name": "exportActiveVersionClassifications",
"displayName": "Активная версия",
"description": "Активная версия",
"type": "BOOLEAN",
"kind": "SINGLE",
"required": false,
"hidden": false,
"readOnly": false,
"defaultValue": false,
"owner": {
"name": "exportClassifications",
"value": true
},
"selector": null
}
]
}
Добавлен параметр 'owner' в operationOptions дескрипторов bulk-operations.
org.unidata.mdm.rest.v2.data
Добавлена атомик операция клонирования записей и связей и т. п.
org.universe.mdm.job.duplicate
Добавлена пакетная операция консолидации.
org.universe.mdm.job.matching
Добавлена пакетная операция сопоставления.