Напишите сервис для сокращения длинных URL.
- Сервер должен быть доступен по адресу: http://localhost:8080.
- Сервер должен предоставлять два эндпоинта: POST / и GET /{id}.
- Эндпоинт POST / принимает в теле запроса строку URL для сокращения и возвращает ответ с кодом 201 и сокращённым URL в виде текстовой строки в теле.
- Эндпоинт GET /{id} принимает в качестве URL-параметра идентификатор сокращённого URL и возвращает ответ с кодом 307 и оригинальным URL в HTTP-заголовке Location.
- Нужно учесть некорректные запросы и возвращать для них ответ с кодом 400.
Покройте сервис юнит-тестами. Сконцентрируйтесь на покрытии тестами эндпоинтов, чтобы защитить API сервиса от случайных изменений.
Вы написали приложение с помощью стандартной библиотеки net/http. Используя любой пакет (роутер или фреймворк), совместимый с net/http, перепишите ваш код.
Задача направлена на рефакторинг приложения с помощью готовой библиотеки.
Обратите внимание, что необязательно запускать приложение вручную: тесты, которые вы написали до этого, помогут вам в рефакторинге.
Добавьте в сервер новый эндпоинт POST /api/shorten, принимающий в теле запроса JSON-объект {"url":"<some_url>"} и возвращающий в ответ объект {"result":"<shorten_url>"}.
Не забудьте добавить тесты на новый эндпоинт, как и на предыдущие.
Помните про HTTP content negotiation, проставляйте правильные значения в заголовок Content-Type.
Добавьте возможность конфигурировать сервис с помощью переменных окружения:
- адрес запуска HTTP-сервера с помощью переменной SERVER_ADDRESS.
- базовый адрес результирующего сокращённого URL с помощью переменной BASE_URL.
Сохраняйте все сокращённые URL на диск в виде файла.
При перезапуске приложения все URL должны быть восстановлены.
Путь до файла должен передаваться в переменной окружения FILE_STORAGE_PATH.
При отсутствии переменной окружения или при её пустом значении вернитесь к хранению сокращённых URL в памяти.
Поддержите конфигурирование сервиса с помощью флагов командной строки наравне с уже имеющимися переменными окружения:
- флаг -a, отвечающий за адрес запуска HTTP-сервера (переменная SERVER_ADDRESS);
- флаг -b, отвечающий за базовый адрес результирующего сокращённого URL (переменная BASE_URL);
- флаг -f, отвечающий за путь до файла с сокращёнными URL (переменная FILE_STORAGE_PATH).
Добавьте поддержку gzip в ваш сервис. Научите его:
- принимать запросы в сжатом формате (HTTP-заголовок Content-Encoding);
- отдавать сжатый ответ клиенту, который поддерживает обработку сжатых ответов (HTTP-заголовок Accept-Encoding).
Добавьте в сервис функциональность аутентификации пользователя.
Сервис должен:
- Выдавать пользователю симметрично подписанную куку, содержащую уникальный идентификатор пользователя, если такой куки не существует или она не проходит проверку подлинности.
- Иметь хендлер GET /api/user/urls, который сможет вернуть пользователю все когда-либо сокращённые им URL в формате:
[
{
"short_url": "http://...",
"original_url": "http://..."
},
...
]
- При отсутствии сокращённых пользователем URL хендлер должен отдавать HTTP-статус 204 No Content.
- Получить куки запроса можно из поля (*http.Request).Cookie, а установить — методом http.SetCookie.
- Добавьте в сервис функциональность подключения к базе данных. В качестве СУБД используйте PostgreSQL не ниже 10 версии.
- Добавьте в сервис хендлер GET /ping, который при запросе проверяет соединение с базой данных. При успешной проверке хендлер должен вернуть HTTP-статус 200 OK, при неуспешной — 500 Internal Server Error.
- Строка с адресом подключения к БД должна получаться из переменной окружения DATABASE_DSN или флага командной строки -d.
Перепишите сервис так, чтобы СУБД PostgreSQL стала хранилищем сокращённых URL вместо текущей реализации.
Сервису нужно самостоятельно создать все необходимые таблицы в базе данных. Схема и формат хранения остаются на ваше усмотрение.
При отсутствии переменной окружения DATABASE_DSN или флага командной строки -d или при их пустых значениях вернитесь последовательно к:
- хранению сокращённых URL в файле при наличии соответствующей переменной окружения или флага командной строки;
- хранению сокращённых URL в памяти.
Добавьте новый хендлер POST /api/shorten/batch, принимающий в теле запроса множество URL для сокращения в формате:
[
{
"correlation_id": "<строковый идентификатор>",
"original_url": "<URL для сокращения>"
},
...
]
В качестве ответа хендлер должен возвращать данные в формате:
[
{
"correlation_id": "<строковый идентификатор из объекта запроса>",
"short_url": "<результирующий сокращённый URL>"
},
...
]
Сделайте в таблице базы данных с сокращёнными URL уникальный индекс для поля с исходным URL. Это позволит избавиться от дублирующих записей в базе данных.
При попытке пользователя сократить уже имеющийся в базе URL через хендлеры POST / и POST /api/shorten сервис должен вернуть HTTP-статус 409 Conflict, а в теле ответа — уже имеющийся сокращённый URL в правильном для хендлера формате.
Стратегии реализации:
- Чтобы не проверять наличие оригинального URL в базе данных отдельным запросом, можно воспользоваться конструкцией INSERT ... ON CONFLICT в PostgreSQL. Однако в этом случае придётся самостоятельно возвращать и проверять собственную ошибку.
- Чтобы определить тип ошибки PostgreSQL, с которой завершился запрос, можно воспользоваться библиотекой github.com/jackc/pgerrcode, в частности pgerrcode.UniqueViolation. В таком случае нужно будет сделать дополнительный запрос к хранилищу, чтобы определить сокращённый вариант URL.