openapi: 3.0.3

info:
  title: Drawings API
  version: "0.0.1"
  description: |
    REST API сервиса **drawings-api** (`gitlab.com/sarex-team/rnd/drawings-api`) —
    управление разрезами (cross-sections) чертежей, их данными и экспортом
    в DWG через сервис workflows.

    Сервис написан на **Go** (gorilla/mux, go-pg). Роутер собирается в
    `cmd/drawings-api/bootstrap.go`. Помимо служебных эндпоинтов
    (`/ping`, `/metrics`) есть две группы бизнес-маршрутов с одинаковым
    набором операций:

      - `/api/v1/*` — публичный роутинг;
      - `/internal/v1/*` — внутренний роутинг (набор тот же плюс webhook
        экспорта, вызываемый воркером workflow).

    На все бизнес-маршруты навешены middleware: JSON-ответ (`rest.JSONResponse`),
    request-id (`reqid.Middleware`) и логирование. Явной аутентификации в коде
    сервиса нет — доступ ограничивается на уровне ingress/сети кластера.

    ### Экспорт в DWG
    `POST /exports` создаёт запись экспорта и запускает workflow из двух задач
    (парсинг cross-section в DWG + webhook-уведомление). По завершении workflow
    вызывает `POST /internal/v1/exports/{export_id}/webhook`, который переводит
    экспорт в статус `done`.

servers:
  - url: /api/v1
    description: Публичный префикс
  - url: /internal/v1
    description: Внутренний префикс

tags:
  - name: service
    description: Служебные эндпоинты
  - name: cross-sections
    description: Разрезы чертежей
  - name: exports
    description: Экспорт разрезов в DWG

paths:
  /ping:
    get:
      tags: [service]
      summary: Healthcheck
      description: Возвращает статус готовности. Доступен в корне (без префикса).
      responses:
        "200":
          description: Сервис готов
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: ready

  /metrics:
    get:
      tags: [service]
      summary: Prometheus-метрики
      description: Метрики в формате Prometheus. Доступен в корне (без префикса).
      responses:
        "200":
          description: Метрики
          content:
            text/plain:
              schema:
                type: string

  # ----- Публичные маршруты (/api/v1) и внутренние (/internal/v1) идентичны,
  #       кроме webhook, который есть только на /internal/v1. Пути ниже указаны
  #       относительно префикса из блока servers. -----

  /cross-sections:
    post:
      tags: [cross-sections]
      summary: Создать разрез
      description: Создаёт cross-section вместе с его данными (`data.raw_data`).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateCrossSectionRequest"
      responses:
        "200":
          description: Созданный разрез
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CrossSection"
        "400":
          $ref: "#/components/responses/BadRequest"
        "500":
          $ref: "#/components/responses/StorageError"
    get:
      tags: [cross-sections]
      summary: Список разрезов по instance_id
      parameters:
        - name: instance_id
          in: query
          required: true
          description: UUID инстанса (чертежа)
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: Массив разрезов (пустой, если ничего не найдено)
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/CrossSection"
        "400":
          $ref: "#/components/responses/BadRequest"
        "500":
          $ref: "#/components/responses/StorageError"

  /cross-sections/{cs_id}:
    delete:
      tags: [cross-sections]
      summary: Удалить разрез (soft-delete)
      parameters:
        - $ref: "#/components/parameters/CrossSectionId"
      responses:
        "200":
          $ref: "#/components/responses/OK"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          description: Разрез не найден (возможно, уже удалён)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "500":
          $ref: "#/components/responses/StorageError"

  /cross-sections/{cs_id}/data:
    get:
      tags: [cross-sections]
      summary: Данные разреза
      parameters:
        - $ref: "#/components/parameters/CrossSectionId"
      responses:
        "200":
          description: Данные разреза
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Data"
        "400":
          $ref: "#/components/responses/BadRequest"
        "500":
          $ref: "#/components/responses/StorageError"

  /exports:
    post:
      tags: [exports]
      summary: Создать экспорт разреза в DWG
      description: |
        Создаёт запись экспорта и запускает workflow экспорта в DWG.
        В ответе `workflow_status` = `running`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateExportRequest"
      responses:
        "200":
          description: Созданный экспорт
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Export"
        "400":
          $ref: "#/components/responses/BadRequest"
        "500":
          $ref: "#/components/responses/StorageError"
    get:
      tags: [exports]
      summary: Список экспортов по фильтрам
      description: |
        Хотя бы один из фильтров должен быть задан, иначе `400`.
        Каждый параметр — список UUID/чисел через запятую.
      parameters:
        - name: cross_section_ids
          in: query
          required: false
          description: UUID разрезов через запятую
          schema:
            type: string
        - name: export_ids
          in: query
          required: false
          description: UUID экспортов через запятую
          schema:
            type: string
        - name: workflow_ids
          in: query
          required: false
          description: UUID workflow через запятую
          schema:
            type: string
        - name: attachment_ids
          in: query
          required: false
          description: ID вложений (целые) через запятую
          schema:
            type: string
      responses:
        "200":
          description: Массив экспортов (пустой, если ничего не найдено)
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Export"
        "400":
          $ref: "#/components/responses/BadRequest"
        "500":
          $ref: "#/components/responses/StorageError"

  /exports/{export_id}:
    delete:
      tags: [exports]
      summary: Удалить экспорт
      parameters:
        - $ref: "#/components/parameters/ExportId"
      responses:
        "200":
          $ref: "#/components/responses/OK"
        "400":
          $ref: "#/components/responses/BadRequest"
        "500":
          $ref: "#/components/responses/StorageError"

  /exports/{export_id}/webhook:
    post:
      tags: [exports]
      summary: Webhook завершения экспорта (только /internal/v1)
      description: |
        Вызывается воркером workflow по завершении экспорта. Переводит
        экспорт в статус `done`. Доступен только по внутреннему префиксу
        `/internal/v1`.
      parameters:
        - $ref: "#/components/parameters/ExportId"
      responses:
        "200":
          $ref: "#/components/responses/OK"
        "400":
          $ref: "#/components/responses/BadRequest"
        "500":
          $ref: "#/components/responses/StorageError"

components:
  parameters:
    CrossSectionId:
      name: cs_id
      in: path
      required: true
      description: UUID разреза
      schema:
        type: string
        format: uuid
    ExportId:
      name: export_id
      in: path
      required: true
      description: UUID экспорта
      schema:
        type: string
        format: uuid

  responses:
    OK:
      description: Успешно (тело — строка `"OK"`)
      content:
        application/json:
          schema:
            type: string
            example: OK
    BadRequest:
      description: Некорректный запрос
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
    StorageError:
      description: Внутренняя ошибка (ошибка хранилища)
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"

  schemas:
    CrossSection:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        created_at:
          type: string
          format: date-time
        deleted_at:
          type: string
          format: date-time
        instance_id:
          type: string
          format: uuid
        documents:
          type: object
          additionalProperties:
            $ref: "#/components/schemas/ConnectedDocument"
        exports:
          type: array
          items:
            $ref: "#/components/schemas/Export"

    ConnectedDocument:
      type: object
      properties:
        color:
          type: string
        name:
          type: string

    Data:
      type: object
      properties:
        cross_section_id:
          type: string
          format: uuid
        raw_data:
          type: string

    CreateCrossSectionRequest:
      type: object
      description: Разрез плюс его данные. Наследует поля CrossSection.
      allOf:
        - $ref: "#/components/schemas/CrossSection"
        - type: object
          properties:
            data:
              $ref: "#/components/schemas/Data"

    Author:
      type: object
      properties:
        id:
          type: integer
          format: int64
        first_name:
          type: string
        last_name:
          type: string

    Export:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        author:
          $ref: "#/components/schemas/Author"
        created_at:
          type: string
          format: date-time
        deleted_at:
          type: string
          format: date-time
          nullable: true
        cross_section_id:
          type: string
          format: uuid
        workflow_id:
          type: string
          format: uuid
          nullable: true
        workflow_status:
          type: string
          nullable: true
          enum: [done, running, error]
        file_type:
          type: string
          enum: [dwg]
        attachment_id:
          type: integer
          nullable: true

    CreateExportRequest:
      type: object
      required: [cross_section_id, author, file_type, company_id]
      properties:
        cross_section_id:
          type: string
          format: uuid
        author:
          $ref: "#/components/schemas/Author"
        file_type:
          type: string
          enum: [dwg]
        company_id:
          type: integer
          format: int64

    Error:
      type: object
      description: Ответ об ошибке (gotools/httperror).
      properties:
        error:
          type: string
