К старту курса по тестированию на Python при помощи шаблона Read the Docs, пакетов restclient, ob-restclient и Org Mode в Emacs напишем красивую, полезную документацию API, которая генерируется автоматически и легко отображается на статическом сайте. По шкале полезности от «NFT» до «Integer» грамотное программирование обычно занимает чуть ниже нижнего уровня. Это несправедливо и не совсем заслуженно, но есть вещи, для которых LitProg подходит очень хорошо. Вы сможете полностью избавиться от Postman или любого другого проприетарного клиента REST API. При таком подходе документация API — это программа. Единственное требование здесь — наличие API, с которым можно общаться. Мы воспользуемся бесплатным и общедоступным API — JSON Placeholder. Я писал этот пост в режиме Org Mode, поэтому включённые в него примеры Org экспортируются без подсветки синтаксиса. Достаточно сказать, что текст Org выглядит в Emacs гораздо приятнее. TL;DR. Законченный пример файла Org. Сначала установите и настройте в вашем Emacs пакеты restclient и ob-restclient. Затем создайте в Emacs файл jsonplaceholder.org. В верхней части этого файла включите следующую информацию заголовка, измените автора и электронную почту: Не сразу понятно, что происходит, поэтому давайте немного поговорим об этом заголовке. Верхний раздел не требует пояснений. Второй раздел просто указывает Emacs на отображение org-режима с отступами, задаёт имя файла для экспорта и управляет отображением заголовков разделов и оглавления. Последний раздел загружает HTML-шаблон setupfile, исрользуемый при экспорте в HTML. На практике для настройки CSS-темы ReadTheOrg я также добавляю несколько элементов #+html_head:. Первый раздел документа будет служить функциональной цели и инициализировать документ. На практике этот раздел может быть довольно подробным, позволяя получить токен Bearer и значения из базы данных или иным образом инициализировать элементы, которые будут использоваться в программе позже. Для нашего простого примера нужен только URL-адрес API. Параметризация URL API может показаться странной. Разве мы не хотим, чтобы параметры отображались в наших запросах? Возможно. Но, может быть, как у большинства инженеров-программистов, у нас есть отдельные среды — разработка, стейджинг и продакшн. Для изменения среды пришлось бы найти и заменить каждый URL. А так мы можем просто изменить адрес в одном месте. Одно небольшое замечание: не хочется экспортировать этот раздел в HTML. Можно сообщить об этом Emacs, добавив в заголовок раздела тег :noexport:. В файл jsonplaceholder.org добавьте этот код: Установка #+name: api-url означает, что можно ссылаться на скалярное значение : https://jsonplaceholder.typicode.com из других блоков src нашего документа. api-url можно взять как входные данные. Хотя на практике для выполнения запросов API мы будем использовать jsonplaceholder.org. При экспорте в HTML для создания документации API-запросы также будет выполнять Emacs. Это напоминает о том, что мы работаем с документацией. Поэтому напишите для своих читателей хорошее введение. В файле jsonplaceholder.org добавьте этот код. Он скопирован из документации по API JSON Placeholder: Давайте задокументируем наш первый запрос и ответ. JSON Placeholder предоставляет шесть ресурсов и маршруты для всех методов HTTP. Начнём с ресурса /posts. В файл jsonplaceholder.org добавьте этот код: Обратите внимание, что в +begin_src мы указываем Org в качестве языка использовать restclient. Мы также устанавливаем переменную api в значение, хранящееся в скаляре с именем api-url. Вы просто используете переменную внутри блока src Org, как обычно в этом языке. В restclient переменные предваряются двоеточием, поэтому в запросе она используется так: GET :api/posts. Теперь выполните вызов API. Для этого внутри блока src наберите C-c C-c. Ответ во всей своей красе отобразится под блоком запроса. И это иллюстрирует полезность грамотной документации API.  Мы не собираемся включать примеры запросов и ответов, которые должны обновляться каждый раз, когда изменяются ответы или конечные точки API. Включены только реальные запросы! У нас только один запрос. Для экспорта его достаточно. Давайте попробуем! Для экспорта в index.html в том же каталоге, что и ваш файл org, в файле jsonplaceholder.org введите C-c C-e h o. Затем откройте этот файл в браузере: Хорошо, но где же ответ? Emacs не сделал запрос API и не включил ответ. Но всё в порядке.  По умолчанию Emacs экспортирует только блок src. Чтобы экспортировать результаты, отредактируйте заголовок блока src с именем get-posts. Добавьте :exports both, чтобы код выглядел так: И экспорт. Теперь вы должны увидеть ответ: Больше не нужно обновлять примеры ответов, внося в них досадные опечатки. Это реальные, интерактивные данные ответа. Прежде чем мы продолжим «программирование» как часть «грамотного программирования», давайте на минуту вспомним, что мы пишем документацию API и хотим, чтобы она была чистой и читабельной. Потратим немного времени на внешний вид документации. Сейчас у нас некрасивый документ: Якоря к заголовкам выглядят некрасиво и читаются как index.html#orga1b1b2d. Ответ слишком большой. Чтобы добраться до следующего запроса, придётся прокручивать страницы. Заголовки ответа выгружаются в нижней части ответа. Это не чистый JSON. Проблема некрасивых якорей решается легко. Когда Emacs выполняет экспорт в HTML, для заголовков он генерирует случайные теги якорей, но легко можно указать пользовательский идентификатор. В файле jsonplaceholder.org установите курсор в конец заголовка Introduction. Чтобы добавить свойство, введите C-c C-x p. В качестве имени свойства введите CUSTOM_ID, а в качестве значения — introduction. То же самое сделайте для заголовка Posts, установив CUSTOM_ID в resource-posts. Заголовок теперь должен выглядеть так: Осмотр index.html показывает, что размер блока ответа определяется тегом и классом pre.src. После некоторой работы с моей стороны получился такой фрагмент: При помощи заголовка #+html_head: можно очень легко добавить его в наш экспортированный index.html в Org Mode. После строки, которая с началом #+setupfile: добавьте: Кроме того, никому не нравится светлая тема, поэтому я перешёл на тёмную. Некрасивые заголовки ответа приходят из пакета restclient. Без взлома самого restclient подавить их нельзя. Нет, это, конечно, можно сделать… В конце концов, это… это Emacs. Воспользуемся грамотным решением. Одна из самых мощных особенностей грамотного программирования — языковая диагностика. Из Org Mode выполняется SQL-запрос, его данные передаются в блок src Python, где мы манипулируем ими, а затем передаём их в Bash, R, Elisp, Common Lisp и обратно в Python, сохраняя их в базе данных. Мы воспользуемся оболочкой и CLI-процессором JSON jq. Установим jq и изменим запрос Posts: Поместим курсор в нижний блок src (shell) и введём C-c C-c. Блок ответа вызывает блок запроса get-posts и передаёт $response в jq. И больше никаких заголовков ответов. Только чистый, приятный JSON. Давайте посмотрим, что под капотом. Первое значительное изменение коснулось запроса. Мы больше не говорим: :exports both, но говорим :results value. Как уже упоминалось, по умолчанию для :exports используется только блок src. Удалив :exports both, мы вернёмся к умолчанию. Этот блок src не будет вычисляться, а результаты не будут экспортироваться. Теперь мы говорим запросу: :results value. Что это значит? Это сложно, но в основном это означает, что Org не использует внешний процесс, а получает значение из самого вычисляемого кода. Более очевидное изменение заключается в том, что мы добавили второй блок src. Он создан только для результатов. В заголовке блока происходит несколько важных вещей: На этот раз мы вызываем оболочку. Любую (bash, cmd.exe, fish и так далее), которую вы используете. Устанавливаем новую переменную :var response=get-posts. Теперь этот блок src будет вызывать блок запроса с именем get-posts и помещать результаты в переменную с именем response. Устанавливаем :results value raw, потому что не хотим, чтобы Org обернул результаты в тип shell. Устанавливаем :wrap src js, потому что на самом деле хочется, чтобы Org обернул результаты в тип js. Устанавливаем :exports results, потому что хочется видеть только результаты, а не исходный код. Наконец, внутри блока shell мы передаём переменную $results в jq, который удаляет строки комментариев в заголовке ответа. Для блока результатов он выводит красиво отформатированный JSON. Давайте посмотрим, как сработали наши изменения внешнего вида. Экспортируйте ваш файл jsonplaceholder.org снова с помощью C-c C-e h o. Вы должны увидеть нечто подобное: Выглядит намного лучше, но есть несколько недоработок. Быстро пройдём до конца, потому что применяются те же принципы, которые мы уже обсуждали. Измените файл jsonplaceholder.org, чтобы он выглядел так: Файл index.html в браузере должен выглядеть так: Вкратце остановимся на нескольких пунктах вышеуказанных изменений. Обратите внимание на новые свойства :HTML_CONTAINER_CLASS: request и :HTML_CONTAINER_CLASS: response.  При экспорте это свойство прикрепляет соответствующий CSS-класс (.request или .response) к элементу div, который содержит заголовок. Таким образом, чтобы было понятно, для чего предназначен каждый из этих блоков, можно стилизовать div.request>h5 и div.response>h5. Также обратите внимание, что разделы запроса и ответа перенесены в новый подзаголовок Posts, данные для которого вызываются запросом GET /posts. Теперь, нажимая на "Posts" в меню боковой панели, мы увидим GET /posts как пункт подменю.  Это поведение контролируется включением директивы заголовка Org #+options: toc:2. Установив значение toc:1, мы не увидим подменю. А установив значение toc:3 в подменю ниже "GET /posts" увидим "Request" и "Response". Попробуйте и посмотрите! Оставшаяся часть API документируется по установленной схеме. Процесс можно ускорить, используя yasnippet для вставки нового раздела метода. Однако это не отражает полезность подхода грамотного программирования к документации API.  Обычно я пишу эту документацию по мере создания новых конечных точек API, чтобы протестировать и усовершенствовать конечную точку. Этот метод я использую вместо Postman, и, когда я заканчиваю работу над API, он уже документирован и находится в системе контроля версий вместе с исходным кодом. Например, я написал бы то, что мы имеем на данный момент, в то же время, когда писал GET :api/posts, и широко использовал этот файл, делая заметки обо всех необходимых параметрах. После публикации кода любой пользователь Emacs из моей команды может открыть README.org и протестировать API самостоятельно. Прежде чем я начну писать код, я могу сгенерировать index.html и поместить его в общедоступный статический каталог. Переходя по базовому URL моего API, вместо сообщения об ошибке вы увидите документацию API.  Позвольте завершить этот пост документированием остальных методов ресурса /posts. Документирование проиллюстрирует, как использовать restclient для выполнения HTTP-методов POST, PUT и DELETE. Добавим заголовок POST /posts под GET /posts. Вы можете скопировать, вставить и изменить эти части кода: :CUSTOM_ID: method-get-posts на method-post-posts. "Запрос" #+name: c get-posts в post-posts "Ответ" :var response get-posts на post-posts В заголовок блока src запроса post-posts добавьте переменную :var user-id=1. В requests нужно указать тип содержимого и полезную нагрузку JSON для доставки. Обратите внимание, что мы можем использовать переменные Org внутри полезной нагрузки JSON. Окончательный POST-запрос должен выглядеть так: Теперь, когда вы поместите курсор в поле «Response» и введёте C-c C-c, вы должны получить данные нового POST, которые создали в JSON Placeholder. Это должно выглядеть примерно так: Как вы можете себе представить, процесс происходит аналогичным образом. Скопируйте и вставьте POST /posts в PUT /posts/:id, измените пользовательский ID, имя запроса и переменную ответа на put-posts. Теперь наш запрос должен выглядеть аналогично POST-запросу, только нужно указать id поста в URL. Используемый JSON будет обновлять существующий POST, а не создавать новый. Финальный запрос PUT должен быть таким: Снова скопируйте, вставьте, измените и выполните. Окончательная документация не очень подробна (мы задокументировали только ресурс /posts), но остальное — это просто повторение того, что мы уже обсуждали. Тем не менее она довольно приятная. И легко сделать её ещё лучше: Кроме того, как уже говорилось, для автоматизации создания новых разделов метода достаточно просто было бы написать yasnippet, а затем переходить от поля к полю, заполняя его. Также повторюсь, что это не самый полезный подход к написанию документации API в стиле грамотного программирования. Полезнее писать её по ходу дела, используя документ вместо Postman. Тогда ваша документация не только никогда не будет ошибочной или не синхронизированной с API. Ещё вам не придётся возвращаться назад и тратить время на документирование того, что вы уже закончили! При написании документации API в стиле грамотного программирования могут оказаться полезными более сложные методы грамотного программирования.  У меня есть один документ, который я написал для Wurkzen API. Он позволяет получить и кешировать токен Bearer, который используется в последующих запросах. Затем с помощью API он конструирует ряд объектов и использует их, чтобы создать фиктивного клиента [программу], клиентов [компании], местоположения и выполнить все действия API над созданными документом фиктивными данными, а затем удалить эти данные при помощи самого документа.  При этом если в моём API есть ошибки, то они отображаются в документации API. Он также действует как интеграционный тест, подробный настолько, насколько вы захотите. Каждый запрос, выполненный Emacs при экспорте в HTML, отображается в Emacs minibuffer и логируется в буфере Emacs *Messages*, так что ошибки можно найти с помощью инкрементного поиска, не читая index.html. Исходник org В итоге я написал yasnippet. Введите lit-api и C-<tab> и переходите от поля к полю. А продолжить изучение тестирования или разработки вы сможете на наших курсах: Курс QA-инженер на Python Профессия Fullstack-разработчик на Python Узнайте подробности здесь. Data Science и Machine Learning Профессия Data Scientist Профессия Data Analyst Курс «Математика для Data Science» Курс «Математика и Machine Learning для Data Science» Курс по Data Engineering Курс «Machine Learning и Deep Learning» Курс по Machine Learning Python, веб-разработка Профессия Fullstack-разработчик на Python Курс «Python для веб-разработки» Профессия Frontend-разработчик Профессия Веб-разработчик Мобильная разработка Профессия iOS-разработчик Профессия Android-разработчик Java и C# Профессия Java-разработчик Профессия QA-инженер на JAVA Профессия C#-разработчик Профессия Разработчик игр на Unity От основ — в глубину Курс «Алгоритмы и структуры данных» Профессия C++ разработчик Профессия Этичный хакер А также Курс по DevOps Все курсы Редактор-переводчик