← Все проекты уровня 6
Уровень 6 · Первые веб-сервисыВариант B

Список чтения

Личный каталог закладок с поиском по названию и явным состоянием прочтения каждой записи.

Техническое задание

Модель данных и назначение

Создайте на Go REST API личного списка чтения. Состояние существует только в памяти процесса и теряется при перезапуске. Запись закладки состоит из id, url, title, status, createdAt и updatedAt. status принимает ровно значения unread и read. Сервер задаёт id и временные метки; клиент не может подменить их. Внешняя аутентификация и несколько пользователей в рамках этого задания не требуются.

API, проверки и эксплуатация

POST /api/bookmarks создаёт закладку и отвечает 201. url обязателен, разбирается стандартным URL-парсером, использует только http или https, содержит hostname и не содержит userinfo; после нормализации схемы и hostname сравниваются без учёта регистра, fragment полностью исключается из сравнения. Путь и query сохраняют семантику URL, поэтому разные query считаются разными адресами. Дубликат нормализованного URL запрещён: сервер отвечает 409 с кодом duplicate_url и не создаёт вторую запись. title обязателен, после trim содержит 1–200 символов; status при создании необязателен и по умолчанию unread. Неправильное поле возвращает 400 с указанием поля.

GET /api/bookmarks поддерживает limit и offset с теми же пределами: default limit 20, диапазон 1–100, offset от нуля. Необязательный status фильтрует по одному из двух допустимых значений, а q выполняет регистронезависимый поиск по title и hostname; пустой q трактуется как отсутствие фильтра. Ответ содержит items, limit, offset и total. Порядок — createdAt по убыванию, затем id по возрастанию. GET /api/bookmarks/{id} возвращает запись или 404. PATCH /api/bookmarks/{id} принимает одно или оба поля title и status, отклоняет неизвестные поля, пустой патч и некорректные значения; при успехе обновляет updatedAt и возвращает 200. URL после создания неизменяем. DELETE отвечает 204 либо 404. Ошибки имеют единый JSON-конверт, неизвестный маршрут даёт 404, неподдерживаемый метод — 405. Ограничьте тело запроса, установите HTTP-таймауты и корректно прекращайте работу при отмене контекста. Общие проверки уникальности URL и запись должны выполняться как одна синхронизированная операция, в том числе при параллельных POST. Не записывайте URL целиком в логи, поскольку query может содержать личные данные.

Структура программы

Небольшой internal/httpapi содержит обработчики и DTO, internal/bookmarks — нормализацию URL, уникальность и правила закладок, internal/bookmarkmemory — хранилище в памяти. main.go связывает пакеты, запускает сервер и выполняет shutdown; правила URL тестируются без HTTP.

Критерии готовности

Ожидаемый результат

Поставка

  • Запускаемый Go REST API хранит закладки в памяти и поддерживает создание, фильтрацию, поиск, чтение, изменение статуса и удаление.
  • README фиксирует правила нормализации URL, поля ответов и пагинацию; детерминированные офлайн-тесты используют временный локальный сервер.

Приёмка

  • Валидный HTTPS URL создаёт unread-запись с кодом 201; повтор адреса с другим регистром схемы и hostname возвращает 409.
  • URL с javascript-схемой, title из пробелов и неизвестный status дают 400 без вставки.
  • Совместные status=read и q возвращают только совпадающие записи, правильный total и стабильный порядок.
  • PATCH меняет только переданные title/status; пустое тело даёт 400, попытка изменить URL отклоняется.
  • Неизвестный id возвращает 404; конкурентные одинаковые POST создают ровно одну закладку.
  • README показывает зависимости между HTTP, правилами URL и памятью; нормализация и уникальность тестируются без HTTP.