iac/apps/contracts/openapi.yaml

405 lines
14 KiB
YAML
Raw 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: Contracts Service API
version: "1.0.0"
description: |
REST API сервиса **contracts** (`platform/contracts`) — управление
договорами (контрактами): создание (в т.ч. массовое), получение по id,
списочный вывод с фильтрами и пагинацией, обновление.
Сервис написан на Go (**Fiber v3**). Приложение собирается в
`internal/app/http/app.go` (`New`). Роутинг вложен под общий префикс
`/api/v0` (`app.server.Group("/api/v0")`), внутри — группа
`/contracts` (`internal/controller/http/v0`).
### Аутентификация
Все эндпоинты группы `/api/v0/contracts` защищены middleware
(`internal/adapter/auth/middleware.go`). Поддерживаются два режима:
1. **Zitadel** — если передан заголовок `Identity: Bearer <jwt>`,
пользователь берётся из полезной нагрузки этого токена
(`urn:zitadel:iam:user:metadata`). Подпись на уровне приложения
не проверяется (валидность обеспечивается сетевым слоем/Istio).
2. **sarex-backend** — если заголовка `Identity` нет, подпись основного
токена `Authorization: Bearer <jwt>` проверяется публичным RSA-ключом
(`PUBLIC_KEY`).
Корневой эндпоинт `GET /api/v0/` (health/ping) аутентификации не требует.
### Идентификаторы
Идентификатор договора — **ULID** (строка), парсится через
`ulid.Parse`. `resource_id` — **UUID**.
### Пагинация
Списочный вывод использует `limit`/`offset` (query-параметры, по умолчанию
`limit=100`, `offset=0`).
### Обработка ошибок
Ошибки возвращаются с соответствующим HTTP-статусом; тело — либо строка
с описанием, либо `{"error": "..."}` (при внутренней панике). Коды:
`400` — некорректный запрос/невалидный id, `401` — проблемы аутентификации,
`403` — пользователь не состоит в компании (`tenant_id`), `404` — договор
не найден, `500` — внутренняя ошибка, `501` — метод не реализован.
### Замечания (расхождения кода)
- `PATCH` и `DELETE` (как по коллекции, так и по id) возвращают
**`501 Not Implemented`** — обработчики-заглушки.
- `POST /api/v0/contracts` принимает **как одиночный объект, так и массив**:
тип создания выбирается по форме тела (объект → создание одного договора,
массив → пакетное создание).
- `GET /api/v0/contracts` (список) требует query-параметр `tenant_id`
(middleware `UserInCompanyMiddleware` проверяет, что пользователь состоит
в этой компании; иначе `400`/`403`).
servers:
- url: https://api.sarex.io/contracts
description: production
- url: https://stage-api.sarex.io/contracts
description: stage
- url: https://api.preprod.sarex.io/contracts
description: preprod
security:
- bearerAuth: []
tags:
- name: contracts
description: Договоры
- name: service
description: Служебные эндпоинты
paths:
/api/v0/:
get:
tags: [service]
summary: Health / ping
description: Возвращает `200 OK` без тела. Аутентификация не требуется.
security: []
responses:
"200":
description: OK
/api/v0/contracts:
post:
tags: [contracts]
summary: Создать договор или несколько договоров
description: |
Принимает либо одиночный объект `CreateContractRequest`, либо массив
таких объектов. Форма тела определяет режим создания.
requestBody:
required: true
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/CreateContractRequest"
- type: array
items:
$ref: "#/components/schemas/CreateContractRequest"
responses:
"201":
description: Договор(ы) создан(ы)
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/ContractResponse"
- type: array
items:
$ref: "#/components/schemas/ContractResponse"
"400":
description: Некорректное тело запроса / ошибка валидации
"401":
description: Ошибка аутентификации
"500":
description: Внутренняя ошибка
get:
tags: [contracts]
summary: Список договоров
description: |
Возвращает договоры с фильтрацией и пагинацией. Требует `tenant_id`
(проверяется принадлежность пользователя к компании).
parameters:
- name: tenant_id
in: query
required: true
schema:
type: integer
format: int64
description: Идентификатор компании/арендатора
- name: limit
in: query
required: false
schema:
type: integer
format: int64
default: 100
- name: offset
in: query
required: false
schema:
type: integer
format: int64
default: 0
- name: resource_id
in: query
required: false
schema:
type: string
format: uuid
- name: contractor_id
in: query
required: false
schema:
type: integer
format: int64
responses:
"200":
description: Список договоров
content:
application/json:
schema:
$ref: "#/components/schemas/ContractPaginatedResponse"
"400":
description: Некорректные параметры запроса / отсутствует tenant_id
"401":
description: Ошибка аутентификации
"403":
description: Пользователь не состоит в указанной компании
"500":
description: Внутренняя ошибка
put:
tags: [contracts]
summary: Пакетное обновление договоров
requestBody:
required: true
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/UpdateContractRequest"
responses:
"200":
description: Договоры обновлены
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/ContractResponse"
"400":
description: Некорректное тело запроса
"401":
description: Ошибка аутентификации
"500":
description: Внутренняя ошибка
patch:
tags: [contracts]
summary: Пакетное частичное обновление (не реализовано)
responses:
"501":
description: Not Implemented
delete:
tags: [contracts]
summary: Пакетное удаление (не реализовано)
responses:
"501":
description: Not Implemented
/api/v0/contracts/{id}:
parameters:
- name: id
in: path
required: true
schema:
type: string
description: Идентификатор договора (ULID)
get:
tags: [contracts]
summary: Получить договор по id
responses:
"200":
description: Договор
content:
application/json:
schema:
$ref: "#/components/schemas/ContractResponse"
"400":
description: Невалидный id
"401":
description: Ошибка аутентификации
"404":
description: Договор не найден
"500":
description: Внутренняя ошибка
put:
tags: [contracts]
summary: Обновить договор
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/UpdateContractRequest"
responses:
"200":
description: Договор обновлён
content:
application/json:
schema:
$ref: "#/components/schemas/ContractResponse"
"400":
description: Невалидный id / некорректное тело
"401":
description: Ошибка аутентификации
"404":
description: Договор не найден
"500":
description: Внутренняя ошибка
patch:
tags: [contracts]
summary: Частичное обновление (не реализовано)
responses:
"501":
description: Not Implemented
delete:
tags: [contracts]
summary: Удалить договор (не реализовано)
responses:
"501":
description: Not Implemented
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
description: |
Основной токен `Authorization: Bearer <jwt>` (проверяется по RSA-ключу).
Опционально может передаваться заголовок `Identity: Bearer <jwt>`
(режим Zitadel), который имеет приоритет.
schemas:
Contractor:
type: object
description: Произвольный JSON-объект с данными контрагента (в БД — JSONB).
additionalProperties: true
CreateContractRequest:
type: object
required: [number, tenant_id, started_at, deadline_at]
properties:
number:
type: string
minLength: 1
description: Номер договора
tenant_id:
type: integer
format: int64
description: Идентификатор компании/арендатора
resource_id:
type: string
format: uuid
nullable: true
contractor:
$ref: "#/components/schemas/Contractor"
started_at:
type: string
format: date-time
deadline_at:
type: string
format: date-time
cost:
type: number
format: double
minimum: 0
description:
type: string
UpdateContractRequest:
type: object
required: [number, tenant_id, started_at, deadline_at]
properties:
id:
type: string
description: ULID договора
number:
type: string
minLength: 1
tenant_id:
type: integer
format: int64
resource_id:
type: string
format: uuid
nullable: true
contractor:
$ref: "#/components/schemas/Contractor"
started_at:
type: string
format: date-time
deadline_at:
type: string
format: date-time
cost:
type: number
format: double
minimum: 0
description:
type: string
ContractResponse:
type: object
properties:
id:
type: string
description: ULID договора
number:
type: string
tenant_id:
type: integer
format: int64
resource_id:
type: string
format: uuid
nullable: true
contractor:
$ref: "#/components/schemas/Contractor"
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
started_at:
type: string
format: date-time
deadline_at:
type: string
format: date-time
cost:
type: number
format: double
description:
type: string
ContractPaginatedResponse:
type: object
properties:
count:
type: integer
format: int64
limit:
type: integer
format: int64
offset:
type: integer
format: int64
results:
type: array
items:
$ref: "#/components/schemas/ContractResponse"