Skip to content

About

Test task

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Subscription aggregation API

REST-сервис для учёта онлайн-подписок пользователей: CRUDL по записям, агрегат суммарной стоимости за календарный период. Цены — целые рубли, даты подписки — месяц и год.

Стек

  • Go 1.26
  • PostgreSQL
  • Swagger
  • Docker Compose

Быстрый старт

Локально

  1. Поднимите PostgreSQL и создайте БД (по умолчанию совпадает с config.yaml: пользователь/пароль/имя subscribe).
  2. Примените миграции из каталога migrations/ (или task migrate:local, если установлен CLI migrate).
  3. При необходимости скопируйте .env.example в .env и задайте переменные (см. раздел «Конфигурация»).
  4. Запуск:
task run

Сервер слушает адрес из config.yaml (по умолчанию :8080).

Docker Compose

docker compose up --build

или:

task docker:up

API будет на http://localhost:8080. В образе используется config.docker.yaml.

PostgreSQL с хоста подключается к 5433 порту (внутри compose сервисы по-прежнему используют порт 5432). Так не пересекается с локальным Postgres на :5432.

Конфигурация

Путь к YAML задаётся переменной окружения SUBSCRIBE_CONFIG (в compose: /app/config.yaml). Если не задана, читается config.yaml из текущей директории.

Переменная Назначение
SUBSCRIBE_SERVER_HOST, SUBSCRIBE_SERVER_PORT HTTP
SUBSCRIBE_DB_HOST, SUBSCRIBE_DB_PORT, SUBSCRIBE_DB_USER, SUBSCRIBE_DB_PASSWORD, SUBSCRIBE_DB_NAME, SUBSCRIBE_DB_SSLMODE PostgreSQL
SUBSCRIBE_LOG_LEVEL debug, info, warn, error

Формат дат

Во всех полях и query-параметрах, где указан месяц и год, используется строка MM-YYYY, например 07-2025.

Базовый URL API

http://localhost:8080/api/v1

Документация OpenAPI

После запуска сервера:

  • Swagger UI: http://localhost:8080/swagger/index.html
  • Статические файлы: docs/swagger.json, docs/swagger.yaml

Перегенерация осуществляется командой

task swagger

Служебные эндпоинты

Метод Путь Описание
GET /health Тело ok, статус 200

Подписки (/api/v1/subscriptions)

Модель ответа Subscription

Поле Тип Описание
id string (UUID) Идентификатор записи
service_name string Название сервиса
price integer Стоимость месяца в рублях (≥ 0)
user_id string (UUID) Пользователь (внешний id)
start_date string Начало подписки, MM-YYYY
end_date string или отсутствует Конец подписки, MM-YYYY; если нет — подписка без даты окончания
created_at, updated_at string Временные метки

Создание — POST /api/v1/subscriptions

Тело запроса

Поле Обязательное Описание
service_name да Непустая строка
price да Целое ≥ 0
user_id да UUID
start_date да MM-YYYY
end_date нет null, пустая строка или отсутствие поля — без даты окончания; иначе валидный MM-YYYY, не раньше start_date

Пример запроса

POST /api/v1/subscriptions HTTP/1.1
Host: localhost:8080
Content-Type: application/json

{
  "service_name": "Yandex Plus",
  "price": 400,
  "user_id": "60601fee-2bf1-4721-ae6f-7636e79a0cba",
  "start_date": "07-2025"
}

Пример ответа

{
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "service_name": "Yandex Plus",
  "price": 400,
  "user_id": "60601fee-2bf1-4721-ae6f-7636e79a0cba",
  "start_date": "07-2025",
  "created_at": "2026-04-03T12:00:00Z",
  "updated_at": "2026-04-03T12:00:00Z"
}

Получение — GET /api/v1/subscriptions/{id}

{id} — UUID подписки.

Обновление — PUT /api/v1/subscriptions/{id}

Частичное обновление: передавайте только поля, которые нужно изменить. Поле с null или отсутствующее в JSON обычно означает «не менять».

Пример

PUT /api/v1/subscriptions/a1b2c3d4-e5f6-7890-abcd-ef1234567890 HTTP/1.1
Host: localhost:8080
Content-Type: application/json

{
  "price": 500,
  "end_date": "12-2026"
}

Удаление — DELETE /api/v1/subscriptions/{id}

Успех: 204 No Content, без тела.

Список — GET /api/v1/subscriptions

Query-параметры (все необязательны):

Параметр Описание
user_id UUID пользователя
service_name Подстрока для поиска; в БД используется ILIKE (без учёта регистра)
limit Размер страницы (если не задан — 20, максимум 100)
offset Смещение (≥ 0)

Пример

GET /api/v1/subscriptions?user_id=60601fee-2bf1-4721-ae6f-7636e79a0cba&service_name=Plus&limit=10&offset=0 HTTP/1.1
Host: localhost:8080
{
  "items": [ { "...": "Subscription" } ],
  "total": 42
}

Сумма за период — GET /api/v1/subscriptions/total

Суммируется стоимость по всем календарным месяцам периода [from, to], пересекающимся с подпиской (с учётом опциональной даты окончания).

Query-параметры

Параметр Обязательный Описание
from да Начало периода, MM-YYYY
to да Конец периода, MM-YYYY (from не позже to)
user_id нет UUID — только подписки пользователя
service_name нет фильтр по названию сервиса

Пример

GET /api/v1/subscriptions/total?from=01-2025&to=03-2025&user_id=60601fee-2bf1-4721-ae6f-7636e79a0cba HTTP/1.1
Host: localhost:8080
{
  "total_rub": 1200,
  "from": "01-2025",
  "to": "03-2025"
}

Примеры curl

# Health
curl -s http://localhost:8080/health

# Создание
curl -s -X POST http://localhost:8080/api/v1/subscriptions \
  -H "Content-Type: application/json" \
  -d '{"service_name":"Yandex Plus","price":400,"user_id":"60601fee-2bf1-4721-ae6f-7636e79a0cba","start_date":"07-2025"}'

# Список
curl -s "http://localhost:8080/api/v1/subscriptions?limit=5"

# Total
curl -s "http://localhost:8080/api/v1/subscriptions/total?from=01-2025&to=12-2025"

# Удаление (подставьте UUID)
curl -s -X DELETE http://localhost:8080/api/v1/subscriptions/00000000-0000-0000-0000-000000000000

About

Test task

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages