Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 58 additions & 32 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,26 @@ HTTP API, протокол HTTP, Postman, js-playwright. Уроки и само
## Как собран сервер

Четыре спецификации на TypeSpec (`typespec/<app>/`) компилируются в OpenAPI
(`make compile` → `tsp-output/`), и каждую отдаёт свой мок prism. Рядом живёт
приложение на fastify (`custom-server/`) с рукописными эндпоинтами: `/http-api/rpc`,
`/http-api/echo`, стрим и куки для курса про протокол, статика, а также REST-маршруты
коллекций `/http-api/tasks`, `/users`, `/posts`, `/comments` — их забрали у
статичного мока: тот не умеет ни пагинации, ни выборки полей, ни отбора по пути.
Снаружи всё сшивает Caddy на `$PORT`.
(`make compile` → `tsp-output/`), по одной на курс: `http-api`, `http-protocol`,
`js-playwright`, `postman`. Каждую поднимает свой prism, и за ним остались только
`/courses` и `/login`. Всё остальное обслуживает приложение на fastify
(`custom-server/`): коллекции `tasks`, `users`, `posts`, `comments` по каждому
префиксу плюс рукописные эндпоинты `/http-api/rpc`, `/http-api/echo`,
`/http-protocol/login`, стрим и куки для курса про протокол, статика. Снаружи всё
сшивает Caddy на `$PORT`.

```text
Caddy :$PORT
├── :4010 fastify коллекции, rpc, echo, стрим, куки, статика, swagger-ui
├── :4011 prism http-api: только /courses и /login
├── :4012 prism http-protocol
├── :4013 prism js-playwright
└── :4014 prism postman
├── :4011 prism http-api: только /courses и /login
├── :4012 prism http-protocol: только /courses
├── :4013 prism js-playwright: только /courses и /login
└── :4014 prism postman: только /courses и /login
```

У `http-protocol` логин рукописный и отвечает строкой `Done!`: курс про протокол
разбирает его через telnet и печатает в уроке `Content-Length: 5`.

Список приложений и путь к документации задаёт `app.config.json`: имя приложения
плюс `docRoute` дают маршрут вида `/http-api-openapi`.

Expand All @@ -38,26 +42,33 @@ prism получает `/tasks`. Директива `handle /http-api/rpc` пр
эндпоинт поверх мока это блок `handle` более специфичный, чем общий
`handle_path`.

## Мок `http-api` статичный, остальные три динамические
## Мок статичный у всех четырёх, и данных почти не отдаёт

Ни у одной спецификации в `bin/start.sh` нет флага `-d`. Динамический режим
генерировал данные faker'ом, а тот не смотрит на объявленный тип: при `uint16`
(0..65535) приходило `"id":-39177245`, ответы менялись на каждый запрос, и
опираться на них уроки не могли. Статичный режим отдаёт примеры из спецификации
(`@example` у моделей), поэтому у `Course`, `Courses` и `AuthToken` они заведены
во всех четырёх — это всё, что мок ещё обслуживает.

В `bin/start.sh` у `http-api` **нет** флага `-d`, у остальных трёх он есть. Это
намеренная разница.
Коллекции мок не обслуживает вовсе: они реализованы в `custom-server/` общим
кодом (`routes.js`, `collections.js`, таблица в `resources.js`). В статичном
режиме пример отдаётся дословно, поэтому `skip`, `limit` и `select` не
применялись, а на любой `/users/{id}` приходила одна и та же запись.
Спецификации коллекции по-прежнему описывают, и по ним читают документацию, но
поведение задаёт код.

В статичном режиме prism отдаёт примеры из спецификации (`@example` у моделей).
В динамическом генерирует данные faker'ом: тот не смотрит на объявленный тип,
поэтому при `uint16` (0..65535) выдавал `"id":-39177245`, а ответы менялись на
каждый запрос, и опираться на них уроки не могли.
Различия между спецификациями настоящие. Они сняты с `typespec/<app>/services/`
и выписаны в таблице `SPECS`:

Три остальные спецификации остаются динамическими, потому что примеров в них нет:
статичный режим отдал бы вместо данных заглушки вида `"string"`. Чтобы перевести
любую из них на статику, сначала заводят `@example` во всех её моделях.
* задачи курса Postman закрыты **Basic**, причём включая чтение одной задачи;
* у `js-playwright` есть только задачи и пользователи, всё открыто, вложенных
ресурсов нет;
* остальное закрыто Bearer по тому же рисунку, что у `http-api`.

Маршруты `/http-api/tasks` не обслуживает ни один режим мока: они реализованы в
`custom-server/src/tasks-rest.js`. В статичном режиме пример отдаётся дословно,
поэтому `skip` и `limit` не применялись, а на любой `/tasks/{id}` приходила одна
и та же задача.
Спецификация их по-прежнему описывает, и по ней читают документацию, но
поведение задаёт код.
Создание везде отвечает **201**. Три спецификации объявляли обычный ответ, то
есть 200, и урок `850-api-testing` курса Playwright из-за этого не проходил, хотя
учил правильному коду. Спецификации выровнены по `http-api`, а не урок по ним.

## Примеры ответов это текст уроков

Expand Down Expand Up @@ -120,10 +131,17 @@ prism получает `/tasks`. Директива `handle /http-api/rpc` пр

## Прогон

`make test` поднимает статичный мок с приложением и проверяет ответы запросами:
совпадение данных REST и RPC, коды из таблицы выше, границы `uint16`. Нужна
`make test` поднимает **пять** сервисов, четыре мока и приложение, и проверяет
ответы запросами: пагинацию и `select` по каждому префиксу, отбор по пути и
вложенные ресурсы, коды из таблицы выше, схемы авторизации, границы `uint16`,
неизменяемость наборов и совпадение примеров спецификации с данными. Нужна
скомпилированная спецификация, то есть сначала `make setup` либо `make compile`.

Пять сервисов на загруженной машине поднимаются долго, поэтому ожидание
готовности щедрое. Если сервис не поднялся, прогон печатает его вывод. Пустой
вывод у всех сразу означает не поломку кода, а нехватку ресурсов: стоит
посмотреть `uptime` и снять лишние контейнеры.

Caddy в прогоне не участвует, в раннере его нет: порты опрашиваются напрямую, как
это делает Caddy со срезанным префиксом. Сборка гоняется и на пуллреквестах,
публикация образа — только с `main`.
Expand Down Expand Up @@ -158,13 +176,21 @@ make deploy APP=http_example HOST=timeweb SKIP=caddy,cron
* у автора 1 восемь постов — урок показывает вложенный ресурс
`/users/1/posts`, и на пустом списке он ничего не объясняет;
* первые три пользователя и первые два поста автора 1 приведены в уроке
дословно.
дословно;
* пользователей больше пяти — урок `200-complex-requests` курса Postman учит на
`?skip=3&limit=5`.

Наборы **общие на все четыре префикса**: курсы не зависят от того, чтобы у каждого
были свои записи, а одна копия не расходится сама с собой.

Данные держатся **на латинице**. Сервер один на все локали курсов, и русский
текст в ответах читался бы как ошибка у испанского или английского студента.

## Набор данных не меняется

`POST` и `DELETE` отвечают 201 и 204, но список задач остаётся прежним: создание
и удаление только сообщают, что произошло бы. Сервер учебный, запросы к нему идут
одновременно от множества студентов, и мутации сделали бы уроки
`POST` и `DELETE` отвечают 201 и 204, но наборы остаются прежними: создание,
обновление и удаление только сообщают, что произошло бы. Сервер учебный, запросы
к нему идут одновременно от множества студентов, и мутации сделали бы уроки
невоспроизводимыми: самостоятельная описывает один набор, а следующий студент
получил бы другой. Прогон `make test` проверяет, что после `POST` набор задач
остался прежним.
Expand Down
83 changes: 83 additions & 0 deletions Caddyfile
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,39 @@ handle_path /http-api/* {
}

# http-protocol course
# Коллекции обслуживает приложение, а не мок: см. комментарий у /http-api.
# За моком остаются только /login и /courses.
handle /http-protocol/tasks {
reverse_proxy 127.0.0.1:4010
}

handle /http-protocol/tasks/* {
reverse_proxy 127.0.0.1:4010
}

handle /http-protocol/users {
reverse_proxy 127.0.0.1:4010
}

handle /http-protocol/users/* {
reverse_proxy 127.0.0.1:4010
}

handle /http-protocol/posts {
reverse_proxy 127.0.0.1:4010
}

handle /http-protocol/posts/* {
reverse_proxy 127.0.0.1:4010
}

handle /http-protocol/comments {
reverse_proxy 127.0.0.1:4010
}

handle /http-protocol/comments/* {
reverse_proxy 127.0.0.1:4010
}
handle /http-protocol/example {
reverse_proxy 127.0.0.1:4010
}
Expand Down Expand Up @@ -89,6 +122,23 @@ handle_path /http-protocol/* {
}

# js-playwright course
# Коллекции обслуживает приложение, а не мок: см. комментарий у /http-api.
# За моком остаются только /login и /courses.
handle /js-playwright/tasks {
reverse_proxy 127.0.0.1:4010
}

handle /js-playwright/tasks/* {
reverse_proxy 127.0.0.1:4010
}

handle /js-playwright/users {
reverse_proxy 127.0.0.1:4010
}

handle /js-playwright/users/* {
reverse_proxy 127.0.0.1:4010
}
handle /js-playwright/users-list {
reverse_proxy 127.0.0.1:4010
}
Expand All @@ -102,6 +152,39 @@ handle_path /js-playwright/* {
}

# postman course
# Коллекции обслуживает приложение, а не мок: см. комментарий у /http-api.
# За моком остаются только /login и /courses.
handle /postman/tasks {
reverse_proxy 127.0.0.1:4010
}

handle /postman/tasks/* {
reverse_proxy 127.0.0.1:4010
}

handle /postman/users {
reverse_proxy 127.0.0.1:4010
}

handle /postman/users/* {
reverse_proxy 127.0.0.1:4010
}

handle /postman/posts {
reverse_proxy 127.0.0.1:4010
}

handle /postman/posts/* {
reverse_proxy 127.0.0.1:4010
}

handle /postman/comments {
reverse_proxy 127.0.0.1:4010
}

handle /postman/comments/* {
reverse_proxy 127.0.0.1:4010
}
handle /postman/cookie {
reverse_proxy 127.0.0.1:4010
}
Expand Down
Loading