Files
sfera/integrations/1c/sfera-extension/xml/HTTPServices/BridgeHTTP/Ext/Module.bsl
T
m 29bbe1dca6
CI / python (push) Has been cancelled
CI / rust (push) Has been cancelled
Dry run publish 1C access profiles
2026-05-21 19:22:02 +03:00

306 lines
18 KiB
Plaintext

#Область ОбработчикиHTTPСервиса
Функция Health(Запрос) Экспорт
Возврат ОтветJSON(BridgeHealth());
КонецФункции
Функция MetadataSnapshot(Запрос) Экспорт
Контекст = ПрочитатьКонтекст(Запрос);
Если Не Авторизован(Запрос, Контекст) Тогда
Возврат ОтветОшибка(401, "Unauthorized");
КонецЕсли;
Возврат ОтветJSON(BridgeMetadataSnapshot());
КонецФункции
Функция Query(Запрос) Экспорт
Контекст = ПрочитатьКонтекст(Запрос);
Если Не Авторизован(Запрос, Контекст) Тогда
Возврат ОтветОшибка(401, "Unauthorized");
КонецЕсли;
Возврат ОтветJSON(BridgeExecuteQuery(
ПолучитьПоле(Контекст, "payload", Новый Структура),
ПолучитьПоле(Контекст, "dry_run", Истина),
ПолучитьПоле(Контекст, "allow_mutation", Ложь)));
КонецФункции
Функция DataRead(Запрос) Экспорт
Контекст = ПрочитатьКонтекст(Запрос);
Если Не Авторизован(Запрос, Контекст) Тогда
Возврат ОтветОшибка(401, "Unauthorized");
КонецЕсли;
Возврат ОтветJSON(BridgeReadData(ПолучитьПоле(Контекст, "payload", Новый Структура)));
КонецФункции
Функция DataWrite(Запрос) Экспорт
Контекст = ПрочитатьКонтекст(Запрос);
Если Не Авторизован(Запрос, Контекст) Тогда
Возврат ОтветОшибка(401, "Unauthorized");
КонецЕсли;
Возврат ОтветJSON(BridgeWriteData(
ПолучитьПоле(Контекст, "payload", Новый Структура),
ПолучитьПоле(Контекст, "dry_run", Истина),
ПолучитьПоле(Контекст, "allow_mutation", Ложь)));
КонецФункции
Функция MetadataApply(Запрос) Экспорт
Контекст = ПрочитатьКонтекст(Запрос);
Если Не Авторизован(Запрос, Контекст) Тогда
Возврат ОтветОшибка(401, "Unauthorized");
КонецЕсли;
Возврат ОтветJSON(BridgeMetadataApply(
ПолучитьПоле(Контекст, "payload", Новый Структура),
ПолучитьПоле(Контекст, "dry_run", Истина),
ПолучитьПоле(Контекст, "allow_mutation", Ложь)));
КонецФункции
#КонецОбласти
#Область ЛогикаSFERA
Функция BridgeHealth()
Результат = Новый Структура;
Результат.Вставить("status", "ok");
Результат.Вставить("extension", "SFERA");
Результат.Вставить("version", "0.1.0");
Результат.Вставить("timestamp", ТекущаяДата());
Результат.Вставить("mutation_supported", Истина);
Результат.Вставить("metadata_apply_supported", Ложь);
Результат.Вставить("access_profile_apply_supported", Истина);
Возврат Результат;
КонецФункции
Функция BridgeMetadataSnapshot()
Коллекции = Новый Массив;
ДобавитьКоллекциюМетаданных(Коллекции, "Справочники", Метаданные.Справочники);
ДобавитьКоллекциюМетаданных(Коллекции, "Документы", Метаданные.Документы);
ДобавитьКоллекциюМетаданных(Коллекции, "Перечисления", Метаданные.Перечисления);
ДобавитьКоллекциюМетаданных(Коллекции, "Отчеты", Метаданные.Отчеты);
ДобавитьКоллекциюМетаданных(Коллекции, "Обработки", Метаданные.Обработки);
ДобавитьКоллекциюМетаданных(Коллекции, "ПланыОбмена", Метаданные.ПланыОбмена);
ДобавитьКоллекциюМетаданных(Коллекции, "ПланыВидовХарактеристик", Метаданные.ПланыВидовХарактеристик);
ДобавитьКоллекциюМетаданных(Коллекции, "ПланыСчетов", Метаданные.ПланыСчетов);
ДобавитьКоллекциюМетаданных(Коллекции, "ПланыВидовРасчета", Метаданные.ПланыВидовРасчета);
ДобавитьКоллекциюМетаданных(Коллекции, "РегистрыСведений", Метаданные.РегистрыСведений);
ДобавитьКоллекциюМетаданных(Коллекции, "РегистрыНакопления", Метаданные.РегистрыНакопления);
ДобавитьКоллекциюМетаданных(Коллекции, "РегистрыБухгалтерии", Метаданные.РегистрыБухгалтерии);
ДобавитьКоллекциюМетаданных(Коллекции, "РегистрыРасчета", Метаданные.РегистрыРасчета);
ДобавитьКоллекциюМетаданных(Коллекции, "БизнесПроцессы", Метаданные.БизнесПроцессы);
ДобавитьКоллекциюМетаданных(Коллекции, "Задачи", Метаданные.Задачи);
ДобавитьКоллекциюМетаданных(Коллекции, "ОбщиеМодули", Метаданные.ОбщиеМодули);
ДобавитьКоллекциюМетаданных(Коллекции, "Роли", Метаданные.Роли);
ДобавитьКоллекциюМетаданных(Коллекции, "Подсистемы", Метаданные.Подсистемы);
ДобавитьКоллекциюМетаданных(Коллекции, "HTTPСервисы", Метаданные.HTTPСервисы);
ДобавитьКоллекциюМетаданных(Коллекции, "WebСервисы", Метаданные.WebСервисы);
Результат = Новый Структура;
Результат.Вставить("status", "ok");
Результат.Вставить("collections", Коллекции);
Возврат Результат;
КонецФункции
Функция BridgeExecuteQuery(Параметры, DryRun, AllowMutation)
ТекстЗапроса = Строка(ПолучитьПоле(Параметры, "query", ""));
Если ПустаяСтрока(ТекстЗапроса) Тогда
Возврат ОшибкаSFERA("query is required");
КонецЕсли;
Если ЗапросОпасный(ТекстЗапроса) И Не AllowMutation Тогда
Возврат ОшибкаSFERA("Mutation query is blocked. Set allow_mutation=true and disable dry_run.");
КонецЕсли;
Если DryRun Тогда
Результат = Новый Структура;
Результат.Вставить("status", "dry_run");
Результат.Вставить("query", ТекстЗапроса);
Результат.Вставить("mutation_detected", ЗапросОпасный(ТекстЗапроса));
Возврат Результат;
КонецЕсли;
Запрос = Новый Запрос(ТекстЗапроса);
ПараметрыЗапроса = ПолучитьПоле(Параметры, "parameters", Неопределено);
Если ТипЗнч(ПараметрыЗапроса) = Тип("Структура") Тогда
Для Каждого Параметр Из ПараметрыЗапроса Цикл
Запрос.УстановитьПараметр(Параметр.Ключ, Параметр.Значение);
КонецЦикла;
КонецЕсли;
Таблица = Запрос.Выполнить().Выгрузить();
Строки = Новый Массив;
Для Каждого СтрокаТаблицы Из Таблица Цикл
Строки.Добавить(СтрокаТаблицыВСтруктуру(СтрокаТаблицы, Таблица.Колонки));
Если Строки.Количество() >= 1000 Тогда
Прервать;
КонецЕсли;
КонецЦикла;
Результат = Новый Структура;
Результат.Вставить("status", "ok");
Результат.Вставить("rows", Строки);
Результат.Вставить("limit", 1000);
Возврат Результат;
КонецФункции
Функция BridgeReadData(Параметры)
Если ЗначениеЗаполнено(ПолучитьПоле(Параметры, "query", "")) Тогда
Возврат BridgeExecuteQuery(Параметры, Ложь, Ложь);
КонецЕсли;
Возврат ОшибкаSFERA("For generic data read use query with SELECT text.");
КонецФункции
Функция BridgeWriteData(Параметры, DryRun, AllowMutation)
Если Не AllowMutation Тогда
Возврат ОшибкаSFERA("Write operation is blocked.");
КонецЕсли;
Если DryRun Тогда
Результат = Новый Структура;
Результат.Вставить("status", "dry_run");
Результат.Вставить("message", "Write request accepted for preview only.");
Результат.Вставить("payload", Параметры);
Возврат Результат;
КонецЕсли;
Возврат ОшибкаSFERA("Generic write adapter is intentionally not enabled yet. Implement object-specific handlers first.");
КонецФункции
Функция BridgeMetadataApply(Параметры, DryRun, AllowMutation)
Операция = Строка(ПолучитьПоле(Параметры, "operation", ""));
Если Операция = "access.profile.apply" Тогда
Возврат BridgeAccessProfileApply(Параметры, DryRun, AllowMutation);
КонецЕсли;
Результат = Новый Структура;
Результат.Вставить("status", "planned");
Результат.Вставить("message", "Changing configuration structure is performed by SFERA Windows Agent through Designer and .cfe update, not by runtime HTTP.");
Результат.Вставить("dry_run", DryRun);
Результат.Вставить("payload", Параметры);
Возврат Результат;
КонецФункции
Функция BridgeAccessProfileApply(Параметры, DryRun, AllowMutation)
Профиль = ПолучитьПоле(Параметры, "profile", Новый Структура);
Операции = ПолучитьПоле(Параметры, "operations", Новый Массив);
ИмяПрофиля = Строка(ПолучитьПоле(Профиль, "qualified_name", ПолучитьПоле(Профиль, "name", "")));
Если ПустаяСтрока(ИмяПрофиля) Тогда
Возврат ОшибкаSFERA("profile.name or profile.qualified_name is required for access.profile.apply");
КонецЕсли;
Если Не DryRun И Не AllowMutation Тогда
Возврат ОшибкаSFERA("Access profile mutation is blocked. Use dry_run=true or allow_mutation=true with project mutation guard enabled.");
КонецЕсли;
Результат = Новый Структура;
Результат.Вставить("status", ?(DryRun, "dry_run", "planned"));
Результат.Вставить("operation", "access.profile.apply");
Результат.Вставить("profile", ИмяПрофиля);
Результат.Вставить("operations_count", КоличествоЭлементовSFERA(Операции));
Результат.Вставить("operations", Операции);
Результат.Вставить("message", "Access profile plan was accepted by SFERA extension. Runtime mutation is not executed by the generic bridge; apply through a configuration-specific adapter or Windows Agent.");
Результат.Вставить("dry_run", DryRun);
Возврат Результат;
КонецФункции
#КонецОбласти
#Область СлужебныеПроцедурыИФункции
Функция ПрочитатьКонтекст(Запрос)
Тело = Запрос.ПолучитьТелоКакСтроку();
Если ПустаяСтрока(Тело) Тогда
Возврат Новый Структура;
КонецЕсли;
Чтение = Новый ЧтениеJSON;
Чтение.УстановитьСтроку(Тело);
Возврат ПрочитатьJSON(Чтение);
КонецФункции
Функция Авторизован(Запрос, Контекст)
// Основная авторизация выполняется публикацией 1С/IIS. Токен SFERA включается после настройки секрета.
ОжидаемыйТокен = "";
Если ПустаяСтрока(ОжидаемыйТокен) Тогда
Возврат Истина;
КонецЕсли;
Возврат Запрос.Заголовки.Получить("X-SFERA-Token") = ОжидаемыйТокен;
КонецФункции
Функция ОтветJSON(Данные, КодСостояния = 200)
Запись = Новый ЗаписьJSON;
Запись.УстановитьСтроку();
ЗаписатьJSON(Запись, Данные);
Тело = Запись.Закрыть();
Ответ = Новый HTTPСервисОтвет(КодСостояния);
Ответ.Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");
Ответ.УстановитьТелоИзСтроки(Тело, КодировкаТекста.UTF8);
Возврат Ответ;
КонецФункции
Функция ОтветОшибка(КодСостояния, Сообщение)
Данные = Новый Структура;
Данные.Вставить("status", "error");
Данные.Вставить("message", Сообщение);
Возврат ОтветJSON(Данные, КодСостояния);
КонецФункции
Функция ПолучитьПоле(СтруктураИсточник, ИмяПоля, ЗначениеПоУмолчанию = Неопределено)
Если ТипЗнч(СтруктураИсточник) = Тип("Структура") И СтруктураИсточник.Свойство(ИмяПоля) Тогда
Возврат СтруктураИсточник[ИмяПоля];
КонецЕсли;
Возврат ЗначениеПоУмолчанию;
КонецФункции
Функция КоличествоЭлементовSFERA(Значение)
Если Значение = Неопределено Тогда
Возврат 0;
КонецЕсли;
Если ТипЗнч(Значение) = Тип("Массив") Или ТипЗнч(Значение) = Тип("Структура") Или ТипЗнч(Значение) = Тип("Соответствие") Тогда
Возврат Значение.Количество();
КонецЕсли;
Возврат 0;
КонецФункции
Процедура ДобавитьКоллекциюМетаданных(Коллекции, ИмяКоллекции, КоллекцияМетаданных)
Объекты = Новый Массив;
Для Каждого ОбъектМетаданных Из КоллекцияМетаданных Цикл
Описание = Новый Структура;
Описание.Вставить("name", ОбъектМетаданных.Имя);
Описание.Вставить("synonym", ОбъектМетаданных.Синоним);
Описание.Вставить("full_name", ИмяКоллекции + "." + ОбъектМетаданных.Имя);
Объекты.Добавить(Описание);
КонецЦикла;
Коллекция = Новый Структура;
Коллекция.Вставить("name", ИмяКоллекции);
Коллекция.Вставить("count", Объекты.Количество());
Коллекция.Вставить("objects", Объекты);
Коллекции.Добавить(Коллекция);
КонецПроцедуры
Функция ЗапросОпасный(ТекстЗапроса)
Текст = НРег(ТекстЗапроса);
Возврат Найти(Текст, "вставить") > 0
Или Найти(Текст, "изменить") > 0
Или Найти(Текст, "удалить") > 0
Или Найти(Текст, "поместить") > 0
Или Найти(Текст, "создать") > 0;
КонецФункции
Функция СтрокаТаблицыВСтруктуру(СтрокаТаблицы, Колонки)
Результат = Новый Структура;
Для Каждого Колонка Из Колонки Цикл
Результат.Вставить(Колонка.Имя, ЗначениеДляJSON(СтрокаТаблицы[Колонка.Имя]));
КонецЦикла;
Возврат Результат;
КонецФункции
Функция ЗначениеДляJSON(Значение)
Если Значение = Неопределено Или Значение = Null Тогда
Возврат Неопределено;
КонецЕсли;
Если ТипЗнч(Значение) = Тип("Строка")
Или ТипЗнч(Значение) = Тип("Число")
Или ТипЗнч(Значение) = Тип("Булево") Тогда
Возврат Значение;
КонецЕсли;
Если ТипЗнч(Значение) = Тип("Дата") Тогда
Возврат Формат(Значение, "ДФ=yyyy-MM-ddTHH:mm:ss");
КонецЕсли;
Возврат Строка(Значение);
КонецФункции
Функция ОшибкаSFERA(Сообщение)
Результат = Новый Структура;
Результат.Вставить("status", "error");
Результат.Вставить("message", Сообщение);
Возврат Результат;
КонецФункции
#КонецОбласти