Skip to content
Snippets Groups Projects
Unverified Commit 793577c8 authored by Braden Shepherdson's avatar Braden Shepherdson Committed by GitHub
Browse files

Mechanism for internal use of premium features (#32240)

Some premium features (and in theory even non-premium EE features) use
parts of others, and the token might not have those other features
enabled.

This mechanism allows dynamically overriding the set of features the
token is considered to have, enabling eg. `:audit-app` code to use
`:serialization` internally even if the user's token doesn't support
`:serialization`.

```clojure
(premium-features/with-premium-feature-overiddes [:foo :bar]
  (has-feature? :foo)) ;=> true
```
parent 8338a86e
No related branches found
No related tags found
No related merge requests found
......@@ -4,7 +4,7 @@
[metabase.config :as config]
[metabase.db.env :as mdb.env]
[metabase.models.database :refer [Database]]
[metabase.public-settings.premium-features :refer [defenterprise]]
[metabase.public-settings.premium-features :refer [defenterprise] :as premium-features]
[metabase.sync.sync-metadata :as sync-metadata]
[metabase.util :as u]
[metabase.util.log :as log]
......@@ -77,7 +77,9 @@
;; load instance analytics content (collections/dashboards/cards/etc.) when the resource exists:
(when analytics-root-dir-resource
(log/info (str "Loading Analytics Content from: " analytics-root-dir-resource))
(let [report (log/with-no-logs (serialization.cmd/v2-load analytics-root-dir-resource {}))]
;; The EE token might not have :serialization enabled, but audit features should still be able to use it.
(let [report (premium-features/with-premium-feature-overrides [:serialization]
(log/with-no-logs (serialization.cmd/v2-load analytics-root-dir-resource {})))]
(if (not-empty (:errors report))
(log/info (str "Error Loading Analytics Content: " (pr-str report)))
(log/info (str "Loading Analytics Content Complete (" (count (:seen report)) ") entities synchronized.")))))))
......@@ -236,13 +236,32 @@
[]
(boolean (seq (token-features))))
(def ^:dynamic *premium-feature-overrides*
"Dynamic var holding a set of tokens which are temporarily considered to be enabled, even if the user's token does
not have that feature.
This allows eg. `:audit-app` functionality to use `:serialization` internally, even if the token only has
`:audit-app`.
Don't touch this directly - prefer to use [[with-premium-feature-overrides]]."
#{})
(defmacro with-premium-feature-overrides
"Helper to dynamically override [[*premium-feature-overrides*]] and properly merge any existing value.
Used like `(with-premium-feature-overrides [:serialization] (something-using-serdes ...))`."
[features & body]
`(binding [*premium-feature-overrides* (into *premium-feature-overrides* ~features)]
~@body))
(defn has-feature?
"Does this instance's premium token have `feature`?
(has-feature? :sandboxes) ; -> true
(has-feature? :toucan-management) ; -> false"
[feature]
(contains? (token-features) (name feature)))
(or (contains? (token-features) (name feature))
(*premium-feature-overrides* feature)))
(defn- default-premium-feature-getter [feature]
(fn []
......
......@@ -125,6 +125,26 @@
(is (:valid result))
(is (contains? (set (:features result)) "test")))))))
(deftest feature-overrides-test
(let [token (random-token)]
(mt/with-temporary-raw-setting-values [:premium-embedding-token token]
(is (and (not (premium-features/has-feature? :serialization))
(not (premium-features/has-feature? :audit-app)))
"serialization and auditing are not enabled")
(testing "with-premium-feature-overrides works"
(premium-features/with-premium-feature-overrides [:serialization]
(is (premium-features/has-feature? :serialization))
(is (not (premium-features/has-feature? :audit-app)))
(testing "when nested"
(premium-features/with-premium-feature-overrides [:audit-app]
(is (premium-features/has-feature? :serialization))
(is (premium-features/has-feature? :audit-app))))))
(testing "and doesn't persist outside its scope"
(is (not (premium-features/has-feature? :serialization)))
(is (not (premium-features/has-feature? :audit-app)))))))
(deftest not-found-test
(mt/with-log-level :fatal
;; `partial=` here in case the Cloud API starts including extra keys... this is a "dangerous" test since changes
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment