Модуль 07: API и интеграции — Практическое задание
Общая информация
Кейс: Проектирование Task Manager API и интеграция с внешними сервисами.
Цель задания: Применить знания REST API, OpenAPI-спецификации и интеграционных паттернов для проектирования API-контракта и архитектуры интеграции.
Формат сдачи: Один PDF или документ Markdown.
Баллы: 10 баллов.
Задание 1: Спроектируйте REST API (3 балла)
На основе системы управления задачами (Task Manager) спроектируйте RESTful API.
Функциональные требования:
- CRUD для проектов (Project)
- CRUD для задач (Task)
- CRUD для комментариев (Comment)
- Смена статуса задачи (
PATCH /tasks/{id}/status) - Назначение исполнителя на задачу (
PATCH /tasks/{id}/assign) - Получение списка задач с фильтрацией (status, priority, assignee_id, project_id) и пагинацией (page, limit)
- Получение списка комментариев по задаче
- Регистрация и аутентификация пользователей
Заполните таблицу для всех эндпоинтов (минимум 12 строк):
| № | Метод | URL | Описание | Request Body (если есть) | Статус-код успеха |
|---|---|---|---|---|---|
| 1 | POST | /auth/register | Регистрация пользователя | { name, email, password } | 201 |
| 2 | POST | /auth/login | Вход | { email, password } | 200 |
| ... |
Методические указания к Заданию 1
| Подсказка | Детали |
|---|---|
| Именование ресурсов | Используйте существительные во множественном числе: /tasks, не /task; /projects, не /project. Исключение — /auth/* (глагол, но это устоявшийся паттерн). |
| Иерархические URL | Комментарии — дочерний ресурс задачи: /tasks/{taskId}/comments. Проекты — самостоятельный: /projects. |
| Выбор PUT vs PATCH | Для полного обновления ресурса (замена всех полей) — PUT. Для частичного (только status) — PATCH. В Task Manager почти всегда PATCH, так как обновляется 1–2 поля. |
| Статус-коды для DELETE | 204 No Content — тело ответа пустое. Не используйте 200 OK для DELETE (нужно возвращать тело, а его нет). |
| Фильтрация и пагинация | Все через query-параметры: ?status=Done&priority=High&assignee_id=5&page=2&limit=20. Не используйте path-параметры для фильтрации (/tasks/status/Done — нарушение REST). |
| POST vs GET для auth | Регистрация — POST (создаём ресурс). Логин — POST (создаём сессию/токен, хотя ресурс session не хранится постоянно). |
| Асимметрия CRUD | Не все ресурсы требуют полного CRUD. Комментарии, вероятно, не нужно обновлять (только создать и удалить). Используйте только нужные методы. |
| Количество эндпоинтов | Минимум 12. Примерный состав: 2 auth + 5 задач (CRUD + status + assign) + 4 проекта (CRUD) + 3 комментария (list, create, delete) + 1 пользователи (profile) = 15. |
Задание 2: OpenAPI-спецификация (3 балла)
Напишите OpenAPI 3.1 спецификацию (в YAML) для следующих эндпоинтов:
- GET /api/v1/tasks — список задач с query-параметрами:
status,priority,assignee_id,project_id,page,limit. Ответ:{ data: Task[], pagination: { page, limit, total } }. - POST /api/v1/tasks — создание задачи. Request body:
{ title (required), description, priority, assignee_id, project_id (required), deadline }. Ответ: 201, Task. - GET /api/v1/tasks/{taskId} — получение задачи по ID. Ответ: 200, Task. Ошибка: 404.
- PATCH /api/v1/tasks/{taskId}/status — изменение статуса. Request body:
{ status (required, enum) }. Ответ: 200, Task.
Модель Task (components/schemas):
Task:
type: object
properties:
id: integer
title: string
description: string (nullable)
status: enum [To Do, In Progress, Testing, Done, Blocked]
priority: enum [Low, Medium, High, Critical]
assignee: UserBrief (nullable)
project_id: integer
created_at: string (format: date-time)
deadline: string (format: date, nullable)
author: UserBrief
UserBrief: id (integer), name (string), email (string, format: email).
Требования:
- Используйте
$refдля компонентов - Добавьте
operationIdдля каждого эндпоинта - Добавьте
tagsдля группировки - Добавьте
components/responsesдля повторяющихся ответов (Unauthorized, NotFound, ValidationError) - Аутентификация: Bearer JWT (для всех эндпоинтов, кроме /auth/*)
- Укажите 3 сервера: production, staging, localhost
Методические указания к Заданию 2
| Подсказка | Детали |
|---|---|
| Проверка синтаксиса | Используйте Swagger Editor (editor.swagger.io) — он подсвечивает ошибки. Скопируйте YAML и проверьте, что нет синтаксических ошибок. |
| Модель Pagination | Обязательно опишите Pagination в components/schemas: { page, limit, total }. GET /tasks должен возвращать { data: Task[], pagination: Pagination } — для этого создайте TaskListResponse. |
| CreateTaskRequest | Создайте отдельную схему для тела запроса POST /tasks. Не переиспользуйте Task — в запросе нет id, created_at, author. |
| Enum в OpenAPI | Для status и priority используйте type: string + enum: [...]. В OpenAPI 3.1 можно использовать oneOf с константами, но enum — стандарт. |
| Nullable поля | Для description и deadline используйте nullable: true. Иначе валидатор будет требовать эти поля всегда. |
| format: date vs date-time | deadline — это дата (2026-05-30) → format: date. created_at — момент времени (2026-05-30T15:00:00Z) → format: date-time. |
| components/responses | Создайте: Unauthorized (401), NotFound (404), ValidationError (422). Переиспользуйте через $ref. Это сокращает дублирование. |
| securitySchemes | Добавьте components/securitySchemes с bearerAuth (type: http, scheme: bearer, bearerFormat: JWT). На эндпоинтах /auth/* — security: [], на остальных — security: [{ bearerAuth: [] }]. |
| operationId | Должен быть уникальным. Пример: getTasks, createTask, getTaskById, updateTaskStatus. Некоторые генераторы клиентов (OpenAPI Generator) используют operationId как имя функции. |
| Серверы | Укажите три: https://api.taskmanager.com/v1 (production), https://staging-api.taskmanager.com/v1 (staging), http://localhost:8080/api/v1 (local). |
Задание 3: Интеграционные паттерны (2,5 балла)
Кейс: Task Manager интегрируется с тремя внешними системами:
- Slack — уведомления о Critical задачах (немедленно, надёжно)
- Google Calendar — создание события при установке дедлайна задачи (не критично, можно «в фоне»)
- 1С:ЗУП — синхронизация пользователей (должна быть гарантированная доставка, 1 раз в час)
3.1. Выбор паттерна (1 балл)
Для каждой интеграции определите:
- Синхронный или асинхронный подход?
- Какой брокер/протокол?
- Почему?
| Интеграция | Синхр./Асинхр. | Протокол/Брокер | Обоснование |
|---|---|---|---|
| Slack | |||
| Google Calendar | |||
| 1С:ЗУП |
3.2. Saga (1 балл)
При создании задачи с дедлайном выполняется:
- Task Service создаёт задачу
- Google Calendar создаёт событие (внешняя интеграция)
- Notification Service отправляет email исполнителю
Если шаг 2 упал — шаг 1 нужно откатить (задачу удалить). Опишите реализацию Saga (Choreography или Orchestration). Используйте BPMN-подобное описание или текстовую схему.
3.3. Idempotency (0,5 балла)
Клиент (SPA) задерживается при отправке POST /tasks. Пользователь нажал «Создать» дважды. Как предотвратить дубликат? Опишите решение с Idempotency-Key. Как долго нужно хранить ключи?
Методические указания к Заданию 3
| Подсказка | Детали |
|---|---|
| Slack — синхронный (REST) | Уведомление о Critical задаче должно прийти немедленно. Slack API — синхронный REST. Нужен retry (3 попытки, exponential backoff) + Circuit Breaker. Если Slack недоступен — логировать, не блокировать создание задачи. |
| Google Calendar — асинхронный (Queue) | Создание события в календаре не критично по времени. Можно в течение минуты. Подходит очередь (RabbitMQ) или Kafka. Если GC недоступен — компенсация (удалить задачу или пометить как "не синхронизировано"). |
| 1С:ЗУП — асинхронный, пакетный | Синхронизация раз в час — классический batch. Подойдёт очередь (RabbitMQ/SQS) с TTL = 1 час. Или Kafka с пакетной обработкой 1 раз в час. Важна гарантированная доставка (At-least-once). |
| Choreography для Saga | Task Service публикует task.created. Calendar Service подписан — создаёт событие. Если ошибка → Calendar Service публикует calendar.failed. Task Service подписан — удаляет задачу. Просто, но сложно отслеживать. |
| Orchestration для Saga | Отдельный Saga Orchestrator (или BPMN-движок) управляет шагами: (1) создать задачу, (2) создать событие в GC, (3) отправить email. Если (2) упало → компенсация (1): удалить задачу. |
| Idempotency-Key | Клиент генерирует UUID перед отправкой POST /tasks и передаёт в заголовке Idempotency-Key: uuid. Сервер проверяет: если ключ уже обработан — возвращает предыдущий ответ (201 с id задачи), не создавая дубликат. |
| TTL для ключей | Хранить ключи 24 часа. Этого достаточно, чтобы клиент не повторил запрос на следующий день. Если TTL слишком мал (1 час) — клиент может повторить через 2 часа и создать дубликат. Если слишком велик (30 дней) — много данных в таблице idempotency_keys. |
Задание 4: Анализ API-контракта (1,5 балла)
Дан фрагмент API-контракта:
paths:
/tasks:
get:
summary: "List all tasks"
responses:
"200":
description: "OK"
post:
summary: "Create new task"
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
title:
type: string
responses:
"201":
description: "Created"
/tasks/{id}:
delete:
summary: "Delete task"
responses:
"200":
description: "OK"
Найдите и опишите минимум 5 проблем/недостатков:
- Ошибки в статус-кодах
- Отсутствующие обязательные элементы
- Нарушения REST-принципов
- Проблемы безопасности
Для каждой проблемы укажите:
- Проблема
- Почему это плохо
- Как исправить
Методические указания к Заданию 4: глубокий разбор уязвимостей
Ниже приведены категории проблем, которые нужно найти в контракте. Постарайтесь найти не менее 7–8 проблем.
4.1. Проблемы статус-кодов
| Проблема в контракте | Почему это плохо | Как исправить |
|---|---|---|
| DELETE возвращает 200 OK | HTTP-спецификация: DELETE с 200 OK подразумевает тело ответа. Но тело пустое — клиент ждёт данные, а получает пустоту. Это заставляет клиентский код обрабатывать пустой ответ особым образом. | DELETE должен возвращать 204 No Content. Без тела. |
| Нет статус-кодов ошибок | В контракте не описаны 400 Bad Request, 401 Unauthorized, 404 Not Found, 422 Unprocessable Entity, 500 Internal Server Error. Клиент не знает, как обрабатывать ошибки. |
Добавить все релевантные статус-коды ошибок для каждого эндпоинта. |
| POST /tasks всегда 201 | POST может вернуть 400 (невалидные данные), 409 (дубликат), 422 (ошибка валидации). Без этих кодов клиент не может корректно реагировать на ошибки. | Добавить 400, 409, 422 для POST /tasks. |
4.2. Проблемы пагинации и производительности бэкенда
| Проблема в контракте | Почему это плохо | Как исправить |
|---|---|---|
| GET /tasks без параметров пагинации | Если в системе 100 000 задач, один запрос без limit вернёт их все. Сервер: нагрузка на память (сериализация 100 000 JSON-объектов), нагрузка на БД (full scan), сеть (100+ MB ответа). Клиент: тормозит, виснет, падает по памяти. | Добавить query-параметры page (номер страницы) и limit (элементов на странице, max 100). В ответе — пагинационный объект. |
| Отсутствие фильтрации | GET /tasks без параметров фильтрации всегда возвращает все задачи. Клиент не может запросить "только мои задачи" или "только критичные". Это заставляет клиента фильтровать на своей стороне (загрузка всех данных!) или писать отдельные эндпоинты. | Добавить query-параметры: status, priority, assignee_id, project_id. |
| Нет сортировки | Без параметра sort клиент не может управлять порядком вывода. Результат может приходить в произвольном порядке (от БД). |
Добавить sort и order: ?sort=created_at&order=desc. |
| Нет ограничения на размер ответа | Потенциальная DoS-атака: злоумышленник может вызвать GET /tasks без limit и перегрузить сервер. | Ввести дефолтный limit (например, 20) и максимальный (100). |
4.3. Проблемы безопасности
| Проблема в контракте | Почему это плохо | Как исправить |
|---|---|---|
| Нет security схемы (аутентификации) | Любой может вызвать любой эндпоинт без токена. GET /tasks — утечка данных всех задач. POST /tasks — создание задачи от имени другого пользователя. DELETE /tasks/{id} — удаление чужой задачи. | Добавить components/securitySchemes с Bearer JWT. Применить security: [{ bearerAuth: [] }] ко всем эндпоинтам, кроме /auth/*. |
| Нет описания ролевой модели (authorization) | Даже с JWT неясно: кто может удалять задачу? Только автор? Администратор? Исполнитель? Без этого — или всё доступно всем (дыра), или разработчик сам придумывает логику (расхождение с требованиями). | Добавить требования авторизации в описание эндпоинта или через x-acl расширение OpenAPI. |
| Нет валидации входных данных | Схема POST /tasks содержит только title: string. Не указаны: обязательность (required), длина (minLength/maxLength), паттерн (pattern). Клиент может отправить title длиной 100 000 символов — сервер упадёт или сохранит гигантскую строку. |
Добавить required: [title], minLength: 1, maxLength: 255 для title. Для description — maxLength: 5000. |
| Нет rate limiting в контракте | Хотя rate limiting реализуется на Gateway, его отсутствие в контракте означает, что клиент не знает о лимитах. Клиент может слать 1000 запросов в секунду и получить блокировку без объяснения. | Указать лимиты в описании или в заголовках ответа (X-RateLimit-Limit, X-RateLimit-Remaining). |
| Нет HTTPS | В контракте не указано использование HTTPS (хотя это транспортный уровень, не OpenAPI). Параметр schemes или URL сервера должны использовать https://. |
Убедиться, что URL сервера начинается с https://. |
4.4. Проблемы REST-принципов и контракта
| Проблема в контракте | Почему это плохо | Как исправить |
|---|---|---|
| Нет схемы ответа (response schema) | В ответах только description: "OK" и description: "Created". Клиент не знает, какие поля придут в ответе. Невозможно сгенерировать клиентский код. |
Для GET /tasks — схема массива Task с пагинацией. Для POST — схема созданной Task. Для DELETE — пустое тело. |
| Отсутствует operationId | Без operationId инструменты генерации клиентов (OpenAPI Generator, NSwag) создают нечитаемые имена функций. | Добавить: operationId: listTasks, operationId: createTask, operationId: deleteTask. |
| Нет $ref для компонентов | Модели не вынесены в components/schemas. Нельзя переиспользовать Task между эндпоинтами. Каждый эндпоинт описывает модель заново — дублирование и риск рассинхронизации. |
Вынести Task, CreateTaskRequest, Pagination в components/schemas. Ссылаться через $ref. |
| summary слишком короткий | "List all tasks" — не описывает ни фильтры, ни формат ответа, ни возможные ошибки. |
Добавить description с деталями: эндпоинт возвращает список задач с поддержкой фильтрации по статусу, приоритету, исполнителю и проекту, с пагинацией. |
| Нет описания ошибок (4xx, 5xx) | Клиент не знает, какие ошибки могут возникнуть. Невозможно реализовать корректную обработку. | Добавить components/responses для 400, 401, 404, 409, 422, 500. Описать схемы ошибок: { error: string, message: string, details: [...] }. |
Шаблон ответа для Задания 4
| № | Проблема | Почему это плохо | Как исправить |
|---|---|---|---|
| 1 | DELETE возвращает 200 вместо 204 | ||
| 2 | Нет пагинации в GET /tasks | ||
| 3 | Нет аутентификации (security) | ||
| 4 | Нет схемы ответа (response schema) | ||
| 5 | Нет ограничений на title (maxLength) | ||
| 6 | Нет status-кодов ошибок | ||
| 7 | Нет operationId | ||
| 8 | Нет фильтрации GET /tasks |
Дополнительный разбор: как отсутствие пагинации разрушает бэкенд
Рассмотрим сценарий: система выросла до 500 000 задач. Клиент вызывает GET /tasks без ?limit=....
Без пагинации:
1. Сервер: SELECT * FROM tasks → 500 000 записей
2. Сервер: загружает 500 000 объектов в память (500 MB RAM)
3. Сервер: сериализует в JSON (50 MB строка)
4. Сеть: передаёт 50 MB клиенту (10+ секунд)
5. Клиент (SPA): получает 50 MB → пытается отобразить → виснет
6. Если 100 клиентов одновременно: 100 × 50 MB = 5 GB трафика → сервер падает от нагрузки
С пагинацией (limit=20):
1. Сервер: SELECT * FROM tasks LIMIT 20 OFFSET 0 → 20 записей
2. Сервер: 20 объектов в памяти (20 KB)
3. Сеть: 2 KB → 2 мс
4. Клиент: быстрый рендер 20 задач
5. Кнопка "Загрузить ещё" → следующий запрос
Дополнительный разбор: почему неправильные статус-коды — это уязвимость
Сценарий: DELETE /tasks/42 возвращает 200 OK с пустым телом.
- Клиент (SPA) проверяет
response.status === 200→ успех → удаляет задачу из UI - Но задача НЕ удалилась (сервер вернул 200 по ошибке, баг в реализации)
- Пользователь видит, что задача исчезла, а на самом деле она осталась в БД
- Последствие: пользователь создаёт задачу с тем же именем → дубликат. Или, ещё хуже: задача содержала персональные данные, они не удалились — утечка данных.
Правильный подход: DELETE → 204 + реальное удаление. Если удаление не удалось — 500 или 409 (конфликт).
Критерии оценки (суммарно)
| Задание | Баллы |
|---|---|
| Задание 1: Проектирование REST API | 3,0 |
| Задание 2: OpenAPI-спецификация | 3,0 |
| Задание 3: Интеграционные паттерны | 2,5 |
| Задание 4: Анализ API-контракта | 1,5 |
| Итого | 10,0 |
Шкала оценивания:
- 9–10 баллов: Отлично
- 7–8 баллов: Хорошо
- 5–6 баллов: Удовлетворительно
- 0–4 балла: Требуется доработка
Требования к оформлению
- Формат: Markdown или PDF
- OpenAPI-спецификация: YAML в блоке кода (компилируемый)
- Именование файла:
Модуль07_ФамилияИО.mdили.pdf
Советы для успешного выполнения
Задание 1 (REST API)
- Используйте существительные во множественном числе:
/tasks, не/task - Для пагинации:
?page=1&limit=10 - Для фильтрации:
?status=Done&priority=High&assignee_id=5 - Для удаления — статус 204, не 200
- Для частичного обновления — PATCH, не PUT
- Иерархические ресурсы:
/tasks/{taskId}/comments
Задание 2 (OpenAPI)
- Используйте Swagger Editor (editor.swagger.io) — проверяет синтаксис
- Не забудьте
components/schemas,components/responses,securitySchemes - Укажите
format: date-timeдля дат,format: emailдля email - Создайте отдельную схему
CreateTaskRequestиTaskListResponse - Для nullable полей используйте
nullable: true
Задание 3 (Интеграции)
- Slack — синхронный REST (нужен немедленный ответ). Если Slack недоступен — retry + CB
- Google Calendar — асинхронный (может подождать). Подойдёт очередь
- 1С — асинхронный, пакетная обработка (раз в час). Подойдёт очередь
- Для Saga — продумайте компенсацию для каждого шага
- Idempotency-Key: UUID, передаётся в заголовке, хранится 24 часа
Задание 4 (Анализ)
- Статус для DELETE должен быть 204, не 200
- Нет описания request body для POST /tasks (только тип, без required)
- Нет security — эндпоинты доступны без аутентификации (уязвимость!)
- Нет параметров для GET /tasks (нельзя отфильтровать + нет пагинации → проблема производительности)
- Нет описания ошибок (4xx, 5xx)
- Нет схемы ответа (клиент не знает, что приходит)
- Нет operationId
- Нет валидации полей (maxLength для title)