-
Notifications
You must be signed in to change notification settings - Fork 5.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enterprise settings #23441
Enterprise settings #23441
Conversation
Disabled settings will return their default value (else nil if no default is set). This allows us to have enterprise override settings and use them from regular OSS code without classloaders, extra vars, remembering to check if the feature is enabled, etc. Motivating examples are the appearance settings. We allow `application-font` setting to change the font of the application. This is an enterprise feature, but anyone can post to `api/setting/application-font` and set a new value or startup as `MB_APPLICATION_FONT=comic-sans java -jar metabase.jar` and have the functionality. Same thing for application colors in static viz. The calling code just calls `(settings/application-colors)` and uses them but doesn't check if the enterprise settings are enabled. To do this correctly, you have to remember to implement the following onerous procedure: A whole namespace for a setting ```clojure (ns metabase-enterprise.embedding.utils (:require [metabase.models.setting :as setting :refer [defsetting]] [metabase.public-settings :as public-settings] [metabase.public-settings.premium-features :as premium-features] [metabase.util.i18n :refer [deferred-tru]])) (defsetting notification-link-base-url (deferred-tru "By default \"Site Url\" is used in notification links, but can be overridden.") :visibility :internal :getter (fn [] (when (premium-features/hide-embed-branding?) (or (setting/get-value-of-type :string :notification-link-base-url) (public-settings/site-url))))) ``` And then in the calling code you have to do the procedure to conditionally require it and put it behind a var that can handle it being nil: ```clojure ;; we want to load this at the top level so the Setting the namespace defines gets loaded (def ^:private site-url* (or (u/ignore-exceptions (classloader/require 'metabase-enterprise.embedding.utils) (resolve 'metabase-enterprise.embedding.utils/notification-link-base-url)) (constantly nil))) ;; and then the usage (defn- site-url "Return the Notification Link Base URL if set by enterprise env var, or Site URL." [] (or (site-url*) (public-settings/site-url))) ``` Far nicer to just place the following into the regular public-settings namespace: ```clojure (defsetting notification-link-base-url (deferred-tru "By default \"Site Url\" is used in notification links, but can be overridden.") :visibility :internal :enabled? premium-features/hide-embed-branding?) ``` Then no need for a custom namespace to hold this setting, no need to have an extra var to point to the setting else a fallback value. Note that this feature is not required on every enterprise feature we have. We a namespace `metabase-enterprise.sso.integrations.sso-settings` that has 24 settings in it, all of which are enterprise features. But these features are used in our enterprise sso offerings and are directly referenced from the enterprise features. No need for the extra var to point to them and the flag checks happen in other parts.
Mark the following settings as requiring premium-settings/enable-whitelabeling? (aka token check) - application-name - loading-message (override of "doing science") - show-metabot (override of showing our friendly metabot) - application-colors - application-font - application-logo-url - application-favicon-url Updates the helper functions for colors to use the setting rather than feeling entitled to use a lower level `setting/get-value-of-type`. We need the higher level api so it takes into account if its enabled or not.
Codecov Report
@@ Coverage Diff @@
## master #23441 +/- ##
==========================================
- Coverage 64.29% 64.29% -0.01%
==========================================
Files 2635 2634 -1
Lines 82720 82722 +2
Branches 10269 10268 -1
==========================================
+ Hits 53183 53184 +1
- Misses 25326 25329 +3
+ Partials 4211 4209 -2
Flags with carried forward coverage won't be shown. Click here to find out more.
Continue to review full report at Codecov.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Current code looks good to me, so stamping to unblock. But should we prevent writing a disabled setting as well?
I'm not sure about the answer since there are some cases where we need to write enterprise feature data in OSS, so that things make sense when an instance upgrades to enterprise (this happens with download permissions, for example). But I'm not sure if that would ever apply to simple settings.
@@ -744,7 +749,8 @@ | |||
:cache? true | |||
:database-local :never | |||
:user-local :never | |||
:deprecated nil} | |||
:deprecated nil | |||
:enabled? nil} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One option could be to make the default value for enabled?
(constantly true)
so that you don't need to worry about the (nil? enabled)
check in the getter
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I'm going to leave it as is. My thinking is that enabled?
is an optional property and the default is of course settings just have values. I like that its an optional policy and we aren't adding lots of functions to most of them. If we live to regreat this we can easily add the (constantly true)
and drop the nil checks.
Fixes #23414
To correctly implement a setting into OSS code that allows for enhancements with enterprise, there is a lot of ceremony involved. We put the setting in enterprise code, we then try to require it, and put it behind a second var that can delegate either to the enterprise setting if it exists otherwise some default value or nil.
And it was important to remember to put the
(when (premium-features/hide-embed-branding?) ...)
check in there.Notably many settings related to appearance did not remember to do this check. (application-font, application-colors). These are enterprise changes that affect regular OSS code. But to do it right means the regular OSS code has to jump through hoops conditionally requiring setting namespaces, checking if the feature is enabled, etc.
I originally wanted this to have use a more enterprise related key:
But this ran into circularity issues. The enterprise token is a setting. So checking this token in the
defsetting
macro was circular. Could have hacked arounddefsetting
depending on(defsetting premium-embedding-token ...)
, done a delay or something but that's rather gross. So instead it just takes a predicate function:and update the
get
function inmodels.setting
to check for that:This does mean that all settings value checks need to go through
metabase.models.setting/get
rather than going to lower-level apis likeget-value-of-type
. If this becomes an issue we'll have to fixup the:getter
to include this information. Not as fun because its inside the macro instead of in a nice helper. But shouldn't be too hard to write some fn that wraps the getter.