720 lines
26 KiB
YAML
720 lines
26 KiB
YAML
openapi: 3.0.3
|
||
|
||
info:
|
||
title: sarex-subscriptions API
|
||
version: "1.0.0"
|
||
description: |
|
||
REST API сервиса **sarex-subscriptions** — управление подписками на уведомления,
|
||
получателями, шаблонами и просмотр истории рассылок (события, транзакции, сообщения).
|
||
|
||
Сервис на Django REST Framework. Роутинг — `rest_framework.routers.DefaultRouter`,
|
||
все ресурсы смонтированы под префиксом `/api/v1/` (`config/urls.py`).
|
||
|
||
### Аутентификация
|
||
Настроены `BasicAuthentication` и `SessionAuthentication`
|
||
(`REST_FRAMEWORK.DEFAULT_AUTHENTICATION_CLASSES`). `DEFAULT_PERMISSION_CLASSES`
|
||
не заданы, поэтому по умолчанию действует `AllowAny` — эндпоинты доступны без
|
||
авторизации, а Basic-креды используются опционально.
|
||
|
||
### Пагинация
|
||
`LimitOffsetPagination`, `PAGE_SIZE = 1000`. Списки возвращают объект
|
||
`{ count, next, previous, results }`. Управление — query-параметрами `limit` и `offset`.
|
||
|
||
### Фильтрация
|
||
Бэкенд фильтров — `DjangoFilterBackend`. Ряд полей принимают список значений
|
||
через запятую (кастомный `CustomFilterList`, lookup `in`).
|
||
|
||
### Замечания (расхождения кода)
|
||
- `GET /transaction/{id}/` (retrieve) не имеет сериализатора в
|
||
`serializer_class_by_action` (есть ключи `list` и `get`, но не `retrieve`) —
|
||
поведение отдельного объекта может отличаться от списка.
|
||
- `MessageRetrieveSerializer` объявляет поле `created_at`, которого нет в модели
|
||
`Message` — поле показано в схеме как в коде сериализатора, но фактически может
|
||
приводить к ошибке.
|
||
- `RecipientWriteSerializer.extra_kwargs` ссылается на `telegram_user_name`, тогда как
|
||
в модели поле называется `telegram_username`.
|
||
|
||
servers:
|
||
- url: https://api.sarex.io/api/v1
|
||
description: Production (через ЛК)
|
||
- url: https://stage-api.sarex.io/api/v1
|
||
description: Stage
|
||
- url: http://sarex-subscriptions-service.subscriptions-prod/api/v1
|
||
description: Внутрикластерный адрес (ClusterIP)
|
||
|
||
tags:
|
||
- name: subscription
|
||
description: Подписки на уведомления
|
||
- name: recipient
|
||
description: Получатели уведомлений
|
||
- name: event
|
||
description: События уведомлений
|
||
- name: template
|
||
description: Шаблоны уведомлений
|
||
- name: transaction
|
||
description: Транзакции рассылки
|
||
- name: message
|
||
description: Отправленные сообщения
|
||
|
||
security:
|
||
- basicAuth: []
|
||
- {}
|
||
|
||
paths:
|
||
# ==========================================================================
|
||
# Subscription
|
||
# ==========================================================================
|
||
/subscription/:
|
||
get:
|
||
tags: [subscription]
|
||
summary: Список подписок
|
||
operationId: listSubscriptions
|
||
parameters:
|
||
- $ref: '#/components/parameters/Limit'
|
||
- $ref: '#/components/parameters/Offset'
|
||
- name: user_id
|
||
in: query
|
||
description: ID пользователя получателя (фильтр по recipient.user_id)
|
||
schema: { type: integer }
|
||
- name: company_id
|
||
in: query
|
||
description: ID компании; список значений через запятую
|
||
schema: { type: string }
|
||
- name: service_name
|
||
in: query
|
||
description: Название сервиса; список значений через запятую
|
||
schema: { type: string }
|
||
- name: instance_id
|
||
in: query
|
||
description: ID сущности; список значений через запятую
|
||
schema: { type: string }
|
||
- name: instance_uid
|
||
in: query
|
||
description: UUID сущности; список значений через запятую
|
||
schema: { type: string }
|
||
- name: model_name
|
||
in: query
|
||
description: Название модели (точное совпадение)
|
||
schema: { type: string }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema:
|
||
allOf:
|
||
- $ref: '#/components/schemas/PaginatedList'
|
||
- type: object
|
||
properties:
|
||
results:
|
||
type: array
|
||
items: { $ref: '#/components/schemas/Subscription' }
|
||
post:
|
||
tags: [subscription]
|
||
summary: Создать подписку
|
||
description: |
|
||
Требуется хотя бы одно из полей `instance_id` / `instance_uid` /
|
||
`public_instance_id`. Если подписка с таким `instance_uid`/`instance_id`
|
||
(+ `model_name`, `recipient`) уже существует — возвращается существующая.
|
||
Если `period` не передан — устанавливается `TwoTimesDay` c `first_time=9:00`,
|
||
`second_time=17:00`.
|
||
operationId: createSubscription
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/SubscriptionWrite' }
|
||
responses:
|
||
'201':
|
||
description: Created
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/Subscription' }
|
||
'400':
|
||
$ref: '#/components/responses/ValidationError'
|
||
|
||
/subscription/{id}/:
|
||
parameters:
|
||
- $ref: '#/components/parameters/PathId'
|
||
get:
|
||
tags: [subscription]
|
||
summary: Получить подписку
|
||
operationId: retrieveSubscription
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/Subscription' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
delete:
|
||
tags: [subscription]
|
||
summary: Удалить подписку
|
||
operationId: destroySubscription
|
||
responses:
|
||
'204': { description: No Content }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
|
||
# ==========================================================================
|
||
# Recipient
|
||
# ==========================================================================
|
||
/recipient/:
|
||
get:
|
||
tags: [recipient]
|
||
summary: Список получателей
|
||
operationId: listRecipients
|
||
parameters:
|
||
- $ref: '#/components/parameters/Limit'
|
||
- $ref: '#/components/parameters/Offset'
|
||
- name: user_id
|
||
in: query
|
||
description: ID пользователя; список значений через запятую
|
||
schema: { type: string }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema:
|
||
allOf:
|
||
- $ref: '#/components/schemas/PaginatedList'
|
||
- type: object
|
||
properties:
|
||
results:
|
||
type: array
|
||
items: { $ref: '#/components/schemas/Recipient' }
|
||
post:
|
||
tags: [recipient]
|
||
summary: Создать получателя
|
||
operationId: createRecipient
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/RecipientWrite' }
|
||
responses:
|
||
'201':
|
||
description: Created
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/Recipient' }
|
||
'400':
|
||
$ref: '#/components/responses/ValidationError'
|
||
|
||
/recipient/{user_id}/:
|
||
description: |
|
||
Поиск объекта выполняется по полю `user_id` (`lookup_field = "user_id"`),
|
||
а не по первичному ключу `id`.
|
||
parameters:
|
||
- name: user_id
|
||
in: path
|
||
required: true
|
||
description: ID пользователя (lookup_field)
|
||
schema: { type: integer }
|
||
get:
|
||
tags: [recipient]
|
||
summary: Получить получателя
|
||
operationId: retrieveRecipient
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/Recipient' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
put:
|
||
tags: [recipient]
|
||
summary: Обновить получателя
|
||
description: Поля `email`, `user_id`, `id` доступны только на чтение и не изменяются.
|
||
operationId: updateRecipient
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/RecipientUpdate' }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/Recipient' }
|
||
'400': { $ref: '#/components/responses/ValidationError' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
patch:
|
||
tags: [recipient]
|
||
summary: Частично обновить получателя
|
||
operationId: partialUpdateRecipient
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/RecipientUpdate' }
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/Recipient' }
|
||
'400': { $ref: '#/components/responses/ValidationError' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
delete:
|
||
tags: [recipient]
|
||
summary: Удалить получателя
|
||
operationId: destroyRecipient
|
||
responses:
|
||
'204': { description: No Content }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
|
||
# ==========================================================================
|
||
# NotificationEvent
|
||
# ==========================================================================
|
||
/event/:
|
||
get:
|
||
tags: [event]
|
||
summary: Список событий
|
||
operationId: listEvents
|
||
parameters:
|
||
- $ref: '#/components/parameters/Limit'
|
||
- $ref: '#/components/parameters/Offset'
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema:
|
||
allOf:
|
||
- $ref: '#/components/schemas/PaginatedList'
|
||
- type: object
|
||
properties:
|
||
results:
|
||
type: array
|
||
items: { $ref: '#/components/schemas/NotificationEvent' }
|
||
post:
|
||
tags: [event]
|
||
summary: Зарегистрировать событие
|
||
operationId: createEvent
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/NotificationEventWrite' }
|
||
responses:
|
||
'201':
|
||
description: Created
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/NotificationEvent' }
|
||
'400': { $ref: '#/components/responses/ValidationError' }
|
||
|
||
/event/{id}/:
|
||
parameters:
|
||
- $ref: '#/components/parameters/PathId'
|
||
get:
|
||
tags: [event]
|
||
summary: Получить событие
|
||
operationId: retrieveEvent
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/NotificationEvent' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
|
||
# ==========================================================================
|
||
# NotificationTemplate
|
||
# ==========================================================================
|
||
/template/:
|
||
get:
|
||
tags: [template]
|
||
summary: Список шаблонов
|
||
operationId: listTemplates
|
||
parameters:
|
||
- $ref: '#/components/parameters/Limit'
|
||
- $ref: '#/components/parameters/Offset'
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema:
|
||
allOf:
|
||
- $ref: '#/components/schemas/PaginatedList'
|
||
- type: object
|
||
properties:
|
||
results:
|
||
type: array
|
||
items: { $ref: '#/components/schemas/NotificationTemplate' }
|
||
post:
|
||
tags: [template]
|
||
summary: Создать шаблон
|
||
operationId: createTemplate
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/NotificationTemplateWrite' }
|
||
responses:
|
||
'201':
|
||
description: Created
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/NotificationTemplate' }
|
||
'400': { $ref: '#/components/responses/ValidationError' }
|
||
|
||
/template/{id}/:
|
||
parameters:
|
||
- $ref: '#/components/parameters/PathId'
|
||
get:
|
||
tags: [template]
|
||
summary: Получить шаблон
|
||
operationId: retrieveTemplate
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/NotificationTemplate' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
|
||
# ==========================================================================
|
||
# NotificationTransaction (read-only)
|
||
# ==========================================================================
|
||
/transaction/:
|
||
get:
|
||
tags: [transaction]
|
||
summary: Список транзакций рассылки
|
||
operationId: listTransactions
|
||
parameters:
|
||
- $ref: '#/components/parameters/Limit'
|
||
- $ref: '#/components/parameters/Offset'
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema:
|
||
allOf:
|
||
- $ref: '#/components/schemas/PaginatedList'
|
||
- type: object
|
||
properties:
|
||
results:
|
||
type: array
|
||
items: { $ref: '#/components/schemas/NotificationTransaction' }
|
||
|
||
/transaction/{id}/:
|
||
parameters:
|
||
- $ref: '#/components/parameters/PathId'
|
||
get:
|
||
tags: [transaction]
|
||
summary: Получить транзакцию
|
||
description: >
|
||
Внимание: для действия retrieve не задан сериализатор
|
||
(`serializer_class_by_action` содержит только `list`/`get`), поведение
|
||
одиночного объекта может отличаться.
|
||
operationId: retrieveTransaction
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/NotificationTransaction' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
|
||
# ==========================================================================
|
||
# Message (read-only)
|
||
# ==========================================================================
|
||
/message/:
|
||
get:
|
||
tags: [message]
|
||
summary: Список сообщений
|
||
operationId: listMessages
|
||
parameters:
|
||
- $ref: '#/components/parameters/Limit'
|
||
- $ref: '#/components/parameters/Offset'
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema:
|
||
allOf:
|
||
- $ref: '#/components/schemas/PaginatedList'
|
||
- type: object
|
||
properties:
|
||
results:
|
||
type: array
|
||
items: { $ref: '#/components/schemas/Message' }
|
||
|
||
/message/{id}/:
|
||
parameters:
|
||
- $ref: '#/components/parameters/PathId'
|
||
get:
|
||
tags: [message]
|
||
summary: Получить сообщение
|
||
operationId: retrieveMessage
|
||
responses:
|
||
'200':
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/Message' }
|
||
'404': { $ref: '#/components/responses/NotFound' }
|
||
|
||
# ============================================================================
|
||
components:
|
||
securitySchemes:
|
||
basicAuth:
|
||
type: http
|
||
scheme: basic
|
||
|
||
parameters:
|
||
Limit:
|
||
name: limit
|
||
in: query
|
||
description: Кол-во объектов на странице (LimitOffsetPagination)
|
||
schema: { type: integer, default: 1000 }
|
||
Offset:
|
||
name: offset
|
||
in: query
|
||
description: Смещение от начала выборки
|
||
schema: { type: integer, minimum: 0 }
|
||
PathId:
|
||
name: id
|
||
in: path
|
||
required: true
|
||
description: Первичный ключ объекта
|
||
schema: { type: integer }
|
||
|
||
responses:
|
||
NotFound:
|
||
description: Объект не найден
|
||
content:
|
||
application/json:
|
||
schema: { $ref: '#/components/schemas/Detail' }
|
||
ValidationError:
|
||
description: Ошибка валидации
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
additionalProperties: true
|
||
example:
|
||
field_name: ["Обязательное поле."]
|
||
|
||
schemas:
|
||
# ---- Общие ----
|
||
PaginatedList:
|
||
type: object
|
||
properties:
|
||
count: { type: integer, example: 42 }
|
||
next:
|
||
type: string
|
||
format: uri
|
||
nullable: true
|
||
example: "http://host/api/v1/subscription/?limit=1000&offset=1000"
|
||
previous:
|
||
type: string
|
||
format: uri
|
||
nullable: true
|
||
results:
|
||
type: array
|
||
items: {}
|
||
required: [count, results]
|
||
|
||
Detail:
|
||
type: object
|
||
properties:
|
||
detail: { type: string, example: "Не найдено." }
|
||
|
||
# ---- Перечисления ----
|
||
PeriodType:
|
||
type: integer
|
||
description: |
|
||
Периодичность рассылки:
|
||
* 0 — Два раза в день
|
||
* 1 — Каждый день (default)
|
||
* 2 — Один раз в неделю
|
||
* 3 — Сразу при изменении
|
||
enum: [0, 1, 2, 3]
|
||
default: 1
|
||
|
||
WeekdayType:
|
||
type: integer
|
||
description: |
|
||
День недели: 1 — Пн, 2 — Вт, 3 — Ср, 4 — Чт, 5 — Пт, 6 — Сб, 7 — Вс
|
||
enum: [1, 2, 3, 4, 5, 6, 7]
|
||
nullable: true
|
||
|
||
EventType:
|
||
type: integer
|
||
description: |
|
||
Тип события: 0 — Edit (default), 1 — Create, 2 — Delete, 3 — Copy, 4 — Read
|
||
enum: [0, 1, 2, 3, 4]
|
||
default: 0
|
||
|
||
MessageServiceType:
|
||
type: integer
|
||
description: "Способ отправки: 0 — Email (default), 1 — Telegram"
|
||
enum: [0, 1]
|
||
default: 0
|
||
|
||
TransactionStatus:
|
||
type: integer
|
||
description: |
|
||
Статус транзакции: 0 — Новая, 1 — В обработке, 2 — Успешно, 3 — Ошибка
|
||
enum: [0, 1, 2, 3]
|
||
|
||
# ---- Subscription ----
|
||
Subscription:
|
||
type: object
|
||
properties:
|
||
id: { type: integer, readOnly: true }
|
||
model_name: { type: string, maxLength: 255 }
|
||
instance_id: { type: integer, nullable: true }
|
||
instance_uid: { type: string, format: uuid, nullable: true }
|
||
public_instance_id: { type: string, format: uuid, nullable: true }
|
||
company_id: { type: integer }
|
||
service_name: { type: string, maxLength: 255, nullable: true }
|
||
recipient:
|
||
type: integer
|
||
description: ID получателя (FK recipient.Recipient)
|
||
period: { $ref: '#/components/schemas/PeriodType' }
|
||
weekday: { $ref: '#/components/schemas/WeekdayType' }
|
||
first_time: { type: string, maxLength: 6, nullable: true, example: "9:00" }
|
||
second_time: { type: string, maxLength: 6, nullable: true, example: "17:00" }
|
||
created_at: { type: string, format: date-time, readOnly: true }
|
||
updated_at: { type: string, format: date-time, readOnly: true }
|
||
required: [id, model_name, company_id, recipient]
|
||
|
||
SubscriptionWrite:
|
||
type: object
|
||
description: >
|
||
Одно из instance_id / instance_uid / public_instance_id обязательно.
|
||
properties:
|
||
model_name: { type: string, maxLength: 255 }
|
||
instance_id: { type: integer, nullable: true }
|
||
instance_uid: { type: string, format: uuid, nullable: true }
|
||
public_instance_id: { type: string, format: uuid, nullable: true }
|
||
company_id: { type: integer }
|
||
service_name: { type: string, maxLength: 255, nullable: true }
|
||
recipient: { type: integer, description: ID получателя }
|
||
period:
|
||
allOf: [{ $ref: '#/components/schemas/PeriodType' }]
|
||
nullable: true
|
||
weekday: { $ref: '#/components/schemas/WeekdayType' }
|
||
first_time: { type: string, maxLength: 6, nullable: true }
|
||
second_time: { type: string, maxLength: 6, nullable: true }
|
||
required: [model_name, company_id, recipient]
|
||
|
||
# ---- Recipient ----
|
||
Recipient:
|
||
type: object
|
||
properties:
|
||
id: { type: integer, readOnly: true }
|
||
first_name: { type: string, maxLength: 1024 }
|
||
last_name: { type: string, maxLength: 1024 }
|
||
phone: { type: string, maxLength: 128, default: "" }
|
||
telegram_chat_id: { type: string, maxLength: 128, nullable: true }
|
||
telegram_username: { type: string, maxLength: 256, nullable: true }
|
||
email: { type: string, format: email }
|
||
user_id: { type: integer, nullable: true }
|
||
required: [id]
|
||
|
||
RecipientWrite:
|
||
type: object
|
||
properties:
|
||
first_name: { type: string, maxLength: 1024 }
|
||
last_name: { type: string, maxLength: 1024 }
|
||
phone: { type: string, maxLength: 128 }
|
||
telegram_chat_id: { type: string, maxLength: 128, nullable: true }
|
||
telegram_username: { type: string, maxLength: 256, nullable: true }
|
||
email: { type: string, format: email }
|
||
user_id: { type: integer, nullable: true }
|
||
|
||
RecipientUpdate:
|
||
type: object
|
||
description: "email, user_id, id — только для чтения."
|
||
properties:
|
||
first_name: { type: string, maxLength: 1024 }
|
||
last_name: { type: string, maxLength: 1024 }
|
||
phone: { type: string, maxLength: 128 }
|
||
telegram_chat_id: { type: string, maxLength: 128, nullable: true }
|
||
telegram_username: { type: string, maxLength: 256, nullable: true }
|
||
|
||
# ---- NotificationEvent ----
|
||
NotificationEvent:
|
||
type: object
|
||
properties:
|
||
id: { type: integer, readOnly: true }
|
||
registered_at: { type: string, format: date-time }
|
||
routing_key: { type: string, maxLength: 256, description: "Slug-тег" }
|
||
attributes: { type: object, additionalProperties: true, default: {} }
|
||
short_description: { type: string, maxLength: 512, nullable: true }
|
||
required: [id, registered_at, routing_key]
|
||
|
||
NotificationEventWrite:
|
||
type: object
|
||
properties:
|
||
registered_at: { type: string, format: date-time }
|
||
routing_key: { type: string, maxLength: 256 }
|
||
attributes: { type: object, additionalProperties: true }
|
||
short_description: { type: string, maxLength: 512, nullable: true }
|
||
required: [registered_at, routing_key]
|
||
|
||
# ---- NotificationTemplate ----
|
||
NotificationTemplate:
|
||
type: object
|
||
properties:
|
||
id: { type: integer, readOnly: true }
|
||
name: { type: string, maxLength: 1024 }
|
||
title: { type: string, maxLength: 1024 }
|
||
template_text: { type: string }
|
||
attributes: { type: object, additionalProperties: true, default: {} }
|
||
event_type: { $ref: '#/components/schemas/EventType' }
|
||
model_name: { type: string, maxLength: 255, nullable: true }
|
||
required: [id, name, title, template_text]
|
||
|
||
NotificationTemplateWrite:
|
||
type: object
|
||
properties:
|
||
name: { type: string, maxLength: 1024 }
|
||
title: { type: string, maxLength: 1024 }
|
||
template_text: { type: string }
|
||
attributes: { type: object, additionalProperties: true }
|
||
event_type: { $ref: '#/components/schemas/EventType' }
|
||
model_name: { type: string, maxLength: 255, nullable: true }
|
||
required: [name, title, template_text]
|
||
|
||
# ---- NotificationTransaction ----
|
||
NotificationTransaction:
|
||
type: object
|
||
properties:
|
||
id: { type: integer, readOnly: true }
|
||
status: { $ref: '#/components/schemas/TransactionStatus' }
|
||
started_at: { type: string, format: date-time }
|
||
finished_at: { type: string, format: date-time, nullable: true }
|
||
error_message: { type: string, nullable: true }
|
||
event: { type: integer, nullable: true, description: "ID связанного события" }
|
||
required: [id, status, started_at]
|
||
|
||
# ---- Message ----
|
||
Message:
|
||
type: object
|
||
properties:
|
||
id: { type: integer, readOnly: true }
|
||
created_at:
|
||
type: string
|
||
format: date-time
|
||
description: >
|
||
Объявлено в сериализаторе; в модели Message поле отсутствует (см. замечания).
|
||
service_type: { $ref: '#/components/schemas/MessageServiceType' }
|
||
transaction: { type: integer, description: "ID транзакции (FK)" }
|
||
subject: { type: string, default: "" }
|
||
text: { type: string }
|
||
recipients:
|
||
type: array
|
||
items: { type: integer }
|
||
description: "ID получателей (M2M recipient.Recipient)"
|
||
required: [id, transaction, text]
|