openapi: 3.0.3

info:
  title: Mapper Service API
  version: "1.0.0"
  description: |
    REST API сервиса **mapper** (flows mapper) — mini-HTTP-сервис, который
    объединяет (мапит) данные нескольких сервисов Sarex: документы дисков из
    сервиса документации (`documentations`) обогащаются review-данными из
    сервиса процессов (`flows`); заметки из сервиса `notes` обогащаются
    связями (target-links) из Django-бэкенда.

    Сервис написан на Python (**FastAPI**), приложение создаётся в
    `src/app/main.py` (`app = FastAPI()`), маршруты — в `src/app/routers.py`
    с префиксом `API_PREFIX` (по умолчанию `/api/v1`).

    ### Аутентификация
    Оба эндпоинта требуют заголовок `Authorization`. Опциональный заголовок
    `Identity` включает режим Zitadel. Подпись JWT **не проверяется**
    (`verify_signature=False`, `src/app/dependensies.py`) — доверие к токену
    обеспечивается сетевым слоем (Istio/ingress). Из токена извлекается
    `user_id`, который используется как часть ключа кеша Redis.

    ### Агрегация и кеш
    Каждый запрос обращается к двум внешним сервисам через `ServiceManager`
    (`src/app/utils.py`). При `REDIS_USE=True` успешные (200) ответы апстрима
    кешируются в Redis (ключ `"{user_id}_{url}"`, TTL `REDIS_EXPIRE_DAYS`
    суток), а при ошибке апстрима отдаётся кеш. Если хотя бы один апстрим
    вернул ошибку и кеша нет — сервис отвечает `400 Bad Request`.

    Схемы ответов заданы как свободные JSON-структуры (`object`/`array`),
    так как сервис проксирует и объединяет ответы внешних сервисов без
    фиксированной модели.

servers:
  - url: https://api.sarex.io/mapper
    description: production (Sarex)
  - url: https://stage-api.sarex.io/mapper
    description: stage (Sarex)
  - url: https://cde.brusnika.ru/mapper
    description: production (Brusnika)
  - url: https://test.sarex.brusnika.tech/mapper
    description: stage (Brusnika)

tags:
  - name: documents
    description: Документы дисков, обогащённые review-данными процессов
  - name: notes
    description: Заметки, обогащённые связями (target-links)

paths:
  /api/v1/disks/{disk}/documents/:
    get:
      tags:
        - documents
      summary: Документы диска с review-данными
      operationId: get_documents
      description: |
        Возвращает документы диска из сервиса документации, обогащённые
        review-данными из сервиса процессов. Внутри выполняются запросы
        `GET {DOCUMENTATION_HOST}/disks/{disk}/documents` и
        `GET {FLOW_HOST}/documents/?full=true`, после чего review-данные
        добавляются в поле `review_data` соответствующих бандлов документов
        (`modify_pdm_data`).
      security:
        - bearerAuth: []
      parameters:
        - name: disk
          in: path
          required: true
          description: Идентификатор диска
          schema:
            type: string
        - name: Authorization
          in: header
          required: true
          description: Токен доступа (пробрасывается во внешние сервисы)
          schema:
            type: string
        - name: Identity
          in: header
          required: false
          description: Identity-токен (включает режим Zitadel)
          schema:
            type: string
      responses:
        "200":
          description: Успешный ответ
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DocumentsResponse"
        "400":
          description: Один из внешних сервисов недоступен и данных в кеше нет
        "401":
          description: Отсутствует заголовок Authorization
        "422":
          $ref: "#/components/responses/ValidationError"

  /api/v1/notes/{service}/{entity}/{instance_id}/:
    get:
      tags:
        - notes
      summary: Заметки сущности со связями (target-links)
      operationId: get_notes
      description: |
        Возвращает заметки сущности из сервиса `notes`, у которых поле `links`
        заменено на объекты связей (target-links) из Django-бэкенда. Внутри
        выполняются запросы
        `GET {NOTE_HOST}/notes/{service}/{entity}/{instance_id}/?full=true`
        (с проброской всех query-параметров запроса) и
        `GET {DJANGO_HOST}/core/target-links/`, после чего выполняется
        объединение (`modify_notes_data`).
      security:
        - bearerAuth: []
      parameters:
        - name: service
          in: path
          required: true
          description: Логическое имя сервиса-владельца сущности
          schema:
            type: string
        - name: entity
          in: path
          required: true
          description: Тип сущности
          schema:
            type: string
        - name: instance_id
          in: path
          required: true
          description: Идентификатор экземпляра сущности
          schema:
            type: string
        - name: Authorization
          in: header
          required: true
          description: Токен доступа (пробрасывается во внешние сервисы)
          schema:
            type: string
        - name: Identity
          in: header
          required: false
          description: Identity-токен (включает режим Zitadel)
          schema:
            type: string
      responses:
        "200":
          description: |
            Успешный ответ — список заметок. Все дополнительные query-параметры
            запроса проксируются в сервис заметок.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/NotesResponse"
        "400":
          description: Один из внешних сервисов недоступен и данных в кеше нет
        "401":
          description: Отсутствует заголовок Authorization
        "422":
          $ref: "#/components/responses/ValidationError"

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: |
        Токен передаётся заголовком `Authorization`. Подпись не проверяется
        приложением. Для режима Zitadel дополнительно передаётся заголовок
        `Identity`.

  responses:
    ValidationError:
      description: Ошибка валидации параметров запроса
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/HTTPValidationError"

  schemas:
    DocumentsResponse:
      type: object
      description: |
        Ответ агрегатора документов. Структура повторяет ответ сервиса
        документации, где в бандлы добавлено поле `review_data` с данными
        из сервиса процессов.
      properties:
        documents:
          type: array
          items:
            type: object
            additionalProperties: true
      additionalProperties: true

    NotesResponse:
      type: array
      description: |
        Список заметок сервиса `notes`, где поле `links` каждой заметки
        заменено на объекты связей (target-links).
      items:
        type: object
        additionalProperties: true

    ValidationErrorItem:
      type: object
      properties:
        loc:
          type: array
          items:
            anyOf:
              - type: string
              - type: integer
        msg:
          type: string
        type:
          type: string
      required:
        - loc
        - msg
        - type

    HTTPValidationError:
      type: object
      properties:
        detail:
          type: array
          items:
            $ref: "#/components/schemas/ValidationErrorItem"
