- Как запускать
- Реализованные функции
- Описание API
- Скриншоты работы
- Задание
- Задача
- Требования и детали по заданию
- Опциональные задания
- Склонировать репозиторий
git clone https://github.com/capkeik/backend-trainee-assignment-2023.git
cd backend-trainee-assignment-2023
- Запустить с помощью команды
make run
Вопрос | Решение |
---|---|
Какой формат id пользователя? | Было решено оставить целым числом, как в примерах. |
Откуда берутся пользователи? | Добавлен отдельный эндпоинт POST /users для того, чтобы создавать пользователей. ID в базе данных не генерируется, принимается на вход |
Какой формат данных в файлах для отчета? Пример отчета, представленный в задании, не подходит под стандарт. | В результирующем файле значения разделяются запятыми без пробелов, названия полей игнорируются |
- Добавление пользователя (основное задание);
- Добавление нового сегмента (основное задание);
- Удаление сегмента (основное задание);
- Редактирование сегментов пользователя (основное задание);
- Просмотр активных сегментов пользователя (основное задание);
- Получение отчета об изменениях сегментов пользователей (доп. задание 1);
- Обязательные/необязательные поля в теле запросов
- Целочисленный id пользователя > 0
Не успел написать swagger :(
- GET
/users/{id}
- Получает активные сегменты пользователя.- Принимает
id
пользователя как параметр пути - Возвращает:
200
- Возвращает id пользователя;400
- Невалидный id пользователя;409
- Пользователь уже существует;
- Принимает
{
"id": 123,
"slugs": [
"SLUG",
"ANOTHER_SLUG"
]
}
- POST
/users
- Добавляет нового пользователя.201
- Пользователь успешно создан. Возвращает id пользователя;400
- Невалидное тело запроса;404
- Несуществующий пользователь;
{
"id": 123
}
{
"id": 123
}
- PATCH
/users
- редактирует сегменты пользователя.200
- Сегменты пользователя успешно обновлены;400
- Невалидное тело запроса;404
- Несуществующий пользователь;
{
"to_add": [
"SLUG_TO_ADD"
],
"to_remove": [
"SLUG_TO_REMOVE"
],
"id": 1000
}
{
"id": 1000,
"removed": [
"SLUG_TO_REMOVE"
],
"added": [
"SLUG_TO_ADD"
]
}
- POST
/segments
- Добавляет новый сегмент.201
- Сегмент успешно добавлен;400
- Невалидное тело запроса;
{
"slug": "NEW_SLUG"
}
{
"slug": "NEW_SLUG"
}
- DELETE
/segments
- Удаляет сегмент.200
- Сегмент успешно удален;404
- Несуществующий сегмент;400
- Невалидное тело запроса;
{
"slug": "NEW_SLUG"
}
- GET
/records/
- Возвращает ссылку на файл с историей обновлений сегментов пользователя.200
- История успешно собрана;404
- Несуществующий пользователь;400
- Невалидное тело запроса;##### Тело запроса
{
"id": 123
}
{
"link": "/records/download/csv/1-437892746829.csv"
}
- GET
/records/{filename}
- Отдает файл по названию.200
- файл успешно отдан;404
- Файл не найден;
В Авито часто проводятся различные эксперименты — тесты новых продуктов, тесты интерфейса, скидочные и многие другие. На архитектурном комитете приняли решение централизовать работу с проводимыми экспериментами и вынести этот функционал в отдельный сервис.
Требуется реализовать сервис, хранящий пользователя и сегменты, в которых он состоит (создание, изменение, удаление сегментов, а также добавление и удаление пользователей в сегмент)
Сценарии использования:
Хотим провести несколько экспериментов и протестировать новый функционал Авито:
- Голосовые сообщения в чатах
- Новые услуги продвижения
- Скидка 30% на услуги продвижения
- Скидка 50% на услуги продвижения
Кто из пользователей в какой эксперимент попадет будет решать большой отдел аналитики, а мы лишь дадим им возможность для таких тестов.
Допустим аналитики создали сегменты:
- AVITO_VOICE_MESSAGES
- AVITO_PERFORMANCE_VAS
- AVITO_DISCOUNT_30
- AVITO_DISCOUNT_50
и добавили созданные сегменты нескольким пользователям:
Пользователь | Сегменты которым он принадлежит |
---|---|
1000 | [AVITO_VOICE_MESSAGES, AVITO_PERFORMANCE_VAS, AVITO_DISCOUNT_30, …] |
1002 | [AVITO_VOICE_MESSAGES, AVITO_DISCOUNT_50, …] |
1004 | нет сегментов |
Формат хранения данных в базе данных не ограничен - можно выбрать любой удобный
Получили следующие данные:
-
Пользователь 1000 состоит в 3 сегментах: AVITO_VOICE_MESSAGES, AVITO_PERFORMANCE_VAS, AVITO_DISCOUNT_30
-
Пользователь 1002 состоит в 2 сегментах: AVITO_VOICE_MESSAGES, AVITO_DISCOUNT_50
-
Пользователь 1004 не состоит ни в одном из сегментов
Теперь мы хотим через API сервиса по user_id получать список сегментов в которых он состоит.
Технические требования:
- Сервис должен предоставлять HTTP API с форматом JSON как при отправке запроса, так и при получении результата.
- Язык разработки: Golang.
- Фреймворки и библиотеки можно использовать любые.
- Реляционная СУБД: MySQL или PostgreSQL.
- Использование docker и docker-compose для поднятия и развертывания dev-среды.
- Весь код должен быть выложен на Github/Gitlab с Readme файлом с инструкцией по запуску и примерами запросов/ответов (можно просто описать в Readme методы, можно через Postman, можно в Readme curl запросы скопировать, и так далее).
- Если есть потребность в асинхронных сценариях, то использование любых систем очередей - допускается.
- При возникновении вопросов по ТЗ оставляем принятие решения за кандидатом (в таком случае в Readme файле к проекту должен быть указан список вопросов, с которыми кандидат столкнулся и каким образом он их решил).
- Разработка интерфейса в браузере НЕ ТРЕБУЕТСЯ. Взаимодействие с API предполагается посредством запросов из кода другого сервиса. Для тестирования можно использовать любой удобный инструмент. Например: в терминале через curl или Postman.
Будет плюсом:
- Покрытие кода тестами.
- Swagger файл для вашего API.
Основное задание (минимум):
- Метод создания сегмента. Принимает slug (название) сегмента.
- Метод удаления сегмента. Принимает slug (название) сегмента.
- Метод добавления пользователя в сегмент. Принимает список slug (названий) сегментов которые нужно добавить пользователю, список slug (названий) сегментов которые нужно удалить у пользователя, id пользователя.
- Метод получения активных сегментов пользователя. Принимает на вход id пользователя.
Детали по заданию:
- По умолчанию сервис не содержит в себе никаких данных о сегментах и пользователях (пустая табличка в БД). Данные появляются при создании сегментов и добавлении их пользователям.
- Валидацию данных и обработку ошибок оставляем на ваше усмотрение.
- Список полей к методам не фиксированный. Перечислен лишь необходимый минимум. В рамках выполнения доп. заданий возможны дополнительные поля.
- Механизм миграции не нужен. Достаточно предоставить конечный SQL файл с созданием всех необходимых таблиц в БД.
- Сегменты пользователя очень важны - на этих данных в будущем строится аналитика о том насколько продукт востребован. Поэтому нужно следить за тем чтобы сегменты не терялись (не перетирались) и не добавлялись лишним пользователям.
- Мы можем добавлять сегменты пользователю динамически (он уже состоит в нескольких, мы можем добавить еще несколько, не перетирая существующие).
- В методе получения сегментов пользователя мы должны получить АКТУАЛЬНУЮ информацию о сегментах пользователя с задержкой не более 1 минуты после добавления сегмента.
Далее перечислены дополнительные задания.
Они не являются обязательными, но их выполнение даст существенный плюс перед другими кандидатами.
Можно выбрать несколько из представленных
Доп. задание 1:
Иногда пользователи приходят в поддержку и спрашивают почему у них пропал/появился какой-то новый функционал. Нужно иметь возможность посмотреть когда точно пользователь попал в конкретный сегмент.
Задача: реализовать сохранение истории попадания/выбывания пользователя из сегмента с возможностью получения отчета по пользователю за определенный период. На вход: год-месяц. На выходе ссылка на CSV файл.
Пример отчета:
идентификатор пользователя 1;сегмент1;операция (добавление/удаление);дата и время
идентификатор пользователя 1;сегмент2;операция (добавление/удаление);дата и время
идентификатор пользователя 2;сегмент3;операция (добавление/удаление);дата и время
Доп. задание 2:
Бывают ситуации когда нам нужно добавить пользователя в эксперимент на ограниченный срок. Например выдать скидку всего на 2 дня.
Задача: реализовать возможность задавать TTL (время автоматического удаления пользователя из сегмента)
Пример: Хотим чтобы пользователь попал в сегмент на 2 дня - для этого в метод добавления сегментов пользователю передаём время удаления пользователя из сегмента отдельным полем
Доп. задание 3:
Мы хотим добавлять пользователя в сегмент не в ручную, а автоматически. В сегмент будет попадать заданный процент пользователей.
Задача: в методе создания сегмента, добавить опцию указания процента пользователей, которые будут попадать в сегмент автоматически. В методе получения сегментов пользователя, добавленный сегмент должен отдаваться у заданного процента пользователей.
Пример: создали сегмент AVITO_VOICE_MESSAGES и указали что 10% пользователей будут попадать в него автоматически. Пользователь 1000 попал в этот сегмент автоматически. При запросе сегментов пользователя 1000, сегмент AVITO_VOICE_MESSAGES должен отдаваться всегда.