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

Мы описываем наш API, используя Спецификация OpenAPI, который определяет стандартный интерфейс, не зависящий от языка. Использование OpenAPI позволяет нам программно взаимодействовать с метаданными API для создания контента, примеров кода, инструментов и других ценных ресурсов.

Вот пример одной из наших конечных точек API. Он подчеркивает, как мы определяем ПОЛУЧИТЬ запрос и какой удачный(200 ОК) ответ выглядит так:

info:
  title: Backend
  description: A backend is a server identified by IP address or hostname.
  version: 1.0.0

externalDocs:
  url: 

servers:
  - url: 

paths:
  /service/{service_id}/version/{version_id}/backend:
    parameters:
      - $ref: "service.yaml#/components/parameters/service_id"
      - $ref: "version.yaml#/components/parameters/version_id"
    get:
      summary: List backends
      description: List all backends for a particular service and version.
      operationId: list-backends
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/backend_response"
              examples:
                body:
                  value:
                    - $ref: "#/components/examples/backend_response"
Войти в полноэкранный режим

Выйти из полноэкранного режима

НАМЕКАТЬ: Чтобы уменьшить дублирование, мы часто помещаем общие параметры и значения во внешние файлы, на которые можно ссылаться отдельно, мы также делаем это для больших вложенных разделов, которые в противном случае могут затруднить фокусировку на критических точках спецификации API (см. $ref).


Проблема: 700 конечных точек, 7 языков

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

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

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

  • Ресурсы для постоянного повторного посещения клиентов при изменении API, а также огромный объем работы по поддержке такого большого количества конечных точек.


План

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

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


Решение: Генератор OpenAPI

Для достижения этих целей мы исследовали различные варианты и в конечном итоге решили использовать форк официального сайта, созданный сообществом. Swagger CodeGen проект: Генератор OpenAPI (ОАГ).

OAG — это управляемый шаблонами движок, который может генерировать документацию, клиентов API и серверные заглушки на разных языках, анализируя вашу спецификацию OpenAPI.


Как работает ОАГ

ОАГ – это Ява проект, который использует Шаблоны усов настроить каждый поддерживаемый язык программирования. Он предоставляет интерфейс командной строки openapi-generator-cli который загрузит соответствующий файл JAR и вызовет исполняемый файл Java для запуска OAG.

Чтобы создать новый клиент API, мы передаем openapi-generator-cli нашу спецификацию OpenAPI, укажите, какой язык мы хотим использовать и где выводить клиентский код API. Вот фрагмент из нашего Makefile, который использует Docker для правильной настройки инструмента Java CLI:

LOCAL_ROOT:=/local
​​OPENAPI_GENERATOR_CLI_TAG=v5.4.0
OAPI_GENERATOR_CLI:=docker run --rm -v $(PWD):$(LOCAL_ROOT) openapitools/openapi-generator-cli:$(​​OPENAPI_GENERATOR_CLI_TAG)

$(OAPI_GENERATOR_CLI) generate \
    --global-property apiTests=$(GENERATE_TESTS_API),modelTests=$(GENERATE_TESTS_MODEL),apiDocs=$(GENERATE_DOCS_API),modelDocs=$(GENERATE_DOCS_MODEL) \
    -i $(LOCAL_ROOT)/$(BUILT_SCHEMAS_DIR)/$(COLLECTION) \
    -g $(GENERATOR) \
    -t $(LOCAL_ROOT)/$(TEMPLATES_DIR)/$(TEMPLATE) \
    -c $(LOCAL_ROOT)/$(TEMPLATES_DIR)/$(TEMPLATE).yaml \
    -o $(LOCAL_ROOT)/$(TEMPLATE)$(if [$(COLLECTION_NAME) == "fastly"],,/$(COLLECTION_NAME)) \
    >$(TEMPLATE)/.tmp.$(COLLECTION_NAME).log \
    2>$(TEMPLATE)/.tmp.$(COLLECTION_NAME).err.log
Войти в полноэкранный режим

Выйти из полноэкранного режима


Почему это хороший выбор

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

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


Какие языки мы выбрали и почему

До этой инициативы у нас было пять клиентов API, поэтому было важно, чтобы любой процесс, который мы придумали, поддерживал как минимум Рубин, питон, PHP, Перл а также Идти*.

* Это наш написанный вручную клиент Go — версия OAG все еще находится в разработке.

Вдобавок к этому списку мы также хотели производить новые JavaScript а также Ржавчина клиенты, так как это популярные языки, которые используют наши клиенты.

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


Трудности и решения

Было несколько проблем, с которыми мы столкнулись. Во-первых, нам нужен был собственный процесс сборки для управления большим количеством файлов спецификаций OpenAPI (в то время у нас было около 120 файлов, и их число росло).

openapi-generator-cli инструмент мог обрабатывать только один файл спецификаций за раз, а это означало, что нам нужно было сначала объединить наши спецификации в один файл (с помощью передокли).

Чтобы гарантировать, что мы сможем запустить процесс генерации кода внутри нашего Непрерывная интеграция (CI), нам нужно было использовать Докеризированный версия openapi-generator-cli (согласование которых было сложным процессом). Смотрите следующий раздел о том, как мы автоматически восстанавливать клиентов при изменении спецификации.

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

Кроме того, качество наших файлов спецификации OpenAPI вызовет проблемы в генераторе, либо вызывая ошибки времени выполнения в базовом синтаксическом анализаторе Java, либо в самом сгенерированном коде из-за несовместимости с языковыми шаблонами.

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

  1. Документы спецификации Fastly OpenAPI.
  2. шаблоны усов openapi-generator.
  3. парсеры языка openapi-generator (написано на Java).

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

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

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

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

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

Примеры этого выглядят так

x-fastly-preprocess-exclude:
  - api-client  # exclude all clients

x-fastly-preprocess-exclude:
  - fastly-rust # exclude the rust client
  - fastly-js   # exclude the javascript client
Войти в полноэкранный режим

Выйти из полноэкранного режима


Альфа-релизы

Следующим шагом после успешного создания клиента API было отправить код в соответствующий общедоступный репозиторий GitHub, а затем опубликовать код в своем реестре модулей (например, crates.io для ржавчины, npmjs.com для JavaScript, pypi.org для Python и т. д.).

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


Автоматическая регенерация клиентов при изменении спецификации

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

На высоком уровне это выглядело так:

Конвейер Fastly CI для создания клиентов API

НАМЕКАТЬ: Мы используем Действия на GitHub для нашего CI/CD. Вы можете узнать больше об этом, прочитав нашу запись в блоге «Как быстро развертывать веб-сайты Gatsby CMS в GCS с помощью GitHub Actions».

Мы написали задание CI в репозитории GitHub, содержащем наши файлы спецификации OpenAPI. Это задание запускается всякий раз, когда изменения объединяются в основную ветку, и начинается с проверки и объединения спецификаций.

Конечно, при изменении определения API имеет смысл перегенерировать все клиенты API; не только потому, что мы хотим поддерживать наши общедоступные клиенты в актуальном состоянии, но и потому, что в рамках проверки открытых PR здорово иметь возможность захватить клиента и использовать его для фактического тестирования вашего нового API, прежде чем мы интегрируем его определение в наш официальная спецификация OpenAPI.

Оказывается, у GitHub Actions есть невероятно полезный хук под названием workflow_dispatch, что позволяет изменению в одном репо вызвать действие в другом репо! Мы определяем шаг, который использует условную проверку, чтобы предотвратить его выполнение, если не было изменений спецификации, в противном случае он запускает файл рабочего процесса в репозитории GitHub, который содержит логику/процесс сборки нашего клиентского API-генератора.

Задание CI, определенное в репозитории генератора клиента API, настроено с workflow_dispatch мероприятие. Он получает ветку и фиксирует SHA из репозитория спецификации OpenAPI и отправляет обновления статуса обратно в репозиторий OpenAPI, информируя его о ходе сборки.

Следующий шаг в задании CI генератора клиентов API проверяет соответствующую ветвь репозитория спецификации OpenAPI и использует ее для создания всех клиентов API.

Наконец, задание CI отправляет обновление статуса в репозиторий спецификации OpenAPI, сообщая ему, куда были загружены сгенерированные клиенты API.


Вывод

Автоматическое создание наших клиентов API было долгим проектом. Работа началась с первоначального шага по документированию нашего API с использованием спецификаций OpenAPI. Это позволило нам сделать гораздо лучший справочник по API на нашем Центр разработчиков который потребляет эти файлы спецификаций и создает из них содержимое.

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

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

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

Примеры кода Fastly API

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

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