Слой приложений Конструктор микрофронтов¶
Вопрос: Для каких задач необходим Конструктор микрофронтов?
Ответ: Конструктор микрофронтов – это low-code модуль для быстрой разработки пользовательского веб-интерфейса прикладных приложений. Он позволяет создавать специализированные интерфейсы без необходимости привлечения разработчиков. Позволяет настраивать поля, строки, столбцы и элементы управления (кнопки) индивидуально для каждого приложения.
Вопрос: На каком языке пишутся регулярные выражения для формул валидации и какие символы используются?
Ответ: Формулы валидации в системе пишутся на стандартном языке RegExp (Regular Expressions).
Для указания регулярного выражения необходимо использовать специальные символы ^, $, * , +, ?, {}, [], () и другие.
Основные правила:
- ^ – начало строки;
- $ – конец строки;
- [abc] – один из символов a, b или c;
- [0-9] – любая цифра (можно писать \d);
- [A-Za-z] – любая латинская буква;
- {n,m} – от n до m повторений (например, {3,5});
- + – 1 или больше повторений;
- – 0 или больше повторений;
- ? – 0 или 1 повторение;
- | – логическое «или» (например, jpg|png).
Примеры регулярных выражений:
- Телефон: (+7XXXXXXXXXX): ^+7\d{10}$;
- Email: ^[a-z0-9._%+-]+@[a-z0-9.-]+.[a-z]{2,}$;
- Пароль (8+ символов, буквы и цифры): ^(?=.[A-Za-z])(?=.\d).{8,}$.
Вопрос: На чем нужно акцентировать внимание при создании форм, конвейеров (пайплайнов) и коллекций в Конструкторе микрофронтов?
Ответ: Детали проектирования объектов и системных полей описаны в документации: Проектирование объектов и системные поля, доступные через #CONTEXT
При работе с пайплайнами для корректной работы шага «Показать форму сравнения файлов» нужно, чтобы у каждого из сравниваемых документов присутствовал «mimeTуре». Атрибут «mimeType» передается в контекст пайплайна внутри контрола FILE вместе с «fileld». Его нужно передавать при создании документа.
Вопрос: Для чего используется «Transformer» внутри конвектора данных при создании динамических приложений в Конструкторе микрофронтов? Для чего используется Handler?
Ответ: «Transformer» используется для настройки маппинга и преобразования значений, полученных из данных от бэкенда (формирующего источник данных в Конструкторе) и отображении для пользователя этих данных (например, в форме или коллекции создаваемого приложения).
Необходимо создать узлы «nodes», указав в каких ключах находится идентификатор, имя поля и тип узла, затем указать в «links» между какими узлами нужно выстроить связь.
Пример:
{
"nodes": [
{
"id":"i1",
"name":"preferredUsername",
"type":"DataInput"
},
{
"id":"o1",
"name":"preferredUsername",
"type":"DataOutput"
},
],
"links": {
"i1":"o1",
},
"ui": {
"order": {}
}
}
Handler используется для сложных преобразований данных при маппинге.
Пример:
{
"nodes": [
{
"id":"h5",
"name":"result_update",
"code":"''+#givenName+''+' '+''+#familyName+''",
"type":"DataHandler"
},
{
"id":"o5",
"name":"result",
"type":"DataOutput"
}
],
"links": {
"i5":"h5",
"h5":"o5"
},
"ui": {
"order": {}
}
}
Пример формирования динамического поля как значение двух системных полей, записанных через пробел:
{
"nodes": [
{
"id":"i1",
"name":"preferredUsername",
"type":"DataInput"
},
{
"id":"o1",
"name":"preferredUsername",
"type":"DataOutput"
},
{
"id":"i2",
"name":"givenName",
"type":"DataInput"
},
{
"id":"o2",
"name":"givenName",
"type":"DataOutput"
},
{
"id":"i3",
"name":"familyName",
"type":"DataInput"
},
{
"id":"o3",
"name":"familyName",
"type":"DataOutput"
},
{
"id":"i4",
"name":"email",
"type":"DataInput"
},
{
"id":"o4",
"name":"email",
"type":"DataOutput"
},
{
"id":"i5",
"name":"result",
"type":"DataInput"
},
{
"id":"h5",
"name":"result_update",
"code":"''+#givenName+''+' '+''+#familyName+''+' '+'('+#preferredUsername+')'",
"type":"DataHandler"
},
{
"id":"o5",
"name":"result",
"type":"DataOutput"
},
{
"id":"i6",
"name":"creationDate",
"type":"DataInput"
},
{
"id":"o6",
"name":"creationDate",
"type":"DataOutput"
}
],
"links": {
"i1":["o1","h5"],
"i2":["o2","h5"],
"i3":["o3","h5"],
"i4":"o4",
"i5":"h5",
"h5":"o5",
"i6":"o6"
},
"ui": {
"order": {}
}
}
Пример трансформера, который извлекает различные данные из переменной, в которой хранится объект (поступает из источника данных с типом PIPELINE):
{
"nodes": [
{ "id": "i1", "name": "id", "type": "DataInput" },
{ "id": "o1", "name": "id", "type": "DataOutput" },
{ "id": "i2", "name": "versionId", "type": "DataInput" },
{ "id": "o2", "name": "versionId", "type": "DataOutput" },
{ "id": "i3", "name": "documentMain", "type": "DataInput" },
{ "id": "o3", "name": "documentMain", "type": "DataOutput" },
{
"id": "h4",
"name": "name_extracted",
"code": "#documentMain['name']",
"type": "DataHandler"
},
{ "id": "o4", "name": "name", "type": "DataOutput" },
{
"id": "h5",
"name": "className_extracted",
"code": "#documentMain['className']",
"type": "DataHandler"
},
{ "id": "o5", "name": "className", "type": "DataOutput" },
{
"id": "h6",
"name": "creationDate_extracted",
"code": "#documentMain['creationDate']",
"type": "DataHandler"
},
{ "id": "o6", "name": "creationDate", "type": "DataOutput" },
{
"id": "h7",
"name": "modificationDate_extracted",
"code": "#documentMain['modificationDate']",
"type": "DataHandler"
},
{ "id": "o7", "name": "modificationDate", "type": "DataOutput" },
{
"id": "h8",
"name": "recordVersion_extracted",
"code": "#documentMain['recordVersion']",
"type": "DataHandler"
},
{ "id": "o8", "name": "recordVersion", "type": "DataOutput" },
{ "id": "i9", "name": "documentsIncludedFileIds", "type": "DataInput" },
{ "id": "o9", "name": "documentsIncludedFileIds", "type": "DataOutput" }
],
"links": {
"i1": "o1",
"i2": "o2",
"i3": ["o3", "h4", "h5", "h6", "h7", "h8"],
"h4": "o4",
"h5": "o5",
"h6": "o6",
"h7": "o7",
"h8": "o8",
"i9": "o9"
},
"ui": { "order": {} }
}
Вопрос: К каким системным полям можно обращаться через контекст в конвейере (пайплайне), в формулах видимости, блокировки или обязательности?
Ответ: Данные поля описаны в документации: Проектирование объектов и системные поля, доступные через #CONTEXT
Вопрос: Почему не удается выбрать класс объекта при создании источника данных в Конструкторе микрофронтов для нового приложения? При этом классы созданы и прописаны в конфигурации.
Ответ: Необходимо заполнить прокси Конструктора микрофронтов. Сервис «dh-facade-service» обращается к другим сервисам и получает список классов по прокси. Если при проксировании не был прописан правильный путь, то список классов не будет получен источником данных.
Вопрос: В динамическом приложении возникает ошибка «Не найден класс» (404) при выполнении запроса. Каковы возможные способы решения?
Ответ: Для диагностики и устранения проблемы рекомендуется:
- Проверить источник данных.
- Проверить настройку прокси.
- Проверить существование класса в конфигурациях.
- Проверить корректность запроса, воспользовавшись функциональной кнопкой «Send» в интерфейсе.
- Повторно сохранить настройку в сервисе конфигураций, в котором находится данный класс.
Вопрос: Почему в созданном динамическом приложении отсутствуют стандартные кнопки из статических сервисов?
Ответ: Это ожидаемое поведение. Весь функционал, включая кнопки, элементы управления и таблицы, в динамических приложениях создается вручную.
Для добавления кнопки необходимо:
- Перейти в конструктор созданного приложения.
- Создать новое действие.
- Выбрать иконку для кнопки.
- Назначить действие, которое будет выполняться при нажатии кнопки (например, вызов API, навигация).
- После создания элемента публикуется разрешение (пермиссия). Необходимо в сервисе прав доступа выдать данное разрешение на конкретную роль. После выполнения этих настроек кнопка появится в интерфейсе приложения.
Вопрос: Что такое FreeMarker и для чего он используется?
Ответ: FreeMarker – это механизм шаблонов, позволяющий редактировать полученные JSON-ответы. В cистеме FreeMarker используется для сборки результатов нужной структуры и формата по данным контекста, позволяя менять значения, добавлять или удалять поля и возвращать готовый результат в заданном типе.
В Системе для FreeMarker предусмотрены две дополнительные настройки:
1. Тип выводимого значения (STRING / INT / FLOAT / BOOLEAN / OBJECT) – указывает бэкенду, как интерпретировать полученный текст:
- STRING – сохранить как строку;
- INT – преобразовать в целое число (если возможно);
- FLOAT – преобразовать в число с плавающей точкой (если возможно);
- BOOLEAN – преобразовать в логическое значение (если возможно);
- OBJECT – преобразовать текст как JSON и вернуть объект/массив (если возможно).
2. Преобразование пустых строк (доступна только опция BLANK_TO_NULL) – если итоговый вывод пустой, он заменяется на значение «null».
Примеры использования Freemarker описаны в документации для обновления контекста: Обновление контекста
Обновление контекста, а также массовое редактирование описано в документации: Массовое редактирование
Примеры использования:
Необходимо привести преобразование сертификата цифровой подписи в определенный формат. Из информации, полученной о сертификате в формате JSON, нужно объединить переменные из значения «value»:
"issuerName": [
{
"isRequired": false,
"order": 0,
"oid": "2.5.4.3",
"name": null,
"value": "Тестовый УЦ для стенда DSS 2012",
"stringIdentifier": "CN"
},
{
"isRequired": false,
"order": 0,
"oid": "2.5.4.10",
"name": null,
"value": "ООО \"КРИПТО-ПРО\"",
"stringIdentifier": "O"
}, ....
Структуру информации, уже созданной ЭП, необходимо очистить от кавычек и таких конструкций как «CN=», «O=»:
"issuerName": "CN=Тестовый УЦ для стенда DSS 2012, O=\"ООО \"\"КРИПТО-ПРО\"\"\", L=Москва, C=RU, INN=007717107991, OGRN=1037700085444, E=info@cryptopro.ru",
Для этого можно воспользоваться таким преобразованием:
<#assign issuerName = CONTEXT.issuerName>
<#-- Разбиение на части по запятым и обработка каждой части -->
<#assign parts = issuerName?split(",")>
<#assign resultParts = []>
<#list parts as part>
<#assign cleanedPart = part?trim>
<#if cleanedPart?starts_with("CN=")>
<#assign resultParts = resultParts + [cleanedPart?replace("CN=", "")]>
<#elseif cleanedPart?starts_with("O=")>
<#assign orgPart = cleanedPart?replace("O=", "")?replace('\"', '')?replace('\"\"', '"')>
<#assign resultParts = resultParts + [orgPart]>
<#elseif cleanedPart?starts_with("L=")>
<#assign resultParts = resultParts + [cleanedPart?replace("L=", "")]>
<#elseif cleanedPart?starts_with("C=")>
<#assign resultParts = resultParts + [cleanedPart?replace("C=", "")]>
<#elseif cleanedPart?starts_with("INN=")>
<#assign resultParts = resultParts + [cleanedPart?replace("INN=", "")]>
<#elseif cleanedPart?starts_with("OGRN=")>
<#assign resultParts = resultParts + [cleanedPart?replace("OGRN=", "")]>
<#elseif cleanedPart?starts_with("E=")>
<#assign resultParts = resultParts + [cleanedPart?replace("E=", "")]>
</#if>
</#list>
<#-- Сборка в общую строку -->
${resultParts?join(", ")}