Подписка на события, преобразование объекта в JSONAPI, отправка в RabbitMQ



Обработка содержит функции:
— сериализация объекта 1с в JSONAPI
— отправка в RabbitMQ по HTTP

JSONAPI — соглашение об обмене данными о ресурсах в формате JSON.

RabbitMQ — брокер сообщений реализующий протокол AMQP, который имеет менеджмент плагин, позволяющий отправлять сообщения по протоколу HTTP.

Для целей подписки на события "При изменении" в любом справочнике(или документе) можно создать соответствующую подписку в конфигураторе 1с, причем одну на все типы объектов справочников и документов. JSONAPI позволяет указывать тип объекта и легко десериализовать на многих языках программирования.

// Создает exchange имя которого равно строке соединения с базой 1с
Функция PutExchange(Соединение, Заголовки, обработкаJSONAPI, VirtualHost = "")
exchange= СтрокаСоединенияИнформационнойБазы();
vhost= ?(VirtualHost="", "%2f", VirtualHost);
Запрос = Новый HTTPЗапрос("/api/exchanges/"+vhost+"/"+exchange, Заголовки);
телоСтрока = обработкаJSONAPI.JSON(new Структура("type", "topic"));
Запрос.УстановитьТелоИзСтроки(телоСтрока, "CESU-8");
Соединение.Записать(Запрос, "c:shared
mq1c-http-response.txt");
возврат Запрос.АдресРесурса + Символы.ВК + телоСтрока;
КонецФункции

//отправляет ссылку в формате JSONAPI в exchange имя которого равно строке соединения с базой 1с
//routing key равен полному имени типа ссылки
Функция ОтправитьJSONAPIвRMQ(ссылка, обработкаJSONAPI, VirtualHost = "")
Попытка
Соединение = Новый HTTPСоединение("rmq.local", 15672);
Исключение
//Сообщить("Не удалось установить соединение с сервером онлайн-проверки:"
//+ Символы.ПС + ИнформацияОбОшибке().Описание, СтатусСообщения.Важное);
Возврат ИнформацияОбОшибке().Описание;
КонецПопытки;
Заголовки = Новый Map();
Заголовки.Вставить("authorization", "Basic Ym90MWM6Ym90MWM=");
Заголовки.Вставить("Content-Type", "application/json");
PutExchange(Соединение, Заголовки, обработкаJSONAPI, VirtualHost);
exchange= СтрокаСоединенияИнформационнойБазы();
vhost= ?(VirtualHost="", "%2f", VirtualHost);
Запрос = Новый HTTPЗапрос("/api/exchanges/"+vhost+"/"+exchange+"/publish", Заголовки);
payloadJSON = обработкаJSONAPI.j_Экранировать(обработкаJSONAPI.JSONAPI(ссылка));
телоСтруктура = new Структура();
телоСтруктура.Вставить("properties",new Структура());
телоСтруктура.Вставить("routing_key", ссылка.Метаданные().ПолноеИмя());
телоСтруктура.Вставить("payload",payloadJSON);
телоСтруктура.Вставить("payload_encoding","string");
телоСтрока = обработкаJSONAPI.JSON(телоСтруктура);
Запрос.УстановитьТелоИзСтроки(телоСтрока, "CESU-8");
Соединение.ОтправитьДляОбработки(Запрос, "c:shared
mq1c-http-response.txt");
возврат Запрос.АдресРесурса + Символы.ВК + телоСтрока;
КонецФункции

//отправка ссылки, с указанием обработки JSONAPI, в которой находится функция сериализации в JSONAPI
Процедура ОтправитьВОчередь(Ссылка) Экспорт
ОтправитьJSONAPIвRMQ(Ссылка, Обработки.JSONAPI.Создать());
КонецПроцедуры

//отправляем в фоновом режиме, чтобы не тормозить процессы
Процедура ПриЗаписиСправочникаПриЗаписи(Источник, Отказ) Экспорт
Параметры = Новый Массив;
Параметры.Добавить(Источник.ссылка);
ФоновыеЗадания.Выполнить("RMQ.ОтправитьВОчередь",
Параметры, Новый УникальныйИдентификатор, "Пример асинхронной концепции программирования");
//ОтправитьВОчередь(Источник);
КонецПроцедуры

В веб интерфейсе RabbitMQ указываем в какой очереди и по каким routing key (типы объектов) мы хотим получать сообщения с данными.

Недостаток: событие "при записи" происходит перед транзакцией БД, а не после, поэтому если транзакция не пройдет, то событие "запись" отправленное куда либо, будет фальшивым.

протестировано на 1С:Предприятие 8.2 (8.2.19.130)

Перем j_СоответствиеТиповИИмен;
Перем j_Сч;

// Служебная функция из арсенала 1С. Название говорит само за себя
Функция j_ОпределитьПоСсылкеИмяТипа(СсылкаНаОбъект) Экспорт

Если СсылкаНаОбъект = Неопределено Тогда
Возврат "";
КонецЕсли;

ТипОбъекта = ТипЗнч(СсылкаНаОбъект);

j_СоответствиеТиповИИмен = Новый Соответствие();
СтрокаСоответствия = j_СоответствиеТиповИИмен.Получить(ТипОбъекта);

Если СтрокаСоответствия = Неопределено Тогда

МетаданныеСсылки = СсылкаНаОбъект.Метаданные();

Если Метаданные.Справочники.Содержит(МетаданныеСсылки) Тогда

ИмяТипа = "СправочникСсылка";

ИначеЕсли Метаданные.Документы.Содержит(МетаданныеСсылки) Тогда

ИмяТипа = "ДокументСсылка";

ИначеЕсли Метаданные.ПланыВидовХарактеристик.Содержит(МетаданныеСсылки) Тогда

ИмяТипа = "ПланВидовХарактеристикСсылка";

ИначеЕсли Метаданные.ПланыСчетов.Содержит(МетаданныеСсылки) Тогда

ИмяТипа = "ПланСчетовСсылка";

ИначеЕсли Метаданные.ПланыВидовРасчета.Содержит(МетаданныеСсылки) Тогда

ИмяТипа = "ПланВидовРасчетаСсылка";

ИначеЕсли Метаданные.ПланыОбмена.Содержит(МетаданныеСсылки) Тогда

ИмяТипа = "ПланОбменаСсылка";

ИначеЕсли Метаданные.Задачи.Содержит(МетаданныеСсылки) Тогда

ИмяТипа = "ЗадачаСсылка";

ИначеЕсли Метаданные.Перечисления.Содержит(МетаданныеСсылки) Тогда

ИмяТипа = "ПеречислениеСсылка";

ИначеЕсли Метаданные.БизнесПроцессы.Содержит(МетаданныеСсылки) Тогда

ИмяТипа = "БизнесПроцессСсылка";

Иначе

ИмяТипа = "";

КонецЕсли;

j_СоответствиеТиповИИмен.Вставить(ТипОбъекта, ИмяТипа);

Возврат ИмяТипа;

КонецЕсли;

Возврат СтрокаСоответствия;

КонецФункции

// Служебная функция из арсенала 1С. Название говорит само за себя
Функция j_ОпределитьПоСсылкеПредставление(СсылкаНаОбъект) Экспорт

Если СсылкаНаОбъект = Неопределено Тогда
Возврат "";
КонецЕсли;

МетаданныеСсылки = СсылкаНаОбъект.Метаданные();
ИмяТипа = j_ОпределитьПоСсылкеИмяТипа(СсылкаНаОбъект) + "." + МетаданныеСсылки.Имя;
Возврат ИмяТипа;

КонецФункции

// Экранирует спецсимвоы в строке
Функция j_Экранировать(Знач Строка) Экспорт
Строка = СтрЗаменить(Строка, "", "\");
Строка = СтрЗаменить(Строка,Символы.ПС,"
");
Строка = СтрЗаменить(Строка,Символы.ВК,"
");
Строка = СтрЗаменить(Строка,Символы.Таб,"	");
Строка = СтрЗаменить(Строка,"""","""");
//Строка = СтрЗаменить(Строка,"'","'");
//Строка = СтрЗаменить(Строка, "", "\");
Возврат Строка
КонецФункции

Функция JSON(Значение, Форматировать = Истина, Отступ  = "") Экспорт

JSONРазделСтрок = Символы.ВК + Символы.ПС;
Разделитель = "";
ТипЗн = ТипЗнч(Значение);
Отступ = ?(Форматировать, Отступ, "");

// Строка
Если ТипЗн = Тип("Строка") Тогда
Стр = """" + j_Экранировать(Значение) + """"

// Число или Булево
ИначеЕсли ТипЗн = Тип("Число") ИЛИ ТипЗнч(Значение) = Тип("Булево") Тогда
Стр = XMLСтрока(Значение)

// Дата
ИначеЕсли ТипЗн = Тип("Дата") Тогда
Стр = """" + ?(ЗначениеЗаполнено(Значение),XMLСтрока(Значение),"") + """"

// Структура Данных Рекурсивно
ИначеЕсли ТипЗн = Тип("Структура") Тогда
Стр = JSONРазделСтрок + Отступ + "{";
Для Каждого Параметр Из Значение Цикл
Стр = Стр + Разделитель + JSONРазделСтрок + Отступ + """" + Параметр.Ключ + """:" + JSON(Параметр.Значение, Форматировать, Отступ + Символы.Таб);
Разделитель = ","
КонецЦикла;
Стр = Стр + JSONРазделСтрок + Отступ + "}";

// Массив Рекурсивно
ИначеЕсли ТипЗн = Тип("Массив") Тогда
Стр = Отступ + "[";
Для Каждого Элемент Из Значение Цикл
Стр = Стр + Разделитель + JSONРазделСтрок + Отступ + JSON(Элемент, Форматировать, Отступ + Символы.Таб);
Разделитель = ","
КонецЦикла;
Стр = Стр + JSONРазделСтрок + Отступ + "]";

// Таблица Значеий как массив структур Рекурсивно
ИначеЕсли ТипЗн = Тип("ТаблицаЗначений") Тогда
Колонки = Значение.Колонки;
Массив = Новый Массив;
Для Каждого СтрокаТЗ Из Значение Цикл
Структура = Новый Структура;
Для Каждого Колонка Из Колонки Цикл
Структура.Вставить(Колонка.Имя,СтрокаТЗ[Колонка.Имя])
КонецЦикла;
Массив.Добавить(Структура);
КонецЦикла;
Стр = JSON(Массив, Форматировать, Отступ)

// Ссылка на объект
ИначеЕсли Найти(Строка(ТипЗн), "ссылка") >0 Тогда
Структура = Новый Структура;

ИмяТипа = j_ОпределитьПоСсылкеИмяТипа(Значение);

Если Не Значение.Пустая()  Тогда
Если ИмяТипа = "СправочникСсылка" Тогда
Структура.Вставить("UUID",XMLСтрока(Значение));
ИначеЕсли ИмяТипа = "ДокументСсылка" Тогда
Структура.Вставить("UUID",XMLСтрока(Значение));
Структура.Вставить("НомерДокумента",Значение.Номер);
Структура.Вставить("ДатаДокумента",Значение.Дата);
КонецЕсли;
Структура.Вставить("ТипОбъекта",j_ОпределитьПоСсылкеПредставление(Значение));
Структура.Вставить("Представление",j_Экранировать(Значение));
Иначе
Структура.Вставить("ТипОбъекта",j_ОпределитьПоСсылкеПредставление(Значение));
Структура.Вставить("Представление",Неопределено);
КонецЕсли;
Стр = Отступ + JSON(Структура, Форматировать, Отступ + Символы.Таб);

// Коллекция Объектов - Не сериализуется
ИначеЕсли Найти(Строка(ТипЗн), "менеджер") >0 Тогда
Структура = Новый Структура;
Структура.Вставить("ТипОбъекта",j_Экранировать(Значение));
Стр = Отступ + JSON(Структура, Форматировать, Отступ + Символы.Таб);

// Неопределенка
ИначеЕсли не значениеЗаполнено(Значение) Тогда
Стр = "null"

// Все остальное не сериализуется
Иначе
//Стр = "null" + j_Экранировать(Значение);
data = Новый Структура;
data.Вставить("type", Значение.Метаданные().ПолноеИмя());
попытка
data.Вставить("id",строка(Значение.ссылка.УникальныйИдентификатор()));
исключение
data.Вставить("id",j_Экранировать(Значение));
конецпопытки;
Структура = Новый Структура;
Структура.Вставить("data", data);
Стр = Отступ + JSON(Структура, Форматировать, Отступ + Символы.Таб);
КонецЕсли;

Возврат Стр
КонецФункции

Процедура ДобавитьВСтруктуру(Имя, Значение, attributes, relationships)
ТипЗн = ТипЗнч(Значение);
Если не значениеЗаполнено(Значение)Тогда
ИначеЕсли ТипЗн = Тип("Строка") или ТипЗн = Тип("Число") ИЛИ ТипЗнч(Значение) = Тип("Булево") или ТипЗн = Тип("Дата")
или ТипЗн = Тип("Структура")
//или ТипЗн = Тип("Массив") или ТипЗн = Тип("ТаблицаЗначений")
Тогда
attributes.Вставить(Имя, Значение);
иначе
relationships.Вставить(Имя, Значение);
Конецесли;
КонецПроцедуры

Функция JSONAPI(Ссылка)Экспорт
Ссылка = Ссылка.ссылка;
data = Новый Структура();
//Объект = справочники.Контрагенты.найтипокоду("000000001").ПолучитьОбъект();
//Объект.Метаданные().ПолноеИмя()
МД = ссылка.Метаданные();
ПолноеИмяТипа = МД.ПолноеИмя();
data.Вставить("type", ПолноеИмяТипа);
data.Вставить("id", Строка(Ссылка.УникальныйИдентификатор()));
attributes = Новый Структура;
relationships = Новый Структура;
included = Новый Массив;
Для каждого Реквизит из МД.СтандартныеРеквизиты Цикл
ДобавитьВСтруктуру(Реквизит.Имя, Ссылка[Реквизит.Имя], attributes, relationships);
КонецЦикла;
Для каждого Реквизит из МД.Реквизиты Цикл
ДобавитьВСтруктуру(Реквизит.Имя, Ссылка[Реквизит.Имя], attributes, relationships);
КонецЦикла;
Для каждого ТабличнаяЧасть из МД.ТабличныеЧасти Цикл
Значение = Ссылка[ТабличнаяЧасть.Имя].Выгрузить();
Колонки = Значение.Колонки;
Массив = Новый Массив;
Для Каждого СтрокаТЗ Из Значение Цикл
attributes1 = Новый Структура();
relationships1 = Новый Структура();
Для Каждого Колонка Из Колонки Цикл
ДобавитьВСтруктуру(Колонка.Имя, СтрокаТЗ[Колонка.Имя], attributes1, relationships1);
КонецЦикла;
type = ПолноеИмяТипа + "." + ТабличнаяЧасть.Имя;
id = Строка(Ссылка.УникальныйИдентификатор()) + "-" + СтрокаТЗ.НомерСтроки;
item1 = Новый Структура("type,id", type, id);
Массив.Добавить(item1);
item = Новый Структура("type,id", type, id);
item.Вставить("attributes", attributes1);
item.Вставить("relationships", relationships1);
included.Добавить(item);
КонецЦикла;
relationships.Вставить(ТабличнаяЧасть.Имя, Новый Структура("data", Массив));
КонецЦикла;
Если attributes.Количество()>0 тогда
data.Вставить("attributes", attributes);
КонецЕсли;
Если relationships.Количество()>0 тогда
data.Вставить("relationships", relationships);
КонецЕсли;
Если included.Количество()>0 тогда
data.Вставить("included", included);
КонецЕсли;
Структура = Новый Структура("data", data);
возврат JSON(Структура);
КонецФункции

 

1 Comment

  1. nomadon

    Спасибо за возможность копипаста)

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *