Skip to content

Wesp1nzee/backend-fast-api

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 

Repository files navigation

PROMO v2: Promo Code Backend

Note

В директории tests-final опубликован финальный набор тестов Tavern. В файле config-tests-final.yml находится описание групп тестов и разбалловка, а также информация о нагрузочном тестировании.

Результаты тестирования доступны во вкладке Actions в приватном репозитории с решением, подробнее в инструкции.

В рамках апелляции будут рассмотрены ситуации, когда тесты противоречат условию, спецификации и FAQ. К апелляции не принимаются: просьбы пересмотреть разбалловку, перенос теста из одной группы в другую, обсуждение формата url и email.

Для локального тестирования необходимо обновить образ антифрода: docker pull lodthe/prod-backend-antifraud:latest. Для корректного завершения 13 и 14 группы тестов при запуске контейнера антифрода необходимо передать следующие переменные окружения:

SLOWDOWN_AFTER=slowdown@antifraud.ru
BLOCKED_EMAILS=blocked@antifraud.com

docker run -e SERVER_PORT=9090 -e SLOWDOWN_AFTER=slowdown@antifraud.ru -e BLOCKED_EMAILS=blocked@antifraud.com -p 9090:9090 lodthe/prod-backend-antifraud:latest

Important

Ознакомьтесь с FAQ (кликабельно!) и полностью прочитайте условие перед обращением в бота поддержки!

Представьте, что вы работаете в динамичном стартапе, который занимается продвижением и агрегацией промокодов от различных компаний. Ваша платформа позволяет бизнесам создавать и настраивать промокоды с таргетингом, а конечным пользователям — легко находить и активировать промокоды и взаимодействовать с ними. Ваши коллеги разрабатывают фронтенд и другие части системы, но сейчас им срочно нужна ваша помощь в реализации надежного и масштабируемого бэкенда, который обеспечит все необходимые API-эндпоинты и пройдет все тесты по юзеркейсам. Это ваш шанс внести значимый вклад в успех стартапа и продемонстрировать свои навыки в области бэкенд-разработки!

Ваши коллеги разрабатывают инновационную платформу для управления промокодами, предназначенную как для бизнес-пользователей (B2B), так и для конечных потребителей (B2C). В рамках B2B-части компания может управлять своими промокодами и получать статистику по их активации. Конечным пользователям платформы доступна лента промокодов с базовым описанием, которое предоставляет компания. Если пользователю нравится промокод, он может его активировать и получить значение, которое указала компания. Чтобы избежать выгрузки базы промокодов недобросовестными пользователями, в компании уже реализован сервис антифрода, который будет отлавливать такое поведение.

В команде обсуждали, что вы знакомы с Git, HTTP, Docker, PostgreSQL, Redis и OpenAPI. Помогите реализовать HTTP-сервер по имеющейся спецификации, коллеги будут сильно благодарны!

Результатом выполнения данного задания является репозиторий с исходным кодом приложения (директория solution).

Caution

Задание необходимо выполнять в личном (приватном) репозитории, созданным Classroom. Не отправляйте Pull Request с решением задания в репозиторий с условием.

Про приложение

Приложение должно представлять собой HTTP-сервер, реализующий необходимое API. В наследие от предыдущей команды вам достались инстансы PostgreSQL и Redis, которые вы можете использовать для хранения данных (что именно использовать — решаете вы сами). Изначально инстансы СУБД пустые (без таблиц), вы можете создать произвольную схему.

Приложение конфигурируется через переменные окружения:

  • SERVER_ADDRESS — хост и порт, которые будет слушать запущенный HTTP-сервер. Например, 0.0.0.0:8080.

  • SERVER_PORT — содержит порт; запущенный сервер должен слушать IP 0.0.0.0 и указанный порт. Используйте эту переменную, если вам не подошел формат данных в переменной SERVER_ADDRESS (переданные параметры равнозначны).

  • POSTGRES_CONN — URL-строка для подключения к PostgreSQL в формате postgres://{username}:{password}@{host}:{5432}/{dbname}.

  • POSTGRES_JDBC_URL — JDBC-строка для подключения к PostgreSQL в формате jdbc:postgresql://{host}:{port}/{dbname}.

  • POSTGRES_USERNAME — имя пользователя для подключения к PostgreSQL.

  • POSTGRES_PASSWORD — пароль для подключения к PostgreSQL.

  • POSTGRES_HOST — хост для подключения к PostgreSQL (например, localhost).

  • POSTGRES_PORT — порт для подключения к PostgreSQL (например, 5432).

  • POSTGRES_DATABASE — имя базы данных PostgreSQL, с которой должно работать приложение.

  • REDIS_HOST — хост для подключения к Redis (например, localhost).

  • REDIS_PORT — порт для подключения к Redis (например, 6379).

  • ANTIFRAUD_ADDRESS — адрес (домен или IP) и порт API сервиса антифрода для подключения по HTTP (например, localhost:9090).

  • RANDOM_SECRET — псевдослучайная последовательность из 128 символов (a-z, A-Z, 0-9), сгенерированная тестирующей системой. Можете использовать ее, если вашему приложению необходим секретный ключ (например, для JWT). Если вам не требуется данное значение, можете его не использовать. Значение одинаковое в рамках запуска группы тестов.

Автор приложения сам выбирает, с какими из переменных окружения ему комфортно работать. Тестирующая система самостоятельно подставит необходимые и правильные значения переменных окружения, нет необходимости проставлять ENV в Dockerfile.

Учитывая современные реалии, приложение будет запускаться через Docker-контейнер. В репозитории присутствует Dockerfile, с помощью которого будет собираться образ приложения. Так как приложение сравнительно небольшое, мы обойдемся одним Docker-контейнером, docker compose определить не получится.

Список используемых зависимостей (и фреймворков) не ограничен (любая версия языка программирования, без ограничений на библиотеки), однако вы должны убедиться, что необходимые зависимости загружаются и подключаются в Dockerfile. Вы сами вправе выбирать стек вашего приложения, от вас зависит успех всего проекта! Вы вправе выбирать любой язык программирования, но если его нет среди списка поддерживаемых, в случае проблем мы не сможем помочь. Настоятельно рекомендуем выбирать ЯП среди поддерживаемых.

Описание API находится ниже, но если вы хотите ознакомиться с точными требованиями, не стесняйтесь использовать Swagger и предоставленную спецификацию Open API.

Тестирование решения происходит с помощью Github CI. Для отправки решения на тестирование необходимо обновить исходный код вашего репозитория на Github (git commit & git push). Вам нужно разработать приложение, которое будет подключаться к существующим СУБД и сервису антифрода.

Тестирующая система самостоятельно запустит PostgreSQL, Redis и другие зависимости. Вы можете запустить данные СУБД и другие зависимости локально для проведения локального тестирования, но это никак не повлияет на тестирование в Github CI.

Версии запущенных зависимостей: PostgreSQL 16.6 и Redis 7.4. Подключение к Redis необходимо выполнять без пароля, к стандартной db (0).

Вы можете редактировать файлы в директории solution.gitignore в корне). Если в репозитории содержатся изменения в других файлах, решение не будет принято. Изменять шаги пайплайна CI запрещено.

Schema

Важная деталь: Антифрод

Помимо основной функциональности в платформе есть дополнительная проверка пользователя при активации промокода — антифрод. Данный сервис уже реализован на стороне компании и представляет собой мощный инструмент, который с помощью алгоритмов искусственного интеллекта оценивает, не пытается ли пользователь «выгрузить» все промокоды платформы (или не занимается ли он другими мошенническими действиями).

Ваше решение должно обращаться к сервису антифрода, когда пользователь делает запрос на получение значения промокода.

Эндпоинт антифрода

Сервис антифрода предоставляет единственный эндпоинт:

POST ${ANTIFRAUD_ADDRESS}/api/validate

Запрос

{
  "user_email": "user@example.com",
  "promo_id": "PROMO12345"
}
  • user_email (string) — e-mail пользователя, пытающегося активировать промокод.
  • promo_id (string) — идентификатор промокода, который пользователь активирует.

В запросах должен быть указан заголовок Content-Type: application/json.

Ответ

{
  "ok": true,
  "cache_until": "2025-01-16T00:17:57.567"
}
  • ok (boolean) — указывает, разрешена ли активация промокода (true) или нет (false).
  • cache_until (string) — если присутствует, указывает момент времени (UTC+0), до которого ответ антифрода необходимо кешировать. Повторные обращения того же пользователя к любому (или именно к тому же) промокоду не должны снова вызывать антифрод до истечения данного срока; ваше приложение должно считать, что вердикт в течение указанного времени остается прежним.

Логика работы с антифродом

  1. При каждой новой попытке пользователя активировать промокод ваш бэкенд должен отправить запрос в антифрод-сервис.
  2. Если сервис отвечает с status code != 200, нужно повторить запрос еще один раз. Если второй запрос также не вернул 200, нужно отказать пользователю в активации.
  3. Если ok = false в теле ответа, необходимо отказать в активации промокода.
  4. Если в ответе содержится поле cache_until, то при повторной активации любого промокода (или именно этого promo_id — решение за автором) от того же пользователя до момента, указанного в cache_until, не нужно повторно обращаться в антифрод; достаточно использовать закешированный результат.
  5. По истечении cache_until (или если cache_until не было возвращено в ответе) при следующей попытке активации промокода необходимо повторно делать запрос в антифрод.

Требования по корректному использованию cache_until строгие. Тестирующая система будет проверять их выполнение. Ваше решение должно кешировать результаты сервиса антифрода даже в условиях наличия перезагрузок приложения.

Оценивание

Тестирование выполняется по группам тестов. В таблице представлены описание каждой группы тестов.

Группы тестов могут зависеть друг от друга. Если группа B зависит от группы A, при тестировании группы B могут использоваться эндпоинты, участвовавшие в тестировании группы A. Это свойство транзитивно! При этом тестирующая система всегда проверяет все группы тестов, вне зависимости от результатов проверки других групп.

Название группы Описание Баллы От каких групп зависит
01/ping Успешный ответ на GET /api/ping 1
02/business/auth/sign-up Регистрация новой компании 3
03/business/auth/sign-in Аутентификация компании и получение токена доступа 4 02/business/auth/sign-up
04/business/promo/create Создание нового промокода 6 03/business/auth/sign-in
05/business/promo/list Получение списка промокодов компании 8 04/business/promo/create
06/business/promo/{id} Получение и редактирование промокода по ID 9 05/business/promo/list
07/user/auth/sign-up Регистрация нового пользователя 3
08/user/auth/sign-in Аутентификация пользователя и получение токена доступа 3 07/user/auth/sign-up
09/user/profile Получение и редактирование профиля пользователя 6 08/user/auth/sign-in
10/user/feed Получение ленты промокодов 14 09/user/profile; 06/business/promo/{id}
11/user/promo/{id}/like Лайки и удаление лайков промокодов 7 10/user/feed
12/user/promo/{id}/comments Управление комментариями к промокодам 10 10/user/feed
13/user/promo/{id}/activate Активация промокода и получение исторической сводки 20 11/user/promo/{id}/like; 12/user/promo/{id}/comments
14/business/promo/{id}/stat Получение статистики по промокоду 6 13/user/promo/{id}/activate

Названия групп тестов в таблице могут не совпадать с эндпоинтами. Обратитесь к спецификации.

Одна группа тестов может быть разделена на несколько шагов. Чтобы получить баллы за шаг, необходимо пройти все тесты в рамках данного шага. Один шаг может представлять собой отправку и валидацию HTTP запросов к решению, перезагрузку приложения, выполнение системных действий (замедление сервиса антифрода) и прочее. Если один из шагов не проходит валидацию, последующие шаги в рамках данной группы тестов могут не проверяться (и баллы за них не начислятся). Распределение баллов по шагам будет доступно после проведения финального тестирования.

Обратите внимание! Ваше решение должно хранить данные в СУБД. Чтобы проверить это, тестирующая система может перезагружать ваше приложение (повторно дожидаясь готовности, отправляя запросы на /api/ping). Гарантируется, что перезагрузка приложения будет выполняться после того, как приложение ответит на все поступившие ранее запросы. При перезагрузке не сохраняются файлы, которые приложение сохранило на диск (не получится использовать sqlite). Если ваше решение не может восстановить состояние из предоставленных СУБД (PostgreSQL, Redis), вы можете не получить баллы за группу тестов :(

Время на запуск тестов ограничено: 20 минут. Время на запуск группы тестов тоже может быть ограничено, ознакомьтесь с конфигурационным файлом тестов.

Чтобы получить полный балл, время обработки HTTP-запроса не должно превышать 200 миллисекунд. При этом если вашему решению необходимо время "для прогрева", и первый запрос будет обрабатываться дольше 200мс, это приемлемо. Тестирующая система будет ориентироваться на 90ый перцентиль времени ответа (90% запросов должны укладываться в 200мс). Если решение не кеширует ответы сервиса антифрода, возможно получение частичных баллов за некоторые группы тестов.

В спорных ситуациях будет оцениваться качество кода.

На данный момент в CI тестирование производится на публичном наборе тестов. Данные тесты помогают проверить минимальную логику приложения, но не гарантируют прохождения финальных тестов. Публичный набор тестов будет пополняться примерами. Финальные тесты будут более полными и будут проверять больше крайних случаев.

При финальном тестировании будут учитываться только те ограничения, что описаны в данном файле и спецификации.

Группы тестов

Общие требования

У всех эндпоинтов есть префикс /api.

Обратите внимание, возврат успешного ответа на GET /api/ping является обязательным условием для начала тестирования приложения.

Поступающие запросы и возвращаемые ответы должны соответствовать структуре и требованиям, описанным в спецификации Open API. Обращайте внимание на ожидаемые status code, ограничения по длине и разрешенные символы в строках.

Если запрос подразумевает указание токена, а пользователь его не предоставил, возвращайте 401. Если структура запроса не соответствует требованиям и описанному формату, по умолчанию возвращается код ответа 400. Валидация токена является более приоритетной операций над валидацией тела запроса. Если указан более специфичный код ответа, используйте его.

Если запрос некорректен хотя бы в одном параметре, весь запрос отвергается и признается некорректным.

При ответе на запросы, подразумевающие пагинацию, сервер должен возвращать заголовок X-Total-Count с суммарным числом элементов, подходящих под критерии фильтрации.

В случае, когда сервер должен создать новую сущность и назначить ей идентификатор, используйте UUID (любой версии). Если структура ответа предполагает опциональность поля, сервер не должен возвращать данное поле при его отсутствии. Запросы с неописанными в спецификации полями можно отклонять (400), на ваше усмотрение.

При возвращении ошибок вы можете использовать произвольную структуру тела ответа. Тестирующая система будет проверять только status code (400, 409 и прочие).

01/ping

Достаточно реализовать возврат успешного ответа (с кодом 200) на запрос GET /api/ping. Содержимое тела ответа при этом не валидируется, можно возвращать "PROOOOOOOOOOOOOOOOOD".

Данная логика является блокирующей для всех остальных групп тестов.

02/business/auth/sign-up

Эндпоинт /business/auth/sign-up используется для регистрации новой компании.

Сервер должен поддерживать базу данных компаний, валидировать запросы и не допускать наличия компаний с эквивалентными регистрационными данными (проверка выполняется только по e-mail).

Не храните пароли компаний в открытом виде, используйте хеширование (например, bcrypt; если bcrypt слишком медленный, можно рассмотреть Argon2).

Дополнения:

  • Если хотя бы один переданный параметр является некорректным, весь запрос считается некорректным и возвращается 400 Bad Request.
  • Убедитесь, что название компании не превышает разумных значений.

03/business/auth/sign-in

Эндпоинт /business/auth/sign-in предназначен для аутентификации компании по e-mail и паролю и генерации токена доступа, который в дальнейшем будет использоваться для авторизованных запросов. Пароль должен соответствовать требованиям безопасности (соблюдение политики сложности).

Генерируемый токен должен уникально идентифицировать компанию и быть сложным для подбора (можно использовать JWT, можно использовать другой формат токена).

Данный токен в дальнейшем будет передаваться компанией в заголовке Authorization: Bearer {token}, и приложение должно уметь понять, какая компания хочет сделать запрос.

Временно будем считать, что время действия токена (TTL) должно составлять от 1 до 24 часов (на усмотрение разработчика). Важно: успешная аутентификация инвалидирует ранее выданные токены для данной компании.

04/business/promo/create

Эндпоинт /business/promo используется для создания нового промокода.

POST /business/promo

Создание нового промокода с настройкой таргетинга и типом промокода (COMMON или UNIQUE).

COMMON-промокоды имеют фиксированное значение (например, winter-sale-50). Компания может ограничить число активаций данного промокода. Каждая активация данного промокода отдаёт пользователю фиксированное значение.

Для UNIQUE-промокодов компания передает уникальный список промокодов. При активации промокода пользователь должен получить очередное значение, которое ранее не было выдано (необязательно в том порядке, который указала компания). При этом бэкенд может не валидировать уникальность переданных значений (ответственность лежит на компании). Если все указанные компанией значения были активированы, промокод принимает значение active = false. Промокоды такого типа могут использоваться, когда компания хочет явно знать, с какой площадки пришел пользователь.

Обратите внимание, если указан mode = COMMON, в запросе должно быть поле promo_common. Соответственно, если mode = UNIQUE, в запросе необходимо передать поле promo_unique. Если для mode = UNIQUE передано значение для promo_common (и наоборот), этот запрос считается некорректным (400). Не позволяйте визуализации Swagger сбить вас с толку.

Опционально компания может определить настройки таргетинга для промокода. Если они определены, промокод должен показываться и быть доступен для активации только пользователям, соответствующим указанным критериям (по возрасту и стране). Поле target является обязательным для передачи, но его содержимое может быть пустым.

При создании промокода сервер должен назначить уникальный идентификатор.

05/business/promo/list

Эндпоинт /business/promo используется для получения списка промокодов компании.

GET /business/promo

Получение списка промокодов с возможностью фильтрации, сортировки и пагинации.

Параметр sort_by позволяет сортировать по дате начала/конца действия промокода. По умолчанию — сортируется в порядке создания промокодов. Сортировка всегда выполняется по убыванию. Если существует несколько корректных порядков сортировки, вы можете вернуть любой.

Параметр country позволяет указать стран целевой аудитории в формате ISO 3166-1 alpha-2, по которому нужно фильтровать промокоды. В ответе должны содержаться промокоды либо без указанного региона (таргетинг), либо с регионом, который присутствует среди указанного списка при фильтрации. Если параметр не указан, подходят промокоды с произвольным регионом. Пользователь может указать несколько стран, в таком случае на бэкенд будет отправлен запрос вида ...&country=ru&country=fr&country=us или ...&country=ru,fr,us (необходимо поддержать оба варианта).

Дополнения:

  • Если хотя бы один переданный параметр фильтрации (например, country) некорректен, возвращайте 400 Bad Request.
  • Реализуйте регистронезависимый поиск по параметрам фильтрации.
  • Не забывайте про заголовок X-Total-Count.

06/business/promo/{id}

Эндпоинт /business/promo/{id} используется для получения и редактирования промокода по его id.

GET /business/promo/{id}

Получение данных промокода по его ID. Сервер должен проверять принадлежность промокода компании.

PATCH /business/promo/{id}

Редактирование данных промокода по его ID.

Если передано поле target, вся структура с таргетированными настройками перезаписывается. Убедитесь, что при редактировании промокода соблюдаются все ограничения по длине и форматированию полей.

07/user/auth/sign-up

Эндпоинт /user/auth/sign-up используется для регистрации нового пользователя.

На один e-mail не может быть зарегистрировано несколько конечных пользователей. На один и тот же e-mail одновременно может быть зарегистрирован аккаунт компании и аккаунт конечного пользователя.

Убедитесь, что длина e-mail и пароля соответствует требованиям безопасности.

08/user/auth/sign-in

Эндпоинт /user/auth/sign-in предназначен для аутентификации пользователя по e-mail и паролю и генерации токена доступа.

Временно будем считать, что время действия токена (TTL) должно составлять от 1 до 24 часов (на усмотрение разработчика). Важно: успешная аутентификация инвалидирует ранее выданные токены для данного пользователя.

09/user/profile

GET /user/profile

Эндпоинт используется для получения информации о своем профиле.

PATCH /user/profile

Возможность изменить свои пользовательские настройки (не все). Убедитесь, что при обновлении настроек соблюдаются все требования к форматированию и длине полей.

При обновлении профиля передавайте только те поля, что необходимо изменить.

10/user/feed

GET /user/feed

Эндпоинт /user/feed используется для получения ленты промокодов.

Обратите внимание: данный эндпоинт используется пользователем для получения базовой информации о промокодах. В рамках данного эндпоинта пользователь не должен видеть значений промокодов, то есть процедуры активации не происходит.

Сервер при этом оперирует понятием «активного» промокода. Промокод считается активным (и возможным для активации пользователями), если:

  • Текущая дата входит в указанный промежуток [active_from; active_until]. Сравнивайте даты, исходя из предположения, что ваше приложение должно работать во временной зоне UTC+3.
  • Для mode = COMMON число активаций меньше max_count.
  • Для mode = UNIQUE остались неактивированные значения.

Реализуйте регистронезависимый поиск по параметрам фильтрации.

GET /user/promo/{id}

Данный эндпоинт используется для получения информации по одному промокоду. Активации промокода не происходит.

Обратите внимание, в рамках ответа на данный запрос сервер не должен раскрывать значение промокода.

11/user/promo/{id}/like

Эндпоинт /user/promo/{id}/like используется для управления лайками промокодов.

POST /user/promo/{id}/like

Поставить лайк промокоду.

DELETE /user/promo/{id}/like

Удалить лайк с промокода.

Дополнения:

  • Поддержите идемпотентность. Если пользователь пытается поставить лайк повторно (либо удалить несуществующий лайк), сервер должен ответить 200 и не обновлять число лайков.
  • Убедитесь, что промокод существует.

12/user/promo/{id}/comments

Эндпоинт /user/promo/{id}/comments используется для управления комментариями к промокодам.

POST /user/promo/{id}/comments

Создать новый комментарий. Пользователи используют эту механику, чтобы оставить отзыв.

GET /user/promo/{id}/comments

Получение списка комментариев к промокоду. Пользователи хотят знать мнение других участников сообщества! Не забудьте про пагинацию.

GET /user/promo/{id}/comments/{comment_id}

Пользователь может получить комментарий по его идентификатору.

PUT /user/promo/{id}/comments/{comment_id}

Если пользователь хочет отредактировать свой комментарий, он может это сделать! Помните, что редактировать можно только свои комментарии.

DELETE /user/promo/{id}/comments/{comment_id}

Если пользователю больше не нравится собственный комментарий, он может удалить его.

Дополнения:

  • Убедитесь, что текст комментария соответствует требованиям по длине.
  • При редактировании или удалении комментария проверяйте, что комментарий принадлежит текущему пользователю.

13/user/promo/{id}/activate

POST user/promo/{id}/activate

Эндпоинт /user/promo/{id}/activate используется для активации промокода пользователем. Под активацией подразумевается возможность увидеть само значение промокода со стороны пользователя. Все пользовательские эндпоинты, кроме этого, не раскрывают значение промокода.

Промокод может быть активирован, если:

  • Промокод находится в активном состоянии (active = true).
  • Пользователь соответствует настройкам таргетирования промокода. Для указанных настроек таргетирования промокода настройки пользователя имеют необходимое значение. Настройки категорий при этом не учитываются.
  • Антифрод-сервис отдал успешный вердикт по этому пользователю.
  • Соблюдены условия по max_count (для COMMON) и остались неактивированные промокоды (для UNIQUE).

В рамках данной группы тестов будет проверяться, что ваше решение действительно обращается к сервису антифрода и кеширует его ответы (учтите, что ваше приложение может быть перезагружено).

Если промокод не может быть использован (исходя из ответа антифрод-сервиса или настроек таргетинга; или если active = false), возвращайте 403 Forbidden. Пользователь может активировать один промокод несколько раз, если это позволяет антифрод-сервис. Для UNIQUE-промокодов повторная активация должна выдавать новое значение.

GET /user/promo/history

Данный эндпоинт позволяет пользователю получить историческую сводку по активированным промокодам.

14/business/promo/{id}/stat

GET /business/promo/{id}/stat

Данная механика используется компаниями для получения статистики по промокоду. Массив со сводкой по странам должен быть отсортирован по коду региона, в лексикографическом порядке (от a к z).

Не забудьте проверить, что компания может получить статистику только по собственным промокодам.

Ничего не понятно, с чего подступиться?

  • Начните с изучения основ работы с HTTP и СУБД в вашем языке программирования.
  • Ознакомьтесь со спецификацией Open API. Изучите её с помощью инструмента Swagger.
  • Далее ознакомьтесь с концептом авторизации и аутентификации. Поддержите данные эндпоинты в своем приложении.
  • Остальные эндпоинты мы рекомендуем реализовывать в порядке следования в условии.
  • Не забывайте регулярно отправлять код на github; не откладывайте решение под конец, когда скопится очередь из посылок.

Также можете ознакомиться с сессией Q&A, в рамках которой мы отвечали на популярные вопросы и рассказывали, как подступиться к решению. Запись доступна по ссылке.

Тестирование

Для тестирования решения отразите ваши изменения в репозитории. Разрешено изменять только директорию solution и .gitignore, иначе тесты не будут запущены.

Тестирование в CI

Для тестирования решений используется Github CI. При отправке новых изменений в репозиторий активируется тестирующий пайплайн.

Пайплайн состоит из двух этапов:

  • Сборка Docker-образа с вашим приложением (на основании исходного кода репозитория и Dockerfile).

  • Запуск тестов. Для каждой группы тестов:

    • запускаются Docker-контейнеры с вашим приложением и зависимостями;

    • тестирующая система дожидается готовности PostgreSQL, Redis и сервиса антифрода;

    • тестирующая система дожидается успешного (200) ответа на GET /api/ping, на это дается не более 10 секунд;

    • приложение считается запущенным, начинается запуск HTTP-тестов;

    • после окончания тестирования вы можете перейти на вкладку Actions, перейти к последнему запуску, выбрать Run tests, открыть лог тестирования и увидеть информацию о запускаемых публичных тестах и набранных баллах.

Убедитесь, что ваше приложение готово запускать HTTP-сервер на адресе, переданном в переменной окружения SERVER_ADDRESS. В качестве хоста (IP) передается 0.0.0.0, а не localhost или 127.0.0.1. Это важно!

Также проверьте локально, чтобы Docker-образ с вашим приложением собирался (выполните docker build . в директории solution).

Существующие ограничения:

  • Решению выделяется 1.5 vCPU, 3 GB RAM (не учитывая PostgreSQL и Redis).

  • В рамках тестирования ваше приложение не должно самостоятельно завершать работу (помните о защите от Exception, panic и прочих причин аварийного завершения).

  • Сетевое взаимодействие разрешено только с PostgreSQL, Redis и сервисом антифрода. Обращаться к сторонним ресурсам по сети нельзя. Обнаружение таких походов может привести к аннулированию работы.

Прохождение публичного набора тестов не дает гарантию прохождения финальных тестов.

Просмотр результатов тестов в CI

Для начала необходимо в вашем репозитории перейти на вкладку Actions, где будет доступен список всех запущенных воркфлоу.

Для просмотра актуальных результатов тестирования необходимо открыть первый запуск.

Далее будет доступна информация по шагам пайплайна тестирования.

  • Перейдите к Build image для просмотра логов сборки Docker-образа.
  • Перейдите к Run tests для просмотра результатов тестирования.

Если вы перешли к Run tests, вы можете раскрыть вкладку Run tests для просмотра сводной информации по запуску тестов.

Раскройте Running testgroup '01/ping' (или другая группа тестов) для просмотра подробного лога тестирования.

Раскройте Application output for testgroup... для просмотра логов вашего приложения. Это полезно в ситуации, когда ваше приложение "падает" ошибкой.

Локальное тестирование

Для запуска HTTP-тестов используется Python-фреймворк Tavern. Tavern принимает на вход схему с запросом и ожидаемым ответом, выполняет данный запрос и валидирует ответ.

Исходные файлы с доступными тестами на языке Tavern находятся в директории tests. Файлы с тестами имеют суффикс .tavern.yml.

Перед запуском локальных тестов необходимо:

  1. Установить python (желательно версии 3.12).
  2. Установить нужные зависимости:
# Перейти в директорию с тестами и зависимостями
cd tests

# Установить Tavern и необходимые для него зависимости
python3 -m pip install -r requirements.txt

# Установить дополнительный пакет для работы с Tavern после Python 3.12
python3 -m pip install setuptools

Для запуска локальных тестов необходимо запустить все необходимые приложения, перейти в директорию tests и выполнить команды:

# Создать переменную окружения, в которую передать базовый URL вашего API, запущенного локально
export BASE_URL="http://localhost:8080/api"

# Запустить антифрод-сервис и указать переменную окружения, в которую передать базовый URL внутреннего API
docker run -e SERVER_PORT=9090 -e CACHE_DURATION_MS=5000 -p 9090:9090 lodthe/prod-backend-antifraud:latest
export ANTIFRAUD_URL="http://localhost:9090/internal"

# Запустить py.test, указав путь к файлам Tavern, которые хочется запустить
py.test test_01_ping.tavern.yml

Помните, что изменять директорию tests в репозитории нельзя. Если вы хотите дописать собственные тесты, рекомендуем скопировать директорию с тестами в директорию solution.

Чтобы локальное тестирование было максимально приближенным к тестированию в CI, мы рекомендуем запускать PostgreSQL, Redis и ваше приложение в Docker-контейнерах (связанных одной сетью). Например, с помощью docker compose.

Также вам доступен план тестирования в CI. В файле config-tests-public.yml доступно описание публичных групп тестов. Каждая группа тестов состоит из нескольких шагов, в рамках каждого шага может выполняться запуск тестов Tavern, перезагрузка приложения и прочее. За прохождение некоторых шагов начисляются баллы.

Локальный запуск сервиса антифрода

Нам повезло и от команды нам достался Docker-образ с готовым приложением сервиса антифрода: lodthe/prod-backend-antifraud:latest. Вы можете запустить контейнер с данными образом локально, чтобы протестировать работу приложения локально.

Для загрузки и запуска образа необходимо выполнить команду:

# Загрузить новую версию образа с Docker Hub
docker pull lodthe/prod-backend-antifraud:latest

# Запустить контейнер:
docker run -e SERVER_PORT=9090 -e CACHE_DURATION_MS=5000 -p 9090:9090 lodthe/prod-backend-antifraud:latest
  • -e SERVER_PORT=9090 определяет порт, на котором будет запущено приложение внутри контейнера
  • -e CACHE_DURATION_MS=5000 конфигурирует длительность, на которую клиенты сервиса должны кешировать ответ (в миллисекундах)
  • -p 9090:9090 пробрасывает порт из контейнера наружу, чтобы сервис был доступен с вашей хоста (операционной системы)
  • Если у вас ноутбук на Arm (MacBook M-версии), добавьте флаг --platform linux/amd64

После запуска к контейнеру можно обращаться по HTTP:

[$] curl 0:9090/api/validate -v -H 'Content-Type: application/json' -d '{"user_email": "user@example.co", "promo_id": "PROMO145"}'

> {"ok":true,"cache_until":"2025-01-16T14:11:55.425"}

По умолчанию антифрод-сервис отдаёт успешные ответы на все запросы. С помощью системного эндпоинта можно обновить значение ok, которое сервис будет выдавать для запросов по указанному e-mail'у.

[$] curl localhost:9090/internal/update_user_verdict -v -H 'Content-Type: application/json' -d '{"user_email": "user@example.com", "ok": false}'

Лог изменений

Здесь будут отражены важные правки в спецификации и условии.

  1. Успешный ответ на запрос регистрации должен сопровождаться Status Code 200 (не 201).
  2. При запросе на регистрацию пользователя должен быть указан параметр password.
  3. Если структура ответа предполагает опциональность поля, сервер не должен возвращать данное поле при его отсутствии.
  4. Код страны может быть указан в любом регистре.
  5. Для /business/promo/{id}/stat переименовано поле для счетчика, необходимо использовать activations_count.
  6. Помимо получение списка комментариев необходимо реализовать получение комментарий по идентификатору: GET /user/promo/{id}/comments/{comment_id}.
  7. Расслаблены ограничения на время обработки запросов: 90% запросов должны обрабатываться быстрее 200мс. Если ваше приложение разово обрабатывает запрос дольше, это приемлемо.
  8. В ответе на GET /business/promo/{id}/stat массив со сводкой по странам должен быть отсортирован по коду региона, в лексикографическом порядке (от a к z). Подробнее смотрите в спецификации.
  9. Необходимо использовать регистронезависимое сравнение при сравнении кодов стран и категорий. То есть rU эквивалентно Ru, а коТЫ экививалентно кОтЫ.
  10. Некорректно (см. 13): Сервер может возвращать коды стран и категории в любом регистре.
  11. Приложение должно кешировать ответы антифрод-сервиса даже в условиях наличия перезагрузок (после перезагрузки необходимо восстановить состояние).
  12. При получении списка собственных промокодов со стороны компании необходимо возвращать начальный набор значений (promo_common или promo_unique), который компания передала при создании промокода.
  13. Правка #10 некорректная. Сервер должен возвращать коды стран и категории с сохранением регистра, в котором эти значения были переданы в запросе. Значения этих полей в ответе должны равняться тому, что было передано в запросе. Для /business/promo/{id}/stat коды регионов можно возвращать в любом регистре (но помните, что rU и ru эквивалентны).
  14. Для запусков тестов, проверяющих активацию промокода, локально дополнительно требуется установить переменную окружения ANTIFRAUD_URL. Подробнее смотрите в инструкции по локальному тестированию в этом документе.

About

backend fast api

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published