package queryapi import ( "encoding/json" "errors" "fmt" "net/http" "queryorchestration/internal/database/repository" "queryorchestration/internal/uisettings" "github.com/google/uuid" "github.com/labstack/echo/v4" openapi_types "github.com/oapi-codegen/runtime/types" ) // uiSettingFromRow maps a repository row to the API response type. func uiSettingFromRow(r *repository.UiSetting) (UISetting, error) { var valueMap map[string]interface{} if len(r.Value) > 0 { if err := json.Unmarshal(r.Value, &valueMap); err != nil { return UISetting{}, fmt.Errorf("value json: %w", err) } } if valueMap == nil { valueMap = make(map[string]interface{}) } createdAt := r.CreatedAt.Time updatedAt := r.UpdatedAt.Time return UISetting{ Id: openapi_types.UUID(r.ID), Namespace: r.Namespace, Key: r.Key, Value: valueMap, IsDeleted: r.IsDeleted, CreatedAt: createdAt, UpdatedAt: updatedAt, }, nil } // CreateUISetting creates a new UI setting. // POST /ui-settings func (s *Controllers) CreateUISetting(ctx echo.Context) error { var req UISettingCreate if err := ctx.Bind(&req); err != nil { return echo.NewHTTPError(http.StatusBadRequest, err) } valueBytes, err := json.Marshal(req.Value) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("invalid value: %v", err)) } row, err := s.svc.UISettings.Create(ctx.Request().Context(), req.Namespace, req.Key, valueBytes) if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("create setting: %v", err)) } resp, err := uiSettingFromRow(row) if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) } return ctx.JSON(http.StatusCreated, resp) } // ListUISettings lists UI settings, optionally filtered by namespace. // GET /ui-settings func (s *Controllers) ListUISettings(ctx echo.Context, params ListUISettingsParams) error { var rows []*repository.UiSetting var err error if params.Namespace != nil && *params.Namespace != "" { rows, err = s.svc.UISettings.ListByNamespace(ctx.Request().Context(), *params.Namespace) } else { rows, err = s.svc.UISettings.List(ctx.Request().Context()) } if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("list settings: %v", err)) } settings := make([]UISetting, 0, len(rows)) for _, row := range rows { item, err := uiSettingFromRow(row) if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) } settings = append(settings, item) } return ctx.JSON(http.StatusOK, UISettingListResponse{ Settings: settings, //nolint:gosec // settings count is expected to fit in int32 for this endpoint TotalCount: int32(len(settings)), }) } // GetUISetting returns a single setting by id. // GET /ui-settings/{id} func (s *Controllers) GetUISetting(ctx echo.Context, id UISettingID) error { uid := uuid.UUID(id) row, err := s.svc.UISettings.Get(ctx.Request().Context(), uid) if err != nil { if errors.Is(err, uisettings.ErrNotFound) { return echo.NewHTTPError(http.StatusNotFound, "setting not found") } return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("get setting: %v", err)) } resp, err := uiSettingFromRow(row) if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) } return ctx.JSON(http.StatusOK, resp) } // UpdateUISetting updates a setting's value. // PATCH /ui-settings/{id} func (s *Controllers) UpdateUISetting(ctx echo.Context, id UISettingID) error { uid := uuid.UUID(id) var req UISettingUpdate if err := ctx.Bind(&req); err != nil { return echo.NewHTTPError(http.StatusBadRequest, err) } valueBytes, err := json.Marshal(req.Value) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("invalid value: %v", err)) } err = s.svc.UISettings.Update(ctx.Request().Context(), uid, valueBytes) if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("update setting: %v", err)) } row, err := s.svc.UISettings.Get(ctx.Request().Context(), uid) if err != nil { if errors.Is(err, uisettings.ErrNotFound) { return echo.NewHTTPError(http.StatusNotFound, "setting not found") } return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("get setting: %v", err)) } resp, err := uiSettingFromRow(row) if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) } return ctx.JSON(http.StatusOK, resp) } // DeleteUISetting soft-deletes a setting. // DELETE /ui-settings/{id} func (s *Controllers) DeleteUISetting(ctx echo.Context, id UISettingID) error { uid := uuid.UUID(id) err := s.svc.UISettings.SoftDelete(ctx.Request().Context(), uid) if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("delete setting: %v", err)) } return ctx.NoContent(http.StatusNoContent) }