Реализация CloudEvents 1.0 для OneScript - вендор-нейтрального описания события: набор контекстных атрибутов плюс полезная нагрузка.
CloudEvents убирает необходимость договариваться о форме события с каждым получателем отдельно: маршрутизаторы, брокеры и системы трассировки читают одни и те же атрибуты, не разбирая полезную нагрузку. Пакет реализует JSON Event Format и привязку к HTTP в двоичном и структурном режимах.
id source specversion type subject time datacontenttype dataschema
|-------------------------------| |----------------------------------------|
обязательные атрибуты опциональные атрибуты
opm install cloudevents
#Использовать cloudevents
Событие = CloudEvents.Новое("com.example.заказ.создан", "/склад/1")
.Тема("заказ-42")
.ТипДанных("application/json")
.Данные(Новый Структура("Номер,Сумма", 42, 1500));
Сообщить(Событие.ПолучитьИд()); // 01KZ6YEDDC1A5N6SJFE7411R7T
Сообщить(Событие.ПолучитьВремя()); // 2026-08-04T17:52:31.628ZАтрибут specversion всегда равен 1.0. Если id не задан, он заполняется идентификатором ULID - он уникален и сортируется по времени создания. Если не задано time, подставляется текущий момент UTC.
Все методы построения возвращают само событие, поэтому вызовы выстраиваются в цепочку. Неопределено убирает атрибут:
Событие.Тема(Неопределено); // subject больше не передаётсяЕсли Не Событие.Корректно() Тогда
...
КонецЕсли;
// Перечисляет все нарушения сразу:
// CloudEvents: событие не соответствует спецификации 1.0: не задан обязательный
// атрибут source; не задан обязательный атрибут type
Событие.Проверить();Значения проверяются сразу при установке, поэтому собрать событие с некорректным атрибутом не получится:
Событие.Ид(""); // id не может быть пустым
Событие.Время("04.08.2026 17:52"); // не RFC 3339
Событие.ТипДанных("application"); // не RFC 2046
Событие.СхемаДанных("/schemas/order.json"); // dataschema требует абсолютного URI
Событие.Расширение("TraceId", "abc"); // имя не в нижнем регистреСобытие.Расширение("traceid", "0af7651916cd43dd8448eb211c80319c")
.Расширение("retries", 5)
.Расширение("urgent", Истина);
Сообщить(Событие.ПолучитьРасширение("retries")); // 5
Сообщить(Событие.Расширения().Количество()); // 3Имена состоят из строчных латинских букв и цифр, начинаются с буквы и не совпадают с основными атрибутами. Значения приводятся к типам спецификации: Дата становится меткой RFC 3339, двоичные данные - строкой Base64.
Текст = CloudEvents.ВJson(Событие);
// {"specversion":"1.0","id":"01KZ6YEDDC1A5N6SJFE7411R7T","source":"/склад/1",
// "type":"com.example.заказ.создан","datacontenttype":"application/json",
// "subject":"заказ-42","time":"2026-08-04T17:52:31.628Z",
// "traceid":"0af7651916cd43dd8448eb211c80319c","retries":5,"urgent":true,
// "data":{"Номер":42,"Сумма":1500}}
Сообщить(CloudEvents.ВJson(Событие, Истина)); // с отступами
Полученное = CloudEvents.ИзJson(Текст);Разбор строгий: чужая версия спецификации, отсутствие обязательного атрибута, значение недопустимого типа, повторяющийся атрибут, одновременные data и data_base64, мусор вместо JSON - всё это исключения, а не молчаливое искажение события.
Событие = CloudEvents.Новое("com.example.файл.загружен", "/хранилище")
.ТипДанных("application/octet-stream")
.ДвоичныеДанные(ПолучитьДвоичныеДанныеИзСтроки("Привет", "UTF-8"));
Сообщить(Событие.ПолучитьДанныеBase64()); // 0J/RgNC40LLQtdGC
Сообщить(CloudEvents.ВJson(Событие)); // ..."data_base64":"0J/RgNC40LLQtdGC"Данные и ДвоичныеДанные взаимно исключают друг друга: спецификация запрещает одновременное присутствие data и data_base64, поэтому установка одного снимает другое.
Атрибуты уходят в заголовки ce-*, datacontenttype - в обычный Content-Type, телом становятся данные.
Событие = CloudEvents.Новое("com.example.заказ.создан", "/склад/1")
.Ид("42")
.Время("2026-08-04T17:52:31.628Z")
.ТипДанных("application/json")
.Данные(Новый Структура("Номер,Сумма", 42, 1500));
Сообщение = CloudEvents.ВHttpДвоичный(Событие);
// ce-specversion: 1.0
// ce-id: 42
// ce-source: /%D1%81%D0%BA%D0%BB%D0%B0%D0%B4/1
// ce-type: com.example.%D0%B7%D0%B0%D0%BA%D0%B0%D0%B7.%D1%81%D0%BE%D0%B7%D0%B4%D0%B0%D0%BD
// ce-time: 2026-08-04T17:52:31.628Z
// Content-Type: application/json
//
// {"Номер":42,"Сумма":1500}
Запрос = Новый HTTPЗапрос("/events");
Для Каждого Заголовок Из Сообщение.Заголовки Цикл
Запрос.Заголовки.Вставить(Заголовок.Ключ, Заголовок.Значение);
КонецЦикла;
Запрос.УстановитьТелоИзСтроки(Сообщение.Тело);
// Обратно - регистр имён заголовков не важен
Событие = CloudEvents.ИзHttpДвоичного(Сообщение.Заголовки, Сообщение.Тело);Телом становится событие целиком, Content-Type объявляет медиатип формата.
Сообщение = CloudEvents.ВHttpСтруктурный(Событие);
// Content-Type: application/cloudevents+json; charset=UTF-8
// {"specversion":"1.0","id":"42", ... }
Событие = CloudEvents.ИзHttpСтруктурного(Сообщение.Заголовки, Сообщение.Тело);| Метод | Возвращает | Описание |
|---|---|---|
Новое(ТипСобытия = Неопределено, ИсточникСобытия = Неопределено) |
CloudEvent |
Новое событие с заполненными specversion, id, time |
ВJson(Событие, СОтступами = Ложь) |
Строка |
Сериализация в JSON Event Format |
ИзJson(Текст) |
CloudEvent |
Строгий разбор JSON Event Format |
ВHttpДвоичный(Событие) |
Структура |
Заголовки (Соответствие) и Тело для двоичного режима |
ИзHttpДвоичного(Заголовки, Тело = Неопределено) |
CloudEvent |
Событие из двоичного режима |
ВHttpСтруктурный(Событие) |
Структура |
Заголовки и Тело для структурного режима |
ИзHttpСтруктурного(Заголовки, Тело) |
CloudEvent |
Событие из структурного режима |
Константы медиатипов: МедиатипСобытия (application/cloudevents+json), МедиатипПакетаСобытий (application/cloudevents-batch+json), МедиатипСтруктурногоРежима (application/cloudevents+json; charset=UTF-8), МедиатипJson (application/json).
Построение - каждый метод возвращает то же событие:
| Метод | Атрибут | Значение |
|---|---|---|
Ид(Значение) |
id |
непустая строка |
Источник(Значение) |
source |
непустой URI-reference |
ТипСобытия(Значение) |
type |
непустая строка |
Тема(Значение) |
subject |
непустая строка |
Время(Значение) |
time |
Строка RFC 3339 либо Дата в UTC |
ТипДанных(Значение) |
datacontenttype |
медиатип RFC 2046 |
СхемаДанных(Значение) |
dataschema |
абсолютный URI |
Данные(Значение) |
data |
значение, представимое в JSON |
ДвоичныеДанные(Значение) |
data_base64 |
ДвоичныеДанные или БуферДвоичныхДанных |
Расширение(Имя, Значение) |
extension | Строка, Число, Булево, Дата, двоичные данные |
Чтение: ВерсияСпецификации(), ПолучитьИд(), ПолучитьИсточник(), ПолучитьТипСобытия(), ПолучитьТему(), ПолучитьВремя(), ПолучитьВремяКакДату(), ПолучитьТипДанных(), ПолучитьСхемуДанных(), ПолучитьДанные(), ПолучитьДанныеBase64(), ПолучитьДвоичныеДанные(), Расширения(), ПолучитьРасширение(Имя).
Проверка и сериализация: Проверить(), Корректно(), ВСтруктуру(), ВJson(СОтступами = Ложь).
Реализованы Core 1.0, JSON Event Format и HTTP Protocol Binding.
- Обязательные атрибуты
id,source,specversion,typeпроверяются на непустоту;specversionвсегда1.0, иная версия при разборе отвергается. - Опциональные
datacontenttype(RFC 2046),dataschema(абсолютный URI),subject(непустая строка) иtime(RFC 3339) проверяются по своим правилам; отсутствующий атрибут не сериализуется, аnullпри разборе равносилен отсутствию. - Типы значений атрибутов: Boolean, Integer (32 бита со знаком), String без управляющих символов, Binary (Base64 по RFC 4648), URI и URI-reference (RFC 3986), Timestamp (RFC 3339). Метка времени сохраняется вместе с исходным смещением,
ПолучитьВремяКакДату()переводит её в UTC. - Имена extension-атрибутов - строчные латинские буквы и цифры; имя
dataи имена основных атрибутов запрещены. - Уникальность события определяется парой
source+id; библиотека заполняетidзначением ULID, поэтому пара уникальна по построению. - JSON: все атрибуты, включая расширения, - члены верхнего уровня. Данные лежат в
data, двоичные - вdata_base64; одновременное присутствие обоих членов отвергается. Медиатип структурного представления -application/cloudevents+json, кодировка UTF-8. - Если
datacontenttypeне объявляет JSON (подтип неjsonи не*+json), тоdataдолжно быть строкой - иначе событие некорректно. - HTTP, двоичный режим: атрибуты передаются заголовками
ce-*(ce-id,ce-source,ce-type,ce-specversion,ce-subject,ce-time,ce-dataschema),datacontenttype- вContent-Type, тело равно данным. Заголовокce-datacontenttypeпри разборе отвергается, как требует спецификация. - Значения заголовков процентно кодируются: пробел, кавычка, знак процента и всё вне
U+0021..U+007Eпревращаются в%XYпо байтам UTF-8. При разборе снимаются кавычки RFC 7230, а некорректные последовательности UTF-8 (например избыточное%C0%A0) отвергаются. - HTTP, структурный режим:
Content-Type: application/cloudevents+json; charset=UTF-8, тело - сериализованное событие. Медиатип сравнивается без учёта регистра.
ТипСобытиявместоТип.Тип- глобальная функция OneScript, её нельзя использовать как имя метода. По той же причине геттеры вынесены в отдельные методы с префиксомПолучить, а не совмещены с сеттерами.- Имя расширения обязано начинаться с буквы. Спецификация лишь рекомендует это (SHOULD), но имя становится ключом структуры в
ВСтруктуру(), а ключ структуры не может начинаться с цифры. Ограничение длины в 20 символов остаётся рекомендацией и не проверяется. - Не-ASCII в
sourceиdataschemaдопускается. Строго по RFC 3986 такие символы требуют процентного кодирования, но/склад/1- практичное и распространённое значение. Проверяются символы ASCII, а не-ASCII трактуются как символы IRI (RFC 3987) и процентно кодируются при передаче в HTTP-заголовках. dataсо значениемnullне отличается от отсутствия данных. Формат JSON позволяет различать эти случаи; здесьНеопределеноединообразно означает «атрибут не задан» и для данных тоже.- Свой разбор JSON. Встроенное
ПрочитатьJSONраспознаёт строки, похожие на дату ISO 8601, и превращает их вДата, обрезая дробные секунды и часовой пояс. Это испортило бы и атрибутtime, и любые строки внутриdata, поэтому разбор выполняется по RFC 8259 своими средствами, а типы значений сохраняются точно. - Двоичный режим HTTP теряет типы расширений. Заголовки несут канонические строки, поэтому
Расширение("retries", 5)после обратного преобразования вернётся строкой"5". Так устроена сама привязка. - Данные JSON без
datacontenttypeобъявляются явно. При переносе в HTTP такому событию ставитсяContent-Type: application/json- спецификация рекомендует объявлять подразумеваемый тип при смене привязки. - Пакетный режим не поддерживается.
application/cloudevents-batch+jsonпри разборе отвергается с внятным сообщением; медиатип пакета доступен как константа.
opm install -l
oneunit execute -d ./tests