diff --git a/README.md b/README.md index a1b891c..eb71a8e 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,9 @@ * 2.3\. [Swagger UI](#swagger-ui) * 3\. [Совместимость](#compatibility) * 4\. [Программный интерфейс](#api) -* 5\. [Ограничения](#limitations) -* 6\. [Сравнение с httpbin.org](#comparison) +* 5\. [Кастомные эндпоинты](#custom-endpoints) +* 6\. [Ограничения](#limitations) +* 7\. [Сравнение с httpbin.org](#comparison) ## Установка @@ -39,6 +40,7 @@ httpbin run | --- | --- | --- | | `-h`, `--host` | Имя хоста или IP-адрес сервиса | `127.0.0.1` | | `-p`, `--port` | TCP-порт сервиса | `3333` | +| `--routes-handlers` | Путь к файлу или каталогу кастомных контроллеров | | ### Тестирование с [asserts](https://github.com/oscript-library/asserts) и [1connector](https://github.com/vbondarevsky/1connector) @@ -132,6 +134,47 @@ IP-адрес или имя хоста.
| `URL(<АдресРесурса>)` | Формирует полный URL-адрес сервиса с опциональным путем к ресурсу. | | `ТаймаутЗапуска()` | Возвращает текущее значение таймаута запуска сервиса. | | `УстановитьТаймаутЗапуска(<Таймаут>)` | Устанавливает максимальное время ожидания запуска сервиса. Применяется при синхронном запуске. | +| `РасположениеКонтроллеров()` | Возвращает текущий путь к папке или файлу с кастомными контроллерами. | +| `УстановитьРасположениеКонтроллеров(<Расположение>)` | Устанавливает путь к папке или файлу с кастомными контроллерами, определяющими маршруты сервиса. | + +## Кастомные эндпоинты + +Сервис поддерживает подключение пользовательских контроллеров для расширения функциональности и добавления собственных эндпоинтов. + +### Создание контроллера + +Контроллер представляет собой класс OneScript с аннотацией `&Контроллер`, определяющей базовый путь маршрута. Более подробно можно прочитать в документации [WINOW](https://github.com/autumn-library/winow). + +**Пример контроллера** + +``` bsl +&Контроллер("/order") +Процедура ПриСозданииОбъекта() +КонецПроцедуры + +&ТочкаМаршрута("add") +Процедура Главная(Ответ) Экспорт + // Бизнес-логика +КонецПроцедуры +``` + +### Подключение контроллеров + +**Через программный интерфейс** + +Для подключения кастомных контроллеров используйте метод `УстановитьРасположениеКонтроллеров()`, указав путь к папке или файлу с контроллерами: + +``` bsl +HttpBin = Новый HttpBin() + .УстановитьРасположениеКонтроллеров("./path/to/routes") + .Запустить(); +``` + +**Через CLI** + +``` bash +httpbin run --routes-handlers './path/to/routes' +``` ## Ограничения diff --git "a/src/app/\320\236\321\201\320\275\320\276\320\262\320\275\320\276\320\271\320\232\320\276\320\275\321\202\321\200\320\276\320\273.os" "b/src/app/\320\236\321\201\320\275\320\276\320\262\320\275\320\276\320\271\320\232\320\276\320\275\321\202\321\200\320\276\320\273\320\273\320\265\321\200.os" similarity index 99% rename from "src/app/\320\236\321\201\320\275\320\276\320\262\320\275\320\276\320\271\320\232\320\276\320\275\321\202\321\200\320\276\320\273.os" rename to "src/app/\320\236\321\201\320\275\320\276\320\262\320\275\320\276\320\271\320\232\320\276\320\275\321\202\321\200\320\276\320\273\320\273\320\265\321\200.os" index ef2538b..1bd1114 100644 --- "a/src/app/\320\236\321\201\320\275\320\276\320\262\320\275\320\276\320\271\320\232\320\276\320\275\321\202\321\200\320\276\320\273.os" +++ "b/src/app/\320\236\321\201\320\275\320\276\320\262\320\275\320\276\320\271\320\232\320\276\320\275\321\202\321\200\320\276\320\273\320\273\320\265\321\200.os" @@ -1,4 +1,3 @@ -#Использовать "../internal" #Использовать 1connector Перем _Помощник; // ПомощникПодготовкиОтветов diff --git a/src/cmd/main.os b/src/cmd/main.os index 911b586..c0642e9 100644 --- a/src/cmd/main.os +++ b/src/cmd/main.os @@ -1,7 +1,10 @@ #Использовать autumn #Использовать autumn-cli -#Использовать winow #Использовать "." +#Использовать "../internal" + +ПодключательКастомныхКонтроллеров = Новый ПодключательКастомныхКонтроллеров(); +ПодключательКастомныхКонтроллеров.НайтиИПодключить(); Поделка = Новый Поделка; Поделка.ЗапуститьПриложение(); \ No newline at end of file diff --git "a/src/cmd/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\276\320\274\320\260\320\275\320\264\320\260\320\227\320\260\320\277\321\203\321\201\321\202\320\270\321\202\321\214.os" "b/src/cmd/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\276\320\274\320\260\320\275\320\264\320\260\320\227\320\260\320\277\321\203\321\201\321\202\320\270\321\202\321\214.os" index dda2906..613ed0e 100644 --- "a/src/cmd/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\276\320\274\320\260\320\275\320\264\320\260\320\227\320\260\320\277\321\203\321\201\321\202\320\270\321\202\321\214.os" +++ "b/src/cmd/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\276\320\274\320\260\320\275\320\264\320\260\320\227\320\260\320\277\321\203\321\201\321\202\320\270\321\202\321\214.os" @@ -1,5 +1,7 @@ // BSLLS:UsingHardcodeNetworkAddress-off +#Область Опции + &Опция( Имя = "h host", Описание = "Имя хоста или IP-адрес сервиса" @@ -24,15 +26,27 @@ &ПоУмолчанию(0) Перем _ИдентификаторРодительскогоПроцесса; // Число -&Пластилин("Настройки") -Перем _НастройкиВебСервера; // НастройкиВебСервера - см. winow +&Опция( + Имя = "routes-handlers", + Описание = "Путь к файлу или каталогу кастомных контроллеров" +) +&ТМассивСтрок +Перем _КастомныеКонтроллеры; // BSLLS:UnusedLocalVariable-off + +#КонецОбласти -&Пластилин("ЗапускательВебПриложения") -Перем _ЗапускательВебПриложения; // ЗапускательВебПриложения - см. winow +#Область ОписаниеПеременных &Пластилин("КонтроллерРодительскогоПроцесса") Перем _КонтроллерРодительскогоПроцесса; // КонтроллерРодительскогоПроцесса +&Пластилин("ЗапускательСервиса") +Перем _ЗапускательСервиса; // ЗапускательСервиса + +#КонецОбласти + +#Область СлужебныеПроцедурыИФункции + &КомандаПриложения(Имя = "run", Описание = "Запуск сервиса") Процедура ПриСозданииОбъекта() КонецПроцедуры @@ -40,18 +54,12 @@ &ВыполнениеКоманды Процедура Запустить() Экспорт - _НастройкиВебСервера.РазмерБуфера = 0; - _НастройкиВебСервера.ИмяХоста = _Хост; - _НастройкиВебСервера.Порт = _Порт; - - Если ПолучитьПеременнуюСреды("HTTPBIN_IS_TEST_MODE") = "true" Тогда - _НастройкиВебСервера.ЗадержкаПередЧтениемСокета = 400; - КонецЕсли; - Если _ИдентификаторРодительскогоПроцесса > 0 Тогда _КонтроллерРодительскогоПроцесса.НачатьНаблюдение(_ИдентификаторРодительскогоПроцесса); КонецЕсли; - _ЗапускательВебПриложения.Запустить(); + _ЗапускательСервиса.Запустить(_Хост, _Порт); + +КонецПроцедуры -КонецПроцедуры \ No newline at end of file +#КонецОбласти \ No newline at end of file diff --git "a/src/core/\320\232\320\273\320\260\321\201\321\201\321\213/HttpBin.os" "b/src/core/\320\232\320\273\320\260\321\201\321\201\321\213/HttpBin.os" index f9eb541..e04edc8 100644 --- "a/src/core/\320\232\320\273\320\260\321\201\321\201\321\213/HttpBin.os" +++ "b/src/core/\320\232\320\273\320\260\321\201\321\201\321\213/HttpBin.os" @@ -5,12 +5,13 @@ #Использовать fs #Использовать logos -Перем _ТаймаутЗапуска; // Число - Таймаут запуска сервиса в секундах -Перем _ТаймаутПроверки; // Число - Таймаут HTTP-проверки доступности в секундах -Перем _ИмяХоста; // Строка - IP-адрес или доменное имя хоста -Перем _Порт; // Число - Номер TCP-порта сервиса -Перем _Процесс; // Процесс, Неопределено - Объект запущенного процесса -Перем _Лог; // Лог - Логгер для отладочной информации +Перем _ТаймаутЗапуска; // Число - Таймаут запуска сервиса в секундах +Перем _ТаймаутПроверки; // Число - Таймаут HTTP-проверки доступности в секундах +Перем _ИмяХоста; // Строка - IP-адрес или доменное имя хоста +Перем _Порт; // Число - Номер TCP-порта сервиса +Перем _РасположениеКонтроллеров; // Строка, Неопределено - Путь к папке или файлу с кастомными контроллерами +Перем _Процесс; // Процесс, Неопределено - Объект запущенного процесса +Перем _Лог; // Лог - Логгер для отладочной информации #Область ПрограммныйИнтерфейс @@ -197,6 +198,28 @@ Возврат _ТаймаутЗапуска; КонецФункции +// Устанавливает путь к папке или файлу с кастомными контроллерами, определяющими маршруты сервиса. +// +// Параметры: +// Расположение - Строка - Путь к папке или файлу с контроллерами (например, "./controllers" или "./МойКонтроллер.os") +// +// Возвращаемое значение: +// ЭтотОбъект - Для возможности цепочки вызовов +// +Функция УстановитьРасположениеКонтроллеров(Расположение) Экспорт + _РасположениеКонтроллеров = Расположение; + Возврат ЭтотОбъект; +КонецФункции + +// Возвращает текущий путь к папке или файлу с кастомными контроллерами. +// +// Возвращаемое значение: +// Строка, Неопределено +// +Функция РасположениеКонтроллеров() Экспорт + Возврат _РасположениеКонтроллеров; +КонецФункции + #КонецОбласти #Область СлужебныеПроцедурыИФункции @@ -242,6 +265,12 @@ СтрокаКоманды.Добавить(Формат(_Порт, "ЧГ=")); СтрокаКоманды.Добавить("--parent-pid"); СтрокаКоманды.Добавить(Формат(ТекущийПроцесс().Идентификатор, "ЧГ=")); + + Если ЗначениеЗаполнено(_РасположениеКонтроллеров) Тогда + СтрокаКоманды.Добавить("--routes-handlers"); + СтрокаКоманды.Добавить(ОбернутьВКавычки(_РасположениеКонтроллеров)); + КонецЕсли; + СтрокаКоманды = СтрСоединить(СтрокаКоманды, " "); _Лог.Отладка("Запуск процесса: %1", СтрокаКоманды); diff --git "a/src/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\227\320\260\320\277\321\203\321\201\320\272\320\260\321\202\320\265\320\273\321\214\320\241\320\265\321\200\320\262\320\270\321\201\320\260.os" "b/src/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\227\320\260\320\277\321\203\321\201\320\272\320\260\321\202\320\265\320\273\321\214\320\241\320\265\321\200\320\262\320\270\321\201\320\260.os" new file mode 100644 index 0000000..7732c4c --- /dev/null +++ "b/src/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\227\320\260\320\277\321\203\321\201\320\272\320\260\321\202\320\265\320\273\321\214\320\241\320\265\321\200\320\262\320\270\321\201\320\260.os" @@ -0,0 +1,43 @@ + +#Использовать winow + +Перем _Хост; // Строка +Перем _Порт; // Число + +&Пластилин("Настройки") +Перем _НастройкиВебСервера; // Настройки - см. winow + +&Пластилин("ЗапускательВебПриложения") +Перем _ЗапускательВебПриложения; // ЗапускательВебПриложения - см. winow + +&Желудь +Процедура ПриСозданииОбъекта() +КонецПроцедуры + +// Запускает сервис через winow +// +// Параметры: +// Хост - Строка - IP-адрес или имя хоста +// Порт - Число - Номер TCP-порта +Процедура Запустить(Хост, Порт) Экспорт + + _Хост = Хост; + _Порт = Порт; + + НастроитьВебСервер(); + + _ЗапускательВебПриложения.Запустить(); + +КонецПроцедуры + +Процедура НастроитьВебСервер() + + _НастройкиВебСервера.РазмерБуфера = 0; + _НастройкиВебСервера.ИмяХоста = _Хост; + _НастройкиВебСервера.Порт = _Порт; + + Если ПолучитьПеременнуюСреды("HTTPBIN_IS_TEST_MODE") = "true" Тогда + _НастройкиВебСервера.ЗадержкаПередЧтениемСокета = 400; + КонецЕсли; + +КонецПроцедуры \ No newline at end of file diff --git "a/src/cmd/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\276\320\275\321\202\321\200\320\276\320\273\320\273\320\265\321\200\320\240\320\276\320\264\320\270\321\202\320\265\320\273\321\214\321\201\320\272\320\276\320\263\320\276\320\237\321\200\320\276\321\206\320\265\321\201\321\201\320\260.os" "b/src/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\276\320\275\321\202\321\200\320\276\320\273\320\273\320\265\321\200\320\240\320\276\320\264\320\270\321\202\320\265\320\273\321\214\321\201\320\272\320\276\320\263\320\276\320\237\321\200\320\276\321\206\320\265\321\201\321\201\320\260.os" similarity index 100% rename from "src/cmd/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\276\320\275\321\202\321\200\320\276\320\273\320\273\320\265\321\200\320\240\320\276\320\264\320\270\321\202\320\265\320\273\321\214\321\201\320\272\320\276\320\263\320\276\320\237\321\200\320\276\321\206\320\265\321\201\321\201\320\260.os" rename to "src/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\276\320\275\321\202\321\200\320\276\320\273\320\273\320\265\321\200\320\240\320\276\320\264\320\270\321\202\320\265\320\273\321\214\321\201\320\272\320\276\320\263\320\276\320\237\321\200\320\276\321\206\320\265\321\201\321\201\320\260.os" diff --git "a/src/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\237\320\276\320\264\320\272\320\273\321\216\321\207\320\260\321\202\320\265\320\273\321\214\320\232\320\260\321\201\321\202\320\276\320\274\320\275\321\213\321\205\320\232\320\276\320\275\321\202\321\200\320\276\320\273\320\273\320\265\321\200\320\276\320\262.os" "b/src/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\237\320\276\320\264\320\272\320\273\321\216\321\207\320\260\321\202\320\265\320\273\321\214\320\232\320\260\321\201\321\202\320\276\320\274\320\275\321\213\321\205\320\232\320\276\320\275\321\202\321\200\320\276\320\273\320\273\320\265\321\200\320\276\320\262.os" new file mode 100644 index 0000000..cc54481 --- /dev/null +++ "b/src/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\237\320\276\320\264\320\272\320\273\321\216\321\207\320\260\321\202\320\265\320\273\321\214\320\232\320\260\321\201\321\202\320\276\320\274\320\275\321\213\321\205\320\232\320\276\320\275\321\202\321\200\320\276\320\273\320\273\320\265\321\200\320\276\320\262.os" @@ -0,0 +1,83 @@ +#Использовать fs + +#Область ПрограммныйИнтерфейс + +// Находит и подключает кастомные контроллеры из путей, указанных в аргументах командной строки. +// +Процедура НайтиИПодключить() Экспорт + + МассивФайлов = НайтиФайлыИзПереданныхАргументов(); + + Для Каждого ПолноеИмя Из МассивФайлов Цикл + + Если ФС.КаталогСуществует(ПолноеИмя) Тогда + ПодключитьИзКаталога(ПолноеИмя); + ИначеЕсли ФС.ФайлСуществует(ПолноеИмя) Тогда + Подключить(ПолноеИмя); + Иначе + ВызватьИсключение СтрШаблон("Не удалось подключить контроллер '%1': файл не существует", ПолноеИмя); + КонецЕсли; + + КонецЦикла; + +КонецПроцедуры + +// Подключает контроллер из указанного файла. +// +// Параметры: +// ПолноеИмя - Строка - Полный путь к файлу контроллера (.os) +// +Процедура Подключить(ПолноеИмя) Экспорт + + Имя = Новый Файл(ПолноеИмя).ИмяБезРасширения; + + Попытка + ПодключитьСценарий(ПолноеИмя, Имя); + Исключение + ВызватьИсключение СтрШаблон( + "Не удалось подключить контроллер '%1': %2", + ПолноеИмя, + КраткоеПредставлениеОшибки(ИнформацияОбОшибке()) + ); + КонецПопытки; + +КонецПроцедуры + +#КонецОбласти + +#Область СлужебныеПроцедурыИФункции + +Процедура ПодключитьИзКаталога(Каталог) + + Файлы = НайтиФайлы(Каталог, "*.os"); + + Для Каждого Файл Из Файлы Цикл + Подключить(Файл.ПолноеИмя); + КонецЦикла; + +КонецПроцедуры + +Функция НайтиФайлыИзПереданныхАргументов() + + Файлы = Новый Массив(); + + НайденКлюч = Ложь; + Для Каждого Значение Из АргументыКоманднойСтроки Цикл + + Если НайденКлюч Тогда + Файлы.Добавить(Значение); + НайденКлюч = Ложь; + Продолжить; + КонецЕсли; + + Если Значение = "--routes-handlers" Тогда + НайденКлюч = Истина; + КонецЕсли; + + КонецЦикла; + + Возврат Файлы; + +КонецФункции + +#КонецОбласти \ No newline at end of file diff --git a/tests/HttpBin_test.os b/tests/HttpBin_test.os index 8cd4da2..151c36a 100644 --- a/tests/HttpBin_test.os +++ b/tests/HttpBin_test.os @@ -1,6 +1,8 @@ +// BSLLS:MagicNumber-off // BSLLS:UnusedLocalVariable-off #Использовать asserts +#Использовать 1connector #Использовать "../src/core" Перем HttpBin; // HttpBin @@ -14,8 +16,10 @@ &Тест Процедура ТестДолжен_ЗапуститьСервисСинхронноИОстановить() Экспорт + // Действие HttpBin = Новый HttpBin().Запустить(); + // Проверка Ожидаем.Что(HttpBin.Отвечает(), "Должен быть запущен").ЭтоИстина(); КонецПроцедуры @@ -41,4 +45,36 @@ // Проверка Ожидаем.Что(HttpBin.Отвечает(), "Должен быть запущен").ЭтоИстина(); +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ЗапуститьСервисСКастомнымКонтроллеромИзФайла() Экспорт + + // Подготовка + HttpBin = Новый HttpBin() + .УстановитьРасположениеКонтроллеров("./tests/fixtures/КастомныеКонтроллеры/КастомныйКонтроллер.os") + .Запустить(); + + // Действие + Ответ = КоннекторHTTP.Get(HttpBin.URL("/order/add"), , Новый Структура("Таймаут", 3)).Текст(); + + // Проверка + Ожидаем.Что(Ответ).Равно("success"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ЗапуститьСервисСКастомнымКонтроллеромИзКаталога() Экспорт + + // Подготовка + HttpBin = Новый HttpBin() + .УстановитьРасположениеКонтроллеров("./tests/fixtures/КастомныеКонтроллеры") + .Запустить(); + + // Действие + Ответ = КоннекторHTTP.Get(HttpBin.URL("/order/add"), , Новый Структура("Таймаут", 3)).Текст(); + + // Проверка + Ожидаем.Что(Ответ).Равно("success"); + КонецПроцедуры \ No newline at end of file diff --git "a/tests/fixtures/\320\232\320\260\321\201\321\202\320\276\320\274\320\275\321\213\320\265\320\232\320\276\320\275\321\202\321\200\320\276\320\273\320\273\320\265\321\200\321\213/\320\232\320\260\321\201\321\202\320\276\320\274\320\275\321\213\320\271\320\232\320\276\320\275\321\202\321\200\320\276\320\273\320\273\320\265\321\200.os" "b/tests/fixtures/\320\232\320\260\321\201\321\202\320\276\320\274\320\275\321\213\320\265\320\232\320\276\320\275\321\202\321\200\320\276\320\273\320\273\320\265\321\200\321\213/\320\232\320\260\321\201\321\202\320\276\320\274\320\275\321\213\320\271\320\232\320\276\320\275\321\202\321\200\320\276\320\273\320\273\320\265\321\200.os" new file mode 100644 index 0000000..afcf92e --- /dev/null +++ "b/tests/fixtures/\320\232\320\260\321\201\321\202\320\276\320\274\320\275\321\213\320\265\320\232\320\276\320\275\321\202\321\200\320\276\320\273\320\273\320\265\321\200\321\213/\320\232\320\260\321\201\321\202\320\276\320\274\320\275\321\213\320\271\320\232\320\276\320\275\321\202\321\200\320\276\320\273\320\273\320\265\321\200.os" @@ -0,0 +1,8 @@ +&Контроллер("/order") +Процедура ПриСозданииОбъекта() +КонецПроцедуры + +&ТочкаМаршрута("add") +Процедура Главная(Ответ) Экспорт + Ответ.ТелоТекст = "success"; +КонецПроцедуры \ No newline at end of file