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\rmq1c-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\rmq1c-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_Экранировать(Знач Строка) Экспорт
Строка = СтрЗаменить(Строка, "\", "\\");
Строка = СтрЗаменить(Строка,Символы.ПС,"\n");
Строка = СтрЗаменить(Строка,Символы.ВК,"\r");
Строка = СтрЗаменить(Строка,Символы.Таб,"\t");
Строка = СтрЗаменить(Строка,"""","\""");
//Строка = СтрЗаменить(Строка,"'","\'");
//Строка = СтрЗаменить(Строка, "\", "\\");
Возврат Строка
КонецФункции
Функция 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(Структура);
КонецФункции