iac/apps/checklists/openapi.yaml

964 lines
33 KiB
YAML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

openapi: 3.0.3
info:
title: Checklists
version: "0.1.0"
description: |
REST API сервиса **checklists-backend** — управление чек-листами
(`Checklist`) и их результатами (`ChecklistResult`).
Сервис написан на Python (**FastAPI** + ORM **Piccolo**). Приложение
собирается фабрикой `create_app` в `src/app/http.py`. Роутинг состоит из
двух групп:
- публичный API — префикс `/api/v1` (`controller/http/api`);
- внутренний API — префикс `/internal/v1` (`controller/http/internal_api`),
предназначен для вызовов внутри кластера (через ingress не публикуется).
Интерактивная документация (ReDoc) доступна по `/docs/`, схема —
по `/openapi.json/` (с учётом `root_path`). Админ-панель Piccolo монтируется
по `/admin/` при `HTTP_APP_ADMIN_ENABLE=true`.
### Аутентификация
Аутентификация включается флагом `JWT_AUTH_ENABLE`. При включённом
`JWTAuthMiddleware` (`controller/http/middlewares.py`) публичные эндпоинты
требуют заголовок `Authorization: Bearer <jwt>`. Поддерживаются два режима:
1. **Zitadel** — если передан дополнительный заголовок `identity`
(`Identity <jwt>`), полезная нагрузка (`user_id`, `company_ids`) берётся
из этого токена (`urn:zitadel:iam:user:metadata`).
2. **sarex-backend** — если заголовка `identity` нет, разбирается основной
токен (алгоритм `RS512`, ключ `JWT_AUTH_PUBLIC_KEY`).
В обоих режимах разбор выполняется с `verify_signature=False` — подпись на
уровне приложения не проверяется, доверие обеспечивается сетевым слоем.
Внутренние эндпоинты (`/internal/*`) и пути `/docs/`, `/openapi.json/`,
`/admin/*` аутентификацию пропускают. При `JWT_AUTH_ENABLE=false` middleware
не подключается и используется дефолтный пользователь.
### Пагинация
Списочные ответы используют пагинацию limit/offset и оборачиваются в
`PaginatedResponse` — `{ count, result }`, где `count` — число объектов в
текущем ответе, `result` — сами объекты. Параметры: `limit` (по умолчанию
`100`), `offset` (по умолчанию `0`), сортировка — `order_by`/`ascending`.
### Обработка ошибок
Доменные ошибки (`controller/http/errors.py`) возвращаются как
`application/json` с телом `{ "detail": "<текст>" }`. Маппинг:
- `ResourceNotPermittedError` → **403**;
- `ResourceNotFoundError` → **404**;
- `StateConflictError` → **409**;
- `ChecklistResultValidationError` → **422**;
- прочее → **500**.
Ошибки валидации тела/параметров запроса (Pydantic) отдаются FastAPI в
стандартном формате `422` (`HTTPValidationError`). Все публичные эндпоинты
(`/api/*`) при невалидном/отсутствующем токене возвращают `401`.
contact:
name: checklists-backend
url: https://gitlab/proc/checklists-backend
servers:
- url: https://api.sarex.io/checklists
description: Production (ingress, root_path=/checklists)
- url: https://stage-api.sarex.io/checklists
description: Stage (ingress, root_path=/checklists)
- url: http://checklists-backend-service.proc.svc.cluster.local
description: Внутрикластерный адрес (ClusterIP, порт 80 → 8000). Единственный способ достучаться до /internal/v1
- url: http://localhost:8000
description: Локальный запуск (Uvicorn, порт по умолчанию 8000)
tags:
- name: Checklists
description: Чек-листы — создание, просмотр, поиск, удаление
- name: Checklist results
description: Результаты чек-листов — создание, просмотр, обновление, удаление
- name: internal
description: Внутренние эндпоинты (только внутри кластера)
security:
- bearerAuth: []
paths:
# ==========================================================================
# Checklists
# ==========================================================================
/api/v1/checklists/:
post:
tags: [Checklists]
summary: Создать чек-лист
operationId: createChecklist
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ChecklistCreate'
responses:
'201':
description: Созданный чек-лист
content:
application/json:
schema:
$ref: '#/components/schemas/ChecklistReadFull'
'401': { $ref: '#/components/responses/Unauthorized' }
'403': { $ref: '#/components/responses/Forbidden' }
'422': { $ref: '#/components/responses/ValidationError' }
get:
tags: [Checklists]
summary: Список чек-листов
operationId: listChecklists
parameters:
- { $ref: '#/components/parameters/OrderByChecklist' }
- { $ref: '#/components/parameters/Ascending' }
- { $ref: '#/components/parameters/Limit' }
- { $ref: '#/components/parameters/Offset' }
- name: company_id
in: query
required: false
description: Фильтр по ID компаний
schema:
type: array
nullable: true
items:
type: integer
minimum: 1
responses:
'200':
description: Страница чек-листов (компактное представление)
content:
application/json:
schema:
$ref: '#/components/schemas/PaginatedResponse_ChecklistReadCompact'
'401': { $ref: '#/components/responses/Unauthorized' }
'422': { $ref: '#/components/responses/ValidationError' }
/api/v1/checklists/filter/:
post:
tags: [Checklists]
summary: Список чек-листов (фильтры в теле)
description: Аналог `GET /api/v1/checklists/`, но фильтры и пагинация передаются в теле запроса.
operationId: filterChecklists
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ChecklistFiltersWithPagination'
responses:
'200':
description: Страница чек-листов (компактное представление)
content:
application/json:
schema:
$ref: '#/components/schemas/PaginatedResponse_ChecklistReadCompact'
'401': { $ref: '#/components/responses/Unauthorized' }
'422': { $ref: '#/components/responses/ValidationError' }
/api/v1/checklists/{instance_id}/:
get:
tags: [Checklists]
summary: Чек-лист по id
operationId: getChecklist
parameters:
- { $ref: '#/components/parameters/InstanceId' }
responses:
'200':
description: Чек-лист (полное представление)
content:
application/json:
schema:
$ref: '#/components/schemas/ChecklistReadFull'
'401': { $ref: '#/components/responses/Unauthorized' }
'403': { $ref: '#/components/responses/Forbidden' }
'404': { $ref: '#/components/responses/NotFound' }
'422': { $ref: '#/components/responses/ValidationError' }
delete:
tags: [Checklists]
summary: Удалить чек-лист
operationId: deleteChecklist
parameters:
- { $ref: '#/components/parameters/InstanceId' }
responses:
'204':
description: Чек-лист удалён
'401': { $ref: '#/components/responses/Unauthorized' }
'403': { $ref: '#/components/responses/Forbidden' }
'404': { $ref: '#/components/responses/NotFound' }
'422': { $ref: '#/components/responses/ValidationError' }
# ==========================================================================
# Checklist results
# ==========================================================================
/api/v1/results/:
post:
tags: [Checklist results]
summary: Создать результат чек-листа
description: |
Создаёт результат по `checklist_id`. Данные чек-листа переносятся бэком
автоматически; в теле передаются метаданные и список значений инпутов
(`input_values`).
operationId: createChecklistResult
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ChecklistResultCreate'
responses:
'201':
description: Созданный результат
content:
application/json:
schema:
$ref: '#/components/schemas/ChecklistResultRead'
'401': { $ref: '#/components/responses/Unauthorized' }
'403': { $ref: '#/components/responses/Forbidden' }
'422': { $ref: '#/components/responses/ValidationError' }
get:
tags: [Checklist results]
summary: Список результатов чек-листов
operationId: listChecklistResults
parameters:
- { $ref: '#/components/parameters/OrderByChecklistResult' }
- { $ref: '#/components/parameters/Ascending' }
- { $ref: '#/components/parameters/Limit' }
- { $ref: '#/components/parameters/Offset' }
- { $ref: '#/components/parameters/FilterResultId' }
- { $ref: '#/components/parameters/FilterChecklistId' }
- { $ref: '#/components/parameters/FilterCreatorId' }
- { $ref: '#/components/parameters/FilterIsDraft' }
- { $ref: '#/components/parameters/FilterIsLocked' }
responses:
'200':
description: Страница результатов
content:
application/json:
schema:
$ref: '#/components/schemas/PaginatedResponse_ChecklistResultRead'
'401': { $ref: '#/components/responses/Unauthorized' }
'422': { $ref: '#/components/responses/ValidationError' }
/api/v1/results/{instance_id}/:
get:
tags: [Checklist results]
summary: Результат чек-листа по id
operationId: getChecklistResult
parameters:
- { $ref: '#/components/parameters/InstanceId' }
responses:
'200':
description: Результат чек-листа
content:
application/json:
schema:
$ref: '#/components/schemas/ChecklistResultRead'
'401': { $ref: '#/components/responses/Unauthorized' }
'403': { $ref: '#/components/responses/Forbidden' }
'404': { $ref: '#/components/responses/NotFound' }
'422': { $ref: '#/components/responses/ValidationError' }
patch:
tags: [Checklist results]
summary: Обновить результат чек-листа
description: Частичное обновление значений инпутов и флага черновика.
operationId: updateChecklistResult
parameters:
- { $ref: '#/components/parameters/InstanceId' }
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ChecklistResultPartialUpdate'
responses:
'200':
description: Обновлённый результат
content:
application/json:
schema:
$ref: '#/components/schemas/ChecklistResultRead'
'401': { $ref: '#/components/responses/Unauthorized' }
'403': { $ref: '#/components/responses/Forbidden' }
'404': { $ref: '#/components/responses/NotFound' }
'422': { $ref: '#/components/responses/ValidationError' }
delete:
tags: [Checklist results]
summary: Удалить результат чек-листа
operationId: deleteChecklistResult
parameters:
- { $ref: '#/components/parameters/InstanceId' }
responses:
'204':
description: Результат удалён
'401': { $ref: '#/components/responses/Unauthorized' }
'403': { $ref: '#/components/responses/Forbidden' }
'404': { $ref: '#/components/responses/NotFound' }
'422': { $ref: '#/components/responses/ValidationError' }
# ==========================================================================
# Internal
# ==========================================================================
/internal/v1/results/:
get:
tags: [internal]
summary: Список результатов (внутренний)
description: Внутрикластерный эндпоинт. Аутентификация на уровне приложения не выполняется.
operationId: internalListChecklistResults
security: []
parameters:
- { $ref: '#/components/parameters/OrderByChecklistResult' }
- { $ref: '#/components/parameters/Ascending' }
- { $ref: '#/components/parameters/Limit' }
- { $ref: '#/components/parameters/Offset' }
- { $ref: '#/components/parameters/FilterResultId' }
- { $ref: '#/components/parameters/FilterChecklistId' }
- { $ref: '#/components/parameters/FilterCreatorId' }
- { $ref: '#/components/parameters/FilterIsDraft' }
- { $ref: '#/components/parameters/FilterIsLocked' }
responses:
'200':
description: Страница результатов
content:
application/json:
schema:
$ref: '#/components/schemas/PaginatedResponse_ChecklistResultRead'
'422': { $ref: '#/components/responses/ValidationError' }
/internal/v1/results/lock:
patch:
tags: [internal]
summary: Массовое обновление блокировки результатов
description: Устанавливает флаг `is_locked` для списка результатов по их id. Внутрикластерный эндпоинт.
operationId: internalBulkUpdateLock
security: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ChecklistResultBulkLockingPartialUpdate'
responses:
'204':
description: Флаги блокировки обновлены
'422': { $ref: '#/components/responses/ValidationError' }
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
description: |
JWT в заголовке `Authorization: Bearer <token>`. Алгоритм `RS512`,
ключ `JWT_AUTH_PUBLIC_KEY`. Разбор выполняется без проверки подписи
(`verify_signature=False`).
identityToken:
type: apiKey
in: header
name: identity
description: |
Опциональный заголовок `identity` (`Identity <jwt>`) для режима Zitadel.
При его наличии полезная нагрузка (`user_id`, `company_ids`) берётся из
этого токена.
parameters:
InstanceId:
name: instance_id
in: path
required: true
schema:
type: integer
Limit:
name: limit
in: query
required: false
description: Максимальное количество объектов
schema:
type: integer
default: 100
Offset:
name: offset
in: query
required: false
description: Количество пропущенных объектов
schema:
type: integer
default: 0
Ascending:
name: ascending
in: query
required: false
description: Сортировка по возрастанию
schema:
type: boolean
default: true
OrderByChecklist:
name: order_by
in: query
required: false
description: Поле для сортировки
schema:
type: string
enum: [id]
default: id
OrderByChecklistResult:
name: order_by
in: query
required: false
description: Поле для сортировки
schema:
type: string
enum: [id]
default: id
FilterResultId:
name: id
in: query
required: false
description: Фильтр по ID результата
schema:
type: array
nullable: true
items:
type: integer
minimum: 1
FilterChecklistId:
name: checklist_id
in: query
required: false
description: Фильтр по ID чек-листа
schema:
type: array
nullable: true
items:
type: integer
minimum: 1
FilterCreatorId:
name: creator_id
in: query
required: false
description: Фильтр по ID создателя результата
schema:
type: array
nullable: true
items:
type: integer
minimum: 1
FilterIsDraft:
name: is_draft
in: query
required: false
description: Признак «чернового» результата
schema:
type: boolean
nullable: true
FilterIsLocked:
name: is_locked
in: query
required: false
description: Признак блокировки результата
schema:
type: boolean
nullable: true
responses:
Unauthorized:
description: Токен не предоставлен или невалиден
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPError'
Forbidden:
description: Недостаточно прав
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPError'
NotFound:
description: Запрошенный ресурс не найден
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPError'
Conflict:
description: Конфликт состояния
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPError'
ValidationError:
description: |
Ошибка валидации тела/параметров запроса (FastAPI/Pydantic) либо
доменная ошибка валидации результата (`{ detail }`).
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
schemas:
# ---- Общие ----
HTTPError:
type: object
properties:
detail:
type: string
description: Описание ошибки
required: [detail]
HTTPValidationError:
type: object
properties:
detail:
type: array
items:
$ref: '#/components/schemas/ValidationError'
ValidationError:
type: object
properties:
loc:
type: array
items:
anyOf:
- type: string
- type: integer
msg:
type: string
type:
type: string
required: [loc, msg, type]
# ---- Ограничения инпутов ----
InputChoiceOption:
type: object
properties:
id:
type: string
format: uuid
description: Уникальный идентификатор опции
name:
type: string
description: Название опции
example: "Да"
order:
type: integer
description: Порядковый номер опции
example: 1
color:
type: string
nullable: true
description: Цвет опции (hex или именованный html-цвет)
example: "#00aa00"
selected_tip:
type: string
nullable: true
description: Заметка при выборе опции
example: 'Рекомендуется добавить комментарий при ответе "Нет"'
alt_name:
type: string
nullable: true
description: Название опции для записи в историю
example: "Одобрено"
required: [id, name, order, selected_tip]
InputChoiceConstraints:
type: object
properties:
type:
type: string
enum: [choice]
options:
type: array
nullable: true
description: Список опций для выбора
items:
$ref: '#/components/schemas/InputChoiceOption'
required: [type, options]
InputStringConstraints:
type: object
properties:
type:
type: string
enum: [string]
min_length:
type: integer
minimum: 0
default: 0
description: Минимально допустимое количество символов
max_length:
type: integer
minimum: 1
default: 1000
description: Максимально допустимое количество символов
required: [type]
InputConstraints:
oneOf:
- $ref: '#/components/schemas/InputChoiceConstraints'
- $ref: '#/components/schemas/InputStringConstraints'
discriminator:
propertyName: type
mapping:
choice: '#/components/schemas/InputChoiceConstraints'
string: '#/components/schemas/InputStringConstraints'
# ---- Чек-лист (создание) ----
ChecklistInputCreate:
type: object
properties:
name:
type: string
maxLength: 1024
description: Название
example: "Комментарий"
order:
type: integer
description: Порядковый номер
is_required:
type: boolean
description: Обязательное ли поле для заполнения
constraints:
$ref: '#/components/schemas/InputConstraints'
required: [name, order, is_required, constraints]
ChecklistItemCreate:
type: object
properties:
description:
type: string
maxLength: 8192
description: Описание шага
order:
type: integer
description: Порядковый номер
inputs:
type: array
description: Список элементов ввода
items:
$ref: '#/components/schemas/ChecklistInputCreate'
required: [description, order, inputs]
ChecklistCreate:
type: object
properties:
name:
type: string
maxLength: 250
description: Название
example: "Чек-лист проверки документов"
description:
type: string
maxLength: 8192
description: Описание чек-листа
company_id:
type: integer
minimum: 1
description: ID компании
example: 1
items:
type: array
description: Список шагов чек-листа
items:
$ref: '#/components/schemas/ChecklistItemCreate'
required: [name, description, company_id, items]
# ---- Чек-лист (чтение) ----
ChecklistInputRead:
type: object
properties:
id:
type: integer
example: 1
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
name:
type: string
maxLength: 1024
order:
type: integer
is_required:
type: boolean
constraints:
$ref: '#/components/schemas/InputConstraints'
required: [id, created_at, updated_at, name, order, is_required, constraints]
ChecklistItemRead:
type: object
properties:
id:
type: integer
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
description:
type: string
maxLength: 8192
order:
type: integer
inputs:
type: array
description: Список элементов ввода
items:
$ref: '#/components/schemas/ChecklistInputRead'
required: [id, created_at, updated_at, description, order, inputs]
ChecklistReadCompact:
type: object
properties:
id:
type: integer
example: 1
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
name:
type: string
description:
type: string
company_id:
type: integer
minimum: 1
required: [id, created_at, updated_at, name, description, company_id]
ChecklistReadFull:
allOf:
- $ref: '#/components/schemas/ChecklistReadCompact'
- type: object
properties:
items:
type: array
description: Список шагов чек-листа
items:
$ref: '#/components/schemas/ChecklistItemRead'
required: [items]
ChecklistFiltersWithPagination:
type: object
properties:
order_by:
type: string
enum: [id]
default: id
ascending:
type: boolean
default: true
limit:
type: integer
default: 100
offset:
type: integer
default: 0
company_id:
type: array
nullable: true
description: Фильтр по ID компаний
items:
type: integer
minimum: 1
# ---- Результаты чек-листов ----
ChecklistResultInputCreate:
type: object
properties:
input_id:
type: integer
description: ID инпута, для которого устанавливается значение
example: 1
value:
description: 'Значение (тип зависит от инпута: id опции для choice, строка для string)'
nullable: true
example: "Да"
required: [input_id, value]
ChecklistResultCreate:
type: object
properties:
entity_type:
type: string
description: Сущность, для которой создан результат
example: "review"
entity_id:
type: string
description: ID сущности, для которой создан результат
example: "1"
checklist_id:
type: integer
description: ID чек-листа
example: 1
accessible_by:
type: array
description: SA ID роли/места/пользователя, которым доступен результат
items:
type: string
format: uuid
is_draft:
type: boolean
description: Является ли результат черновым
input_values:
type: array
description: Список устанавливаемых значений
items:
$ref: '#/components/schemas/ChecklistResultInputCreate'
required: [entity_type, entity_id, checklist_id, accessible_by, is_draft, input_values]
ChecklistResultPartialUpdate:
type: object
properties:
input_values:
type: array
description: Список устанавливаемых значений
items:
$ref: '#/components/schemas/ChecklistResultInputCreate'
is_draft:
type: boolean
description: Является ли результат черновым
required: [input_values, is_draft]
ChecklistResultInput:
type: object
properties:
input_id:
type: integer
description: ID инпута, для которого создан результат
name:
type: string
description: Название
order:
type: integer
description: Порядковый номер
value:
type: string
nullable: true
description: Введённое значение (строка или UUID выбранной опции)
value_text:
type: string
nullable: true
description: Текстовое представление введённого значения
color:
type: string
nullable: true
description: Цвет значения
required: [input_id, name, order, value, value_text, color]
ChecklistResultItem:
type: object
properties:
description:
type: string
description: Описание шага
order:
type: integer
description: Порядковый номер
inputs:
type: array
description: Список введённых значений
items:
$ref: '#/components/schemas/ChecklistResultInput'
required: [description, order, inputs]
ChecklistResultRead:
type: object
properties:
id:
type: integer
example: 1
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
entity_type:
type: string
example: "review"
entity_id:
type: string
example: "1"
checklist_id:
type: integer
accessible_by:
type: array
items:
type: string
format: uuid
is_draft:
type: boolean
company_id:
type: integer
description: ID компании
is_locked:
type: boolean
description: Заблокирован ли результат для изменений
items:
type: array
description: Список шагов чек-листа
items:
$ref: '#/components/schemas/ChecklistResultItem'
required:
- id
- created_at
- updated_at
- entity_type
- entity_id
- checklist_id
- accessible_by
- is_draft
- company_id
- is_locked
- items
ChecklistResultBulkLockingPartialUpdate:
type: object
properties:
ids:
type: array
description: Список id результатов, которым нужно обновить флаг
items:
type: integer
example: [1, 2, 3]
is_locked:
type: boolean
description: Заблокирован ли результат для изменений
required: [ids, is_locked]
# ---- Пагинация ----
PaginatedResponse_ChecklistReadCompact:
type: object
properties:
count:
type: integer
description: Количество объектов
example: 1
result:
type: array
description: Объекты
items:
$ref: '#/components/schemas/ChecklistReadCompact'
required: [count, result]
PaginatedResponse_ChecklistResultRead:
type: object
properties:
count:
type: integer
description: Количество объектов
example: 1
result:
type: array
description: Объекты
items:
$ref: '#/components/schemas/ChecklistResultRead'
required: [count, result]