Рендеринг. Работа с потоком выполнения через REST

Создание и изменение данных происходят в системе атомарно. Подробнее о потоках выполнения можно прочитать в статье о потоках выполнения.

Запрос на сервер

Для работы через рест используется POST /universe-backend/api/v2/data/atomic

Структура запроса содержит блок payload с коллекцией идентификаторов модулей, которые обработают запрос

{
    "payload": {
        "org.unidata.mdm.rest.v2.data": {..},
        "MODILE_ID": {...},
    }
}

Рассмотрим на примере реестра ree с одним атрибутом id, связью link на себя и назначенным классификатором clsf. При работе в черновике записи, если заполнена запись и запись классифицирована, на сервер будет уходить запрос с двумя MODULE_ID.

{
    "payload": {
        "org.unidata.mdm.rest.v2.data": {},
        "com.unidata.mdm.rest.v1.classifiers": {}
}

Значению ключа org.unidata.mdm.rest.v2.data соответствует объект org.unidata.mdm.rest.v2.data.ro.records.UpsertRequestRO.

Обработка запроса на сервере

Рассмотрим, что происходит в процессе обработки атомик-запроса.

RenderingFragment

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

Поле

Обязательность

Описание

id

да

идентификатор фрагмента

name

да

отображаемое имя фрагмента

description

да

отображаемое описание фрагмента

InputType

да

какой класс обрабатывает фрагмент

OutputType

да

возвращаемый класс

moduleId

нет

идентификатор модуля

ExecuteEarly

нет

задание приоритета выполнения, true - максимальный

ExecuteOnNoInput

нет

выполнение запроса, даже если он пустой

RenderByModuleId

нет

обработчик контекста будет использовать поле moduleId вместо id

Пример конфигурации фрагмента. Фрагмент обрабатывает UpsertRequestRO.class, результат обработки будет типа UpsertRecordResultRO. Фрагмент соответствует модулю DataRestModule и будет выполнен с максимальным приоритетом.

new RenderingFragment("fragment_id")
        .withDisplayName(() -> TextUtils.getText("fragment_name"))
        .withDescription(() -> TextUtils.getText("fragment_description"))
        .withModuleId(DataRestModule.MODULE_ID)
        .withExecuteEarly(true)
        .withRenderByModuleId(true)
        .withInputType(UpsertRequestRO.class)
        .withOutputType(UpsertRecordResultRO.class);

org.unidata.mdm.system.type.rendering.RenderingType

RenderingType задает контракт для коллекции фрагментов, которые должны быть обработаны, способ преобразования и класс результата. Допустимо использовать пустые входные тип NullInputCollector, NullOutputContainer.

RenderingType<>(RECORD_ATOMIC_UPSERT_TYPE, UpsertRequestContextBuilder.class, UpsertRecordDTO.class)
        .withDisplayName(() -> TextUtils.getText("_name"))
        .withDescription(() -> TextUtils.getText("_description"))
        .withFragments(DataRestRenderingFragments.RECORD_UPSERT_FRAGMENT);

RenderingExtension

RenderingExtension - группировка RenderingType, которая используется в аннотации и позволяет вызывать типизировать рендеринг.

@RenderingRef(DataRestFunctionalExtensions.DATA_RENDERING_V2_NAME)
RenderingInstance dataRenderingInstance;
RenderingExtension DATA_V2_RENDERING =
        new RenderingExtension(DATA_RENDERING_V2_NAME)
            .withDisplayName(() -> TextUtils.getText(DataRestModule.MODULE_ID + ".rendering.extension.name"))
            .withDescription(() -> TextUtils.getText(DataRestModule.MODULE_ID + ".rendering.extension.description"))
            .withRenderingTypes(DataRestRenderingTypes.values())

Регистрация в методе Module:

В интерфейсе ``org.unidata.mdm.system.type.module.Module`` есть метод getFunctionalExtensions()

@Override
public FunctionalExtension[] getFunctionalExtensions() {
    return new FunctionalExtension[] { DataRestFunctionalExtensions.DATA_V2_RENDERING };
}

После старта модуля(реализация AfterModuleStartup) в renderingInstance добавляется метод рендеринга:

renderingInstance.inputAdd(
       DataRestRenderingTypes.RECORD_ATOMIC_UPSERT,
       DataRestRenderingFragments.RECORD_UPSERT_FRAGMENT, this::renderUpsertInput);



   var rcb = RenderingContext.builder(DataRestRenderingTypes.RECORD_ATOMIC_UPSERT)
           .collectorSupplier(UpsertRequestContext::builder)
           .executionFunction(b -> dataRecordsService.upsertRecord(b.build()));

   return renderRequest(renderingInstance, DataRestRenderingTypes.RECORD_ATOMIC_UPSERT, rcb, req, new AtomicDataUpsertResultRO());

RestRenderingSupport

Пример использования:

    var rcb = RenderingContext.builder(DataRestRenderingTypes.RECORD_ATOMIC_UPSERT)
        .collectorSupplier(UpsertRequestContext::builder)
        .executionFunction(b -> dataRecordsService.upsertRecord(b.build()));

return renderRequest(renderingInstance, DataRestRenderingTypes.RECORD_ATOMIC_UPSERT, rcb, req, new AtomicDataUpsertResultRO());

где метод renderRequest предоставляется RestRenderingSupport и преобразует объект из rest запроса во внутренние контексты, выполняет соответствующий контекст потока выполнения и преобразует результат потока выполнения в заданный объект new AtomicDataUpsertResultRO().