iac/apps/notes/openapi.yaml

738 lines
26 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: Notes Service API
version: "0.0.1"
description: |
REST API сервиса **notes-backend** (`aero/notes-backend`) — управление
заметками к сущностям (workspace), их ссылками, документами и вложениями,
а также генерацией документов через workflow.
Сервис написан на Python (**FastAPI**, pydantic v1). Приложение собирается
фабрикой `get_app` в `src/app/main.py`. Публичный роутинг подключается с
префиксом `/api/v1` (`src/app/routers/__init__.py`) и включает группы
`notes`, `links`, `documents`, `attachments`.
Опционально (при `ENABLE_ND=true`) подключается роутер `nd_service` с
префиксом `/api/v1/nd` (`src/app/nd_service/router.py`).
### Аутентификация
Аутентификация выполняется middleware `DjangoUserMiddleware`
(`src/app/middleware.py`). При `DJANGO_USE=true` каждый запрос (кроме путей,
содержащих `/nd`) должен содержать заголовок `Authorization` — токен
проверяется обращением к Django `/client/settings/`. Опционально
передаётся заголовок `Identity` (режим Zitadel). Если заголовок
`Authorization` отсутствует — возвращается `401`.
При `DJANGO_USE=false` middleware подставляет тестового пользователя-
администратора (использовать только локально).
Дополнительно, права проверяются зависимостью `PermissionManager`:
для не-админов метод сопоставляется с правом (`base.can_add_note` для POST,
`base.can_view_note` для GET, `base.can_change_note` для PUT/PATCH,
`base.can_delete_note` для DELETE); при отсутствии права — `403`.
Часть эндпоинтов НД (`/api/v1/nd/nd_proxy/*` на запись) защищена
JWT (`JWTBearer`, включается флагом `ND_JWT_ENABLE`).
### Замечания (расхождения кода)
- Ошибки валидации тела/параметров (Pydantic) отдаются FastAPI в
стандартном формате `422`.
- Подключение к БД всегда идёт с `sslmode=verify-full` независимо от
значения `PG_SSL_MODE`.
- Многие пути завершаются слэшем (`/api/v1/notes/{id}/`).
contact:
name: notes-backend
url: https://gitlab/aero/notes-backend
servers:
- url: https://api.sarex.io/notes
description: Production (ingress, BASE_HOST)
- url: https://api.preprod.sarex.io/notes
description: Preprod (ingress, BASE_HOST)
- url: https://stage-api.sarex.io/notes
description: Stage (ingress, BASE_HOST)
- url: http://notes-backend-service:8000
description: Внутрикластерный адрес (ClusterIP)
- url: http://localhost:8000
description: Локальный запуск (gunicorn/docker, порт 8000)
tags:
- name: notes
description: Заметки — создание, просмотр, обновление, поиск, документы и вложения
- name: links
description: Ссылки, привязанные к заметкам
- name: documents
description: Документы, привязанные к заметкам
- name: attachments
description: Вложения заметок
- name: nd_service
description: Проксирование НД (подключается при ENABLE_ND=true)
security:
- bearerAuth: []
paths:
/api/v1/notes/:
post:
tags: [notes]
summary: Создать заметку
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/BaseNote'
responses:
'201':
description: Создано
content:
application/json:
schema:
$ref: '#/components/schemas/Note'
'400':
description: Некорректный company_id
get:
tags: [notes]
summary: Список заметок
parameters:
- { name: search, in: query, schema: { type: string } }
- { name: limit, in: query, schema: { type: integer, default: 1000, minimum: 0 } }
- { name: offset, in: query, schema: { type: integer, default: 0, minimum: 0 } }
- { name: created_from, in: query, schema: { type: string, format: date-time } }
- { name: created_to, in: query, schema: { type: string, format: date-time } }
- { name: time_start, in: query, schema: { type: string, format: date-time } }
- { name: time_end, in: query, schema: { type: string, format: date-time } }
- { name: author, in: query, schema: { type: string } }
- { name: description, in: query, schema: { type: boolean } }
- { name: document, in: query, schema: { type: boolean } }
- { name: resource_id, in: query, description: "Список UUID через запятую", schema: { type: string } }
responses:
'200':
description: OK
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Note'
/api/v1/notes/{service}/{entity}/{instance_id}/:
get:
tags: [notes]
summary: Заметки по сервису/сущности/инстансу
parameters:
- { name: service, in: path, required: true, schema: { $ref: '#/components/schemas/Service' } }
- { name: entity, in: path, required: true, schema: { $ref: '#/components/schemas/Entity' } }
- { name: instance_id, in: path, required: true, schema: { type: string } }
- { name: full, in: query, description: "Вернуть расширенные заметки (ExtendedNote)", schema: { type: boolean, default: false } }
- { name: search, in: query, schema: { type: string } }
- { name: limit, in: query, schema: { type: integer, default: 1000 } }
- { name: offset, in: query, schema: { type: integer, default: 0 } }
responses:
'200':
description: OK
content:
application/json:
schema:
type: array
items:
oneOf:
- $ref: '#/components/schemas/Note'
- $ref: '#/components/schemas/ExtendedNote'
/api/v1/notes/{instance_id}/:
get:
tags: [notes]
summary: Заметка по id
parameters:
- { name: instance_id, in: path, required: true, schema: { type: integer } }
responses:
'200':
description: OK
content:
application/json:
schema: { $ref: '#/components/schemas/Note' }
'404':
description: Не найдено
put:
tags: [notes]
summary: Обновить заметку
parameters:
- { name: instance_id, in: path, required: true, schema: { type: integer } }
requestBody:
required: true
content:
application/json:
schema: { $ref: '#/components/schemas/BaseNote' }
responses:
'200':
description: OK
content:
application/json:
schema: { $ref: '#/components/schemas/Note' }
'400': { description: Некорректный company_id }
'404': { description: Не найдено }
delete:
tags: [notes]
summary: Удалить заметку
parameters:
- { name: instance_id, in: path, required: true, schema: { type: integer } }
responses:
'200':
description: Удалено (возвращает удалённый объект)
content:
application/json:
schema: { $ref: '#/components/schemas/Note' }
'404': { description: Не найдено }
/api/v1/notes/{instance_id}/documents/:
post:
tags: [notes]
summary: Добавить документы к заметке
parameters:
- { name: instance_id, in: path, required: true, schema: { type: integer } }
requestBody:
required: true
content:
application/json:
schema:
type: array
items: { $ref: '#/components/schemas/BaseDocument' }
responses:
'201':
description: Создано
content:
application/json:
schema:
type: array
items: { $ref: '#/components/schemas/Document' }
'404': { description: Заметка не найдена }
/api/v1/notes/{instance_id}/attachments/:
post:
tags: [notes]
summary: Загрузить файлы-вложения к заметке
parameters:
- { name: instance_id, in: path, required: true, schema: { type: integer } }
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
properties:
files:
type: array
items: { type: string, format: binary }
responses:
'200':
description: OK
content:
application/json:
schema:
type: array
items:
type: object
properties:
name: { type: string }
link: { type: string }
id: { type: integer }
attachment_type: { type: string }
'404': { description: Заметка не найдена }
get:
tags: [notes]
summary: Вложения заметки
parameters:
- { name: instance_id, in: path, required: true, schema: { type: integer } }
responses:
'200':
description: OK
content:
application/json:
schema: { type: array, items: { type: object } }
'404': { description: Заметка не найдена }
/api/v1/notes/{instance_id}/bound_attachments/:
post:
tags: [notes]
summary: Привязать существующее вложение к заметке
parameters:
- { name: instance_id, in: path, required: true, schema: { type: integer } }
requestBody:
required: true
content:
application/json:
schema: { $ref: '#/components/schemas/BaseAttachment' }
responses:
'201':
description: Создано
content:
application/json:
schema: { $ref: '#/components/schemas/Attachment' }
'404': { description: Заметка не найдена }
/api/v1/notes/{instance_id}/generate_document/:
post:
tags: [notes]
summary: Сгенерировать документ по заметке (через workflow)
parameters:
- { name: instance_id, in: path, required: true, schema: { type: integer } }
requestBody:
required: true
content:
application/json:
schema: { $ref: '#/components/schemas/DocumentCreation' }
responses:
'200':
description: OK
content:
application/json:
schema: { $ref: '#/components/schemas/WFResponse' }
'404': { description: Заметка не найдена }
/api/v1/links/:
post:
tags: [links]
summary: Создать ссылку
requestBody:
required: true
content:
application/json:
schema: { $ref: '#/components/schemas/BaseLink' }
responses:
'201':
description: Создано
content:
application/json:
schema: { $ref: '#/components/schemas/Link' }
'400': { description: Некорректный note_id }
get:
tags: [links]
summary: Список ссылок
parameters:
- { name: note, in: query, description: "Фильтр по note_id", schema: { type: integer } }
- { name: limit, in: query, schema: { type: integer, default: 1000 } }
- { name: offset, in: query, schema: { type: integer, default: 0 } }
responses:
'200':
description: OK
content:
application/json:
schema: { type: array, items: { $ref: '#/components/schemas/Link' } }
/api/v1/links/{instance_id}/:
get:
tags: [links]
summary: Ссылка по id
parameters:
- { name: instance_id, in: path, required: true, schema: { type: integer } }
responses:
'200':
description: OK
content:
application/json:
schema: { $ref: '#/components/schemas/Link' }
'404': { description: Не найдено }
put:
tags: [links]
summary: Обновить ссылку
parameters:
- { name: instance_id, in: path, required: true, schema: { type: integer } }
requestBody:
required: true
content:
application/json:
schema: { $ref: '#/components/schemas/BaseLink' }
responses:
'200':
description: OK
content:
application/json:
schema: { $ref: '#/components/schemas/Link' }
'404': { description: Не найдено }
delete:
tags: [links]
summary: Удалить ссылку
parameters:
- { name: instance_id, in: path, required: true, schema: { type: integer } }
responses:
'200':
description: Удалено
content:
application/json:
schema: { $ref: '#/components/schemas/Link' }
'404': { description: Не найдено }
/api/v1/documents/{instance_id}/:
delete:
tags: [documents]
summary: Удалить документ заметки
parameters:
- { name: instance_id, in: path, required: true, schema: { type: integer } }
responses:
'200':
description: Удалено
content:
application/json:
schema: { $ref: '#/components/schemas/Document' }
'404': { description: Не найдено }
/api/v1/attachments/{instance_id}/:
delete:
tags: [attachments]
summary: Удалить вложение
parameters:
- { name: instance_id, in: path, required: true, description: "attachment_id", schema: { type: integer } }
responses:
'200':
description: Удалено
content:
application/json:
schema: { $ref: '#/components/schemas/Attachment' }
'404': { description: Не найдено }
/api/v1/nd/nd_proxy/:
post:
tags: [nd_service]
summary: Создать НД-прокси
security: [{ bearerAuth: [] }]
requestBody:
required: true
content:
application/json:
schema: { $ref: '#/components/schemas/NDProxyCreate' }
responses:
'200':
description: OK
content:
application/json:
schema: { $ref: '#/components/schemas/NDProxySchema' }
'400': { description: Нарушение целостности }
get:
tags: [nd_service]
summary: Список НД-прокси
security: [{ bearerAuth: [] }]
parameters:
- { name: is_bound, in: query, schema: { type: boolean } }
- { name: limit, in: query, schema: { type: integer, default: 1000 } }
- { name: offset, in: query, schema: { type: integer, default: 0 } }
responses:
'200':
description: OK
content:
application/json:
schema: { type: array, items: { $ref: '#/components/schemas/NDProxySchema' } }
/api/v1/nd/nd_proxy/{instance_id}/:
get:
tags: [nd_service]
summary: НД-прокси по коду
parameters:
- { name: instance_id, in: path, required: true, description: "nd_code", schema: { type: string } }
responses:
'200':
description: OK
content:
application/json:
schema: { $ref: '#/components/schemas/NDProxySchema' }
'404': { description: Не найдено }
put:
tags: [nd_service]
summary: Обновить НД-прокси
parameters:
- { name: instance_id, in: path, required: true, schema: { type: string } }
requestBody:
required: true
content:
application/json:
schema: { $ref: '#/components/schemas/NDProxyCreate' }
responses:
'200':
description: OK
content:
application/json:
schema: { $ref: '#/components/schemas/NDProxySchema' }
'404': { description: Не найдено }
patch:
tags: [nd_service]
summary: Частично обновить НД-прокси
parameters:
- { name: instance_id, in: path, required: true, schema: { type: string } }
requestBody:
required: true
content:
application/json:
schema: { $ref: '#/components/schemas/NDProxyUpdate' }
responses:
'200':
description: OK
content:
application/json:
schema: { $ref: '#/components/schemas/NDProxySchema' }
'404': { description: Не найдено }
delete:
tags: [nd_service]
summary: Удалить НД-прокси
parameters:
- { name: instance_id, in: path, required: true, schema: { type: string } }
responses:
'200':
description: Удалено
content:
application/json:
schema: { $ref: '#/components/schemas/NDProxySchema' }
'404': { description: Не найдено }
/api/v1/nd/nd_proxy/{instance_id}/bound/:
post:
tags: [nd_service]
summary: Привязать заметку и файл к НД-прокси
parameters:
- { name: instance_id, in: path, required: true, description: "nd_code", schema: { type: string } }
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
properties:
note_id: { type: integer }
name: { type: string }
file: { type: string, format: binary }
responses:
'200':
description: OK
'404': { description: Не найдено }
/api/v1/nd/notes/{instance_id}/:
get:
tags: [nd_service]
summary: Заметка НД с изображением
parameters:
- { name: instance_id, in: path, required: true, schema: { type: integer } }
responses:
'200':
description: OK
content:
application/json:
schema: { type: object }
'404': { description: Не найдено }
patch:
tags: [nd_service]
summary: Обновить время заметки (НД)
parameters:
- { name: instance_id, in: path, required: true, schema: { type: integer } }
requestBody:
required: true
content:
application/json:
schema: { $ref: '#/components/schemas/NDUpdateTimeNote' }
responses:
'200':
description: OK
content:
application/json:
schema: { $ref: '#/components/schemas/Note' }
'404': { description: Не найдено }
/api/v1/nd/users/:
post:
tags: [nd_service]
summary: Проверить существование пользователя по username
requestBody:
required: true
content:
application/json:
schema: { $ref: '#/components/schemas/User' }
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
exist: { type: boolean }
'400': { description: Ошибка запроса к Django }
/api/v1/nd/token/:
get:
tags: [nd_service]
summary: Сгенерировать JWT для НД-сервиса
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
token: { type: string }
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
description: |
Заголовок `Authorization` (проверяется через Django `/client/settings/`).
Опционально заголовок `Identity` для режима Zitadel. Эндпоинты `/api/v1/nd/*`
на запись используют собственный JWT (`JWTBearer`, флаг ND_JWT_ENABLE).
schemas:
Service:
type: string
enum: [workspace]
Entity:
type: string
enum: [workspace]
BaseNote:
type: object
required: [name, service, entity, instance_id, company_id]
properties:
name: { type: string }
body: { type: string, nullable: true }
time_start: { type: string, format: date-time, nullable: true }
time_end: { type: string, format: date-time, nullable: true }
service: { $ref: '#/components/schemas/Service' }
entity: { $ref: '#/components/schemas/Entity' }
instance_id: { type: string }
meta_data: { type: object, nullable: true }
height_base_plane: { type: number, format: float, nullable: true }
height_geom_prmtv: { type: number, format: float, nullable: true }
created_by: { type: integer, nullable: true, minimum: 1 }
company_id: { type: integer, minimum: 1 }
resource_id: { type: string, format: uuid, nullable: true }
Note:
allOf:
- $ref: '#/components/schemas/BaseNote'
- type: object
required: [id, created_at, updated_at]
properties:
id: { type: integer }
created_at: { type: string, format: date-time }
updated_at: { type: string, format: date-time }
ExtendedNote:
allOf:
- $ref: '#/components/schemas/Note'
- type: object
properties:
links:
type: array
items: { type: integer }
documents:
type: array
items: { $ref: '#/components/schemas/Document' }
DocumentCreation:
type: object
required: [path, values, file_name, template]
properties:
path: { type: string }
values: { type: object, additionalProperties: true }
file_name: { type: string }
template: { type: string }
WFResponse:
type: object
required: [context]
properties:
context: { type: object }
BaseLink:
type: object
required: [link_id, note_id]
properties:
link_id: { type: integer, minimum: 1 }
note_id: { type: integer, minimum: 1 }
Link:
allOf:
- $ref: '#/components/schemas/BaseLink'
- type: object
required: [id]
properties:
id: { type: integer }
BaseDocument:
type: object
required: [document_id, bundle_id]
properties:
document_id: { type: integer }
bundle_id: { type: string }
note_id: { type: integer, nullable: true }
Document:
allOf:
- $ref: '#/components/schemas/BaseDocument'
- type: object
required: [id]
properties:
id: { type: integer }
BaseAttachment:
type: object
required: [attachment_id, object_name, attachment_type, is_state]
properties:
attachment_id: { type: integer, minimum: 1 }
note_id: { type: integer, nullable: true, minimum: 1 }
object_name: { type: string }
attachment_type: { type: string }
is_state: { type: boolean }
Attachment:
allOf:
- $ref: '#/components/schemas/BaseAttachment'
- type: object
required: [id]
properties:
id: { type: integer }
NDProxyCreate:
type: object
required: [nd_code, creator, is_locked]
properties:
nd_code: { type: string }
work_description: { type: string, nullable: true }
equipment_description: { type: string, nullable: true }
description: { type: string, nullable: true }
creator: { type: string }
is_locked: { type: boolean }
note_id: { type: integer, nullable: true }
NDProxyUpdate:
type: object
properties:
nd_code: { type: string, nullable: true }
work_description: { type: string, nullable: true }
equipment_description: { type: string, nullable: true }
description: { type: string, nullable: true }
creator: { type: string, nullable: true }
is_locked: { type: boolean, nullable: true }
note_id: { type: integer, nullable: true }
NDProxySchema:
allOf:
- $ref: '#/components/schemas/NDProxyCreate'
- type: object
required: [id]
properties:
id: { type: integer }
NDUpdateTimeNote:
type: object
properties:
time_start: { type: string, format: date-time, nullable: true }
time_end: { type: string, format: date-time, nullable: true }
User:
type: object
required: [username]
properties:
username: { type: string }