# Спецификация в первую очередь и мок (/ru/docs/tutorials/spec-first)

«Спецификация в первую очередь» значит, что документ OpenAPI появляется раньше кода: вы договариваетесь об API,
пробуете его на моке, а когда настоящий сервер появится, сверяете его с тем же документом. В этом руководстве вы
добавите спецификацию магазина Alpenglow в рабочее пространство, спроектируете эндпоинт, которого в магазине ещё нет,
запустите на своём Mac мок обоих и сверите ответы настоящего API со спецификацией.

- **Время:** 20 минут.
- **Вам понадобится:** Echolot и рабочее пространство, например из руководства
  [Ваше первое рабочее пространство](/docs/tutorials/first-workspace) или новое пустое.
- **Вы узнаете:** про страницу «Спецификации», линтер, запросы, следующие за спецификацией, моки и проверку
  **Спецификация OpenAPI**.

## 1. Добавьте спецификацию [#1-add-the-spec]

Демо-магазин публикует свою спецификацию по адресу `https://demo.echolot.dev/openapi.yaml`.

1. Сохраните её на Mac. В Терминале:

   ```sh
   curl -o ~/Downloads/openapi.yaml https://demo.echolot.dev/openapi.yaml
   ```

   Или откройте адрес в Safari и выберите **Файл › Сохранить как…**.
2. В окне рабочего пространства переключитесь на **Спецификации** (<kbd>⌘</kbd><kbd>3</kbd>).
3. Выберите **Добавить файл спецификации…** (на пустой странице или в меню **+** боковой панели) и укажите
   `openapi.yaml`. Копия попадёт в папку `specs` рабочего пространства.

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

<Callout title="Если начинать с нуля">
  Выберите вместо этого **Новая спецификация**. Вы получите документ OpenAPI 3.1 с названием **Новый API** и одним
  эндпоинтом, `GET /items`, который можно переименовать и развивать. Остальная часть руководства работает так же.
</Callout>

## 2. Спроектируйте эндпоинт, которого нет в магазине [#2-design-an-endpoint-the-shop-doesnt-have]

Магазину нужен список его торговых точек. Спроектируйте его до того, как кто-то напишет код.

1. В редакторе **Визуально** выберите **Добавить эндпоинт**.
2. В **Новый эндпоинт** оставьте **Метод** **GET**, введите путь `/stores`, описание `List stores` и тег `Catalog`.
   Нажмите **Добавить эндпоинт**. Echolot добавит операцию с ответом `200`, от которого можно отталкиваться.
3. В редакторе эндпоинта задайте **ID операции** `listStores`.
4. Выберите **Показать в коде**. Замените `responses` эндпоинта на тело и пример:

   ```yaml
         responses:
           '200':
             description: The shop's stores.
             content:
               application/json:
                 schema:
                   type: array
                   items:
                     type: object
                     required: [id, city]
                     properties:
                       id: { type: string }
                       city: { type: string }
                       address: { type: string }
                 example:
                   - { id: zrh, city: Zürich, address: Bahnhofstrasse 1 }
                   - { id: brn, city: Bern, address: Spitalgasse 4 }
   ```

Правки сохраняются по мере внесения, а <kbd>⌘</kbd><kbd>Z</kbd> их отменяет. Визуальные правки меняют только строки
затронутой записи, поэтому остальной файл, включая комментарии, остаётся таким, как вы его написали. Подробнее — в
разделе [редактирование спецификаций](/docs/specs/editing).

<Shot id="tutorials-spec-visual-editor" alt="Страница «Спецификации» в редакторе «Визуально» с выбранным эндпоинтом спецификации Alpenglow" />

## 3. Проверьте спецификацию линтером [#3-lint-the-spec]

Кнопка проблем на панели инструментов показывает зелёную галочку и **Проблем нет** либо число ошибок и предупреждений.

1. Выберите её. Во всплывающем окне **Проблемы** сказано, что документ соответствует спецификации OpenAPI и все ссылки
   разрешаются.
2. Сломайте что-нибудь, чтобы увидеть проблему: в **Код** замените в одной строке `$ref: '#/components/schemas/Product'`
   на `'#/components/schemas/Produkt'`. Кнопка станет красной. Выберите её и укажите проблему: редактор перейдёт к её
   строке.
3. Отмените изменение (<kbd>⌘</kbd><kbd>Z</kbd>).

Тот же линтер работает в CI с инструментом командной строки. `--strict` завершает проверку ошибкой и при
предупреждениях, а `--hints` добавляет советы, например о пропущенных ID операций:

```sh
echolot spec lint specs/openapi.yaml --strict
```

## 4. Создайте запросы, следующие за спецификацией [#4-make-requests-that-follow-the-spec]

1. На панели инструментов «Спецификации» выберите **Создать запросы**. В **Запросы** появится папка **Alpenglow demo shop** с папкой на каждый тег (**Catalog**, **Auth**, **Orders**) и запросом на каждый эндпоинт, включая **List stores**.
2. Посмотрите на **Catalog › List products**. Его URL — `{{baseUrl}}/products`. На его вкладке **Тесты** уже есть две
   проверки из спецификации: **Статус** равно `200` и **JSON Schema** описанного тела.

Папка следует за спецификацией. Когда спецификация позже изменится, меню **Запросы** на панели инструментов
«Спецификации» предложит **Проверить изменения для «Alpenglow demo shop»…**: оно перечислит, что в спецификации
появилось, изменилось и исчезло, и применит это, не затирая ваши собственные правки запросов. См.
[синхронизацию с OpenAPI](/docs/specs).

Созданные запросы начинаются с `{{baseUrl}}` (строчные `rl`). Значение для неё вы зададите в следующих двух шагах.

## 5. Запустите мок спецификации [#5-serve-a-mock-of-the-spec]

1. Переключитесь на **Моки** (<kbd>⌘</kbd><kbd>4</kbd>). Откройте меню **+** и выберите **Мок спецификации «Alpenglow demo shop»**.
2. Нажмите **Старт** (<kbd>⌘</kbd><kbd>R</kbd>). На панели инструментов появятся зелёная точка и адрес
   `http://127.0.0.1:4010/v1`: первый свободный порт начиная с 4010 и базовый путь из сервера спецификации.
3. Выберите **Использовать в окружении**. Echolot создаст окружение **Mock: Alpenglow demo shop**, в котором
   `{{baseUrl}}` — адрес мока, и выберет его.

Карточка **Настройки** содержит **Порт** мока, **Отвечать другим устройствам в сети**, **Задержку** перед каждым ответом,
долю **Ошибок** (внедряемые 500) и **Проверки**: **Отклонять запросы, нарушающие спецификацию (400 с причинами)**. Мок
работает, пока окно открыто.

<Shot id="tutorials-spec-mock-running" alt="Страница «Моки» с работающим моком Alpenglow demo shop на 127.0.0.1:4010, его настройками и журналом запросов" />

## 6. Отправьте запросы к моку [#6-send-requests-to-the-mock]

1. Вернувшись в **Запросы**, отправьте **Catalog › List stores**. Мок отвечает `200` с двумя точками из вашего примера.
   На вкладке **Заголовки** виден `Server: Echolot mock`.
2. Отправьте **Catalog › List products**. Примера для него у мока нет, поэтому он строит тело по схеме: правильной формы,
   но с условными значениями. Обе проверки запроса проходят.
3. В **Параметры** запроса **List products** добавьте `sort` со значением `cheapest` и отправьте. Мок отклоняет его: `400`
   с `query “sort”: not one of the allowed values`. Спецификация допускает `name`, `price`, `-price` и `rating`. Удалите
   параметр.
4. В **Get a product** добавьте заголовок `Prefer: code=404` и отправьте. Мок отвечает описанной проблемой `404`. Удалите
   заголовок.

**ЖУРНАЛ** на странице «Моки» перечисляет каждый запрос, на который ответил мок, с найденной им операцией и возможной
проблемой. В карточке **Ответы** можно сделать так, чтобы операция на каждый запрос отвечала другим описанным статусом
или примером. Маршруты на JavaScript для ответов, которым нужно состояние, описаны в разделе
[маршруты со скриптами](/docs/mocks/scripted-routes).

## 7. Сверьте настоящий API со спецификацией [#7-check-the-real-api-against-the-spec]

1. На странице **Окружения** добавьте окружение `Demo` с переменной `baseUrl`, равной `https://demo.echolot.dev/v1`, и
   выберите его в заголовке страницы.
2. Откройте **List products**, на вкладке **Тесты** откройте **Добавить тест** и выберите **Спецификация OpenAPI**.
   Заполнять в ней нечего: она проверяет тело по схеме, которую спецификация описывает для вернувшегося статуса.
3. Отправьте. Проходят все три проверки: настоящий магазин соответствует своей спецификации.
4. Отправьте **List stores**. Настоящий магазин отвечает `404`: эндпоинт существует только в спецификации. В этом и
   смысл подхода «спецификация в первую очередь»: мок позволяет строить на нём уже сейчас.

Чтобы выполнять те же проверки в CI, запустите папку командой [`echolot run`](/docs/cli/run), а мок поднимите там
командой `echolot mock`:

```sh
echolot mock specs/openapi.yaml --port 4010
```

## Дальше [#next-steps]

- [Спецификации из трафика](/docs/specs/specs-from-traffic): напишите спецификацию по запросам, которые вы уже
  отправляете.
- [Моки](/docs/mocks): примеры, сохранённые из истории, задержки, ошибки и доступ по сети.
- [`echolot spec`](/docs/cli/spec): `check` завершает CI ошибкой, если папка отстала от спецификации.
