VEU.WathBody/1-docs/4-modules/m07-fit-fetch/module_reqs.md

5.6 KiB
Raw Blame History

Требования к модулю: 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 — зелёные; сборка решения без ошибок.