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
+26 -11
View File
@@ -29,9 +29,11 @@ type iotDeviceCreateRequest struct {
Metadata map[string]string `json:"metadata,omitempty"`
}
// iotDeviceUpdateRequest — тело PATCH.
// iotDeviceUpdateRequest — тело PATCH. Хотя бы одно поле обязательно:
// enabled и/или metadata (metadata раньше нельзя было обновить — ревью 2026-10-10).
type iotDeviceUpdateRequest struct {
Enabled *bool `json:"enabled"`
Enabled *bool `json:"enabled,omitempty"`
Metadata map[string]string `json:"metadata,omitempty"`
}
// iotDeviceResponse — ответ API. MQTTPassword заполняется ТОЛЬКО при создании
@@ -314,20 +316,33 @@ func (h *Handler) UpdateIoTDevice(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusBadRequest, errResp("invalid JSON: "+err.Error()))
return
}
if req.Enabled == nil {
writeJSON(w, http.StatusBadRequest, errResp("enabled field is required"))
if req.Enabled == nil && req.Metadata == nil {
writeJSON(w, http.StatusBadRequest, errResp("enabled или metadata обязательны"))
return
}
err := h.Devices.UpdateEnabled(r.Context(), ns, name, *req.Enabled)
if err != nil {
if errors.Is(err, store.ErrNotFound) {
writeJSON(w, http.StatusNotFound, errResp("iot device not found"))
// Обновляем только переданные поля (enabled и/или metadata).
if req.Enabled != nil {
if err := h.Devices.UpdateEnabled(r.Context(), ns, name, *req.Enabled); err != nil {
if errors.Is(err, store.ErrNotFound) {
writeJSON(w, http.StatusNotFound, errResp("iot device not found"))
return
}
h.Log.Error("update device enabled", "namespace", ns, "name", name, "err", err)
writeJSON(w, http.StatusInternalServerError, errResp("failed to update device"))
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
}
h.Log.Error("update device", "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)
+18
View File
@@ -97,6 +97,24 @@ func (s *DeviceStore) UpdateEnabled(ctx context.Context, namespace, name string,
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 удаляет устройство.
func (s *DeviceStore) Delete(ctx context.Context, namespace, name string) error {
res, err := s.db.ExecContext(ctx,