Swagger в RBK.money — про наши внешние API

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


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



    Так вот, о чем я сегодня. В этом посте я расскажу, почему мы в RBK.money используем Swagger, как он помогает нам в работе и какие у него есть косяки.


    Итак, Swagger aka OpenAPI (https://swagger.io/). Swagger хорош тем, что он подразумевает сразу кучу полезных плюшек, вот смотрите:


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

    Схема описывается лаконичным итоговым YAML или JSON-файлом, её легко провалидировать, а интерфейсы можно оперативно генерировать для любого языка. Описать интерфейс на Swagger тоже можно быстро. Тут как — в принципе, особых альтернатив и нету. Конечно, можно вспомнить xml, но лично мне он представляется какой-то неудобной древностью чисто с эстетической точки зрения. Мне это на самом деле кажется важным, потому что я пытаюсь относиться к коду как товарищ Туполев к самолетам:



    Хорошо летать могут только красивые самолеты


    Да, тут можно похоливарить насчет точности цитаты, да и вообще вспомнить Марселя Дассо и его «un bel avion est un avion qui vole bien», но мы пока не про это. Надеюсь, суть я всё же передал. Так вот. В моём случае, в Sublime Text то, что мы делаем, должно выглядеть красиво. Если это спецификация, то и она тоже. К xml этот тезис, увы, не относится.


    А вот YAML — там нет мусора, там только то, что ты написал.


    Спецификация Swagger занятная ещё и тем, что можно нормально делать description, definitions. И таким макаром получается, что сам протокол является интерактивной документацией. В арсенале масса классных штук, которые позволяют сделать из YAML вполне приличную вещь. Всё, что мне требуется для её нормальной поддержки — чуток подредактировать спеки в YAML, и всё, у меня готовая документация.


    Пример интерактивной документации, построенной на спеке с использованием скриптов ReDoc используется нами на нашем портале разработчика: https://developer.rbk.money/api/.


    Ещё о плюсах


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


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


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


    Типов данных хватает с запасом, у нас пока за всё время не было ситуаций, чтобы нам не хватило именно типов данных.


    И вот ещё что. Собрал ты распределённую команду, замокал всё, кто-то пишет клиент, кто-то — сервер, ты вообще пошел писать UX/UI для бекенда. Сам бекенд делать долго, но у нас есть swagger-спека, берем её, генерим по ней мок-сервер — и всё. Можно начинать писать фронтенд ещё до того, как бекендер в принципе допьёт свой кофе и включит уже комп, чтобы начать работать. А ты потом пишешь бекенд, меняешь endpoint и получаешь готовое решение. То есть не только можно распараллелить разработку, но и отвязать зависимости команд. Это вот крутая штука.


    Минусы, минусы-то есть?


    А куда без них, это отличная штука, но пока не серебряная пуля.


    Во-первых, Swagger codegen по факту не работает. Нам пришлось его перепиливать, да так увлеклись, что в итоге свой написали. Теперь можем генерить методы и классы. А так на бумаге всё было зашибись, берёшь спеку, генеришь клиент, генеришь сервер, радуешься жизни. На самом же деле тебе ни клиента, ни сервера. Услышав пару отчаянных матов от разрабов на эту тему, я принял решение писать своё.


    Во-вторых, у нас на старте был конфликт вот такого рода. Внутри платформы у нас Thrift. Наружу у нас при этом — Swagger. Я, кстати, до сих пор не могу себе признаться, что точно всё правильно сделали, что хорошо, что не везде сразу Swagger взяли. А так было в два раза больше работы, трансляторы протоколов и прочие радости.


    Кстати, почему Thrift — у нас были готовые наработки на rpc и клиент-серверные штуки, которые решали транспортные проблемы. Мы быстро туда прикрутили реализацию Google Dapper, реализацию идеи о том, что в распределённой микросервисной среде надо обязательно уметь трассировать запросы. То есть понимать, по какой цепочке микросервисов эти самые вопросы идут, и где тупят, если тупят. В одном из следующих постов об этом будет подробнее. И вышло, что всё есть внутри, а вот наружу надо делать что-то своё.


    А ещё чейнджлог. Тут дико слабая автоматизация чейнджлога. Меняешь какие-то методы, чтобы показать, что раньше в этом методе было одно — а тут теперь другое, так вот, кроме диффов в репозитории такого нет. Поэтому чейнджлог надо писать руками. А раз что-то приходится постоянно делать руками, потому что не получилось автоматизировать — это не будут делать вообще. И оно сейчас немного мешает в работе, я, например, не могу клиентам нормально объяснить на пальцах, в чем у нас разница между API 2.0 и API 3.0 — чейнджлога-то нету.


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


    Итого


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


    А даже если бы и не нравилось что-то большее — пока сваливать-то некуда. Вдруг подскажете в комментах что-то интересное по теме. Само собой, на какой-то отдельный костыльный самопал я Swagger не променяю, потому что что-то костыльное и своё — оно в итоге ещё хуже, чем что-то средненькое, но стандартное.


    И вот почему.


    Я в принципе был бы очень рад, если бы все, кто сейчас открывает API, пошли бы в сторону Swagger, смотрите — это коммьюнити, прежде всего. Коммьюнити, которое придет, посмотрит на стандарт, на спецификацию, и это начнет развиваться. Где кому чего не понравится — допилят и сделают лучше. Стандарт тут всегда предпочтительнее.



    Потому что стандарт можно доработать. А что-то своё можно начать делать в отдельном углу, а потом забить на это, или вообще — тянуть в разные стороны постоянно.


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


    Взять и оценить можно вот тут.


    Ремарка после обсуждения в комментариях. Если начинать с нуля, то стоит сразу закладываться на спецификацию Open API Specification 3.0. Там кардинально больше плюшек, можно сказать, что это практически новый стандарт, сильно отличающийся от 2 версии.

    RBK.money
    RBK.money – современная платежная платформа

    Comments 24

    • UFO just landed and posted this here
        0

        Эрланговый стафф. Историю изменений, в принципе, можно смотреть в нашем форке, он открытый https://github.com/rbkmoney/swagger-codegen/pulls?q=is%3Apr+is%3Aclosed

        • UFO just landed and posted this here
            0

            Да, полностью. Пока не припомню чтобы в какие-то ограничения упирались

      • UFO just landed and posted this here
          +1
          Ну сейчас уже использовать swagger смысла особо нет. А у вас в stable именно он.
            0

            О, а что появилась адекватная альтернатива?

              +1
              OpenAPI v3.0 он уже не swagger банально даже по формату.
                0

                Согласен, стоило сделать об этом ремарку в статье, поправлю.

            +1
            В OpenApi очень напрягает, что они устроили внутри свой не совсем совместимый форк JSON Schema (например nullable-поля по-разному делаются, и нет части фич типа patternProperties), в итоге смешанные чувства — вроде и стандарт используешь, но и велосипед в наличии.
            0

            openapi-codegen работает значительно лучше, по крайне мере для python и typescript. Он поддерживает не все фишки, к сожалению, но клиенты работают довольно неплохо.

              0
              А как тестируете соответствие спеки и фактической системы? Кормите логами прода для профилактики?

              PS. пользуясь случаем, привет пятисотому аски-котику, который был не валидным json
                0

                Интеграционным тестом. При каждой сборке любого микросервиса для любой ветки в CI разворачивается инстанс процессинга и запускается моча-тест. Он покрывает все 100% методов внешних апи. Для него генерится клиент сваггер-кодгеном из нужной ветки со спекой.


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


                Ну а если серьезно, то котик отдается только со статусом HTTP/500, тут уж не до валидации (это для тех кто не еще работал с нами).

                  +1
                  Котик шикарен :)

                  Его, кстати, можно принарядить:
                  {
                  "error": "oops, 504",
                  "message":"Мы все починим, а пока держите котика:",
                  "01":"───────────────────────▄▄",
                  "02":"──▄──────────────────▄███▌",
                  "03":"─▐██▄───────────────▄█░░██",
                  "04":"─▐█░███────────────██░░░██",
                  "05":"─▐█▌░███──────────██▌░░░▐█",
                  "06":"──██▌░░██▄███████▄███▌░██",
                  "07":"──███▌░███░░░░░░░░░░█▌░█▌",
                  "08":"───██████░░░░░░░░░░░░███",
                  "09":"───████░░░░░░░░░░░░░░░██",
                  "10":"───▐██░░░░░▄█░░█▄░░░░░██▌",
                  "11":"───██▌░▄▓▓▓██░░██▓▓▓▄░██▌",
                  "12":"───██░▐██████░░██████▌░██",
                  "13":"──▐██░█▄▐▓▌█▓░░▓█▐▓▌▄█░██",
                  "14":"──███░▓█▄▄▄█▓░░▓█▄▄▄█▓░██▌",
                  "15":"──██▌░▀█████▓░░▓████▓▀░░██",
                  "16":"─▐██░░░▀███▀░░░░▀███▀░░░██",
                  "17":"─███░░░░░░░░▀▄▄▀░░░░░░░░██",
                  "18":"─██▌░░░░░░░░░▐▌░░░░░░░░░██▌",
                  "19":"─██░░░░░░░░▄▀▀▀▀▄░░░░░░░░██",
                  "20":"▐█▌░░░░░░░▀░░░░░░▀░░░░░░░██▌",
                  "21":"██░░░░░░░░░░░░░░░░░░░░░░░░██",
                  "22":"██░░░░░░░░░░░░░░░░░░░░░░░░██",
                  "23":"██░░░░░░░░░░░░░░░░░░░░░░░░██",
                  "24":"██░░░░░░░░░░░░░░░░░░░░░░░░██",
                  "25":"██░░░░░░░░░░░░░░░░░░░░░░░░██",
                  "26":"██░░░░░░░░░░░░░░░░░░░░░░░░██",
                  "27":"██░░░░░░░░░░░░░░░░░░░░░░░░██"
                  }
                  
                0
                у Сваггера одна беда — он кукисы не поддерживает (или только за денюжку?), то есть если ваше API пишет токен сессии в куки, то вам такое API с помощью Сваггера не удастся проверить, то есть не получиться на Свеггере сделать интерфейс для работы с вашим API.
                OpenAPI v3.0 конечно отличается от Swagger v2.0 и в документации примеры не на все случаи жизни, но за два дня можно полностью освоиться.
                Но конечно если у вас кодогенератор для версии два, то на версию три этот кодогенератор придётся существенно переписать. И тут то станет понятно насколько ваши абстракции абстрактны, и так ли уж сильно они оторваны от реализации
                  +1

                  Платной версии нет, это ж просто спецификация, ее разве что патентом закрыть.


                  Но тут вопрос в другом — это спецификация в REST иделогии, соответственно как считаете, сессионный токен в кукисах соответствует ей?

                    0
                    Платной версии нет, это ж просто спецификация

                    Я занимался этим вопросом полгода назад и наверное что то путаю, возможно у них есть опция развернуть Сваггер на каких то их серверах где будет поддержка кукисов, а может быть они в версии 2 не поддерживается вообще ни как, а в версии 3 уже есть варианты (если верить коменту ниже).
                    В чём была моя задача? Я писал новое API (публичное), но конечно в рамках имеющегося «фреймворка», сиречь кодовой базы, и аутентификация была реализованы через кукисы.
                    И мне захотелось сделать доку на новое API на Сваггере, за одно у наших клиентов появилась бы возможность интерактивно знакомиться с нашим новым API.
                    Спеку на API я с горем пополам написал, но дальше аутентификации работать с интерфейсом, который Сваггер нагенерил, было нельзя, потому что кукисы не поддерживались, при этом в IDEA (PhpStorm) я сделал scratch и отлично у меня запросы выполнялись, потому что у IDEA между запросами кукисы сохранятся.
                      0
                      А, вот тут уточнение — в swagger-интерфейсе (и в OpenAPI) куки так и не поддерживаются. Пишут что это ограничение браузеров (возможно CORS?).
                      Спека позволяет описать куки-авторизацию и на swaggerhub тоже работают куки.
                    0
                    OpenAPI v3.0 поддерживает кукисы. И если вы начинаете новый проект, то какой смысл брать устаревший Swagger когда есть OpenAPI.

                    Есть, зато, другая проблема — описание для интерфейса с вебсокетами не поддерживается. И ничего, в общем-то, подходящего тоже нет…
                    Вроде бы был RAML, но он тоже, кажется, притормозил в развитии (читал что разработчики ушли в OpenAPI)
                    0

                    Использовали 2 года назад js и php клиенты и генерацию документации. Тоже дописывали его. Были проблемы с неймспейсами в php. На фронте токен в хидеры добавляли.

                      +1

                      Там уже выше упомянули openapi-generator, но хочу более развернуто описать ситуацию


                      Жил-был swagger-codegen, поддерживал себе спецификацию swagger 2.0 и не тужил. Потом вдруг откуда ни возьмись появилась OpenAPI Spec 3.0 с новыми вкусными фишками, и разрабы swagger-codegen рьяно принялись пилить его поддержку. Так рьяно, что сильно ушли в сторону от изначального кода, поддерживающего 2.0. На сегодняшний дней поддержка 3.0 все еще ведется в отдельной ветке, и на dockerhub пушатся отдельные образы для v2 и v3. Печаль :(


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


                      Пару лет назад (может, поменьше) части разработчиков swagger-codegen эта ситуация сильно не понравилась, они сговорились и сделали свой форк — openapi-generator. Этот форк (на настоящий момент уже значительно разошедшийся со своим прародителем) поддерживается гораздо более активно, следует более агрессивному расписанию выката релизов. Единая кодовая база поддерживает и v2, и v3 — один образ, одни и те же люди на поддержке. Комьюнити больше — больше сторонних патчей.
                      Из минусов — апгрейд минорной версии таки бывает не вполне обратно совместимым.

                      Only users with full accounts can post comments. Log in, please.