Система для добавления и чтения постов и комментариев с использованием GraphQL
Реализовать систему для добавления и чтения постов и комментариев с использованием GraphQL, аналогичную комментариям к постам на популярных платформах, таких как Хабр или Reddit.
- Можно просмотреть список постов.
- Можно просмотреть пост и комментарии под ним.
- Пользователь, написавший пост, может запретить оставление комментариев к своему посту.
- Комментарии организованы иерархически, позволяя вложенность без ограничений.
- Длина текста комментария ограничена до, например, 2000 символов.
- Система пагинации для получения списка комментариев.
- Клонирование репозитория
make docker.run.db
- запуск postgresmake docker.run.migrate
- запуск миграций (либо использоватьmake migrate.up
, если утилита golang-migrate установлена локально)make local.run
- локальный запуск сервера
- Клонирование репозитория
make docker.run
- запуск всех необходимых служб в Docker
В данном случае миграции для базы данных накатятся сами.
Для запуска тестов необходимо прописать make tests.run
Для остановки и удаления контейнеров используйте команду make docker.down
API описано в graphqls файлах в директории graph. Запросы были разделены на два домена: посты и комментарии.
Для удобства тестирования сервиса предлагается воспользоваться коллекцией Postman. В коллекции описаны все Queries, Mutations и Subscriptions с возможностью изменения аргументов. Если Postman пользоваться неудобно, то ниже будут примеры запросов.
Для работы с GraphQL была исползована библиоткека gqlgen
Были реализованы запросы:
- Получение списка постов с пагинацией (указывается номер и размер страницы)
- Получение подробной информации о посте с получением комментариев с неограниченной вложеностью и пагинацией
Для решения проблемы N+1 комментарии для поста и ответы на комментарий подгружаются только в том случае, если они запрашиваются в GraphQL запросе.
Были реализованы мутации:
- Создание поста
- Создание комментария
При создании поста можно указать, разрешены ли для него комментарии. Комментарий создается для конректного поста, при этом он может быть ответом на какой-то другой комментарий. При создании комментария проверяется, можно ли к посту оставлять комментарии.
Реализована Подписка на комментарии к конкретному посту.
Для управления подписками был реализован CommentObserver, с помощью которого можно создавать, удалять и передавать информацию слушателям.
Слушатели представляют собой структуру из канала для комментариев и идентификатора слушателя. В данной работе не реализована работа с иднетификаторами пользователей, но в перспективе можно создать хранилище, в котором каждый слушатель идентифицируется id поста и id пользователя. Для этого можно реализовать систему аутентификации, к примеру, на основе JWT токенов.
При закрытии контекста подключения слушатель удаляется из хранилища.
Были реализованы unit-тесты для разных слоев программы. Для создания моков я использовал утилиту gomock. Для тестирования я сгенерировал моки для интерфейсов слоёв gateway, service и интерфейса Observers в транспортном слое.
Настроен CI пайплайн запуска тестов и билда приложения на основе GitHub Actions. Каждый коммит и пуллреквест будет проверяться на прохождение всех тестов и сборку. Поэтому, в принципе, нет нужды запускать тесты локально.
Есть возможность выбора хранилища для постов и комментариев: либо PostgeSQL, либо самописное in-memory хранилище. При локальном запуске для выбора хранилища необходимо указать в .env файле прописать USE_IN_MEMORY=true для выбора in-memory хранилища. В других случаях будет использоваться PostgreSQL.
В случае с запуском Docker-контейнеров необхолимо прописать то же значение переменной окружения в docker-compose.yml у сервиса app (11 строчка).
mutation CreatePost {
CreatePost(
post: {
name: "Something Interesting"
content: "A lot of water"
author: "Author1"
commentsAllowed: true
}
) {
id
createdAt
name
author
content
}
}
query GetAllPosts {
GetAllPosts(page: 1, pageSize: 3) {
id
createdAt
name
author
content
}
}
query GetPostById {
GetPostById(id: 1) {
id
createdAt
name
author
content
commentsAllowed
comments(page: 1, pageSize: 2) {
id
createdAt
author
content
post
replies {
id
createdAt
author
content
post
replyTo
}
}
}
}
mutation CreateComment {
CreateComment(input: { author: "Comm1", content: "WOW", post: "1" }) {
id
createdAt
author
content
post
replyTo
}
}
subscription CommentsSubscription {
CommentsSubscription(postId: "1") {
id
createdAt
author
content
post
replyTo
}
}