From 0255bed6c45526b04e5ed41cc5a2bfd5a9828428 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marina=20L=C3=B3pez?= Date: Fri, 25 Sep 2026 11:22:43 +0200 Subject: [PATCH] :sparkles: Add deployment info to events (#11867) * :sparkles: Add deployment info to events * :sparkles: Add get-environment-data RPC method Add a single public RPC method returning the deployment type and the enabled environment flags. It replaces get-deployment on the management API and get-enabled-flags on the main API. get-enabled-flags stays as a deprecated alias returning only the flags, so existing callers keep working until it is removed. The frontend event initialization now reads the flags from the new method. The exposed flags stay limited to audit-log and telemetry to avoid leaking internal backend flags. AI-assisted-by: deepseek-v4.1-flash --------- Co-authored-by: Andrey Antukh --- backend/resources/rlimit.edn | 3 ++ backend/src/app/config.clj | 4 ++ backend/src/app/rpc/commands/audit.clj | 38 ++++++++++++++-- backend/test/backend_tests/rpc_audit_test.clj | 35 +++++++++++++++ frontend/src/app/main/data/event.cljs | 33 ++++++++++---- .../app/main/ui/dashboard/subscription.cljs | 1 + .../app/main/ui/settings/subscription.cljs | 5 +++ .../test/frontend_tests/data/event_test.cljs | 43 +++++++++++++++++++ frontend/test/frontend_tests/runner.cljs | 2 + 9 files changed, 152 insertions(+), 12 deletions(-) create mode 100644 frontend/test/frontend_tests/data/event_test.cljs diff --git a/backend/resources/rlimit.edn b/backend/resources/rlimit.edn index 68b3153848..86247690f1 100644 --- a/backend/resources/rlimit.edn +++ b/backend/resources/rlimit.edn @@ -98,6 +98,9 @@ :main/get-subscription-usage} [[:get-access-tokens :bucket "150/75/30s"]] + #{:main/get-environment-data} + [[:get-environment-data :bucket "250/125/30s"]] + #{:main/get-enabled-flags} [[:get-enabled-flags :bucket "250/125/30s"]] diff --git a/backend/src/app/config.clj b/backend/src/app/config.clj index a0ef5d344d..48081d6430 100644 --- a/backend/src/app/config.clj +++ b/backend/src/app/config.clj @@ -49,6 +49,9 @@ :host "localhost" :tenant "default" + ;; The SaaS host also sets penpotIsSaas in the browser. Keep this server + ;; value in sync so Admin Console can query the same deployment type. + :is-saas false :redis-uri "redis://redis/0" @@ -109,6 +112,7 @@ [:secret-key {:optional true} :string] [:tenant {:optional false} :string] + [:is-saas ::sm/boolean] [:public-uri {:optional false} ::sm/uri] [:host {:optional false} :string] diff --git a/backend/src/app/rpc/commands/audit.clj b/backend/src/app/rpc/commands/audit.clj index fceb258cf8..9b15b05e27 100644 --- a/backend/src/app/rpc/commands/audit.clj +++ b/backend/src/app/rpc/commands/audit.clj @@ -178,13 +178,45 @@ (rph/wrap nil))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; GET-ENABLED-FLAGS +;; GET-ENVIRONMENT-DATA ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(sv/defmethod ::get-enabled-flags +(def ^:private enabled-flags + "Subset of the backend flags that are safe to expose publicly and that + drive frontend event collection." + #{:audit-log :telemetry}) + +(defn- environment-data + [] + {:deployment (if (cf/get :is-saas) "saas" "selfhost") + :flags (set/intersection cf/flags enabled-flags)}) + +(def ^:private schema:get-environment-data-result + [:map + [:deployment [:enum "saas" "selfhost"]] + [:flags [::sm/set [:enum :audit-log :telemetry]]]]) + +(sv/defmethod ::get-environment-data + "Returns the deployment type and the enabled environment flags." {::audit/skip true ::rpc/auth false ::doc/skip true + ::doc/added "2.19" + ::sm/params [:map] + ::sm/result schema:get-environment-data-result} + [_cfg _params] + (environment-data)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; GET-ENABLED-FLAGS (DEPRECATED) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(sv/defmethod ::get-enabled-flags + "Deprecated. Use `get-environment-data` instead." + {::audit/skip true + ::rpc/auth false + ::doc/skip true + ::doc/deprecated true ::doc/added "1.20"} [_cfg _params] - (set/intersection cf/flags #{:audit-log :telemetry})) + (:flags (environment-data))) diff --git a/backend/test/backend_tests/rpc_audit_test.clj b/backend/test/backend_tests/rpc_audit_test.clj index 8336e2dba3..51963bcd88 100644 --- a/backend/test/backend_tests/rpc_audit_test.clj +++ b/backend/test/backend_tests/rpc_audit_test.clj @@ -304,6 +304,41 @@ (t/is (nil? (:error out))) (t/is (= 0 (count (th/db-exec! ["select * from audit_log"]))))))) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; GET-ENVIRONMENT-DATA +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(t/deftest get-environment-data-returns-selfhost-by-default + (with-redefs [cf/flags #{:audit-log}] + (let [out (th/command! {::th/type :get-environment-data})] + (t/is (th/success? out)) + (t/is (= "selfhost" (-> out :result :deployment)))))) + +(t/deftest get-environment-data-returns-saas-when-configured + (with-redefs [cf/flags #{:telemetry}] + (binding [cf/config (assoc cf/config :is-saas true)] + (let [out (th/command! {::th/type :get-environment-data})] + (t/is (th/success? out)) + (t/is (= "saas" (-> out :result :deployment))))))) + +(t/deftest get-environment-data-only-exposes-event-flags + (with-redefs [cf/flags #{:audit-log :telemetry :graph :nrepl-server}] + (let [out (th/command! {::th/type :get-environment-data})] + (t/is (th/success? out)) + (t/is (= #{:audit-log :telemetry} (-> out :result :flags)))))) + +(t/deftest get-environment-data-returns-empty-flags-when-none-enabled + (with-redefs [cf/flags #{:graph}] + (let [out (th/command! {::th/type :get-environment-data})] + (t/is (th/success? out)) + (t/is (= #{} (-> out :result :flags)))))) + +(t/deftest get-enabled-flags-still-returns-flat-set + (with-redefs [cf/flags #{:audit-log :telemetry :graph}] + (let [out (th/command! {::th/type :get-enabled-flags})] + (t/is (th/success? out)) + (t/is (= #{:audit-log :telemetry} (:result out)))))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; PURE HELPER UNIT TESTS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/frontend/src/app/main/data/event.cljs b/frontend/src/app/main/data/event.cljs index 0c507b3f0c..c381583e12 100644 --- a/frontend/src/app/main/data/event.cljs +++ b/frontend/src/app/main/data/event.cljs @@ -359,6 +359,23 @@ (rx/map deref) (rx/map snapshot-performance-info))))) +(defn fetch-environment-data + "Fetches the backend environment data (deployment type and enabled + flags). On RPC failure, falls back to telemetry so event collection is + not silently dropped; the backend rejects events if truly disabled." + [] + (->> (rp/cmd! :get-environment-data) + (rx/catch (fn [cause] + (l/debug :hint "unable to fetch backend environment data, proceeding with event collection" + :cause cause) + (rx/of {:flags #{:telemetry}}))))) + +(defn events-enabled? + "Returns true when the environment flags enable frontend event collection." + [flags] + (or (contains? flags :audit-log) + (contains? flags :telemetry))) + (defn initialize [] (ptk/reify ::initialize @@ -378,16 +395,14 @@ (l/debug :hint "event instrumentation initialized") - ;; Fetch backend flags and only start event collection if - ;; :audit-log or :telemetry is enabled. On RPC failure, proceed - ;; with event collection anyway (backend will reject if truly disabled). - (->> (rp/cmd! :get-enabled-flags) - (rx/catch (fn [cause] - (l/debug :hint "unable to fetch backend flags, proceeding with event collection" :cause cause) - (rx/of #{:telemetry}))) + ;; Fetch the backend environment data and only start event + ;; collection if :audit-log or :telemetry is enabled. On RPC + ;; failure, proceed with event collection anyway (backend will + ;; reject if truly disabled). + (->> (fetch-environment-data) + (rx/map :flags) (rx/mapcat (fn [flags] - (if (or (contains? flags :audit-log) - (contains? flags :telemetry)) + (if (events-enabled? flags) (do (l/debug :hint "event collection enabled" :flags (str/join " " (map name flags))) (rx/of true)) diff --git a/frontend/src/app/main/ui/dashboard/subscription.cljs b/frontend/src/app/main/ui/dashboard/subscription.cljs index 0643ba37c3..5548a5159e 100644 --- a/frontend/src/app/main/ui/dashboard/subscription.cljs +++ b/frontend/src/app/main/ui/dashboard/subscription.cljs @@ -279,6 +279,7 @@ (fn [] (st/emit! (ev/event {::ev/name "open-subscription-management" ::ev/origin "dashboard" + :deployment (if cf/saas? "saas" "selfhost") :section "team-settings"})) (let [href (-> (rt/get-current-href) (rt/encode-url)) diff --git a/frontend/src/app/main/ui/settings/subscription.cljs b/frontend/src/app/main/ui/settings/subscription.cljs index 957ffc6fa0..b779fc2c70 100644 --- a/frontend/src/app/main/ui/settings/subscription.cljs +++ b/frontend/src/app/main/ui/settings/subscription.cljs @@ -201,6 +201,7 @@ (reset! form nil) (st/emit! (ev/event {::ev/name event-name :type "unlimited" + :deployment (if cf/saas? "saas" "selfhost") :quantity min-members :subscription-mode subscription-mode}) (rt/nav-raw :href href)))))) @@ -216,6 +217,7 @@ href (dm/str "payments/subscriptions/create?type=enterprise&returnUrl=" return-url)] (st/emit! (ev/event {::ev/name event-name :type "enterprise" + :deployment (if cf/saas? "saas" "selfhost") :subscription-mode subscription-mode}) (rt/nav-raw :href href))))) @@ -224,6 +226,7 @@ (fn [] (st/emit! (ev/event {::ev/name "open-subscription-management" ::ev/origin "settings" + :deployment (if cf/saas? "saas" "selfhost") :section "subscription-management-modal"})) (let [current-href (rt/get-current-href) returnUrl (js/encodeURIComponent current-href) @@ -489,6 +492,7 @@ (fn [] (st/emit! (ev/event {::ev/name "open-subscription-management" ::ev/origin "settings" + :deployment (if cf/saas? "saas" "selfhost") :section "subscription"})) (let [current-href (rt/get-current-href) returnUrl (js/encodeURIComponent current-href) @@ -500,6 +504,7 @@ (fn [] (st/emit! (ev/event {::ev/name "open-subscription-management" ::ev/origin "settings" + :deployment (if cf/saas? "saas" "selfhost") :section "nitrate:enterprise"})) (dnt/go-to-nitrate-billing))) diff --git a/frontend/test/frontend_tests/data/event_test.cljs b/frontend/test/frontend_tests/data/event_test.cljs new file mode 100644 index 0000000000..9add050dd4 --- /dev/null +++ b/frontend/test/frontend_tests/data/event_test.cljs @@ -0,0 +1,43 @@ +;; 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 SUBSIDIARY SL + +(ns frontend-tests.data.event-test + (:require + [app.main.data.event :as ev] + [app.main.repo :as rp] + [beicon.v2.core :as rx] + [cljs.test :as t :include-macros true] + [frontend-tests.helpers.async :as async] + [frontend-tests.helpers.mock :as mock])) + +(t/deftest events-enabled?-truth-table + (t/testing "enabled when audit-log is present" + (t/is (true? (ev/events-enabled? #{:audit-log})))) + (t/testing "enabled when telemetry is present" + (t/is (true? (ev/events-enabled? #{:telemetry})))) + (t/testing "enabled when both are present" + (t/is (true? (ev/events-enabled? #{:audit-log :telemetry})))) + (t/testing "disabled when no event flag is present" + (t/is (false? (ev/events-enabled? #{}))) + (t/is (false? (ev/events-enabled? #{:graph}))))) + +(t/deftest ^:async fetch-environment-data-uses-get-environment-data + (let [data (await + (mock/with-mocks* + {rp/cmd! (mock/stub + (fn [cmd] + (t/is (= :get-environment-data cmd)) + (rx/of {:deployment "saas" + :flags #{:audit-log}})))} + (await (async/->promise (ev/fetch-environment-data)))))] + (t/is (= {:deployment "saas" :flags #{:audit-log}} data)))) + +(t/deftest ^:async fetch-environment-data-falls-back-to-telemetry-on-error + (let [data (await + (mock/with-mocks* + {rp/cmd! (mock/stub (fn [_cmd] (rx/throw (ex-info "boom" {}))))} + (await (async/->promise (ev/fetch-environment-data)))))] + (t/is (= {:flags #{:telemetry}} data)))) diff --git a/frontend/test/frontend_tests/runner.cljs b/frontend/test/frontend_tests/runner.cljs index 5e3facbf57..e4b3796282 100644 --- a/frontend/test/frontend_tests/runner.cljs +++ b/frontend/test/frontend_tests/runner.cljs @@ -10,6 +10,7 @@ [frontend-tests.copy-as-svg-test] [frontend-tests.data.comments-filters-test] [frontend-tests.data.dashboard-test] + [frontend-tests.data.event-test] [frontend-tests.data.exports-assets-test] [frontend-tests.data.nitrate-test] [frontend-tests.data.persistence-retry-test] @@ -137,6 +138,7 @@ 'frontend-tests.copy-as-svg-test 'frontend-tests.data.comments-filters-test 'frontend-tests.data.dashboard-test + 'frontend-tests.data.event-test 'frontend-tests.data.nitrate-test 'frontend-tests.data.persistence-retry-test 'frontend-tests.data.persistence-test