VEU.WathBody/1-docs/4-modules/m02-rest-api/api_specs/03-sync-post.md

87 lines
5.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Спецификация: `POST /api/v1/sync` (единица 3)
> Формат пакета — module_reqs разд. 4 (копия tech-requirements 2.1–2.2).
## Запрос
JSON-массив записей (пакет ≤ 50):
```json
[
{ "client_guid": "uuid-v4",
"entity": "workout | workout_set | exercise | program_version | profile | daily_log | break",
"op": "upsert",
"data": { } }
]
```
## Ответы
| Код | Когда | Тело |
|---|---|---|
| 200 | все записи пакета записаны | `{ "accepted": ["guid1", ...] }` — GUID пакета в исходном порядке |
| 400 | пакет > 50; неизвестная `entity`; `op` ≠ `upsert`; невалидный GUID; нарушение схемы/диапазонов; `break` c `source = "auto"`; невалидный JSON | `{ "error": "<текст>" }` |
| 409 | конфликт версии программы (`UNIQUE(program_id, version)`) | `{ "error": "program_version_conflict" }` |
| 401 | нет/чужой/заблокирован ключ (middleware) | `{ "error": "unauthorized" }` |
## Логика
1. Десериализация и валидация пакета (все записи до начала записи);
любая ошибка схемы → `400`, в БД ничего не пишется.
2. Одна транзакция на пакет (🛡 атомарность «все или ничего») —
`TransactionScope`; Kit.Helpers открывает соединение на каждый вызов,
Npgsql подключает их к ambient-транзакции. Все ЧТЕНИЯ (проверки 409,
группы, существование workout'ов) выполняются до открытия
транзакции — select-функции Kit открывают собственную транзакцию
(вложенная запрещена); внутри — только записи:
- записи применяются в порядке массива;
- `athlete_id` для всех записей — из middleware (🛡, из тела не берётся).
3. Маппинг (репозитории m01):
- `workout` → `WorkoutRepository.Insert` (идемпотентен по uuid;
если в `data` есть `ended_at`/`status`/`avg_hr` сверх вставки —
следом `Update` для этих полей);
- `workout_set` → `WorkoutSetRepository.Insert` (идемпотентен);
`workout_id` должен принадлежать атлету — иначе исключение из
функции БД → пакет отклоняется (`400`, текст ошибки функции);
- `exercise` → `ExerciseRepository.Insert` (идемпотентен;
`group` → `group_id` по коду через `ExerciseGroupRepository`);
- `program_version` → при отсутствии программы атлета сначала
`ProgramRepository.Insert` (`title` = «Программа»), затем
`ProgramVersionRepository.Insert` (`body` — как есть);
409-проверка — в пре-проходе (п. 2): `based_on_version` из записи
≠ актуальная максимальная версия → `409` (телефон перечитывает
программу); UNIQUE-конфликт в `program_version_insert` — тоже `409`;
- `profile` → `AthleteProfileRepository.Upsert`;
- `daily_log` → `DailyLogRepository.Upsert` (по `UNIQUE(athlete_id, date)`);
- `break` → `BreakRepository.Insert` (`source` — только `manual`).
4. 🛡 Идемпотентность: PK uuid сущностей = `client_guid`; повтор пакета —
insert-функции возвращают существующие id, дублей нет; ответ — тот же
`accepted` (для этого `program_version_insert` дополнен
идемпотентностью по `p_id` — errata m01, 2026-10-18).
5. Ошибка БД внутри транзакции → rollback → `400` (или `409` для
конфликта версии программы), текст ошибки — в лог.
## Тесты
| # | Проверка | Ожидание |
|---|---|---|
| 1 | Валидный пакет (по одной записи каждой сущности) | 200, `accepted` = все GUID, строки в БД |
| 2 | Повтор того же пакета | 200, тот же `accepted`, дублей в БД нет |
| 3 | Пакет из 51 записи | 400 |
| 4 | Неизвестная `entity` | 400 |
| 5 | `break` с `source = "auto"` | 400 |
| 6 | `rpe = 11` | 400, в БД ничего из пакета не записано |
| 7 | Вторая версия программы с тем же `version` (перезапись uuid) | 409 |
| 8 | `workout_set` с чужим `workout_id` (другой атлет) | 400, пакет откатан |
| 9 | Пакет из 50 записей | 200 |
| 10 | `program_version` при отсутствии программы атлета | 200; созданы `program` и `program_version` |
## Пример вызова
```bash
curl -X POST https://host/api/v1/sync -H "X-Api-Key: ivan-key-1" -H "Content-Type: application/json" \
-d '[{"client_guid":"0b8f...","entity":"daily_log","op":"upsert",
"data":{"date":"2026-10-17","bp_sys":128,"bp_dias":82,"weight_kg":84.5,"hr_resting":58,"wellbeing":4}}]'
# → {"accepted":["0b8f..."]}
```