Go: HTTP-обработчик поиска задания через JobStore — задача с проверкой

Практическая задача на Go по теме «Две реплики не делят Go-память». Условие содержит контракт функций, предоставленные типы и требования к результату. Используйте выданный шаблон и сохраняйте его публичные объявления. Перед проверкой разберите успешный путь, обработку ошибок и отмену операции, когда она предусмотрена контрактом. Здесь оценивается конкретный компонент сервиса; в полном курсе микросервисов на Go эти компоненты связываются с HTTP, PostgreSQL, gRPC и Kafka.

HTTP-обработчик поиска задания через JobStore

Условие

Вам уже предоставлены следующие объявления — не переобъявляйте их:


type Job struct {
    ID     string `json:"id"`
    Status string `json:"status"`
}

type JobStore interface {
    Get(context.Context, string) (Job, error)
}

var ErrJobNotFound = errors.New("job not found")

type ErrorResponse struct {
    Error string `json:"error"`
}

Также предоставлена вспомогательная функция:


func writeJSON(w http.ResponseWriter, status int, value any)

Она выставляет Content-Type: application/json, записывает переданный HTTP-статус и JSON-представление value с завершающим переводом строки. Повторно объявлять её нельзя.

Импорты context, encoding/json, errors, net/http уже подключены.

Задача

Редактируйте функцию GetJob в полном шаблоне Go. Сохраните пакет, импорты, предоставленные типы и публичный адаптер. Функцию main добавлять не нужно. Дополнительную маршрутизацию вне функции писать не нужно.

Реализуйте одну функцию:


func GetJob(store JobStore) http.HandlerFunc

Функция принимает ненулевой store и возвращает HTTP-обработчик. Внешний код направляет в обработчик только запросы вида GET /jobs?id=... с одним непустым параметром id. Валидация метода, пути и параметра не требуется.

Поведение обработчика на каждый запрос:

  1. Вызвать store.Get ровно один раз, передав r.Context() и значение id из строки запроса.
  2. Если err == nil — ответить статусом 200 и точным значением возвращённого Job (только поля id и status согласно JSON-тегам структуры). Не подменять поля константами или значением id из запроса.
  3. Если errors.Is(err, ErrJobNotFound) — ответить статусом 404 и телом {"error":"job_not_found"}. Это работает и для обёрнутых sentinel-ошибок.
  4. При любом другом ненулевом err — ответить статусом 503 и телом {"error":"store_unavailable"}. Не анализировать текст ошибки.
  5. При любом ненулевом err сопровождающее значение Job игнорируется целиком.

Не создавать замещающий store, глобальную map, кеш, горутины, retry или HTTP-запросы. Каждый запрос работает только с переданным store — без сохранения ответов между запросами.

Примеры

Запрос Результат store.Get Статус Тело
GET /jobs?id=job-7 Job{ID:"job-7", Status:"queued"}, nil 200 {"id":"job-7","status":"queued"}
GET /jobs?id=job-7 Job{}, ErrJobNotFound 404 {"error":"job_not_found"}
GET /jobs?id=job-7 Job{}, <другая ошибка> 503 {"error":"store_unavailable"}

Подсказки

  • Для ветвления достаточно проверить err == nil и errors.Is(err, ErrJobNotFound) — третий случай остаётся на else; ErrorResponse уже определён для полей error в ответе.
  • id берётся через r.URL.Query().Get("id") и передаётся напрямую в store.Get вместе с r.Context() — без промежуточного хранения результата между запросами.

Запуск на Latorn: редактируйте реализацию в полном шаблоне, сохраняя предоставленные объявления и публичный адаптер RunScenarios. main не требуется. Адаптер принимает массив сценариев и возвращает наблюдения; ожидаемые результаты сравниваются отдельно на сервере. Для собственного запуска аргументы — JSON-массив с одним массивом сценариев: [[...]].

РешениеGo

Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.

Отправьте решение, чтобы увидеть результаты тестов.

Связанные задачи