81 lines
5.6 KiB
Markdown
81 lines
5.6 KiB
Markdown
# Требования к модулю: m07-fit-fetch
|
||
|
||
> Модуль — проект `Server.WatchBody.Fetch` и режим `--mode fetch`
|
||
> единой точки входа CLI: неофициальный клиент Garmin Connect под
|
||
> учётной записью владельца (Ф-8.1, решение опросника 4.3, ОВ-1-А),
|
||
> скачивание .fit новых силовых тренировок в `Garmin.DownloadDir`,
|
||
> дедупликация по `fit_file_id` (🛡 — повторный запуск не качает
|
||
> дубли), разбор и запись фактов конвейером m06. Самодостаточная
|
||
> копия требований (d-dev-6).
|
||
|
||
## 1. Назначение и границы
|
||
|
||
**В границах модуля:**
|
||
|
||
- класс `GarminClient` (HTTP-клиент Garmin Connect): SSO-логин
|
||
(embed → csrf → signin → ticket → OAuth1 → Bearer) по контракту
|
||
семьи библиотек `garminconnect` (tech-req разд. 5); список
|
||
активностей; скачивание .fit по activityId. `HttpMessageHandler`
|
||
инъецируется (тесты — фейковый handler с формальным SSO-флоу и
|
||
настоящими .fit-байтами; против живого Garmin прогон не
|
||
автоматизируется — прототип ОВ этапа 7);
|
||
- класс `FitFetcher`: `FetchNew()` — логин → список → фильтр
|
||
`strength_training` → скачивание `{activityId}.fit` в
|
||
`Garmin.DownloadDir` (файл уже в папке → скачивания нет) →
|
||
`FitParser` → `WorkoutImporter` (m06) → скачанный файл, давший
|
||
дубль по `fit_file_id` в БД, удаляется. Возврат
|
||
(downloaded, skipped, failed);
|
||
- режим `--mode fetch` в `Program.cs` Web (единая точка входа,
|
||
requirements 2.3): `Garmin.Login/Password` обязательны (иначе код 1),
|
||
коды 0/1/2 — как у import; расписание — внешний планировщик ОС
|
||
(ОВ-4, интервал 15 мин — в код не входит);
|
||
- конфиг `Garmin.Login` / `Garmin.Password` / `Garmin.DownloadDir`
|
||
(+ `Garmin.BaseUrl` — замена точек входа для тестов);
|
||
- тесты (интеграционные против docker `watchbody-pg` + фейковый
|
||
Garmin-handler).
|
||
|
||
**Вне границ модуля:** парсинг/запись фактов (m06, конвейер общий),
|
||
fallback-папка импорта (m06, работает независимо — деградация при
|
||
блокировке логина), метрики (m04 пересчитает при следующем sync),
|
||
несколько учётных записей атлетов (ОВ-10 — v2), расписание (ОС),
|
||
MFA-логин (не поддерживается в v1 — сообщается ошибкой).
|
||
|
||
## 2. Дедупликация (🛡, Ф-8.1)
|
||
|
||
Два рубежа:
|
||
|
||
1. **Быстрый (без сети)**: файл `{activityId}.fit` уже есть в
|
||
`Garmin.DownloadDir` → activityId пропускается, повторного
|
||
скачивания нет.
|
||
2. **По БД**: скачанный файл разбирается, `fit_file_id` (FileId
|
||
сообщения) ищется в `src.workout` — есть → это дубль
|
||
(повторный забор того же .fit): файл удаляется, запись не
|
||
выполняется (`WorkoutImporter` m06 возвращает существующий id —
|
||
запись идемпотентна и без удаления файла).
|
||
|
||
## 3. Инварианты (🛡 — нарушать запрещено)
|
||
|
||
1. **Изоляция атлета** — факты пишутся на атлета-владельца учётки
|
||
(config → атлет с этим именем; не найден — ошибка fetch, файл
|
||
остаётся в DownloadDir).
|
||
2. **Повторный запуск не качает дубли** — оба рубежа разд. 2.
|
||
3. **Сбой одного файла не останавливает пакет** (как import) —
|
||
файл остаётся в DownloadDir, failed++, итоговый код 2.
|
||
4. **Тайны** — пароль Garmin в логи не пишется (З-2, З-6); куки/
|
||
токены не логируются.
|
||
5. **Детерминированность записи** — только конвейер m06, никаких
|
||
собственных SQL-записей.
|
||
|
||
## 4. Критерии приёмки модуля
|
||
|
||
1. Фейковый Garmin: логин (формальный SSO), список из strength +
|
||
cardio активностей — скачивается и импортируется только strength;
|
||
`facts_json`/`avg_hr` в БД.
|
||
2. Повторный `FetchNew()` — 0 скачиваний (папка-кэш), БД без дублей.
|
||
3. .fit, уже импортированный ранее (тот же `fit_file_id` под другим
|
||
activityId) — скачивается, файл удаляется, второй строки нет.
|
||
4. Неуспешный логин (фейковый 401/antics) — исключение с сообщением
|
||
без пароля; CLI-код 2; fallback — подсказка про папку импорта.
|
||
5. Тесты интеграционные против docker `watchbody-pg` — зелёные;
|
||
сборка решения без ошибок.
|