Если вы не слышали ранее об Открытом Пакете Интеграций (что вполне вероятно), то небольшая вводная часть ниже, под катом. Для тех, кто уже знаком с ОПИ, данный раздел можно пропустить.
Открытый Пакет Интеграций (ОПИ) - это open-source набор методов для простой и быстрой интеграции с различными популярными API. Он состоит из аналогичных по функционалу 1С-расширения (CFE), OS-пакета и программы для Windows и Linux, которые предоставляют готовые функции для работы с целым набором различных онлайн-сервисов.
- ОПИ бесплатен и имеет открытый исходный код на GitHub. Вы всегда можете получить последнюю версию библиотеки на странице репозитория, а подписавшись - узнавать о выходе обновлений. Все релизы сопровождаются статьями на Инфостарт и разделами единой документации. На Инфостарт вы можете подписаться уже хоть сейчас, а про документацию я расскажу далее.
- Удобная единая документация. Она расположена на сайте opi.neocities.org и содержит в себе всю информацию, необходимую для работы: инструкции по предварительным действиям для начала интеграции, описания всех методов с параметрами и возвращаемыми значениями, примеры кода и т.д. Каждый API имеет там свой раздел.
- Простая установка. ОПИ распространяется во множестве вариантах: как XML файлы расширения, как EDT проект расширения, как файл расширения формата .cfe (версия 1С 8.3.9), как файл пакета для OneScript и еще в целом наборе пакетов и файлов для Windows и Linux. Из этого набора вы всегда сможете выбрать тот способ установки, который лучше подойдет для вашей конкретной задачи
На момент последнего обновления данной статьи, доступны следующие API:
Наиболее актуальную информацию можно посмотреть в репозитории или на вводной странице документации.
OneScript - реализация виртуальной машины для выполнения программного кода на языке 1С. Даже если вы никогда им не пользовались, то все равно наверняка слышали - на Инфостарте даже целый раздел под него есть.
Вещь отличная и, без всяких скидок, утилитарно применимая: начиная с замены зачастую неуклюжих bat-файлов на os скрипты - написанные при полной мощи всем знакомого основного рабочего языка, заканчивая реальными CLI решениями или даже Web-разработками на базе OneScript.Web
Теперь под него есть и OPI, причем в полном своем составе. Сейчас мы рассмотрим способы его применения, а потом я немного расскажу про сам процесс переноса - статей про OneScript не то чтобы особо много, так что, думаю, даже мой небольшой опыт может оказаться кому-нибудь полезен.
Зачем нужен OPI для OneScript?
Начнем с самого простого и долго на этом останавливаться не будем - OPI можно использовать как часть какого-нибудь своего конечного продукта. Это довольно очевидно: движок имеет достаточное количество средств для а) создания exe файлов и CLI приложений б) создания веб-приложений в) создания GUI приложений (пускай и очень ограниченно в связи с отсутствием действительно мощных реализаций форм/элементов).
Однако не только лишь все, но очень немногие занимаются созданием своего конечного продукта (не считая инструментов разработки). Куда более приземленным и жизненным сценарием будет применение библиотеки в сфере CI/CD и этих ваших DevOps - в первую очередь это касается методов работы с Telegram и Viber.
Думаю, у всех есть как минимум один или несколько bat/os скриптов, отвечающих за некоторые обновления или иные административные действия. Вот некоторый пример для повышения их удобства:
Для начала, если вы еще не используете OneScript
1. Если у вас нет OneScript, то его нужно установить с офф. сайта oscript.io
2. Необходимо забрать из релизов файл с расширением ospx. Версия OPI для OneScript называется OInt - так называется и файл пакета, например oint-1.0.0.ospx
3. Установить библиотеку из файла shell командой
opm install oint-1.0.0.ospx
Что, в общем то, является стандартными действиями, за тем лишь исключением, что OInt нет в хабе и его надо устанавливать из файла, а не напрямую из интернета.
Теперь мы можем написать простой скрипт. Создадим пустой файл с расширением .os.
Для удобства лучше использовать VS Code с плагинами, но эту тему "старта в OneScript" я раскрывать не буду - есть другие отличные статьи (которыми, в том числе, руководствовался и я)
Для нашего примера представим, что некий процесс выполняется bat-файлом. Результатом своей деятельности он оставляет после себя файл лога - log.txt. А мы возьмем, да и отправим его себе в Telegram - узнаем сразу и о том, что работа завершена и, собственно, её результат.
В .os файл пишем вот такое незамысловатый код
#Использовать OInt
Токен = "6129.."; // Токен бота
IDПользователя = "4616.."; // ID телграм - чата
OPI_Telegram.ОтправитьТекстовоеСообщение(Токен, IDПользователя, "Bat файл завершил свою работу");
OPI_Telegram.ОтправитьДокумент(Токен, IDПользователя, "Вот файл лога", "log.txt");
Разница от 1С заключается лишь в директиве #Использовать и написание кода без процедуры. Можно также писать в процедуре или функции, а вызывать её потом.
Теперь необходимо добавить следующую строку в конце своего bat-файла
oscript ./мойскрипт.os
Или же выполнить её в командной строке/терминале. Вот что мы получаем в итоге:
Немного усложним скрипт: пришлем содержимое файла сразу в виде текста. Для этого достаточно использовать знакомые и ничем от 1С не отличающиеся функции работы с текстом:
#Использовать OInt
Токен = "6129...";
IDПользователя = "4616...";
OPI_Telegram.ОтправитьТекстовоеСообщение(Токен, IDПользователя, "Bat файл завершил свою работу");
ЧтениеТекста = Новый ЧтениеТекста("log.txt", "UTF-8");
Текст = ЧтениеТекста.Прочитать();
OPI_Telegram.ОтправитьТекстовоеСообщение(Токен, IDПользователя, Текст);
Думаю, не каждый 1Сник сможет быстро (или вообще) справится с реализацией поиска файлов, чтения и записи текста или парсинга JSON на чистом Bash - я точно не смогу. Так что всем советую попробовать скрипты на OneScript, если вы еще этого не сделали.
К тому же, скрипт всегда можно собрать в автономный исполняемый файл (exe например), и использовать уже вообще где угодно, без установки onescript и библиотек (все запихивается внутрь), просто таская этот файл с собой
oscript -make script.os script.exe
Что касается конкретно OPI, то другие его библиотеки, конечно, куда менее очевидны в использовании. Если применение API Google Календаря я себе еще как-то представляю, то VK и Twitter - только в составе каких-нибудь SMM/CRM решений. Хотя кто его знает.
Так или иначе, портировать все это добро было легко и приятно, о чем мы, собственно, сейчас и поговорим.
О переносе
Начнем с того, что OInt - это даже не порт, а унификация решения между 1С и OneScript.
Читая на странице репозитория OneScript о "независимой кросс-платформенной реализациии виртуальной машины, исполняющей скрипты на языке 1С:Предприятие", я, тем не менее, относился к этому примерно как к Перфоленте или Элементу - язык кириллический, с русскоязычными лексемами и наверняка очень похож на 1С. Но не 1С.
Однако, когда я, ради интереса, просто перенес модули из EDT проекта в отдельную папку и поменял расширение файлов с bsl на os, был приятно удивлен - мне выпало буквально 5-6 ошибок, несовместимости между языками (диалектами :) ).
Т.е. язык совместим очень хорошо - на секунду, это 5-6 ошибок на 4.5 тыс. строк кода чистыми (SonarQube в строках кода учитывает именно что "строки кода" - без комментариев или пустых)
Вот примеры несовместимостей кода, которые всплыли у меня:
- Отсутствует ТекущаяДатаСеанса, есть только ТекущаяДата. Видимо, за ненадобностью при отсутствии Клиент-Серверной архитектуры из коробки. Вроде нормально заменяется через МестноеВремя(ТекущаяУниверсальнаяДата())
- Нет УстановитьБезопасныйРежим. Собственно, тоже не особо нужен, как я понимаю
- Несовместимы функции ЧасовойПояс(), что вообще не очень хорошо - 1Сная функция возвращает понятный IANA формат типа Europ/Moscow, в то время как OS вернул нечто под названием Universal Russian Time (вроде так было)
- У меня есть функция, для получения UnixTime, которая выглядит как
Формат(Дата - Дата(1970, 1, 1, 1, 0, 0), "ЧГ=0")
При этом 1С возвращает 10-значное число, а OneScript - десятизначное число с дробной частью. Интересно. Фиксится легко через тот же формат Длина = 10, Дробная часть = 0
Функция UNIXTime(Знач Дата) Экспорт Возврат Формат(Дата - Дата(1970, 1, 1, 1, 0, 0), "ЧЦ=10; ЧДЦ=0; ЧГ=0"); КонецФункции
-
Нет Новый ЗащищенноеСоединениеOpenSSL, который передается в HttpСоединение при работе через https. Но сам https есть - просто необходим https:// в самом URL сервера соединения. В целом - не страшно
Попытка //@skip-check module-unused-local-variable SSL = Новый ЗащищенноеСоединениеOpenSSL; // BSLLS:UnusedLocalVariable-off Исключение Сервер = "https://" + Сервер; КонецПопытки; ... Функция СоздатьСоединение(Знач Сервер) Попытка SSL = Новый ЗащищенноеСоединениеOpenSSL; Возврат Новый HTTPСоединение(Сервер, 443, , , , 300, SSL); Исключение Возврат Новый HTTPСоединение(Сервер, 443, , , , 300); КонецПопытки; КонецФункции
Ну, собственно и все - никаких действительно серьезных проблем при приведении кода библиотеки к общему знаменателю лично у меня не возникло. Во многом это связано с тем, что в OPI не используются никаких объектов метаданных, кроме общих модулей - решение явно было удачным.
На данный момент между OPI (для 1С) и OInt (для 1Script) имеется всего два отличия - наличие/отсутствие директивы #Использовать и наличие/отсутствие УстановитьБезопасныРежим() в одном месте, где определяется модуль для тестирования (т.е. даже не в основной части). Что невероятно здорово и не может не радовать - одно дело обновлять две разные библиотеки и совсем другое - одну сразу под две платформы.
В связи с этим единой осталась и документация - примеры кода идентичны и для OPI и для OInt, что, также, крайне важно
Немного освежил внешний вид. Найти можно по адресу opi.neocities.org
Однако не могло же все пройти так гладко. Моментом, который забрал, ни много ни мало, процентов 80 всего времени разработки данного обновления стали Unit-тесты.
YaxUNIT x asserts
OPI - проект для души и для опыта, поэтому я стараюсь использовать в нем как можно больше разнообразных технологий - и автоматизированное тестирование не исключение.
Автотесты прекрасны. Рефакторить, ломать и переписывать созданный ранее код - это то, чем необходимо периодически заниматься, а кроме автотестов никаких человеческих способов убедится в его работоспособности после собственных манипуляций нет.
Например, у меня есть функция отправки Get запросов, на которую завязаны чуть менее чем все остальные методы OPI. И я внес в нее изменения. Сама даже мыль о ручном выполнении всех методов для проверки их работоспособности вгоняет в тоску и уныние.
Не знаю, можно ли использовать тесты с таким же невероятным КПД в обычной жизни - в больших системах с формами, пользовательским вводом, доработками и рабочими данными, но для библиотек и отдельных инструментов - это просто must have.
Однако, есть в тестах и один минус - их, собственно, надо писать. И одно дело написать сразу и функцию и тест для неё, но совсем другое, когда у тебя уже есть куча функций и сиди теперь пиши для них тесты. В OPI для 1С я использую YaxUnit - он оказался крайне дружелюбным. Тесты на нем писать легко и удобно, всем советую. Однако, для OneScript YaxUnit нет, а тесты все равно нужны. Писать их заново - 66 на тот момент - желания никакого не было. Да еще и с перспективой писать по два теста при создании новых методов в дальнейшем.
Но благо оказалось, что тесты для asserts - собственно, библиотеки для тестирования на OneScript - и тесты для Yax, вполне можно привести к общему виду.
Отличия
Yax по своим возможностям богаче asserts: так как все ответы методов работы с API у меня это, как правило, соответствия (для структур тоже применимо), то я активно использовал лаконичные конструкции Yaxa типа
ЮТест.ОжидаетЧто(Результат).Свойство("result.caption").Равно(Текст)
Что означает Результат["result"]["caption"] = Текст
Очень удобно, так как позволяет писать в строке через точку, вместо написания квадратных скобок. К сожалению в asserts такого пока нет, в чем и оказалось основное отличие.
ЮТест.ОжидаетЧто(Результат)
.ИмеетТип("Соответствие").Заполнено()
.Свойство("ok").Равно(Истина)
.Свойство("result.contact").ИмеетТип("Соответствие").Заполнено()
.Свойство("result.contact.first_name").Равно(Имя);
//Превратилось в
OPI_ПолучениеДанныхТестов.ОжидаетЧто(Результат).ИмеетТип("Соответствие").Заполнено();
OPI_ПолучениеДанныхТестов.ОжидаетЧто(Результат["ok"]).Равно(Истина);
OPI_ПолучениеДанныхТестов.ОжидаетЧто(Результат["result"]["contact"]).ИмеетТип("Соответствие").Заполнено();
OPI_ПолучениеДанныхТестов.ОжидаетЧто(Результат["result"]["contact"]["first_name"]).Равно(Имя);
Немного не так изящно, но зато работает и для asserts и для Yax. Оставалось лишь унифицировать непосредственно запуск тестов.
Yax вызывает функцию ИсполняемыеСценарии(), а asserts - ПолучитьСписокТестов(), что заложено в самих библиотеках. При этом у YaxUnit свой механизм добавления тестов, а asserts требует просто массив имен методов. В зависимости от варианта, второй метод вызова просто игнорируется:
// Для YaxUnit
Процедура ИсполняемыеСценарии() Экспорт
OPI_ПолучениеДанныхТестов.СформироватьТестыЯкс();
КонецПроцедуры
// Для Asserts
Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт // BSLLS:UnusedParameters-off
Возврат OPI_ПолучениеДанныхТестов.СформироватьТестыАссертс();
КонецФункции
Также отличаются и первые функции внутри самих тестов. Для Yax это ЮТест.ОжидаетЧто(), а для asserts - Ожидаем.Что()
Функция ОжидаетЧто(Значение) Экспорт
Попытка
Модуль = ПолучитьОбщийМодуль("ЮТест");
Ожидаем = ТипЗнч(Модуль) = Тип("ОбщийМодуль");
Возврат Модуль.ОжидаетЧто(Значение);
Исключение
Возврат Ожидаем.Что(Значение);
КонецПопытки;
КонецФункции
Для удобства ведется таблица значений, из которой потом либо добавляются тесты Yax, либо формируется массив для asserts
Функция ПолучитьТаблицуТестов()
Телеграм = "Телеграм";
ВКонтакте = "ВКонтакте";
ЯДиск = "Яндекс.Диск";
Календарь = "Google Calendar";
Твиттер = "Twitter";
Вайбер = "Viber";
ТаблицаТестов = Новый ТаблицаЗначений;
ТаблицаТестов.Колонки.Добавить("Метод");
ТаблицаТестов.Колонки.Добавить("Синоним");
ТаблицаТестов.Колонки.Добавить("Раздел");
НовыйТест(ТаблицаТестов, "Телеграм_ПолучитьИнформациюБота" , "Получить информацию бота" , Телеграм);
НовыйТест(ТаблицаТестов, "Телеграм_ПолучитьОбновления" , "Получить обновления" , Телеграм);
НовыйТест(ТаблицаТестов, "Телеграм_УстановитьWebhook" , "Установить Webhook" , Телеграм);
...
Вот так все незамысловато. Вообще, насколько я знаю, существуют и кросс библиотеки 1С/OneScript - xUnit вроде является как раз такой (могу ошибаться). Но мне очень нравится Yax и, если вам тоже, то при портировании от него отказываться не обязательно.
Подытожим
Работа с OneScript - интересный опыт и отличная возможность реализовать свои задумки, сбросив с себя, при этом, привязку к платформе и бремя лицензионных обязательств. Это здорово.
Что касается OInt, то позже я, вероятно, сделаю CLI оснастку, чтобы проект можно было использовать и просто как exe файл - благо есть библиотеки для этого. Но не сегодня - пока что это все.
Напомню только, что сейчас в Открытом пакете интеграций реализовано множество методов для работы с Telegram, VK, Viber, Twitter, Notion, Google Calendar и Yandex.Диск, которые вы можете использовать в своих 1С/OneScript проектах, забрав расширение/пакет из релизов по ссылке ниже
Используйте OneScript, используйте OPI
Спасибо за внимание!
Репозиторий ОПИ: github.com/Bayselonarrend/OpenIntegrations
Последний релиз: github.com/Bayselonarrend/OpenIntegrations/releases/latest
Другие статьи про Открытый пакет интеграций на Инфостарт:
Мой GitHub: https://gitub.com/Bayselonarrend Лицензия MIT: https://mit-license.org