From 5b4a5776cb05064d86f71e103c8537372d405b36 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Tue, 18 Aug 2026 11:18:44 +0000 Subject: [PATCH] :bug: Prevent nil theme in profile updates Omit nil optional profile fields before frontend schema validation and RPC persistence. Preserve omitted language and theme values in backend updates, and add regression coverage for partial profile saves. AI-assisted-by: gpt-5.6-luna --- backend/src/app/rpc/commands/profile.clj | 8 +++-- .../test/backend_tests/rpc_profile_test.clj | 11 +++++++ frontend/src/app/main/data/profile.cljs | 8 +++-- .../frontend_tests/data/profile_test.cljs | 30 +++++++++++++++++++ frontend/test/frontend_tests/runner.cljs | 2 ++ 5 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 frontend/test/frontend_tests/data/profile_test.cljs diff --git a/backend/src/app/rpc/commands/profile.clj b/backend/src/app/rpc/commands/profile.clj index 77307a1482..114f07be3e 100644 --- a/backend/src/app/rpc/commands/profile.clj +++ b/backend/src/app/rpc/commands/profile.clj @@ -166,8 +166,12 @@ ;; the same row/object. (let [profile (get-profile conn profile-id ::db/for-update true) fullname (d/normalize-string fullname) - lang (d/normalize-string lang) - theme (d/normalize-string theme) + lang (if (contains? params :lang) + (d/normalize-string lang) + (:lang profile)) + theme (if (contains? params :theme) + (d/normalize-string theme) + (:theme profile)) ;; Update the profile map with direct params profile (-> profile (assoc :fullname fullname) diff --git a/backend/test/backend_tests/rpc_profile_test.clj b/backend/test/backend_tests/rpc_profile_test.clj index f9900d44ab..de9ae0aacd 100644 --- a/backend/test/backend_tests/rpc_profile_test.clj +++ b/backend/test/backend_tests/rpc_profile_test.clj @@ -125,6 +125,17 @@ (t/is (= "en" (:lang result))) (t/is (= "dark" (:theme result)))))) + (t/testing "update profile preserves omitted optional fields" + (let [data {::th/type :update-profile + ::rpc/profile-id (:id profile) + :fullname "Updated Name"} + out (th/command! data)] + + (t/is (nil? (:error out))) + (t/is (= "Updated Name" (get-in out [:result :fullname]))) + (t/is (= "en" (get-in out [:result :lang]))) + (t/is (= "dark" (get-in out [:result :theme]))))) + (t/testing "update photo" (let [data {::th/type :update-profile-photo ::rpc/profile-id (:id profile) diff --git a/frontend/src/app/main/data/profile.cljs b/frontend/src/app/main/data/profile.cljs index 3f1237c2b7..9d6db2101d 100644 --- a/frontend/src/app/main/data/profile.cljs +++ b/frontend/src/app/main/data/profile.cljs @@ -122,6 +122,10 @@ ;; --- Update Profile +(defn profile-update-params + [profile] + (d/without-nils (select-keys profile [:fullname :lang :theme]))) + (defn persist-profile [& {:as opts}] (ptk/reify ::persist-profile @@ -130,7 +134,7 @@ (let [on-success (:on-success opts identity) on-error (:on-error opts rx/throw) profile (:profile state) - params (select-keys profile [:fullname :lang :theme])] + params (profile-update-params profile)] (->> (rp/cmd! :update-profile params) (rx/tap on-success) (rx/map set-profile) @@ -143,7 +147,7 @@ props" [profile] - (let [profile (check-profile profile)] + (let [profile (check-profile (d/without-nils profile))] (ptk/reify ::update-profile ptk/WatchEvent (watch [_ state _] diff --git a/frontend/test/frontend_tests/data/profile_test.cljs b/frontend/test/frontend_tests/data/profile_test.cljs new file mode 100644 index 0000000000..97e62f63d6 --- /dev/null +++ b/frontend/test/frontend_tests/data/profile_test.cljs @@ -0,0 +1,30 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS INC Sucursal en EspaƱa SL + +(ns frontend-tests.data.profile-test + (:require + [app.common.uuid :as uuid] + [app.main.data.profile :as dprof] + [cljs.test :as t :include-macros true])) + +(t/deftest profile-update-params-omits-nil-values + (t/is (= {:fullname "Updated Name"} + (dprof/profile-update-params {:fullname "Updated Name" + :lang nil + :theme nil})))) + +(t/deftest profile-update-params-preserves-present-values + (t/is (= {:fullname "Updated Name" + :lang "en" + :theme "dark"} + (dprof/profile-update-params {:fullname "Updated Name" + :lang "en" + :theme "dark"})))) + +(t/deftest update-profile-accepts-nil-optional-values + (t/is (some? (dprof/update-profile {:id uuid/zero + :fullname "Updated Name" + :theme nil})))) diff --git a/frontend/test/frontend_tests/runner.cljs b/frontend/test/frontend_tests/runner.cljs index 31896b987e..f4ae117eda 100644 --- a/frontend/test/frontend_tests/runner.cljs +++ b/frontend/test/frontend_tests/runner.cljs @@ -11,6 +11,7 @@ [frontend-tests.data.dashboard-test] [frontend-tests.data.exports-assets-test] [frontend-tests.data.nitrate-test] + [frontend-tests.data.profile-test] [frontend-tests.data.repo-test] [frontend-tests.data.store-test] [frontend-tests.data.uploads-test] @@ -106,6 +107,7 @@ 'frontend-tests.copy-as-svg-test 'frontend-tests.data.dashboard-test 'frontend-tests.data.nitrate-test + 'frontend-tests.data.profile-test 'frontend-tests.data.repo-test 'frontend-tests.data.store-test 'frontend-tests.data.exports-assets-test