Привет и спасибо, что нашли время для участия в развитии Открытого пакета интеграций! В этом документе приведены основные положения и особенности разработки, знание которых поможет избежать недопониманий и добиться более конструктивной коммуникации в рамках данного проекта
Сразу стоит отметить, что OpenIntegrations имеет явный перекос в сторону авторского проекта, нежели правильного настоящего open-source, где участие максимально широкого круга людей является целью, приветствуется и строго регламентировано. На практике это, в первую очередь, означает ограничение круга задач, которые могут быть взяты вами за основу своего вклада в данный репозиторий:
🟢 Исправление ошибок и доработка существующих модулей
Если вы нашли ошибку в существующем функционале и хотите ее исправить - это всегда приветствуется. Способ исправления (и уточнение, что это действительно ошибка), конечно, лучше все равно обсудить в Issues, но, так или иначе, это тот вид участия, который максимально прост и обычно не требует лишнего погружения во все тонкости проекта
🟡 Добавление новых функций в существующие модули
Это куда более сложный, но допустимый способ участия. Техническая сторона его реализации будет описана в следующем разделе. Тут важно отметить только то, что добавление нового функционала обязательно влечет за собой дискуссию о его целесообразности и поддерживаемости. Не спешите реализовывать и делать PR - сделайте сначала Issues
🔴 Добавление нового модуля
Это практически нереалистичный сценарий. Как уже было сказано выше, OpenIntegrations - по большей части авторский проект, просто с открытым кодом. Это обусловлено во многом спецификой 1Сного open-source, да и open-source в целом: участников мало, они приходят и уходят, не несут никакой ответственности за свой вклад и имеют право не участвовать в проекте как только им это станет не интересно. В этом нет ничего странного или неправильного, но приводит к тому, что любой модуль, целиком или в основе, реализованный сторонним разработчиком, становится в конце концов мертвым грузом. Я бы хотел избежать такого сценария. Если у вас есть идея (или даже наработки) в части какой-либо новой технологии, то вы можете либо реализовать ее самостоятельно в рамках отдельного проекта, используя инструментарий Открытого проекта интеграций, либо написать Issues и, возможно, однажды заготовка этого модуля появится в пакете. Тогда вы сможете там поучаствовать
Important
Также, вне зависимости от желаемого вида участия, я буду очень благодарен, если это участие начнется не с PR, а с Issues. Отказ участнику - это всегда неприятно для обеих сторон, но он неизбежно последует, если предлагаемые изменения не к месту (по моему эгоистичному мнению) или выполнены некорректно с технической части. Велик шанс, что вы просто потратите свое время в пустую
В отличии от сути вносимых изменений, где каждый случай уникален и требует обсуждения, техническая их реализация достаточно сильно регламентирована и практически не терпит отхода в сторону от генеральной линии. Обусловлено это автоматизацией различных процессов, будь то создание документации, тестирование, перевод на английский язык или подготовка релизов. Эти процессы просто не работают для функций и модулей, реализованных без учета установленных стандартов
Разработка проекта ведется в ветке main, исключительно в русской версии пакета для 1С:Предприятие формата EDT. Исходный код данной версии находится в src/ru/OPI. Никакие друге части исходного кода не подлежат изменению: версия для OneScript, CLI, а также все варианты английской ветки проекта, являются продуктом автоматизированного преобразования из оригинала и теряют все ручные изменения при очередном цикле CI
Если ваша доработка является исправлением ошибки, то никаких других особенных условий к ней не применяется. Добавление же новых функций, в свою очередь, сопряжено еще с рядом дополнительных регламентов, которые должны быть соблюдены. В частности, любую добавляемую функцию в Открытом пакете интеграций условно можно разделить на 4 части, каждая из которых должна присутствовать в вашей реализации:
Непосредственно реализуемый функционал. Важной частью любой функции является система приведения типов, которая преобразует входные параметры в ожидаемые. В частности, она жизненно необходима для CLI-версии проекта (с той же кодовой базой, преобразуется из 1С), где входные параметры определяются вводом в консоль и всегда на входе являются только Строками. Ознакомится со всеми видами процедур приведения типов можно в модуле OPI_ПреобразованиеТипов. Однако, проще всего посмотреть в другие функции модуля, изменения для которого вы реализуете - там всегда будут готовые примеры правильного приведения типов, которые можно перенять:
// Пример из модуля Bitrix24
Функция СоздатьЗависимостьЗадач(Знач URL, Знач IDИсточника, Знач IDПриемника, Знач ТипСвязи, Знач Токен = "") Экспорт
// Явное приведение типа через процедуру модуля OPI_ПреобразованиеТипов
OPI_ПреобразованиеТипов.ПолучитьСтроку(ТипСвязи);
Параметры = НормализоватьАвторизацию(URL, Токен, "task.dependence.add");
// Неявные приведения типов при использовании функции ДобавитьПоле()
// Третий параметр описывает тип данных, процедуры OPI_ПреобразованиеТипов используются внутри
OPI_Инструменты.ДобавитьПоле("taskIdFrom", IDИсточника, "Строка", Параметры);
OPI_Инструменты.ДобавитьПоле("taskIdTo" , IDПриемника, "Строка", Параметры);
OPI_Инструменты.ДобавитьПоле("linkType" , ТипСвязи , "Строка", Параметры);
Ответ = OPI_ЗапросыHTTP.PostСТелом(URL, Параметры);
Возврат Ответ;
КонецФункции
В качестве возвращаемого значения функции разрешено использовать только значения, сериализуемые в JSON (т.е.имеющие тип Строка, Число, Булево, Дата, Массив, Структура, Соответствие), а также значения с типом ДвоичныеДанные. В редких случаях может возвращаться объект Native API компоненты. Использовать данные других типов для возвращаемого значения, а также в качестве элементов коллекции, являющейся возвращаемым значением, запрещено
Также, при описании сигнатуры функции в ОПИ, принято использовать ключевое слово Знач для всех параметров. Экспортные функции основного интерфейса библиотеки не должны изменять передаваемые параметры внутри себя - это объект контроля разработчика внутри прикладной логики. Ключевое слово же является скорее маркером, указывающим внешнему пользователю на соблюдение этого правила
Если подытожить, то основное правило при разработке новых функций в существующем модуле можно сформулировать так:
Note
Посмотри на остальные и сделай единообразно
Документирующий комментарий - обязательная и важнейшая часть любой создаваемой функции. На его основе формируется онлайн-документация и CLI-версия. По сути своей, документирующий комментарий в ОПИ - это стандартный комментарий EDT, который можно создать, кликнув правой кнопкой мыши на имени функции и выбрав пункт Источник -> Генерировать комментарий к методу. Однако, есть и несколько дополнительных особенностей:
- Первая строка всегда является синонимом функции, а вторая - ее кратким (в одну строку) описанием
// Выполнить команду
// Выполняет команду по ее описанию
//
// Параметры:
// Соединение - Строка, Произвольный - Соединение или строка подключения - dbc
...- У всех параметров в комментарии должен быть четвертый блок - имя опции для CLI. Он должен представлять из себя короткое слово (полное или сокращенное) латинскими буквами. Оно будет использовано для указания параметра в консольной версии ОПИ. На примере это слово
access:
// Получить информацию об аккаунте
// Получает информацию об аккаунте
//
// Параметры:
// ПараметрыДоступа - Структура Из КлючИЗначение - Параметры доступа - access
//
// Возвращаемое значение:
// Соответствие Из КлючИЗначение - сериализованный JSON ответа от Green API
Функция ПолучитьИнформациюОбАккаунте(Знач ПараметрыДоступа) Экспорт
- В блоке
Параметрынельзя использовать знак-кроме как для разделения групп. Это ломает парсер, разбирающий комментарии в онлайн-документацию. Обычно, вместо него подходит просто пробел или символ>:
// Получить структуру фильтра задач
// Возвращает структуру полей для фильтрации задач в методе ПолучитьСписокЗадач
//
// Параметры:
// Пустая - Булево - Истина > структура с пустыми значениями, Ложь > в значениях будут описания полей - empty
// КакСоответствие - Булево - Истина > возвращает поля фильтра как соответствие - map
//
// Возвращаемое значение:
// Структура Из КлючИЗначение - Структура полей
Функция ПолучитьСтруктуруФильтраЗадач(Знач Пустая = Ложь, Знач КакСоответствие = Ложь) Экспорт- Между блоком с кратким описанием и блоком
Параметры, при необходимости, может быть вставлен блокПримечание. Там можно указать особенности работы с функцией и/или ссылку на оригинальную документацию сервиса (технологии). При указании ссылки,https://должен быть заменен на@
// Получить список пользовательских полей задачи
// Получает список пользовательских полей для задач
//
// Примечание:
// Метод в документации API: [task.item.userfield.getlist](@dev.1c-bitrix.ru/rest_help/tasks/task/userfield/getlist.php)
//
// Параметры:
// URL - Строка - URL внешнего вебхука или адрес Bitrix24 при использовании токена - url
// Токен - Строка - Токен авторизации, если используется не вебхук - token
//
// Возвращаемое значение:
// Соответствие Из КлючИЗначение - сериализованный JSON ответа от Bitrix24
Функция ПолучитьСписокПользовательскихПолейЗадачи(Знач URL, Знач Токен = "") Экспорт- Указание полей коллекций через
*не используется. При указании типов параметровСтруктураилиСоответствие, достаточно указатьСтруктура Из КлючИЗначениеилиСоответствие Из КлючИЗначение. Для параметров с типомМассивиспользуетсяМассив Из <Тип данных>илиМассив Из Произвольный, если типов несколько (в таком случае стоит подробнее разъяснить это в блоке описания параметра). В общем случае, описания одного параметра и описания возвращаемого значения не должны занимать больше одной строки
// Выполнить команду
// Выполняет команду по ее описанию
//
// Параметры:
// Соединение - Строка, Произвольный - Соединение или строка подключения - dbc
// Команда - Строка - Имя команды для выполнения - comm
// Аргумент - Произвольный - Аргумент команды - arg
// База - Строка - База данных, в которой необходимо провести операцию - db
// Данные - Структура Из КлючИЗначение - Основные поля данных для выполнения операции - data
//
// Возвращаемое значение:
// Соответствие Из КлючИЗначение - Результат выполнения операцииКаждая функция должна быть покрыта Unit-тестом - это важнейшая часть поддержки библиотек на длинной дистанции, позволяющая выпускать каждый новый релиз, не задаваясь вопросом "Работают ли созданные ранее функции до сих пор или уже нет?"
Important
Сразу стоит оговорится: далее пойдет достаточно запутанное, если не сказать душное, описание регламента создания тестов. При этом, вы не сможете запустить созданные тесты самостоятельно, чтобы проверить их работоспособность. Поэтому их создание находится в категории "Сделайте как-нибудь": частичное несоответствие добавленных тестов регламенту не будут причиной для отклонения PR. Я буду рад, если вы добавите их хотя бы в каком-то виде и в правильном модуле, а мне будет достаточно привести их к корректному виду, а не писать целиком с нуля. Также тут продолжает работать правило из блока разработки функций: "Посмотри на остальные и сделай единообразно"
Все Unit-тесты находятся в модуле OPI_Тесты, в области СлужебныеПроцедурыИФункции -> АтомарныеТесты -> Имя модуля без префикса, и представляют из себя процедуру, название которой состоит из имени модуля (без префикса OPI_), нижнего подчеркивания и имени тестируемой функции
#Область АтомарныеТесты
#Область Telegram
Процедура Telegram_ПолучитьИнформациюБота(ПараметрыФункции)
Токен = ПараметрыФункции["Telegram_Token"];
Результат = OPI_Telegram.ПолучитьИнформациюБота(Токен);
// END
Обработать(Результат, "Telegram", "ПолучитьИнформациюБота");
КонецПроцедурыПроцедура всегда должна содержать один или несколько вызовов функции Обработать, в которую передается результат выполнения, имя библиотеки и имя тестируемой функции. При желании реализовать несколько проверок в рамках одного теста, можно в качестве четвертого параметра указать строкой Вариант. Эти данные будут переданы в функцию проверки результата теста, о которой пойдет речь в следующем разделе
Important
Для основного результата теста Вариант должен быть не указан
...
// END
Обработать(Результат, "PostgreSQL", "СоздатьТаблицу");
Таблица = "АБВ ГДЕ";
Результат = OPI_PostgreSQL.СоздатьТаблицу(Таблица, СтруктураКолонок, СтрокаПодключения, НастройкиTLS);
Обработать(Результат, "PostgreSQL", "СоздатьТаблицу", "Ошибка имени");
Таблица = "somename";
СтруктураКолонок.Вставить("wtf_field", "WTF");
Результат = OPI_PostgreSQL.СоздатьТаблицу(Таблица, СтруктураКолонок, СтрокаПодключения, НастройкиTLS);
Обработать(Результат, "PostgreSQL", "СоздатьТаблицу", "Ошибка типа");Также, в теле тестовой процедуры используются служебные комментарии // SKIP и // END. Они отслеживаются парсером при создании онлайн-документации: тестовая процедура является там наглядным примером кода для пользователя. При помощи данных служебных комментариев можно отсечь лишние строки, которые нужны в тесте, но не нужны в примере кода.
...
// Тут идет удаление таблицы перед тестом создания, чтобы не получить ошибку "уже существует"
// Но в примере работы с функцией СоздатьТаблицу для документации это не нужно показывать
OPI_PostgreSQL.УдалитьТаблицу(Таблица, СтрокаПодключения, НастройкиTLS); // SKIP
Результат = OPI_PostgreSQL.СоздатьТаблицу(Таблица, СтруктураКолонок, СтрокаПодключения, НастройкиTLS);
// END
// Весь код после END не попадает в документацию
Обработать(Результат, "PostgreSQL", "СоздатьТаблицу");Сама тестовая процедура всегда принимает один параметр - ПараметрыФункции. Несколько атомарных тестов объединяются по смыслу (чаще всего - по области в основном модуле библиотеки) в тестовые наборы. Тестовые наборы создаются также в модуле OPI_Тесты, в области СлужебныйПрограммныйИнтерфейс -> ЗапускаемыеТесты -> Имя модуля без префикса. Каждый тест должен принадлежать тестовому набору, там определяются и ПараметрыФункции
#Область ЗапускаемыеТесты
#Область Ollama
Процедура OLLM_ОбработкаЗапросов() Экспорт
ПараметрыТеста = Новый Структура;
OPI_ПолучениеДанныхТестов.ПараметрВКоллекцию("Ollama_URL" , ПараметрыТеста);
OPI_ПолучениеДанныхТестов.ПараметрВКоллекцию("Ollama_Token", ПараметрыТеста);
Ollama_СкачатьМодель(ПараметрыТеста);
Ollama_ПолучитьВерсию(ПараметрыТеста);
Ollama_ПолучитьОтвет(ПараметрыТеста);
Ollama_ПолучитьОтветВКонтексте(ПараметрыТеста);
Ollama_ПолучитьПредставления(ПараметрыТеста);
Ollama_ПолучитьСтруктуруПараметровЗапроса(ПараметрыТеста);
Ollama_ПолучитьСтруктуруПараметровВКонтексте(ПараметрыТеста);
Ollama_ПолучитьСтруктуруСообщенияКонтекста(ПараметрыТеста);
Ollama_ПолучитьСтруктуруПараметровПредставлений(ПараметрыТеста);
КонецПроцедуры
В тестовом наборе определяются параметры, передаваемые в тесты всего набора:
ПараметрыТеста = Новый Структура;
OPI_ПолучениеДанныхТестов.ПараметрВКоллекцию("Ollama_URL" , ПараметрыТеста);
OPI_ПолучениеДанныхТестов.ПараметрВКоллекцию("Ollama_Token", ПараметрыТеста);
Они добавляются в коллекцию при помощи специальной функции OPI_ПолучениеДанныхТестов.ПараметрВКоллекцию и характеризуются идентификаторами, вроде Ollama_URL. На практике, это ключи JSON файла с секретными и не секретными данными для тестирования. Вам не будет доступен этот файл, поэтому вы можете не сильно заморачиваться над используемыми именами: можно найти уже существующие в тестовом наборе, если они подходят вам по смыслу (по аналогии с существующими тестами), или придумать новые, описав в PR, что они означают.
Как уже было сказано ранее, каждый Unit-тест должен содержать вызов функции Обработать. Эта функция передает значение результата теста и его варианта в соответствующую процедуру проверки, которая должна располагаться в модуле OPI_ПолучениеДанныхТестов, в области СлужебныеПроцедурыИФункции -> Проверки и иметь имя, равное имени процедуры теста, но с префиксом Проверка_, Например Проверка_Telegram_ОтправитьТекстовоеСообщение
В этой процедуре можно использовать утверждения для анализа корректности переданного результата при помощи конструкции ОжидаетЧто(Значение1).Равно(Значение2), а также ветвить проверки в зависимости от переданного значения в параметр Вариант. Процедура проверки должна иметь 2 обязательных параметра (Результат и Вариант), а также до трех нестандартных параметров, которые могут быть переданы из теста при вызове функции Обработать, после параметра Вариант
// Простой вариант вызова из теста: Вариант = Неопределено, доп. параметры не передаются
Обработать(Результат, "Telegram", "ОтправитьТекстовоеСообщение");
// Комплексный вариант вызова из теста: Вариант = "Канал", два доп. параметра
Обработать(Результат, "Telegram", "ОтправитьТекстовоеСообщение", "Канал", ПараметрыФункции, Текст);
...
Функция Проверка_Telegram_ОтправитьТекстовоеСообщение(Знач Результат, Знач Вариант, Параметры = "", Текст = "")
ОжидаетЧто(Результат).ИмеетТип("Соответствие").Заполнено();
ОжидаетЧто(Результат["ok"]).Равно(Истина);
Если Не ЗначениеЗаполнено(Вариант) Тогда
ИмяПараметра = "Telegram_MessageID";
ОжидаетЧто(Результат["result"]["text"]).Равно(Текст);
ИначеЕсли Вариант = "Канал" Тогда
ИмяПараметра = "Telegram_ChannelMessageID";
ОжидаетЧто(Результат["result"]["text"]).Равно(Текст);
Иначе
ИмяПараметра = "";
КонецЕсли;
Если ЗначениеЗаполнено(ИмяПараметра) Тогда
IDСообщения = OPI_Инструменты.ЧислоВСтроку(Результат["result"]["message_id"]);
// Запись нового параметра теста в JSON
ЗаписатьПараметр(ИмяПараметра, IDСообщения);
OPI_Инструменты.ДобавитьПоле(ИмяПараметра, IDСообщения, "Строка", Параметры);
КонецЕсли;
Возврат Результат;
КонецФункции
При ложности какого-либо из утверждений тест будет считаться проваленным, что позволит узнать об изменении внешнего API или поломке при изменении общих функций библиотеки
В целом, это все условия, которые должны быть соблюдены для успешного принятия вашего запроса на слияние. Удачи и хорошего коддинга!
Данный документ может быть дополнен или изменен в будущем. Спасибо за прочтение!