Повторно используемые блоки
Как вынести общую логику в блок, передавать ему данные и выбирать версию, которую использует реакция.
На этой странице
В нескольких реакциях может повторяться одна и та же работа: подготовить строки заказа, проверить условия заявки или рассчитать итог. Её можно вынести в блок с понятными входами и выходами, а затем вызывать из разных реакций.
Входы описывают, какие данные нужны блоку. Выходы описывают, что он возвращает. Временные значения внутри блока остаются внутри него, поэтому одинаковое имя prepared в двух блоках не смешивает их результаты.
Когда использовать блок
| Задача | Что выбрать |
|---|---|
| Одна и та же обработка нужна в нескольких местах | Блок с входами и выходами |
| Несколько изменений записей должны сохраниться или откатиться вместе | Атомарная транзакция коллекций |
| Нужно перейти к другому шагу разговора | Переход или вызов реакции |
| Нужно вернуть прежние настройки одной реакции | История изменений |
Версии блока принадлежат библиотеке и не заменяют историю реакции. Возврат к старой версии блока также не отменяет уже отправленные сообщения или изменения записей.
Сам блок выполняет действия последовательно и не превращает их в одну транзакцию. Если поздний шаг завершится ошибкой, прежние успешные изменения автоматически не откатываются.
Создать блок в редакторе
- В разделе реакций проекта нажмите Библиотека блоков. На узком экране эта команда находится среди дополнительных действий.
- Нажмите Создать блок и укажите понятное название, например «Подготовить строки заказа».
- В разделе Входные данные добавьте нужные входы: имя, тип, обязательность и при необходимости значение по умолчанию. В разделе Выходные данные добавьте имена, типы и значения, которые должен вернуть блок. Действия между ними задаются обычными карточками конструктора.
- Нажмите Сохранить новую версию. При первом сохранении появится версия
1. Следующая сохранённая правка создаст новый номер, сохранив прежнее содержимое в разделе Сохранённые версии.
Название блока может содержать пробелы и русские буквы. Для имён входов и выходов нужны латинские буквы, цифры и _, причём первый символ должен быть буквой. Например, orders и total_amount подходят. Эти имена используются в данных и подстановках, поэтому выбирайте их отдельно от названия блока.
Если существующий блок содержит действие, которое ещё нельзя редактировать в веб-конструкторе, библиотека сохранит его содержимое и покажет ограничение. Такой блок по-прежнему можно вызвать с его объявленными входами и выходами. Для создания нового блока доступны обычные формы.
Пример: подготовить строки заказа
Допустим, разные реакции получают заказы в одном формате:
[
{"product":"Кисть","price":150,"quantity":2},
{"product":"Бумага","price":300,"quantity":1}
]
Для каждой строки нужно рассчитать amount = price × quantity. Сделаем блок Подготовить строки заказа.
1. Объявить вход
Добавьте вход orders типа Массив и сделайте его обязательным. Тип проверяется при вызове: текст, который не удаётся разобрать как массив, не будет принят вместо списка.
Внутри блока этот вход доступен как {{ctx.input.orders}}. Для действия, которое читает список по имени, используйте точный ключ input.orders.
Для результата поиска или другого действия обработки списка выберите вход типа Объект и передавайте весь результат, например {{ctx.search}}. Источник внутри блока остаётся input.orders. Так вместе со строками сохраняются признаки полноты и ограничения выборки. Тип Массив в этом примере подходит для исходного массива из двух строк, показанного выше.
2. Добавить обработку
Добавьте Преобразовать поля списка:
- Источник:
input.orders. - Новое поле:
amount. - Операция: умножить
priceиquantity. - Сохранить остальные поля: включено.
- Сохранить результат:
prepared.
Параметры этого действия:
{
"source":"input.orders",
"fields":[{"as":"amount","op":"multiply","paths":["price","quantity"]}],
"keep_fields":true,
"save_to_ctx":"prepared"
}
Урок по преобразованию полей объясняет остальные операции и обработку отсутствующих значений.
3. Объявить выход
Добавьте выход result типа Объект со значением {{ctx.prepared}}. Возвращайте весь результат обработки, чтобы сохранить количество строк и признак полноты вместе со списком.
{"name":"result","type":"object","value":"{{ctx.prepared}}"}
Ожидаются две строки: у кисти amount=300, у бумаги amount=300. Исходные поля product, price и quantity сохраняются.
4. Вызвать блок из реакции
В реакции, где исходный массив уже сохранён как orders, добавьте Вызвать блок. Выберите блок и его версию, передайте в аргумент orders значение {{ctx.orders}}, сохраните результат как calculation.
Технический формат вызова, где block_id нужно заменить ID своего блока:
{
"block_id":"00000000-0000-4000-8000-000000000001",
"version":1,
"args":{"orders":"{{ctx.orders}}"},
"save_to_ctx":"calculation"
}
Объявленный выход появится в calculation.result. Следующее действие обработки списка должно читать источник calculation.result, чтобы получить весь результат, а не только массив calculation.result.rows.
Для суммы первой строки в этом примере используйте {{ctx.calculation.result.rows.0.amount}}: 0 означает первую строку массива. Перед обращением к ней проверьте, что список не пуст. Автоматического сокращения calculation.result.first у этого объекта нет; пути зависят от содержимого объявленного выхода.
Когда блок возвращает результат
Выходы возвращаются после нормального завершения всех действий блока. Если внутреннее действие выбрало переход по исходу или остановило цепочку, это завершение передаётся наружу. Блок не публикует выходы, а вызывающая реакция не продолжает оставшиеся действия и не выбирает общий success.
Действия branch_value и branch_result внутри блока тоже завершают вызов после выбранной ветки. В обычной реакции вне блока после нормального выполнения ветки родительские действия продолжаются. Если после ветки нужен общий расчёт, учитывайте это различие при переносе действий в блок.
У условного изменения записи внутри блока есть дополнительная защита: conflict и not_found останавливают блок даже без собственных маршрутов этого действия. Результат updated без маршрутов позволяет продолжить расчёт. В обычной реакции вне блока прежняя цепочка «изменить → проверить статус» остаётся доступной. Для своего ответа на отказ настройте исходы внутреннего действия.
Техническую ошибку, возникшую до выбора перехода, можно обработать исходом error у действия Вызвать блок. Например, если проверка ветки не смогла прочитать нужные данные, вызов перейдёт в этот обработчик. Если внутреннее действие уже выбрало другую реакцию, ошибка в той реакции не запускает дополнительно error вызывающего блока. Так один отказ не приводит к двум разным ответам пользователю. Уже выполненные действия при обработке ошибки автоматически не откатываются.
Типы, обязательные поля и значения по умолчанию
Поддерживаются текст, число, логическое значение, объект и массив. Неизвестные аргументы и отсутствующие обязательные входы вызывают ошибку. Для необязательного входа можно указать значение по умолчанию; оно должно соответствовать объявленному типу.
Например, числовое значение по умолчанию записывается как 3, а не строка "3". По умолчанию передаётся буквальное значение, без выполнения подстановок. Обязательный вход тоже может иметь значение по умолчанию, которое используется, если аргумент не передан.
В аргументах подстановки выполняются до преобразования к нужному типу. Поэтому строка "{{ctx.quantity}}" может стать числом, а строка с JSON – массивом или объектом. Неверное число не превращается в ноль. Денежные значения и большие числовые ID сохраняют точность.
В действии Вызвать блок необязательный аргумент передаётся только после включения соответствующего флажка Передать аргумент. Чтобы использовать объявленное значение по умолчанию, оставьте этот флажок выключенным. Показанное значение само не записывается в аргументы; явный null передаётся отдельно и проверяется по типу.
Если после изменения входов в старом вызове остался лишний аргумент, редактор покажет его и предложит явное удаление. Проверьте также новые обязательные входы и изменённые типы. Несохранённые значения не очищаются автоматически при обновлении сведений о блоке.
Как выбрать версию
Каждая сохранённая правка создаёт новую версию. Содержимое прежних версий не изменяется.
В действии Вызвать блок включите Закрепить версию и укажите номер в поле Версия. Чтобы новые вызовы выбирали актуальный вариант, снимите этот флажок.
- Конкретная версия, например
1: реакция продолжит использовать её после публикации новых версий. Это удобно для проверенных рабочих цепочек. - Последняя версия,
version=0или отсутствие номера: новый вызов берёт актуальную версию. Это удобно, когда все потребители должны получать обновления вместе.
При повторе той же сохранённой фоновой задачи после сбоя используется выбранная для неё версия блока. Публикация новой версии самого блока не меняет этот выбор. Вычисленные входы при таком повторе должны совпадать: если они изменились, вызов завершится ошибкой до новых действий, чтобы оставшаяся часть не использовала другие данные.
Версия фиксирует содержимое блока. Она не создаёт копии вызываемых реакций и прав доступа: они проверяются по актуальному состоянию. Если вложенный вызов выбирает последнюю версию другого блока, тот тоже выбирает её в момент своего запуска. Для закрепления вложенного расчёта укажите конкретный номер и в нём.
Перед обновлением блока проверьте, какие реакции его используют, какие закрепили конкретный номер и какие выбирают последнюю версию. Сначала проверьте новую версию на учебных данных, затем переводите на неё потребителей.
Например, в первой версии входом был только orders, а во второй вы добавляете обязательный discount. Вызов последней версии, который передаёт только orders, получит ошибку отсутствующего входа. Если для discount подходит значение 0, задайте его по умолчанию: существующие вызовы смогут работать, пока вы добавляете аргумент в нужные реакции. Вызовы с закреплённой версией 1 продолжат использовать её прежние входы и действия. Изменение имён и типов выходов тоже проверяйте в реакциях, которые читают результат.
Одновременные правки и удаление
Если другой пользователь сохранил блок, пока вы его редактировали, сохранение сообщит о конфликте версий. Локальные правки останутся в форме. Выберите дальнейшее действие:
- Проверить актуальную версию обновляет её номер, сохраняя ваш черновик. Эта кнопка проверяет сведения о версии; её содержимое не показывается для сравнения.
- Сохранить мой черновик новой версией после этой проверки создаёт новую версию целиком из ваших правок. Сохранённый вариант другого автора остаётся в истории. Если за это время появится ещё одна версия, вы снова увидите конфликт.
- Отбросить черновик и загрузить актуальную версию заменяет несохранённую форму последним сохранённым содержимым.
В разделе Использование и возможность удаления проверьте внешние ссылки и активные закреплённые вызовы. Счётчики учитывают потребителей и на ещё не загруженных страницах. Удалить блок доступно при разрешённом удалении и требует явного подтверждения.
В списке потребителей видны реакции и версии блоков, которые вызывают этот блок. Учитываются и прежние версии других блоков, чтобы закреплённый вызов продолжал работать. Такие ссылки мешают удалению. Незавершённые сохранённые фоновые вызовы тоже временно защищают используемый блок.
Ссылка новой версии на раннюю версию того же блока не считается внешним потребителем: при удалении вся его история версий исчезает вместе. Если список использования изменился после открытия страницы, сервер повторно проверит ссылки при удалении и сообщит об отказе.
Что проверить перед использованием
- Передайте обычный заказ и сравните все рассчитанные строки.
- Пропустите обязательный вход: вызов должен сообщить об ошибке.
- Передайте неверный тип, например текст вместо массива.
- Вызовите блок дважды с разными данными и убедитесь, что временные значения не смешались.
- Опубликуйте новую версию и сравните закреплённый вызов с вызовом последней версии.
Блок выполняется в пределах обычных лимитов реакции. Он не предназначен для ожидания ответа пользователя или отложенного продолжения: такие шаги оставляйте в самой реакции. Вложенные вызовы ограничены глубиной, объёмом данных и количеством действий; рекурсивная цепочка не может выполняться бесконечно.
Внутри блока пока недоступны создание оплаты, создание, отмена и изменение подписки, встроенные действия GetCourse, Google Таблиц и Google Диска. Это ограничение действует и для реакций, к которым блок переходит: неподдерживаемый шаг отклоняется до выполнения. В обычной реакции этими действиями по-прежнему можно пользоваться. Связанные ограничения исходов.
Ограничения данных
| Что ограничено | Предел |
|---|---|
| Объявленные входы и выходы | До 256 входов и 256 выходов |
| Сохранённое тело одной версии | 1 МиБ вместе со служебными полями |
| Значения и весь набор входов или выходов | 16 МиБ |
| Элементы одного массива или поля одного объекта | 10 000 |
| Вложенность значения | 64 уровня |
| Сложность проверки JSON | 1 000 000 токенов разбора: значений, ключей и скобок |
| Данные, доступные через ключи контекста внутри дерева вызовов | 8 МиБ суммарно, включая имена ключей и значения |
| Ключи контекста, созданные при передаче входов и выходов | 65 536 на дерево вызовов |
| Работа по подготовке этих ключей и значений | 16 МиБ просматриваемых данных |
| Общая глубина вызовов вместе с реакциями | До 16 уровней |
| Действия на одно событие, включая блоки | 5000 |
| Отправки на одно событие, включая блоки | 50 |
| Обработка одного события | До 5 минут |
Бюджет разбора общий для значений по умолчанию, выходов и параметров действий одной версии, а также для набора выбранных входов. Отдельно разбираемый объект или массив, полученный из подстановки, проходит такую же проверку. При превышении действие завершается ошибкой, данные не обрезаются молча.
Лимит значения в 16 МиБ не означает, что любой объект такого размера можно передать блоку. Чтобы работали обращения вроде input.orders.0.price, конструктор подготавливает и целое значение, и вложенные ключи. У глубоко вложенного объекта одни и те же данные могут входить в несколько таких значений. Поэтому для этой подготовки действует отдельный общий бюджет 8 МиБ. Вложенный вызов не получает его заново. Передавайте только нужные поля с помощью преобразования списка, если большой объект не проходит проверку.
Входы проверяются до выполнения действий блока. Выходы подготавливаются полностью до замены результата у вызывающей реакции: при превышении лимита частичный новый результат не публикуется. Ранее выполненные действия блока при этом не откатываются.
Отдельно проверяется совпадение ключей контекста. Например, у объекта {"a.b":"первое","a":{"b":"второе"}} разные поля превратились бы в один ключ input.payload.a.b. Вызов отклоняется, чтобы одно значение не подменило другое. Для строк списка заранее выберите нужные поля через явные пути /a.b и /a/b, задайте им разные имена и отключите сохранение остальных полей в преобразовании списка. Подготовленный список можно передать блоку без неоднозначных имён.
Вызов блока не начинает отсчёт лимитов заново. Предварительная оценка отправок консервативно учитывает доступные ветви, поэтому сложный блок с множеством альтернатив может быть отклонён ещё до фактической отправки. Для массовых уведомлений используйте рассылки.
Размер тела проверяется и до записи, и после нормализации PostgreSQL. Например, короткая числовая запись 1e256 при сохранении разворачивается в длинное десятичное число. Если из-за этого версия превышает предел, сохранение целиком откатывается и предыдущая версия остаётся актуальной. Для больших справочников передавайте данные из коллекций при вызове, вместо хранения всего массива в значении по умолчанию.
Связанные материалы: обработка списков, поля и объединение, история изменений.