# PROD v2: Pulse Backend Ваши коллеги разрабатывают социальную сеть для инвесторов. Уже совсем скоро им нужно сдавать проект, а вся команда бэкенд-разработчиков ушла в отпуск. Кто-то проболтался о том, что вы знакомы с Git, HTTP, Docker, PostgreSQL и e2e-тестами. Это именно то, что нужно ребятам (а если с чем-то не знакомы, они рассчитывают на ваши навыки поиска информации)! Помогите коллегам успеть завершить проект до дедлайна и реализуйте новое HTTP API :) Результатом выполнения данного задания является Github репозиторий с исходным кодом приложения (директория `solution`). ## Про приложение Приложение должно представлять из себя HTTP сервер, реализующий необходимое [API](./tests/openapi.yml). В наследие от предыдущей команды вам достался инстанс PostgreSQL, который необходимо использовать для хранения данных. Приложение должно конфигурироваться через переменные окружения: - `SERVER_ADDRESS` — хост и порт, которые будет _слушать_ запущенный HTTP сервер. Например, `0.0.0.0:8080`. - `SERVER_PORT` — содержит порт; запущенный сервер должен слушать IP `0.0.0.0` и указанный порт. Используйте эту переменную, если вам не подошел формат данных в переменной `SERVER_ADDRESS` (переданный параметры равнозначны). - `POSTGRES_CONN` — DSN- **или** URL-строка для подключения к PostgreSQL. С форматами можно ознакомиться [здесь](https://pkg.go.dev/github.com/jackc/pgx/v4/pgxpool@v4.11.0#ParseConfig). Пример: `user=postgres password=postgres host=localhost port=5432 dbname=pulse sslmode=verify-ca pool_max_conns=10`. Учитывая современные реалии, приложение будет запускаться через Docker контейнер. В репозитории присутствует Dockerfile, с помощью которого будет собираться образ приложения. Список используемых зависимостей не ограничен, однако вы должны убедиться, что необходимые зависимости загружаются и подключаются в Dockerfile. Описание API находится ниже, но если вы хотите ознакомиться с точными требованиями, не стесняйтесь использовать Swagger и предоставленную [Open API спецификацию](./tests/openapi.yml). Тестирование решения происходит с помощью Github CI. Для отправки решения на тестирование необходимо обновить исходный код вашего репозитория на Github (git commit & git push). **Вы можете редактировать файлы в директории `solution`. Если в репозитории содержатся изменения в других файлах, решение не будет принято.** ## Оценивание Для получения баллов за группу тестов решение должно пройти все тесты из данной группы. Группы тестов могут зависеть друг от друга. Если группа B зависит от группы B, при тестировании группы B могут использоваться эндпоинты, участвовавшие в тестировании группы A. Это свойство транзитивно! | Название группы | Описание | Баллы | От каких групп зависит | |------------------|------------------------------------|-------|------------------------| | 01/ping | Успешный ответ на `/api/ping`. | 1 | | | 02/countries | Получение и фильтрация стран. | 6 | | | 03/auth/register | Регистрация пользователей. | 6 | - 02/countries | | 04/auth/sign-in | Аутентификация и получение токена. | 7 | - 03/auth/register | | 05/me | Получение и редактирование собственного профиля. | 8 | - 04/auth/sign-in | | 06/profiles | Получение профиля по логину. | 5 | - 04/auth/sign-in | | 07/password | Изменение пароля. | 7 | - 05/me | | 08/friends | Друзья! | 12 | - 04/auth/sign-in | | 09/posts/publish | Публикация поста и получение по ID. | 12 | - 05/me
- 08/friends | | 10/posts/feed | Получение новостной ленты. | 16 | - 09/posts/publish | | 11/posts/likes | Лайки и дизлайки. | 20 | - 10/posts/feed | В спорных ситуациях будет оцениваться качество кода. На данный момент в Github CI тестирование производится на публичном наборе тестов. Данные тесты помогают провалидировать минимальную логику приложения, **но не гарантируют прохождения финальных тестов**. ## Группы тестов ### Общие требования **У всех эндпоинтов есть префикс `/api`.** Обратите внимание, возврат успешного ответа на `GET /api/ping` является **обязательным условием для начала тестирования приложения**. Поступающие запросы и возвращаемые ответы должны соответствовать структуре и требованиям, описанным в [Open API](./tests/openapi.yml) спецификации. Обращайте внимание на ожидаемые status code, ограничения по длине и разрешенные символы в строках. ### 01/ping Достаточно реализовать возврат успешного ответа (с кодом `200`) на запрос `GET /api/ping`. Содержимое тела ответа при этом не валидируется, можно возвращать `"ok"`. Данная логика является блокирующей для всех остальных групп тестов. ### 02/countries Как и в любом большом проекте у нас есть собственный словарь стран, который используется при регистрации пользователей и может учитываться рекомендательными системами и системой локализации контента. Про каждую страну известны следующие данные: ```json { "name": "полное название", "alpha2": "двухбуквенный код страны", "alpha3": "трехбуквенный код страны", "region": "географический регион" } ``` Необходимо реализовать следующие эндпоинты: - `GET /countries` — получить список доступных стран, доступна фильтрация по регионам. - `GET /countries/{alpha2}` — получить страну по её уникальному двухбуквенному коду. Самое интересное: **для получения списка стран необходимо использовать предоставленную СУБД PostgreSQL**. Данные находятся в таблице `countries`, которая имеет следующее определение: ```sql CREATE TABLE countries ( id SERIAL PRIMARY KEY, name TEXT, alpha2 TEXT, alpha3 TEXT, region TEXT ); INSERT INTO countries (name, alpha2, alpha3, region) VALUES ('Åland Islands','AX','ALA','Europe'), ('Albania','AL','ALB','Europe'), ...; ``` При тестировании в Github CI база данных уже будет содержать нужный набор данных. Обратите внимание, данные в публичном и закрытом наборе тестов могут отличаться. ### 03/auth/register Эндпоинт `/auth/register` используется для первичной регистрации пользователей. Сервер должен поддерживать базу данных пользователей, валидировать запросы и не допускать наличия пользователей с эквивалентными регистрационными данными. Не храните пароль пользователей в [открытом виде](https://security.stackexchange.com/questions/36833/why-should-i-hash-passwords), используется хеширование (например, bcrypt). ### 04/auth/sign-in Эндпоинт `/auth/sign-in` предназначен для аутентификации пользователя по логину и паролю и генерации сессионного токена, который в дальнейшем будет использоваться для генерации запросов. Генерируемый токен должен уникально идентифицировать пользователя и быть сложным для подбора (можно использовать JWT). Данный токен в дальнейшем будет передаваться пользователем в заголовке `Authorization: Bearer {token}`, и приложение должно уметь понять, какой пользователь хочет сделать запрос. Временно будем считать, что время действия токена ограничено одним часом. ### 05/me Эндпоинт `/me/profile` используется для получения и редактирования параметров собственного профиля пользователя. Действие зависит от указанного метода (`GET` и `PATCH`). Сервер должен идентифицировать пользователя по переданному токену. Значение токена будет подставляться в заголовок `Authorization` в формате `Bearer {token}`. Например, `Authorization: Bearer $deddz$@pp...`. В запросе на редактирование профиля передаются значения только тех полей, которые необходимо обновить. ### 06/profiles Эндпоинт `/profiles/{login}` позволяет получить профиль другого пользователя по логину. Обратите внимание, в некоторых ситуациях профиль пользователя получить нельзя (в зависимости от значения параметра `isPublic`). Для получения дополнительных деталей ознакомьтесь со спецификацией API. ### 07/password С помощью `/me/updatePassword` у пользователя появляется возможность изменить пароль от своего аккаунта. После изменения пароля: - Аутентификация со старым паролем становится невозможной. - Все ранее выпущенные токены должны быть отозваны. После успешной смены пароля при попытке получить свой профиль со старым токеном пользователь должен получать ошибку. ### 08/friends В приложении появляется возможность добавлять и удалять других пользователей в друзья. И конечно же можно посмотреть список своих друзейЙ Чтобы не нагружать сервера и клиенты слишком сильно, в запросах на получение списка друзей используется пагинация. С помощью параметров `offset` и `limit` можно "постранично" получить весь список друзей, запрашивая данные порционно. Вам потребуется запоминать дату и время последнего добавления в друзья для корректно сортировки и реализации пагинации. ### 09/posts/publish В данной группе проверяется возможность создавать публикации со стороны пользователей. Сервер должен генерировать уникальные идентификаторы и запоминать время создания публикаций. У пользователя есть доступ к своим постам, постам пользователей с публичным профилем и постам других пользователей, которые добавили данного пользователя в друзья. В данной группе не проверяются поля с лайками и дизлайками. ### 10/posts/feed У пользователей появилась возможность смотреть новостную ленту со своими и чужими постами. Вместе с пагинацией. В данной группе не проверяются поля с лайками и дизлайками. ### 11/posts/likes Самое интересное: пользователи могут поставить лайк и дизлайк публикации, к которой у них есть доступ. Всегда запоминается последняя реакция пользователя. Если пользователь поставил лайк два раза подряд, эффект лайка остается. В полях `likesCount` и `dislikesCount` необходимо отразить уникальное число лайков и дизлайков.` ## Тестирование Для тестирования решения отразите ваши изменения в Github репозитории. Разрешено изменять только директорию `solution`, иначе тесты не будут запущены. ### Тестирование в CI Для тестирования решений используется [Github CI](https://docs.github.com/en/actions/automating-builds-and-tests/about-continuous-integration). При отправке новых изменений в репозиторий на Github активируется тестирующий пайплайн. Пайплайн состоит из двух этапов: - Сборка Docker образа с вашим приложением (на основании исходного кода репозитория и Dockerfile). - Запуск тестов. Для каждой группы тестов - запускаются Docker контейнеры с вашим приложением и PostgreSQL; - тестирующая система дожидается успешного (`200`) ответа на `GET /api/ping`, на это дается не более 10 секунд; - приложение считается запущенным и начинается запуск HTTP тестов из тестируемой группы. Проверьте, что ваше приложение готово запускать HTTP сервер на адресе, переданном в переменной окружения `SERVER_ADDRESS`. **В качестве хоста (IP) передается `0.0.0.0`, а не localhost или 127.0.0.1. Это важно!** Также проверьте локально, что Docker образ с вашим приложением собирается (выполните `docker build .` в директории `solution`). Существующие ограничения: - Решению выделяется 3 vCPU, 6 GB RAM и до 1 GB дискового пространства (не учитывая PostgreSQL). - В рамках тестирования ваше приложение не должно завершать работу (помните о защите от Exception, panic и прочих причинах аварийного завершения). - Сетевое взаимодействие разрешено только с PostgreSQL и тестирующей системой. Обращаться к сторонним ресурсам по сети нельзя. Во вкладке Actions можно найти лог тестирования, в котором будут отражены результаты запуска тестов на публичном наборе тестов. Прохождение публичного набора тестов не дает гарантию прохождения финальных тестов. ### Локальное тестирование Для локального тестирования вы можете пользоваться [Postman](https://www.postman.com/). В директории проекта кто-то из коллег оставил [Postman коллекцию](./tests/public-tests.json) с публичными тестами для API. Не забудьте переопределить `base_url` в переменных коллекции. Для инициализации СУБД PostgreSQL можно использовать [заранее подготовленный скрипт](./tests/init-database.sh), из которого можно выудить SQL запросы. Чтобы локальное тестирование было максимально приближенным к тестированию в CI, мы рекомендуем запускать PostgreSQL и ваше приложение в Docker контейнерах (связанных одной сетью). ## Changelog Как это часто бывает, заказчики проекта вносят правки в требования! Ваших коллег ждала та же участь... Заказчики просили передать, что они будут стараться делать как можно меньше изменений. Но удача на нашей стороне! Коллеги будут фиксировать все правки в данном документе и вести ченджлог изменений.