diff --git a/.gitignore b/.gitignore index c538573..39e4cd8 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ config.local.json # editor settings settings.json +example/ # ---> macOS # General diff --git a/FINAL_STATUS.md b/docs/FINAL_STATUS.md similarity index 100% rename from FINAL_STATUS.md rename to docs/FINAL_STATUS.md diff --git a/IMPLEMENTATION_STATUS.md b/docs/IMPLEMENTATION_STATUS.md similarity index 100% rename from IMPLEMENTATION_STATUS.md rename to docs/IMPLEMENTATION_STATUS.md diff --git a/docs/INTEGRATION_GUIDE.md b/docs/INTEGRATION_GUIDE.md index 5050620..5553df9 100644 --- a/docs/INTEGRATION_GUIDE.md +++ b/docs/INTEGRATION_GUIDE.md @@ -18,6 +18,10 @@ ## Формат JSON для импорта +> Важно: endpoint использует строгий JSON-декодер (`DisallowUnknownFields`). +> Любое неизвестное поле (включая вложенные объекты) приведет к `400 Bad Request`. +> Используйте только `snake_case` ключи из этого руководства. + ### Структура верхнего уровня ```json @@ -42,7 +46,8 @@ ### Обязательные поля верхнего уровня - `collected_at` (string RFC3339, обязательно) - время сбора информации -- `target_host` (string, обязательно) - IP или hostname сервера +- `target_host` (string, опционально) - IP или hostname сервера +- `hardware.board.serial_number` (string, обязательно) - серийный номер сервера/платы - `source_type` (string, опционально) - тип источника: `api`, `logfile`, `manual` - `protocol` (string, опционально) - протокол сбора: `redfish`, `ipmi`, `snmp`, `ssh` - `filename` (string, опционально) - идентификатор источника данных @@ -532,7 +537,7 @@ ### 1. Валидация входных данных -- Проверка наличия обязательных полей: `collected_at`, `target_host`, `hardware` +- Проверка наличия обязательных полей: `collected_at`, `hardware.board.serial_number` - Проверка формата `collected_at` (RFC3339) - Проверка наличия `hardware.board.serial_number` @@ -545,7 +550,7 @@ vendor_serial = board.serial_number vendor = board.manufacturer (если != "NULL") model = board.product_name (если != "NULL") -name = target_host (если asset новый) или сохраняется существующее +name = target_host (если передан), иначе `hardware.board.serial_number` ``` **Примечание:** Asset должен быть связан с Project. Если project_id не задан, используется default project для данного импорта. @@ -660,7 +665,7 @@ firmware_version = {new version} ## API Endpoint для импорта ```http -POST /api/v1/ingest/hardware +POST /ingest/hardware Content-Type: application/json { @@ -675,17 +680,18 @@ Content-Type: application/json ```json { "status": "success", - "bundle_id": 12345, - "asset_id": 67, + "bundle_id": "lb_01J...", + "asset_id": "mach_01J...", "collected_at": "2026-02-10T15:30:00Z", "duplicate": false, "summary": { - "components_observed": 15, - "components_created": 2, - "components_updated": 13, + "parts_observed": 15, + "parts_created": 2, + "parts_updated": 13, "installations_created": 2, "installations_closed": 1, - "timeline_events_created": 5 + "timeline_events_created": 9, + "failure_events_created": 1 } } ``` @@ -695,8 +701,8 @@ Content-Type: application/json ```json { "status": "success", - "bundle_id": 12345, - "asset_id": 67, + "bundle_id": "lb_01J...", + "asset_id": "mach_01J...", "collected_at": "2026-02-10T15:30:00Z", "duplicate": true, "message": "LogBundle with this content hash already exists" @@ -716,6 +722,13 @@ Content-Type: application/json } ``` +### Частые причины `400 Bad Request` + +- Лишние поля в JSON (даже глубоко во вложенных объектах). +- Неверное имя ключа (например, `targetHost` вместо `target_host`). +- Неверный формат даты (`collected_at` должен быть RFC3339). +- Пустой `hardware.board.serial_number`. + --- ## Правила автоопределения LOT diff --git a/STRING_ID_MIGRATION_STATUS.md b/docs/STRING_ID_MIGRATION_STATUS.md similarity index 100% rename from STRING_ID_MIGRATION_STATUS.md rename to docs/STRING_ID_MIGRATION_STATUS.md diff --git a/TODO.md b/docs/TODO.md similarity index 100% rename from TODO.md rename to docs/TODO.md diff --git a/internal/api/ingest.go b/internal/api/ingest.go index f667ef4..6b37973 100644 --- a/internal/api/ingest.go +++ b/internal/api/ingest.go @@ -155,10 +155,6 @@ func (h ingestHandlers) handleHardware(w http.ResponseWriter, r *http.Request) { } targetHost := strings.TrimSpace(req.TargetHost) - if targetHost == "" { - writeValidationError(w, "target_host", "target_host is required") - return - } if strings.TrimSpace(req.CollectedAt) == "" { writeValidationError(w, "collected_at", "collected_at is required") return