52 lines
2.7 KiB
Markdown
52 lines
2.7 KiB
Markdown
# Спецификация 01: GarminClient — SSO и API Garmin Connect
|
||
|
||
Источники: tech-req разд. 5 (FitFetcher — «по контракту семьи
|
||
библиотек garminconnect»), Ф-8.1, vision (SSO-последовательность).
|
||
Прототип: против живого Garmin не автоматизируется; тесты —
|
||
фейковый `HttpMessageHandler` с теми же маршрутами.
|
||
|
||
## 1. Сигнатуры
|
||
|
||
```csharp
|
||
public class GarminClient
|
||
{
|
||
public GarminClient(string login, string password,
|
||
string baseUrl, HttpMessageHandler? handler = null);
|
||
public void Login(CancellationToken ct = default); // SSO → Bearer
|
||
public List<GarminActivity> ListActivities(int limit, CancellationToken ct = default);
|
||
public byte[] DownloadFit(long activityId, CancellationToken ct = default);
|
||
}
|
||
|
||
public sealed record GarminActivity(
|
||
long ActivityId, DateTime StartTimeLocal, string ActivityType);
|
||
```
|
||
|
||
## 2. Маршруты (по контракту garminconnect; `{base}` = `Garmin.BaseUrl`)
|
||
|
||
| Шаг | Запрос | Что берём |
|
||
|---|---|---|
|
||
| 1 | `GET {base}/sso/embed` | cookies (SSO) |
|
||
| 2 | `GET {base}/sso/signin` | csrf из html (`name="_csrf"`) |
|
||
| 3 | `POST {base}/sso/signin` (form: username, password, embed, csrf) | ticket из редиректа (`?ticket=…`); неуспех → `GarminAuthException` |
|
||
| 4 | `GET {base}/modern/?ticket=…` | cookies современного домена |
|
||
| 5 | `POST {base}/sso/api/2.0/oauth/exchange/service/user/2.0` (OAuth1-подпись тестовыми consumer key/secret) | Bearer `access_token` |
|
||
| 6 | `GET {base}/activitylist-service/activities/search/activities?limit=N&start=0` | JSON список: `activityId`, `startTimeLocal`, `activityType.typeKey` |
|
||
| 7 | `GET {base}/download-service/files/activity/{id}` | байты .fit |
|
||
|
||
Все запросы после шага 5 — заголовок `Authorization: Bearer …`.
|
||
Таймаут HttpClient — 30 с. 🛡 пароль и токены в логи не попадают
|
||
(исключения — без credential-текста).
|
||
|
||
## 3. Тестовый handler
|
||
|
||
`FakeGarminHandler : HttpMessageHandler` — маршруты разд. 2 на
|
||
`{base}`; список и байты .fit задаёт тест (`FitParser.WriteTestFile`);
|
||
режим `FailLogin` — шаг 3 возвращает 401.
|
||
|
||
## 4. Ошибки
|
||
|
||
- `GarminAuthException` — шаги 1–5 (логин/MFA/блокировка); текст
|
||
содержит шаг и код ответа, **не содержит** пароль;
|
||
- `HttpRequestException`/`TaskCanceledException` — сеть/таймаут
|
||
(сеть сбоит в тестах подменой handler).
|