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