Новый API для Python

Я с радостью делюсь fluent-discourse — новым пакетом Python для работы с API Discourse.

Уже существуют несколько пакетов для взаимодействия с API Discourse через Python. Так зачем создавать ещё один?

Другие пакеты для работы с Discourse обычно подходят к задаче схожим образом: для каждого эндпоинта API создается отдельная функция Python.

У такого подхода есть несколько недостатков:

  1. Крайне сложно добиться полного соответствия функционала API. API Discourse частично не имеет документации, и часто приходится реверс-инжинирить эндпоинты. Кроме того, существует множество плагинов, предоставляющих свои эндпоинты API, которые не входят в ядро. Это ставит пользователя библиотеки перед сложным выбором: делать pull request для добавления необходимых ему эндпоинтов в библиотеку или создавать кастомное решение для дополнительных эндпоинтов.
  2. Вам приходится изучать ещё один API. Помимо изучения методов и эндпоинтов самого API Discourse, нужно разбираться, какой вызов функции в Python им соответствует.
  3. Требуется тестировать гораздо больше кода, поэтому сложнее добиться 100% покрытия тестами. Следовательно, меньше уверенности в качестве создаваемого программного обеспечения.

В отличие от этого подхода, я использовал «флюентный» интерфейс, где для построения запросов применяется цепочка вызовов методов. Давайте рассмотрим пример.
Чтобы получить последние темы в категории ‘foo’ с id=5, используется следующий эндпоинт:
GET /c/foo/5.json.
Чтобы выполнить этот запрос с помощью данной библиотеки, достаточно вызвать:
client.c.foo[5].json.get()

Как видно, существует семантическое соответствие между эндпоинтом API и соответствующим вызовом в Python.

Такой подход:

  1. «Из коробки» обеспечивает полное соответствие функционала API Discourse, включая недokumentированные эндпоинты, эндпоинты плагинов и те, которые ещё не определены (перспективность на будущее).
  2. Вам нужно изучить только API Discourse. Легко перевести любой вызов API таким образом, поэтому не нужно искать соответствующую функцию.
  3. Вся библиотека состоит примерно из 150 строк кода. Это делает тривиальным достижение 100% покрытия тестами.

Покрытие тестами составляет 100% благодаря небольшому набору интеграционных тестов против живого сервера Discourse.

Если это звучит привлекательно, надеюсь, вы попробуете этот пакет!

Это интересный подход..

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

В чём преимущество этого подхода по сравнению с простой передачей URL в качестве аргумента: client.get(url)?

Это хороший аргумент: автодополнение в IDE здесь не особенно поможет. Если это важно для вас, возможно, стоит рассмотреть другую библиотеку.

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

post_data = [
  {
    "raw": "Hello World!",
    "topic_id": 123,
    ...
  },
  {
    "raw": "Tester",
    "topic_id": 501,
    ...
  },
  ...
]

Вы можете сохранить цепочку методов «Новый пост» и использовать её для создания каждого поста из списка.

new_post_endpoint = client.posts.json

for post in post_data:
  new_post_endpoint.post(data=post) 

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

Также для реализации этого подхода в PHP обратите внимание на PHP API SendGrid. Меня вдохновила разработка этого решения после прочтения статьи о Python API SendGrid.