# Эндпоинты сервиса mapper

Документ описывает HTTP-интерфейс сервиса `mapper` (flows mapper): собственные эндпоинты, которые сервис публикует, и внешние эндпоинты сервисов Sarex, к которым он обращается для сборки ответа.

## Как устроено взаимодействие

`mapper` — асинхронный FastAPI-прокси-агрегатор. Каждый входящий запрос:

1. проходит аутентификацию (`app/dependensies.py:get_user_data`) — из заголовков `Authorization` и опционального `Identity` извлекается `user_id` (подпись JWT не проверяется);
2. создаёт httpx-клиенты к нужным внешним сервисам (`app/config.py`, базовые хосты — из `*_HOST`, `verify=False`, заголовки авторизации пробрасываются);
3. параллельно-последовательно запрашивает 2 внешних сервиса через `ServiceManager.get_response()` (`app/utils.py`);
4. при `REDIS_USE=True` кеширует успешные (200) ответы в Redis по ключу `"{user_id}_{url}"`, а при ошибке апстрима возвращает данные из кеша;
5. объединяет ответы (`modify_pdm_data` / `modify_notes_data`) и отдаёт результат.

Если любой из двух апстримов вернул `None` (ошибка и нет кеша) — роутер отвечает `400 Bad Request`.

## Собственные эндпоинты (что публикует mapper)

Базовый префикс — `API_PREFIX` (по умолчанию `/api/v1`). Оба эндпоинта требуют заголовок `Authorization` (и `Identity` для режима Zitadel).

| Метод | Путь | Назначение | Ответ |
| --- | --- | --- | --- |
| GET | `/api/v1/disks/{disk}/documents/` | Документы диска, обогащённые review-данными из сервиса процессов | `object` (`{"documents": [...]}`) |
| GET | `/api/v1/notes/{service}/{entity}/{instance_id}/` | Заметки сущности, обогащённые target-links из Django-бэкенда | `array` (список заметок) |

### `GET /api/v1/disks/{disk}/documents/`

Параметры пути: `disk` (string).

Логика (`routers.py:get_documents`):

- запрос к **documentations**: `GET /disks/{disk}/documents` → берётся поле `documents`;
- запрос к **flows**: `GET /documents/?full=true`;
- `modify_pdm_data` матчит по `document_id`/`bundle_id` и добавляет `review_data` в соответствующие бандлы документов.

### `GET /api/v1/notes/{service}/{entity}/{instance_id}/`

Параметры пути: `service`, `entity`, `instance_id` (string). Дополнительно **все query-параметры запроса пробрасываются** в сервис заметок (к ним добавляется `full=true`).

Логика (`routers.py:get_notes`):

- запрос к **notes**: `GET /notes/{service}/{entity}/{instance_id}/?full=true&<проброшенные query>`;
- запрос к **Django**: `GET /core/target-links/` (полный путь — `{DJANGO_HOST}/core/target-links/`);
- `modify_notes_data` заменяет id-ссылки в поле `links` каждой заметки на объекты target-links.

Полное описание схем — в `openapi.yaml`.

## Внешние сервисы и базовые хосты по окружениям

Базовые хосты берутся из `*_HOST` (`app/config.py`). Итоговый URL = `<HOST>` + путь ниже. Значения по окружениям — из `.helm/values.yaml` (деплой из репозитория сервиса) и overlay'ов инфра-репозитория.

| Сервис | Переменная | Дефолт в коде (stage) | preprod | production | brusnika-stage | brusnika-prod |
| --- | --- | --- | --- | --- | --- | --- |
| documentations | `DOCUMENTATION_HOST` | `https://stage-api.sarex.io/documentations/api/v1` | `https://api.preprod.sarex.io/documentations/api/v1` | `https://api.sarex.io/documentations/api/v1` | `https://test.sarex.brusnika.tech/documentations/api/v1` | `https://cde.brusnika.ru/documentations/api/v1` |
| flows | `FLOW_HOST` | `https://stage-api.sarex.io/flows/api/v1` | `https://api.preprod.sarex.io/flows/api/v1` | `https://api.sarex.io/flows/api/v1` | `https://test.sarex.brusnika.tech/flows/api/v1` | `https://cde.brusnika.ru/flows/api/v1` |
| django (sarex-backend) | `DJANGO_HOST` | `https://stage.sarex.io/api` | `https://preprod.sarex.io/api` | `https://lk.sarex.io/api` | `https://test.sarex.brusnika.tech/api` | `https://cde.brusnika.ru/api` |
| notes | `NOTE_HOST` | `https://stage-api.sarex.io/notes/api/v1` | `https://api.preprod.sarex.io/notes/api/v1` | `https://api.sarex.io/notes/api/v1` | `https://test.sarex.brusnika.tech/notes/api/v1` | `https://cde.brusnika.ru/notes/api/v1` |

## Эндпоинты внешних сервисов (что вызывает mapper)

### `documentations`

| Метод | Путь | Параметры | Назначение | Где используется |
| --- | --- | --- | --- | --- |
| GET | `/disks/{disk}/documents` | `disk` (path) | Документы диска (поле `documents` в ответе) | `get_documents` |

### `flows`

| Метод | Путь | Параметры | Назначение | Где используется |
| --- | --- | --- | --- | --- |
| GET | `/documents/` | `full=true` (query) | Документы процессов с review-данными | `get_documents` |

### `notes`

| Метод | Путь | Параметры | Назначение | Где используется |
| --- | --- | --- | --- | --- |
| GET | `/notes/{service}/{entity}/{instance_id}/` | `service`, `entity`, `instance_id` (path); `full=true` + проброшенные query | Заметки сущности | `get_notes` |

### `django` (sarex-backend)

| Метод | Путь | Параметры | Назначение | Где используется |
| --- | --- | --- | --- | --- |
| GET | `/core/target-links/` | — | Связи (target-links); из ответа берётся `results`, если ответ — объект | `get_notes` |

## Заголовки и аутентификация

- `Authorization: <token>` — обязателен для обоих эндпоинтов; пробрасывается во все внешние запросы как есть.
- `Identity: <token>` — опционален; при наличии включается режим Zitadel, заголовок также пробрасывается во внешние запросы.
- Ответы при ошибках: `401 Unauthorized` (нет `Authorization`), `400 Bad Request` (ошибка апстрима без кеша), `422 Unprocessable Entity` (ошибка валидации параметров пути, стандартный ответ FastAPI).

## Замечания

- В коде клиент к сервису заметок называется `NoteSettings`/`note`, к Django — `DjangoSettings`/`django`. Пути `/documents/` (flows) и `/notes/.../` (notes) содержат завершающий слэш — важно для совпадения с маршрутами апстрима.
- Кеш ключуется по `user_id` + URL, поэтому проброшенные query-параметры в `get_notes` не входят в ключ кеша (URL берётся без query). При включённом Redis это стоит учитывать.
- Healthcheck-эндпоинта у сервиса нет.
