Pull to refresh

Comments 100

UFO just landed and posted this here

Знаете как мучительно больно описывать на нем реализацию API который анализирует/исследует перехваченные пакеты camel, diameter, m3ua, esp, rtcp, sccp, tcap, s1ap (кароче все то что входит стандарт 4G/VoLTE) и отдает результат фронту ? Я возненавидел swagger после этого ада...

Ну, тут простой пример и велосипед для решения простой проблемы.

Для решения простой проблемы предлагаю использовать production-ready решение.
Допускаю, что в ряде сложных случаев Swagger окажется не лучшим вариантом, и вот там или велосипед или ручное описание эндпоинтов будет уместнее. Но точно не в примере автора.

То есть то, что описано в статье, вам подойдет больше? Точно?

Нет конечно. Но swagger (со своими ограничениями на тот момент) , изрядно вымотал. Причем начиналось все с простых структур, а когда они стали дополняться новыми свойствами и/или дорабатываться, вот тогда по полной хлебнули.

Хуже swaggera не знаю что может быть. В данном вопросе.

Можете рассказать, в какой организации вы занимались такими интересными вещами?

пока читал статью, тоже в голове крутился swagger, и только хотел про него спросить, а тут уже есть такой комментарий.

о чем вы вообще говорите, сейчас сваггер - это просто ui, который отрисовывает htmlку на основе OpenAPI. Аозможно, до OpenAPI 3. у сваггера был свой стандарт, но сомневаюсь.

Статья как раз и написана про Swagger. Только в 2016 году язык «Swagger» был переименован в «OpenAPI».

Но делиться же можно тем, что выдаёт Swagger-UI. Там как раз всё человеко-читаемо и в примерах.

Полностью поддерживаю! Swagger ни разу не подводил, даже с учётом сложнейших атрибутов описания вложенных схем данных.

Уверен, достаточно просто уметь писать код для генератора и уметь правильно настраивать его, и в 90% случаях этого будет более чем достаточно.

Вопрос к автору: автоматической генерации у вас все также нет. Как и совместимости с генераторами, например в typescript. Получается в целом вы разработали ещё одну спецификацию для описания API вручную..

Может быть вы ещё и миграции вручную пишете?)

Спасибо за вопрос! Я бы сказал, что мы разработали не еще одну, а первую в мире спецификацию, которую можно признать годной для описания API вручную)

добавьте в .Net проект OData и посмотрите что вам нарисует Swagger.

Возможность описывать простые вещи не сложно выглядит весьма заманчиво, нужно будет попробовать.

Всё нормально с OpenAPI, если его достаточно глубоко встроить в проект. К примеру, рабочие валидаторы/извлечение данных из запроса задаются с помощью OpenAPI, а схема ответов всегда автоматически проверяется в тестах. Без достаточной автоматизации это всё не работает.

Верно! Но «достаточная автоматизация» — это не такое уж частое явление в реальных проектах. А JSight неплохо работает, даже когда ничего не автоматизировано. Достаточно только поставить валидатор, который будет проверять все запросы и ответы.

Чем бы это ни закончилось, само начинание достойное и автоматически вписало вас в запутанную историю эволюции технологии. Так что поздравляю!))

Может для вашего случая описывать API вручную свое решение лучше чем OpenAPI, но в целом эта verbosity нужна, потому что openAPI используется не только чтобы его смотрели человеки, по нему например можно генерировать объекты, моки, постман коллекции, итд.

Если нужно посмотреть людям для этого сделали swagger ui, потому что сами понимают что читать это в raw виде не фонтан.

У нас на подходе конвертер JSight → OpenAPI, который решит этот вопрос.

Интернет кишит конвертерами "примерчик на json"->OpenAPI.

Так у нас же не "примерчик на json", а полноценный язык спецификации контракта.

При появлении конвертера я бы попробовал ваш JSight.

По моим наблюдениями OpenAPI-спека используется не только чтобы посмотреть на неё через призму Swagger UI, а как раз для разных вариантов автоматизации:

  • генерация классов реализации по спеке

  • генерация клиентов по спеке

  • валидация запроса на уровне реверс-прокси (до передачи в само приложение)

Именно, поэтому синтаксис OpenAPI выглядит нагруженнее, он просто содержит больше информации для большей функциональности. По итогу если в предложенный вариант добавить это (к примеру, ограничения, по которым на верхних уровнях валидируется запрос), получится OpenAPI, те самые куча строк.

Для юз-кейса внутренней работы подойдет, а вот если нужно взаимодействие между системами, конвенциональность OpenAPI сложно заменить

Именно, поэтому синтаксис OpenAPI выглядит нагруженнее, он просто содержит больше информации для большей функциональности

Никак не могу согласиться, на практике даже супер навороченные API на JSight выглядят гораздо проще, чем на OpenAPI. Более того, чем сложнее API, тем больше преимуществ у JSight.

То есть смысл языка в том, чтобы описывать API не схемой, а примером. Очень интересно. Ведь со сваггером так и получается - поставщик написал ямлик, а потребители смотрят примеры в swagger ui, а не схемы.

Именно! Так и происходит со сваггером.

Описания признаков {optional: true} {const: true} хочется заменить на {optional}, {const}

Вы же язык для людей делаете

Да, понимаю, мне тоже хочется. Но такие решения нужно принимать очень взвешенно. Если вам интересно, могу пригласить вас в наш Technical Advisory Board, в котором специалисты со стороны дают советы и голосуют за те или иные фичи. Как раз для принятия подобных решений.

разработчики терпеть не могут описывать AP

Это не совсем только про разработчиков. В агильном манифесте стоит - работающая система ценнее чем документация к ней. Так по этому принципу и живёт вся индустрия. Только дотошные и себя-нежалеющие этим занимаются. Считаю этот 'закон' неправильным и прививающий неправильную культуру, но ведь кто меня спрашивать будет.

К такому манифесту есть один очень простой вопрос: как определить что система работающая?

Например если система работает в 9-ти случаях из 10-ти, она считается работающей? То есть в одном случае из десяти результат ее работы не адекватный реализованной в ней функции. В этом случае оказывается что без документации нельзя понять, является ли система работающей-работоспособной. Таким образом ценность документации совершенно реабилитируется даже таким манифестом. Как видим это аксиома, поэтому конкретно вас спрашивать не обязательно :) !

Вы всё правильно говорите, но на практике это понимается так, что пока идёт ит-проект, значит ещё 'неработающая система" над которой надо ещё корпеть. А если так, то и документация подождёт. И проходит три года, а кроме use cases и epics, которые разбросаны по sprints или PIs нет никакой другой единой архитектурой, процессуальной, потоков данных, интерфейсов документации. Мой опыт уже в нескольких (>10) проектах, что все в проектах в самом начале громко и эмоционально возмущаются, что им мало той информации, которая им достаётся или имеют на руках от прежних создавателей. Но проходит время, а они сами ещё меньше после себя оставляют. И никакого диссонанса у них тоже не случается.

а еще часто встречаются деятели которые игнорируют существующую документацию с аргументами что это все фигня мы сделаем лучше. В результате перестает работать даже то что до этого работало.

Я к тому что существует другой аспект проблем с документацией - надо еще найти того кто в состоянии ее понимать и применять.

Способность работы с документацией как по созданию, так и по чтению-применению это признак профессионализма.

Отсутствие документации можно интерпретировать как уровень не выше любительского - мы вот вроде как что то сделали, но рассказать вам что же мы сделали мы не в состоянии.

Вот только в манифесте написано другое.
"Работающий продукт важнее исчерпывающей документации".
https://agilemanifesto.org/iso/ru/manifesto.html
Но это не только ваша проблема, действительно, многие читают этот пункт так, как им хочется.

Согласен, конечно работающая система важнее, чем документация. Но также и система с документацией ценнее, чем система без неё.

Всё правильно говорите. Просто не создав условий, где документация частично создаётся в момент создавания системы, то никакой документации никогда не будет. Конечно никому не нужна документация к системе, которая не работает. Но как про код говорят, что он пишется один, а читается много раз, так и с системой - у неё нет конечного состояния - её постоянно видоизменяют, поэтому надо иметь возможность, понимать, что она или её части делаю иди нет - и без наличия тех. документации всегда нелегко.

И ещё одна негативная сторона такого подхода - это концентрация нужной технической или специальной информации в отдельных головах. От этого создаётся вредная завимимомть.

В примере не понятно какой тип id в пути. Это может быть как integer, так и uuid. Бывает и такое, что одновременно в проекте используются два типа. А если принимаем не json, а файл? Или возвращаем файл?

Я только за, когда появляются новые спецификации. В этой борьбе останутся только самые удобные, но предложенный вариант таким пока что не выглядит.

К примеру, в данном случае проще было написать схемы, которые валидирую json, а потом на основе них делать OpanAPI, так к примеру делает FastApi c схемами Pydantic. Программист в итоге описывает только кодом схемы, что сильно удобнее, чем писать руками. Думаю так же будет и при проектировании

Конечно, JSight позволяет сделать всё, о чём вы пишите. Всё-таки мы используем его в реальных проектах, а не как игрушку. Подробная спецификация языка выложена по этим ссылкам:

Чем плох grpc/protobuf? Сильно менее развесистый чем openapi, позволяет этот самый openapi генерировать в масштабе, позволяет генерировать клиенты и документацию, вполне себе позволяет описывать рест, если правильно готовить, огромное количество инструментария и широкое принятие в коммьюнити.

В базе (примерный аналог вашего примера) будет так
Service Users {
  rpc GetUser(GetUserRequest) returns (User) {
    option (google.api.http) = {
      get : "users/{id=*}"
    };
  }
}

message GetUserRequest {
    string id = 1;
}

message User {
  string id = 1;
  string name = 2;
}

Если упороться по best practices, то как-то так
// Service to manage users.
Service Users {
  // RPC to get user by its resource name.
  rpc GetUser(GetUserRequest) returns (User) {
    option (google.api.http) = {
      get : "/v1/{name=users/*}"
    };
    option (google.api.method_signature) = "name";
  }
}

// The request message for
// [Users.GetUser][com.myproject.v1.Users.GetUser].
message GetUserRequest {
  // Resource name of the user to retrieve.
  // Format: users/{user}
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {type : "com.myproject/User"}
  ];
}

// User object.
message User {
  option (google.api.resource) = {
    type : "com.myproject/User"
    pattern : "users/{user}"
  };
  // Resource name of the user.
  // Format: users/{user}
  string name = 1;
  // Display name of the user.
  string display_name = 2;
}

И из второго примера уже можно даже какую-никакую документацию автоматически генерировать (по ней видно будет что она не совсем для людей, но это сильно лучше чем совсем ничего).

Отдельно отмечу что не могу смотреть на аннотации после поля, тем более на той же строке, аж кушать не могу. Не нравится.

Мой дурацкий мозг привык к аннотациям (а это ведь фактически аннотации) до аннотируемого предмета.

Ну и в ширь читать тоже привычка так и не появилась, продолжаем работать в узких окошках, просто теперь нескольких на экран. Но это мое, личное, не принимайте близко к сердцу.

gRPC — замечательный язык. И, всё-таки, у нас лучше) Напомню, как выглядит пример из статьи. Куда проще, чем у вас:

JSIGHT 0.3

GET /users/{id}
  200
    {
      "id" : 123,
      "name": "Tom"
    }

Может сложиться впечатление, что JSight хорош только для простых API. Это не так, при увеличении размера и сложности API, преимущества JSight возрастают многократно. Вот пример: https://editor.jsight.io/r/aL9X1jZ/1

Когда я смотрел на grpc, то не смог найти способа описания ограничений. Типа строка от 1 до 20 символов. Или то, что строка на самом деле uuid.

В grpc всё таки есть такая возможность или нет?

Встроенной нет, мы делали руками через самодельные аннотации в некоторых местах.

Примеры из недавней документации, которые будут неочевидными для Вашего способа описания:

  1. В запросе три параметра, на выбор можно отправлять первые два вместе или третий:

getDiploma:
{ "DiplomSeria": "FF",
  "DiplomNum": "1231231",
  "StudentID": 555}
  1. В запросе параметр может быть разных типов, исходя из этого код на стороне клиента определяет его по-разному (пример - вход на фейсбук с использованием логина, номера телефона или е-мэйла).

  2. Ограничения на длину параметра и regex, который его описывает.

Выглядит как антипаттерн для API, если честно.

Смешивать в одном методе много разных функциональностей — опасная вещь, которая приводит к неочевидным багам, когда метод делает совсем не то, что предполагает вызывающий код (который мог быть написан давно и знающие его люди давно уволились).

Лучше уж выставлять отдельные методы для получения дипломов по серии/номеру и по ID студента.

Соглашусь. Но в реальной практике встречал ещё более страшные вещи. Например, когда "в мир" торчит один эндпоинт, первым параметром которого идёт тип запроса - сведения о студентах, информация о площадях для аренды, сведения о начисленной стипендии и тд. В зависимости от типа запроса, изменяются поля.
Понимаю, что так делать не хорошо. Но такие API уже существуют.

Спасибо за примеры! Все приведенные варианты легко и органично описываются на JSight:

Пример 1

JSIGHT 0.3

GET /diploma
  Request
    @studentIdQuery | @diplomaQuery
  200
    {} // Some response


TYPE @studentIdQuery
{
  "StudentID": 555
}

TYPE @diplomaQuery
{
  "DiplomSeria": "FF",
  "DiplomNum": "1231231"
}

Вот ссылка на пример 1 с подсвеченным синтаксисом: https://editor.jsight.io/r/eBVvgBJ/1

Пример 2

JSIGHT 0.3

GET /something
  Request
    {
      "parameter": @email | @phoneNumber | @login
    }


TYPE @email
  "email@example.com" // {type: "email"}

TYPE @phoneNumber
  "+7123456789" // {regex: "\\+\\d+"}

TYPE @login
  "stringlogin" // {regex: "^[A-Za-z0-9]+$"}

Ссылка на пример 2 с подсвеченным синтаксисом: https://editor.jsight.io/r/DBYvpj3/3

Пример 3

JSIGHT 0.3

GET /something
  Request
    {
      "parameter": "some-limited-regex" // {regex: "^[a-z-]+$", maxLength: 100}
    }

Ссылка на пример 3 с подсвеченным синтаксисом: https://editor.jsight.io/r/YLD976d/2

Видел Cadl от Microsoft, но ещё не пробовал. Обещают решение многих проблем с OpenAPI

Они его переименовали в typespec

В этом случае программист вынужден написать на языке OpenAPI 23 строки (!):

Вы серьезно думаете, что программисты пишут OpenAPI-спеку вручную? Ее генерируют из кода. Специальный код пробегает по вьюхам и генерит пути, методы, схемы. Описание хранят в docstring-ах.

Об этом сказано в статье. И утверждается, что работает это примерно в четверти случаев, а в трёх четвертях - включая случаи довольно больших проектов - так не получается.

В таких случаях код дорабатывают так, чтобы он был как можно более декларативным. Чтобы малейшие изменения отражались и в API, и в спеке. Иначе вы обречены на рассинхрон, когда в коде одно, а в спеке другое.

Должен сказать, что это работает превосходно, просто почти всегда корявые разрабы пытаются нарушать банальные стандарты RestAPI. Да даже верные статусы ответов встречаю в трети проектов

Да, сначала пишут спеку, которая потом может сгенерить все модели. Автор пишет про подход Spec First.

Я не думаю, что программисты пишут OpenAPI-спеки вручную, я это наблюдаю в реальных проектах)

Это плохо. То, что они пишут ее на другом языке, не снимает проблему. Они не должны писать спеку в принципе, она должна генерироваться из кода.

Вы очень безапелляционно говорите.

В обоих случаях есть свои плюсы и минусы. В моей практики использовались оба подхода (генерация кода по спеке и генерация спеки по коду) и выбор зависит от конкретных ситуаций.

Code first удобен на маленьких или типовых crud апи. А часто хочется апи сначала спроектировать, а потом уже кодить. Да даже в самом банальном сценарии, где фрон и бэк разрабатываются разными людьми параллельно, лучше иметь четкую спецификацию и получить один туннель вместо двух.

Поддерживаю, более того в варианте с APIFirst типовые запросы/ответы можно вынести в схемы и переиспользовать в более высокоуровневых элементах (все что касается авторизации, базовых ошибок, Item->Items, и т.д.) в итоге получаются вполне компактные спеки даже в средних по размеру проектах. Еще + в том что для изменения в формате логирования/трейсов/etc достаточно поменять темплейт и перегенерировать код

Не получится, если хотим api-first подход.

На самом деле вы просто выкинули описание схем из OpenAPI документации, заменили required на optional, выбросили пару параметров. Разве нет?

Если вам типизация не нужна, другим может понадобиться, и ваш язык превратится в диалект OpenAPI

Можете написать транслятор вашего языка в OpenAPi и обратно, кстати

Разве нет?

Если судить по их гитхабу, куда осторожно посылали - не совсем.

То что они сделали, как я могу судить - это заменили required на optional и сделали объявления примитивных типов выводимыми из примера. Плюс усложнили грамматику языка, чтобы генератор кода по описанию было сложнее писать. (OpenAPI не зря почти на все есть)

C пользовательским типами, кстати, описываемая идея ломается, потому что используется не пример объекта, а идет ссылка именно на тип.

Согласен, что машиночитаемость JSight сильно уступает OpenAPI, мягко выражаясь) Но у нас уже есть библиотека, которая парсит JSight-текст и выдаёт аккуратное json-описание (https://github.com/jsightapi/jsight-server). К тому же, планируется выпуск конвертера в OpenAPI.

C пользовательским типами, кстати, описываемая идея ломается, потому что используется не пример объекта, а идет ссылка именно на тип.

Стоит подумать над вариантом, когда пользовательский тип определяется не где-то там в области определений, а по месту его применения при первом (или любом) использовании

JSIGHT 0.3

GET /user/{id}
  200
    { // {TYPE: @user}
      "id": 1,
      "name": "John",
      "friend": @user // {optional: true}
    }

Стоит подумать над вариантом, когда пользовательский тип определяется не где-то там в области определений, а по месту его применения при первом (или любом) использовании

Не очень осмысленно. Потому что тип в значительной части определяется, чтобы использоваться в разных местах. Поэтому пусть уж будет сам по себе, а не закопанным где-то внутри чего-нибудь.

Это верно. Но также верно то, что в некоторых простых случаях, вроде моего примера, запись становится короче и лучше воспринимается.

Поэтому еще возможны варианты:

  • Сделать такое определение альтернативным - пусть разработчик API сам решает, каким способом лучше определять тип.

  • Ограничить видимость такого определения типа объектом, в котором он определен. То есть за пределами объекта нельзя будет сослаться на @ user

Да, транслятор туда и обратно планируется.

Типизация в языке JSight, конечно, есть, просто в статье мы об этом не писали.

Вот пример с пользовательским типом:

JSIGHT 0.3

GET /user/{id}
  200
    @user

TYPE @user
{
  "id": 1,
  "name": "John",
  "friend": @user // {optional: true}
}

Тот же пример в редакторе: https://editor.jsight.io/r/KBnr8B1/2

Я все-таки склоняюсь к Code First варианту (в экосистеме Kotlin/Java). При этом нет указанных в статье проблем:

1. Во многих (особенно, в крупных) проектах, в проектировании API, помимо разработчиков, участвуют ещё несколько человек: архитекторы, системные аналитики, QA-инженеры, заинтересованные лица из других команд.

Обсуждать можно по swagger ui. Записывать решения как угодно (любой псевдокод подойдет), а реализацию обновит один из программистов за несколько минут сразу после совещания.

Во многих (особенно, в крупных) проектах, используются устаревшие языки или фреймворки, для которых просто не существует генераторов OpenAPI-спецификаций. В итоге, часть API оказывается без документации (и как правило это очень важные API, работающие в компании уже много лет!).

За другие платформы не скажу -- в деталях не знаю, но на JVM прекрасно работает. Нельзя назвать Java чем-то новомодным или для маленьких проектов.

3. Генераторы OpenAPI-спецификаций нередко содержат ошибки, которые порой не позволяют сгенерировать то, что нужно

Все API так описываю, на практике не встречался. Если даже встречусь, то есть несколько вариантов обхода:

  • самый простой -- добавить детали в описание

  • сложнее -- написать промежуточный api, который бы делал нужные манипуляции

  • еще сложнее -- самому исправить генератор

Опять же -- для меня это теория, т.к. багов не припомню.

4. OpenAPI-генераторы способны сгенерировать только часть документации. Но ведь надо еще добавить подробные описания, примеры использования, примеры сообщений (особенно это важно в крупных проектах). Всё это нужно делать вручную и каким-то образом заталкивать в программный код, чтобы потом, на его основе, генератор создал полноценную спецификацию.

Это просто неправда. Общую информацию можно добавить через специальный бин, информацию на конкретный элемент соответствующими аннотациями. Более того, можно через настройки сделать несколько вариантов спецификации -- например, с внешней авторизацией через API-gateway и с внутренней для внутренних запросов.

Что же касается JSIGHT -- мне все равно -- если станет популярным, то из тех же аннотаций будет генерироваться и эта версия спеки. А руками в чате или на доске все равно проще буду писать (например, обычно без 200).

При этом добавлю, что, как все на свете, OpenAPI имеет ограничения. Особенно плохо с генерацией клиентов (а не спецификации) -- их писали очень странные люди, так что многие клиенты по спецификации не генерируют. Но это другая история.

По OpenAPI спеке можно генерить клиентский код, интерфейсы в тайпскрипте напр. в вашем любимом фреймворка, а с вашим языком вы решаете такую задачу?

Может стоило написать конвертер из вашего формата в OpenApi, а дальше уже использовать готовые инструменты?

Да, Антон, конвертер пишем, скоро будет.

Описываем в конфлюенсе с помощью вложенных буллитов форматы запроса и ответа. Пишем имя параметра, обязательность, формат, пример заполнения. Такое описание одинаково удобно для бизнеса, для разработки, тестировщиков и девопсов.

Верно, многие так делают. Только никто не может сказать, актуально это описание, или устарело.

Ну тут просто: если код отличается от описания, то баг исправляется в рамках стандартной процедуры работы с ошибками. Владелец функционала решает, где правильно, и вносит правки

И все-таки, никто не знает, адекватно описание API или устарело. Я как архитектор скажу так: когда приходишь на новый проект и просишь документацию, то практически никогда (!) она не соответствует реальности.

Как архитектору принимать решения о трансформации архитектуры, если он даже не может понять, как сейчас система работает?

И почему-то никто даже не упомянул GraphQL. Там ведь нет большинства этих проблем в принципе даже.

Из личного опыта все упирается в начальный настрой конкретного разработчика, который отвечает за этот аспект проекта. Если руки чешутся, то в абсолютно любом стандарте и технологии можно найти минусы и оправдать написание своего велосипеда. 🤷‍♂️

Лучше сразу на apiary.io тогда. Вообще, да, странно в такой статье его не упомянуть.

Api Blueprint для описания типов данных использует ту же самую громоздкую JSON-Schema, что и OpenAPI. Поэтому нам он тоже не подошёл.

Крайне воодушевлен такими начинаниями, тема донельзя больная и близкая.

Позволю себе высказать очень настоятельное пожелание - очень хочется поддержку шаблонов/дженериков.

Особо приятно такое в связке с кодогенерацией, да и без неё в целом тоже.

З.ы. В свое время задумывался о чем-то подобном, но скорее на основе тайпскрипта. Увы, ни навыкоы, ни времени, ни решительности не хватило

Кирилл, спасибо! Можете подробнее раскрыть, что вы имеете в виду под шаблонами/дженериками? На каком-нибудь простом примере. У нас есть поддержка макросов, возможно, их получится расширить до дженериков, но нужно понять, что именно вы имеете в виду.

Кодогенерация будет обязательно. У нас на ближайшее будущее запланированы две большие фичи: конвертер в OpenAPI и обратно; кодогенератор.

Также большая просьба поставить нам звезду на Гитхабе :)

Посмотрел макросы. Немного не то

Хочется что-то похоже на дженрики в typescript/Java и почти везде где есть.

Будет что-то вроде

TYPE response<T>
{
"field1": 1,
"field2":T
}

И использовать это примерно как

TYPE othertype
{
"field": response<string>
}

В моих примерах это несколько выбивается из идеи описания типов примерами, но если это как-то адаптировать, будет очень классно

Да, понял, отличная идея! Спасибо, подумаем)

Позволю себе высказать очень настоятельное пожелание - очень хочется поддержку шаблонов/дженериков.

"Каждый язык конфигурирования и описания стремится стать Тьюринг полным языком программирования" (с) не мой. Если идти этой дорогой - оно плохо кончается из за Feature Creep.

А так - препроцессоров для реализации и шаблонов и дженериков и так хватает же? Собираем все в несколько проходов и получаем то что требуется. Синтаксис что так, что так учить придется. Либо встроенный, либо шаблонизатора.

А если имеется в виду, что хочется, что дженерики в описании должны транслироваться в дженерики целевого языка - то все сразу поломается на том, что они в разных языках не совсем одинаковые и не всегда даже существуют. Для языка описания API это не очень хорошо.

  1. Т.е. вы не ослили настройку бизнес-процессов разработки и вместо нее разработки очередной язык? Уже то, что вы вдесятером проектирует АПИ, уже изумляет. Считаете новый язык разработать проще? А кто сейчас его поддерживать будет?

  1. OpenAPI довольно развит. К нему есть претензии, но вы когда свой язык до того же уровня доведете? У вас даже толкового описания элементов АПИ не наблюдается. Title, description где у вас? А ограничения формата комментариями?

  2. Вообще-то OpenAPI неплохо кастомизируется. Можно писать собственные шаблоны и генераторы. Можно даже подключать собственные шаблонизаторы. Я так понял, вы эту тему тоже не осилили.

  3. Я так и не увидел у вас как гененится документация. То, что вы показываете, к доке не имеет отношения, а является спекой. Не вижу как вашу спеку можно превратить в табличное описание, которое попадет в руководство по интеграции.

  4. В новой версии OpenAPI поддерживаются асинхронные протоколы. Не только REST. Вы в своем синтаксисе задумывались вообще о других технологиях типа gRPC, Kafka, etc?

Сергей, спасибо за подробный и вдумчивый комментарий!

Отвечу по порядку:

  1. Понимаю ваше изумление. Разработка нового языка и инструментов к нему стоит на два порядка дороже, чем настройка бизнес-процессов или проектирование API, даже вдесятером. Вряд ли можно порекомендовать решать проблемы с API таким образом. JSight, конечно, нужно рассматривать как отдельный проект с отдельным финансированием. На поддержку также средства есть, кроме того есть расчёт на open-source сообщество.

  2. Один из наших принципов — полное соответствие функциональности OpenAPI. В статье мы описали основную идею, и не рассказывали о нюансах языка. Раз уж зашла речь, скажу, что JSight обладает всеми must-have фичами, и даже больше: пользовательские типы, макросы, ссылки на файлы (include), наследование (allOf), вариативность (or), регулярные выражения, перечисления (enums), пользовательские комментарии, заголовки, query-запросы, маркдаун, и т. д. Title и Description тоже, соответственно, есть. Всё это можно найти в спецификации языков JSight API и JSight Schema.

  3. OpenAPI — классный язык, мы не спорим. Просто хочется ещё лучше :)

  4. Верно, в этой статье речь идёт больше о языке создания спецификаций API. Документацию по JSight-спецификации можно сгенерировать в нашем онлайн-едиторе: https://editor.jsight.io (там есть кнопочки "Export" и "Download"). В этой документации можно включить табличное описание либо с помощью кнопки "Table View" для каждой схемы отдельно, либо сразу для всех схем в настройках.

  5. Да. Поддержка других видов API — это один из наших главных принципов. Мы сразу же проектировали язык не только под стиль REST, но и под стили RPC и Event-bus (асинхронные протоколы). Вот, например, как выглядит JSON-RPC API:

JSIGHT 0.3

URL /rpc-api
  Protocol json-rpc-2.0

  Method add         // Addition.
    Params
    {
      "par1": 123.5, // Parameter 1.
      "par2": -1.0   // Parameter 2.
    }
    Result
      122.5          // Result of addition.

Вот более подробный пример с JSON-RPC: https://editor.jsight.io/r/aL9XejZ/1.

Ещё из интересного есть Scala и sttp.Tapir.

Просто описываются классы запросов и ответов (есть ещё опционально ошибки и аутентификация), в подавляющем большинстве случаев схема выводится автоматически, нет нужды её описывать руками, а если где-то и встречается кастомные типы, то достаточно 1 раз описать его схему или правила вывода, а не каждый конкретный случай использования.

Нет схемы для API - не компилируется

Реализация не соответствует API - не компилируется

Примеры описываются в коде, а не в схеме.

SwaggerUI показывает красоту.

Как итог - API и документация практически неразрывные вещи.

Прекрасно! Но это, всё же, Code First подход, если я правильно понимаю.

Да, но бойлерплейта, связанного с описанием спецификации очень мало, т.к. язык поддерживает вывод "тайпклассов", то есть можно описывать не конкретные схемы, а правила их вывода, поддерживаются generic и т.п. возможности "не повторяться"

Скажите, вы этот подход используете только в разработке внутренних API? Или это относится и к API для внешнего использования?

Если да, то как вы передаёте спеку внешним пользователям? Обучаете их своему JSight?

Используем и для публичных API. Внешнему пользователю обычно нужна HTML версия доки. В ней предстоит еще много доработок, но в текущем виде для моделей данных есть табличный вид, хотя и без него от особенностей jsight там остаются только правила для моделей, которые интуитивно понятны и все равно не имеют общепринятого формы записи.

Sign up to leave a comment.

Articles