feat(iot): PATCH обновляет metadata (Terraform)

iotDeviceUpdateRequest получил поле metadata; UpdateIoTDevice обновляет переданные
поля (enabled и/или metadata). Добавлен store.UpdateMetadata. Хотя бы одно поле
обязательно. Раньше metadata нельзя было изменить (только enabled).
This commit is contained in:
Repinoid
2026-10-10 19:18:01 +03:00
parent 38e3f65c21
commit 59bd743c87
2 changed files with 44 additions and 11 deletions
+22 -7
View File
@@ -29,9 +29,11 @@ type iotDeviceCreateRequest struct {
Metadata map[string]string `json:"metadata,omitempty"` Metadata map[string]string `json:"metadata,omitempty"`
} }
// iotDeviceUpdateRequest — тело PATCH. // iotDeviceUpdateRequest — тело PATCH. Хотя бы одно поле обязательно:
// enabled и/или metadata (metadata раньше нельзя было обновить — ревью 2026-10-10).
type iotDeviceUpdateRequest struct { type iotDeviceUpdateRequest struct {
Enabled *bool `json:"enabled"` Enabled *bool `json:"enabled,omitempty"`
Metadata map[string]string `json:"metadata,omitempty"`
} }
// iotDeviceResponse — ответ API. MQTTPassword заполняется ТОЛЬКО при создании // iotDeviceResponse — ответ API. MQTTPassword заполняется ТОЛЬКО при создании
@@ -314,21 +316,34 @@ func (h *Handler) UpdateIoTDevice(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusBadRequest, errResp("invalid JSON: "+err.Error())) writeJSON(w, http.StatusBadRequest, errResp("invalid JSON: "+err.Error()))
return return
} }
if req.Enabled == nil { if req.Enabled == nil && req.Metadata == nil {
writeJSON(w, http.StatusBadRequest, errResp("enabled field is required")) writeJSON(w, http.StatusBadRequest, errResp("enabled или metadata обязательны"))
return return
} }
err := h.Devices.UpdateEnabled(r.Context(), ns, name, *req.Enabled) // Обновляем только переданные поля (enabled и/или metadata).
if err != nil { if req.Enabled != nil {
if err := h.Devices.UpdateEnabled(r.Context(), ns, name, *req.Enabled); err != nil {
if errors.Is(err, store.ErrNotFound) { if errors.Is(err, store.ErrNotFound) {
writeJSON(w, http.StatusNotFound, errResp("iot device not found")) writeJSON(w, http.StatusNotFound, errResp("iot device not found"))
return return
} }
h.Log.Error("update device", "namespace", ns, "name", name, "err", err) h.Log.Error("update device enabled", "namespace", ns, "name", name, "err", err)
writeJSON(w, http.StatusInternalServerError, errResp("failed to update device")) writeJSON(w, http.StatusInternalServerError, errResp("failed to update device"))
return return
} }
}
if req.Metadata != nil {
if err := h.Devices.UpdateMetadata(r.Context(), ns, name, req.Metadata); err != nil {
if errors.Is(err, store.ErrNotFound) {
writeJSON(w, http.StatusNotFound, errResp("iot device not found"))
return
}
h.Log.Error("update device metadata", "namespace", ns, "name", name, "err", err)
writeJSON(w, http.StatusInternalServerError, errResp("failed to update device"))
return
}
}
device, err := h.Devices.GetByName(r.Context(), ns, name) device, err := h.Devices.GetByName(r.Context(), ns, name)
if err != nil { if err != nil {
+18
View File
@@ -97,6 +97,24 @@ func (s *DeviceStore) UpdateEnabled(ctx context.Context, namespace, name string,
return nil return nil
} }
// UpdateMetadata обновляет metadata устройства.
func (s *DeviceStore) UpdateMetadata(ctx context.Context, namespace, name string, metadata map[string]string) error {
b, err := json.Marshal(metadata)
if err != nil {
return fmt.Errorf("store: marshal metadata: %w", err)
}
res, err := s.db.ExecContext(ctx,
`UPDATE iot_devices SET metadata = $3 WHERE namespace = $1 AND name = $2`,
namespace, name, b)
if err != nil {
return fmt.Errorf("store: update metadata: %w", err)
}
if n, _ := res.RowsAffected(); n == 0 {
return ErrNotFound
}
return nil
}
// Delete удаляет устройство. // Delete удаляет устройство.
func (s *DeviceStore) Delete(ctx context.Context, namespace, name string) error { func (s *DeviceStore) Delete(ctx context.Context, namespace, name string) error {
res, err := s.db.ExecContext(ctx, res, err := s.db.ExecContext(ctx,