openapi: 3.0.3 info: title: Comparisons Service API version: "1.0.0" description: | REST API сервиса **comparisons-backend** (`pdm/comparisons-backend`) — создание и просмотр сравнений (облако-BIM отклонение/статусы, облако-облако, облако-поверхность, pdf-pdf), их элементов, изменений и фильтров. Сервис написан на Go (роутер **gorilla/mux**). Сервер собирается функцией `bootstrapAPI` в `cmd/api/bootstrap.go`. Роутинг состоит из двух групп: - публичный API — префикс `/api/v1` (`cmd/api/routes_api.go`); - внутренний API — префикс `/internal/v1` (`cmd/api/routes_internal.go`), предназначен для вызовов внутри кластера (через ingress не публикуется). ### Аутентификация Публичные эндпоинты (`/api/v1/*`) требуют JWT: middleware `auth.JWTToCtx` + `auth.JWTUserExtractorFromCtx` (`gitlab.com/sarex-team/gotools/auth`). Токен передаётся заголовком `Authorization: Bearer `; middleware `auth.DeleteJWTFromQueryMiddleware` дополнительно позволяет передать токен query-параметром `jwt` (он удаляется из запроса после разбора). Внутренние эндпоинты (`/internal/v1/*`) аутентификации на уровне приложения не требуют — ограничение доступа обеспечивается сетевым слоем. ### Обработка ошибок Ошибки возвращаются функцией `httperror.Write` (`gotools/httperror`). Основные статусы: `400` — некорректный запрос/параметры, `404` — сравнение не найдено, `500` — внутренняя ошибка. Идентификатор запроса пробрасывается middleware `reqid`. ### Замечания (расхождения кода) - `GET /api/v1/comparisons` возвращает данные разной формы в зависимости от query-параметра: `workspace_id` → `{ comparisons: [] }`, `document_id` → `{ results: [] }`, `bundle_id` → одиночный объект сравнения. - `GET /api/v1/filter_fields` и `GET /api/v1/elements` работают только со сравнениями типа `deviation`; для других типов вернётся `400`. - Схема сравнения полиморфна по полю `type` (`deviation`/`c2c`/`c2s`/`abap`/`pdf2pdf`). contact: name: comparisons-backend url: https://gitlab.com/sarex-team/comparisons-backend servers: - url: https://api.sarex.io/comparisons description: Production (ingress) - url: https://stage-api.sarex.io/comparisons description: Stage (ingress) - url: https://api.preprod.sarex.io/comparisons description: Preprod (ingress) - url: http://comparisons-backend-service.comparisons-stage description: Внутрикластерный адрес (ClusterIP). Единственный способ достучаться до /internal/v1 - url: http://localhost:8080 description: Локальный запуск (API_ADDRESS по умолчанию 0.0.0.0:8080) tags: - name: types description: Справочник типов сравнений - name: comparisons description: Сравнения — создание, просмотр, удаление - name: elements description: Элементы сравнения и их изменения - name: internal description: Внутренние эндпоинты (только внутри кластера) security: - bearerAuth: [] paths: # ========================================================================== # Types # ========================================================================== /api/v1/types: get: tags: [types] summary: Справочник типов сравнений description: Возвращает доступные типы сравнений и их параметры (для построения форм). operationId: getTypes responses: '200': description: Список типов сравнений content: application/json: schema: type: object properties: types: type: array items: $ref: '#/components/schemas/CompareType' # ========================================================================== # Comparisons # ========================================================================== /api/v1/comparisons: post: tags: [comparisons] summary: Создать сравнение description: | Создаёт сравнение указанного типа. Автор берётся из JWT. Если `parent_doc_id` и `parent_disk_id` не заданы — вычисляются по рабочей области. operationId: createComparison requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateRequest' responses: '200': description: Созданное сравнение content: application/json: schema: $ref: '#/components/schemas/Comparison' '400': description: Некорректный запрос / неизвестный тип сравнения '500': description: Внутренняя ошибка get: tags: [comparisons] summary: Список сравнений description: | Возвращает сравнения по одному из query-параметров. Форма ответа зависит от параметра (см. описание в `info`). Должен быть задан ровно один параметр. operationId: getComparisons parameters: - name: workspace_id in: query required: false schema: type: string format: uuid description: "Сравнения рабочей области → ответ `{ comparisons: [] }`" - name: bundle_id in: query required: false schema: type: string format: uuid description: Сравнение по бандлу → ответ — одиночный объект - name: document_id in: query required: false schema: type: integer format: int64 description: "Сравнения документа → ответ `{ results: [] }`" responses: '200': description: Результат (форма зависит от параметра запроса) content: application/json: schema: oneOf: - type: object properties: comparisons: type: array items: $ref: '#/components/schemas/Comparison' - type: object properties: results: type: array items: $ref: '#/components/schemas/Comparison' - $ref: '#/components/schemas/Comparison' '400': description: Не задан workspace_id / bundle_id / document_id '500': description: Внутренняя ошибка /api/v1/comparisons/{comparison_id}: parameters: - name: comparison_id in: path required: true schema: type: integer format: int64 get: tags: [comparisons] summary: Сравнение по id operationId: getComparisonById responses: '200': description: Сравнение content: application/json: schema: $ref: '#/components/schemas/Comparison' '404': description: Сравнение не найдено '500': description: Внутренняя ошибка delete: tags: [comparisons] summary: Удалить сравнение description: Мягкое удаление сравнения. Автор берётся из JWT. operationId: deleteComparisonById responses: '200': description: Успешно удалено '400': description: Некорректный id '500': description: Внутренняя ошибка /api/v1/filter_fields: get: tags: [comparisons] summary: Поля фильтрации сравнения description: | Возвращает набор полей фильтрации для сравнения типа `deviation` по документу. Для полей отклонения (`deviation`, `deviation_x/y/z`) заполняются `min`/`max`. Если сравнение по документу не найдено — вернётся пустой `results`. operationId: getFilterFields parameters: - name: doc_id in: query required: true schema: type: integer format: int64 responses: '200': description: Поля фильтрации content: application/json: schema: type: object properties: results: type: array items: $ref: '#/components/schemas/FilterFields' '400': description: Не задан doc_id / сравнение не типа deviation '500': description: Внутренняя ошибка /api/v1/tolerance: get: tags: [comparisons] summary: Допуск (tolerance) сравнения description: Возвращает значение допустимого отклонения для сравнения типа `deviation` по бандлу. operationId: getTolerance parameters: - name: bundle_id in: query required: true schema: type: string format: uuid responses: '200': description: Допуск content: application/json: schema: type: object properties: tolerance: type: number format: double '400': description: Не задан / некорректный bundle_id '404': description: Сравнение не найдено '500': description: Внутренняя ошибка # ========================================================================== # Elements # ========================================================================== /api/v1/elements: get: tags: [elements] summary: Список элементов сравнения description: | Постранично возвращает элементы сравнения типа `deviation` по документу. Параметры отклонения задаются строкой вида `min:max`. Если сравнение не найдено — вернётся пустой `results` с типами по умолчанию. operationId: getListElements parameters: - name: doc_id in: query required: true schema: type: integer format: int64 - name: limit in: query required: false schema: type: integer default: 10 minimum: 0 - name: offset in: query required: false schema: type: integer default: 0 minimum: 0 - name: order_by in: query required: false schema: type: string enum: [deviation, deviation_x, deviation_y, deviation_z, sarex_id, name] default: sarex_id - name: order in: query required: false schema: type: string enum: [asc, desc] default: asc - name: deviation in: query required: false schema: type: string description: Диапазон общего отклонения в формате `min:max` - name: deviation_x in: query required: false schema: type: string description: Диапазон отклонения по X в формате `min:max` - name: deviation_y in: query required: false schema: type: string description: Диапазон отклонения по Y в формате `min:max` - name: deviation_z in: query required: false schema: type: string description: Диапазон отклонения по Z в формате `min:max` - name: deviation_status in: query required: false schema: type: string enum: [in_tolerance, out_of_tolerance] - name: view_status in: query required: false schema: type: string enum: [viewed, not_viewed] - name: only_with_comment in: query required: false schema: type: boolean responses: '200': description: Страница элементов content: application/json: schema: $ref: '#/components/schemas/ListElementsResponse' '400': description: Некорректные параметры запроса '500': description: Внутренняя ошибка /api/v1/elements/{element_id}: parameters: - name: element_id in: path required: true schema: type: integer format: int64 patch: tags: [elements] summary: Обновить элемент description: | Обновляет один из атрибутов элемента: комментарий, статус отклонения или статус просмотра. Должно быть задано ровно одно из полей. Изменение фиксируется записью в истории изменений. operationId: updateElement requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdateElementRequest' responses: '200': description: Успешно обновлено (тело не возвращается) '400': description: Некорректный запрос '500': description: Внутренняя ошибка /api/v1/elements/{element_id}/changes: get: tags: [elements] summary: История изменений элемента operationId: getElementChanges parameters: - name: element_id in: path required: true schema: type: integer format: int64 responses: '200': description: Список изменений content: application/json: schema: type: object properties: changes: type: array items: $ref: '#/components/schemas/Change' '400': description: Некорректный element_id '500': description: Внутренняя ошибка # ========================================================================== # Internal # ========================================================================== /internal/v1/comparisons/{comparison_id}/webhook: post: tags: [internal] summary: Webhook результата сравнения (внутренний) description: | Вызывается после завершения обработки сравнения типа `deviation`. Скачивает `deviation_json` из PDM-хранилища по бандлу, создаёт элементы сравнения и возвращает содержимое `deviation_json`. Аутентификация на уровне приложения не требуется. operationId: comparisonWebhook security: [] parameters: - name: comparison_id in: path required: true schema: type: integer format: int64 responses: '200': description: Содержимое deviation_json content: application/json: schema: $ref: '#/components/schemas/DeviationJSON' '404': description: Сравнение не найдено '500': description: Внутренняя ошибка components: securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: JWT description: | JWT в заголовке `Authorization: Bearer `. Допускается передача query-параметром `jwt` (удаляется middleware после разбора). schemas: ComparisonType: type: string enum: [c2c, c2s, deviation, abap, pdf2pdf] CompareType: type: object description: Тип сравнения и набор его параметров (для формы создания). properties: name: $ref: '#/components/schemas/ComparisonType' verbose_name: type: string params: type: array items: $ref: '#/components/schemas/CompareFormParam' CompareFormParam: type: object properties: name: type: string verbose_name: type: string hint: type: string source: type: string enum: [form, viewer] type: type: string description: Тип поля (напр. cloud, bimv2, number, multichoice, choice, array, pdf, surface) options: type: array items: type: object additionalProperties: true default: {} min: type: number format: double max: type: number format: double CreateRequest: type: object required: [name, workspace_id, type, company_id, params] properties: name: type: string workspace_id: type: string format: uuid parent_doc_id: type: integer format: int64 nullable: true parent_disk_id: type: string format: uuid nullable: true type: $ref: '#/components/schemas/ComparisonType' company_id: type: integer format: int64 params: type: object description: Параметры сравнения, зависят от type (см. /api/v1/types). additionalProperties: true Comparison: type: object description: | Полиморфное сравнение. Поле `type` определяет набор `params`. Ниже приведён пример для типа `deviation`; для других типов набор params отличается. properties: id: type: integer format: int64 type: $ref: '#/components/schemas/ComparisonType' created_by: type: integer format: int64 name: type: string workspace_id: type: string format: uuid workflow_id: type: string format: uuid nullable: true document_id: type: integer format: int64 nullable: true bundle_id: type: string format: uuid nullable: true params: $ref: '#/components/schemas/DeviationParams' DeviationParams: type: object properties: cloud_bundle_id: type: string format: uuid model_bundle_id: type: string format: uuid down_sample_size: type: number format: double resolution: type: number format: double linear_margin: type: number format: double tolerance: type: number format: double sarex_ids: type: array items: type: integer format: int64 transformation: type: array items: type: number format: double applied_statuses: type: array items: type: string crop_margin: type: number format: double nullable: true Element: type: object properties: id: type: integer format: int64 sarex_id: type: integer format: int64 name: type: string deviation_status: type: string enum: [in_tolerance, out_of_tolerance] view_status: type: string enum: [viewed, not_viewed] deviation: type: number format: double deviation_x: type: number format: double deviation_y: type: number format: double deviation_z: type: number format: double axis_and_angle: type: object description: Ось и угол поворота (utils.AxisAndAngle) additionalProperties: true comment: type: string nullable: true bbox_matrix: type: array items: type: number format: double reg_matrix: type: array items: type: number format: double ListElementsResponse: type: object properties: count: type: integer next: type: string nullable: true previous: type: string nullable: true types: type: object additionalProperties: type: array items: $ref: '#/components/schemas/Option' crop_margin: type: number format: double nullable: true results: type: array items: $ref: '#/components/schemas/Element' UpdateElementRequest: type: object description: | Должно быть задано ровно одно из полей (`comment`, `deviation_status`, `view_status`) — валидатор `required_without_all`. properties: comment: type: string deviation_status: type: string enum: [in_tolerance, out_of_tolerance] view_status: type: string enum: [viewed, not_viewed] Change: type: object properties: id: type: integer format: int64 element_id: type: integer format: int64 created_at: type: string format: date-time author: type: integer format: int64 changed_field: type: string enum: [deviation_status, view_status, comment] old_value: type: string new_value: type: string FilterFields: type: object properties: name: type: string enum: [deviation, deviation_x, deviation_y, deviation_z, deviation_status, view_status, only_with_comment] verbose_name: type: string type: type: string enum: [checkbox, selector, slider] options: type: array items: $ref: '#/components/schemas/Option' min: type: number format: double nullable: true max: type: number format: double nullable: true Option: type: object properties: name: type: string verbose_name: type: string DeviationJSON: type: object properties: crop_margin: type: number format: double nodes: type: object additionalProperties: $ref: '#/components/schemas/Node' Node: type: object properties: bbox_matrix: type: array items: type: number format: double reg_matrix: type: array items: type: number format: double node_name: type: string