From 059b2ce10ec7b615744929201e4f173fcdb20178 Mon Sep 17 00:00:00 2001 From: Ryan Laurie <30528226+iethree@users.noreply.github.com> Date: Thu, 12 Sep 2024 12:38:58 -0600 Subject: [PATCH] Remove more metabse settings (#47510) * remove more metabse settings * fix missing update for latest version check * test updates * fix setting selectors --- .../SettingsSAMLForm/SettingsSAMLForm.jsx | 11 ++--- .../src/metabase-enterprise/plugins.js | 4 -- .../no-unconditional-metabase-links-render.js | 37 ++------------ ...itional-metabase-links-render.unit.spec.js | 41 ---------------- .../src/metabase-types/api/mocks/settings.ts | 2 +- frontend/src/metabase-types/api/settings.ts | 1 + frontend/src/metabase/admin/app/actions.ts | 7 +-- .../ModelCachingControl.tsx | 7 ++- .../databases/containers/DatabaseListApp.jsx | 6 +-- .../DataModelApp/ModelEducationalModal.jsx | 4 +- .../people/containers/UserSuccessModal.jsx | 19 ++++--- .../SettingsUpdatesForm.jsx | 7 +-- .../SettingsUpdatesForm.unit.spec.js | 8 +-- .../VersionUpdateNotice.jsx | 23 ++++++--- .../src/metabase/admin/settings/selectors.js | 28 +++++++++-- .../components/HomeHelpCard/HomeHelpCard.tsx | 4 +- frontend/src/metabase/lib/settings.ts | 49 +------------------ frontend/src/metabase/lib/utils.ts | 22 +++++++++ .../NewModelOptions/NewModelOptions.tsx | 5 +- .../PaymentBanner/PaymentBanner.tsx | 6 +-- .../pulse/components/RecipientPicker.tsx | 8 +-- .../components/RecipientPicker.unit.spec.tsx | 12 ++--- .../VisualizationError/VisualizationError.tsx | 6 +-- frontend/src/metabase/routes.jsx | 6 ++- frontend/src/metabase/selectors/settings.ts | 9 ++++ .../CloudMigrationHelp/CloudMigrationHelp.tsx | 4 +- 26 files changed, 143 insertions(+), 193 deletions(-) diff --git a/enterprise/frontend/src/metabase-enterprise/auth/components/SettingsSAMLForm/SettingsSAMLForm.jsx b/enterprise/frontend/src/metabase-enterprise/auth/components/SettingsSAMLForm/SettingsSAMLForm.jsx index 7805839f16e..e897df4e1cb 100644 --- a/enterprise/frontend/src/metabase-enterprise/auth/components/SettingsSAMLForm/SettingsSAMLForm.jsx +++ b/enterprise/frontend/src/metabase-enterprise/auth/components/SettingsSAMLForm/SettingsSAMLForm.jsx @@ -9,7 +9,7 @@ import SettingHeader from "metabase/admin/settings/components/SettingHeader"; import GroupMappingsWidget from "metabase/admin/settings/containers/GroupMappingsWidget"; import { updateSamlSettings } from "metabase/admin/settings/settings"; import { settingToFormField } from "metabase/admin/settings/utils"; -import { useDocsUrl } from "metabase/common/hooks"; +import { useDocsUrl, useSetting } from "metabase/common/hooks"; import Breadcrumbs from "metabase/components/Breadcrumbs"; import ExternalLink from "metabase/core/components/ExternalLink"; import CS from "metabase/css/core/index.css"; @@ -23,7 +23,6 @@ import { FormTextInput, FormTextarea, } from "metabase/forms"; -import MetabaseSettings from "metabase/lib/settings"; import { Stack } from "metabase/ui"; import { @@ -72,11 +71,13 @@ const SettingsSAMLForm = ({ elements = [], settingValues = {}, onSubmit }) => { "people-and-groups/authenticating-with-saml", ); + const siteUrl = useSetting("site-url"); + return ( <FormProvider initialValues={{ ...attributeValues, - [FAKE_ACS_URL_KEY]: getAcsCustomerUrl(), + [FAKE_ACS_URL_KEY]: `${siteUrl}/auth/sso`, }} onSubmit={handleSubmit} enableReinitialize @@ -271,10 +272,6 @@ const getAttributeValues = (values, defaults) => { ); }; -const getAcsCustomerUrl = () => { - return `${MetabaseSettings.get("site-url")}/auth/sso`; -}; - SettingsSAMLForm.propTypes = propTypes; const mapDispatchToProps = { diff --git a/enterprise/frontend/src/metabase-enterprise/plugins.js b/enterprise/frontend/src/metabase-enterprise/plugins.js index 36857097559..cc278b0831a 100644 --- a/enterprise/frontend/src/metabase-enterprise/plugins.js +++ b/enterprise/frontend/src/metabase-enterprise/plugins.js @@ -1,10 +1,6 @@ -import MetabaseSettings from "metabase/lib/settings"; import { PLUGIN_IS_EE_BUILD } from "metabase/plugins"; // SETTINGS OVERRIDES: - -// NOTE: temporarily use "latest" for Enterprise Edition docs -MetabaseSettings.docsTag = () => "latest"; PLUGIN_IS_EE_BUILD.isEEBuild = () => true; import "./shared"; diff --git a/frontend/lint/eslint-rules/no-unconditional-metabase-links-render.js b/frontend/lint/eslint-rules/no-unconditional-metabase-links-render.js index d7746c3bfd4..3f44b835d85 100644 --- a/frontend/lint/eslint-rules/no-unconditional-metabase-links-render.js +++ b/frontend/lint/eslint-rules/no-unconditional-metabase-links-render.js @@ -4,12 +4,11 @@ // // The following cases are considered errors: // -// 1. MetabaseSettings.learnUrl(string) -// 2. useDocsUrl hook -// 3. getDocsUrl selector from "metabase/selectors/settings" -// 4. getLearnUrl selector from "metabase/selectors/settings" -// 5. inline string "metabase.com/docs/" -// 6. inline string "metabase.com/learn/" +// 1. useDocsUrl hook +// 2. getDocsUrl selector from "metabase/selectors/settings" +// 3. getLearnUrl selector from "metabase/selectors/settings" +// 4. inline string "metabase.com/docs/" +// 5. inline string "metabase.com/learn/" // // If a link shouldn't be rendered conditionally e.g. it's only show for admins, or is rendered inside admin settings, you need to disable the rule with a reason. // e.g. "// eslint-disable-next-line no-unconditional-metabase-links-render -- This link only shows for admins." @@ -50,7 +49,6 @@ module.exports = { }, create(context) { - let metabaseSettings; let isGetDocsUrlSelectorImported = false; let isGetLearnUrlSelectorImported = false; let isGetShowMetabaseLinksSelectorImported = false; @@ -89,17 +87,6 @@ module.exports = { return { ImportDeclaration(node) { - if ( - getImportedModuleNode(node, { - isDefault: true, - source: "metabase/lib/settings", - }) - ) { - metabaseSettings = getImportedModuleNode(node, { - isDefault: true, - source: "metabase/lib/settings", - }); - } if ( getImportedModuleNode(node, { named: "getDocsUrl", @@ -171,20 +158,6 @@ module.exports = { message: ERROR_MESSAGE, }); } - - // call `MetabaseSettings.learnUrl` - if ( - metabaseSettings?.references.some( - reference => reference.identifier === node?.callee?.object, - ) && - !isGetShowMetabaseLinksSelectorImported && - ["learnUrl", "docsUrl"].includes(node?.callee?.property?.name) - ) { - context.report({ - node, - message: ERROR_MESSAGE, - }); - } }, Literal(node) { if (typeof node.value !== "string") { diff --git a/frontend/lint/tests/no-unconditional-metabase-links-render.unit.spec.js b/frontend/lint/tests/no-unconditional-metabase-links-render.unit.spec.js index 839116ba6bf..fedce3ded6d 100644 --- a/frontend/lint/tests/no-unconditional-metabase-links-render.unit.spec.js +++ b/frontend/lint/tests/no-unconditional-metabase-links-render.unit.spec.js @@ -19,20 +19,6 @@ const ruleTester = new RuleTester({ const VALID_CASES = [ { code: ` -import MetabaseSettings from "metabase/lib/settings"; -import { getShowMetabaseLinks } from "metabase/selectors/whitelabel"; - -const docsUrl = MetabaseSettings.docsUrl("permissions/data")`, - }, - { - code: ` -import MetabaseSettings from "metabase/lib/settings"; -import { getShowMetabaseLinks } from "metabase/selectors/whitelabel"; - -const docsUrl = MetabaseSettings.learnUrl("permissions/data")`, - }, - { - code: ` import { useSelector } from "metabase/lib/redux"; import { getDocsUrl } from "metabase/selectors/settings"; import { getShowMetabaseLinks } from "metabase/selectors/whitelabel"; @@ -97,33 +83,6 @@ const { url } = useDocsUrl("permissions/data");`, }, ]; const INVALID_CASES = [ - { - name: 'Detect any name of the default import of "metabase/lib/settings"', - code: ` -import Settings from "metabase/lib/settings"; - -const docsUrl = Settings.docsUrl("permissions/data")`, - error: - /Metabase links must be rendered conditionally\.(.|\n)*Please import `getShowMetabaseLinks`(.|\n)*Or add comment to indicate the reason why this rule needs to be disabled/, - }, - { - name: "Detect MetabaseSettings.docsUrl()", - code: ` -import MetabaseSettings from "metabase/lib/settings"; - -const docsUrl = MetabaseSettings.docsUrl("permissions/data")`, - error: - /Metabase links must be rendered conditionally\.(.|\n)*Please import `getShowMetabaseLinks`(.|\n)*Or add comment to indicate the reason why this rule needs to be disabled/, - }, - { - name: "Detect MetabaseSettings.learn()", - code: ` -import MetabaseSettings from "metabase/lib/settings"; - -const docsUrl = MetabaseSettings.learnUrl("permissions/data")`, - error: - /Metabase links must be rendered conditionally\.(.|\n)*Please import `getShowMetabaseLinks`(.|\n)*Or add comment to indicate the reason why this rule needs to be disabled/, - }, { name: "Detect getDocsUrl()", code: ` diff --git a/frontend/src/metabase-types/api/mocks/settings.ts b/frontend/src/metabase-types/api/mocks/settings.ts index 152211f1dd6..fcc99403ab7 100644 --- a/frontend/src/metabase-types/api/mocks/settings.ts +++ b/frontend/src/metabase-types/api/mocks/settings.ts @@ -200,7 +200,7 @@ export const createMockSettings = ( "openai-organization": null, "openai-model": null, "openai-available-models": [], - "other-sso-enabled?": null, + "other-sso-enabled?": false, "password-complexity": { total: 6, digit: 1 }, "persisted-models-enabled": false, "persisted-model-refresh-cron-schedule": "0 0 0/6 * * ? *", diff --git a/frontend/src/metabase-types/api/settings.ts b/frontend/src/metabase-types/api/settings.ts index a7bc61f7ada..9b3a589dfc0 100644 --- a/frontend/src/metabase-types/api/settings.ts +++ b/frontend/src/metabase-types/api/settings.ts @@ -246,6 +246,7 @@ interface AdminSettings { "premium-embedding-token": string | null; "saml-configured"?: boolean; "saml-enabled"?: boolean; + "other-sso-enabled?"?: boolean; // yes the question mark is in the variable name "show-database-syncing-modal": boolean; "token-status": TokenStatus | null; "version-info": VersionInfo | null; diff --git a/frontend/src/metabase/admin/app/actions.ts b/frontend/src/metabase/admin/app/actions.ts index c680a5d62f0..cc6e25a40c2 100644 --- a/frontend/src/metabase/admin/app/actions.ts +++ b/frontend/src/metabase/admin/app/actions.ts @@ -1,13 +1,14 @@ import { updateSetting } from "metabase/admin/settings/settings"; import { createAsyncThunk } from "metabase/lib/redux"; -import Settings from "metabase/lib/settings"; + +import { getCurrentVersion } from "../settings/selectors"; export const disableNotice = createAsyncThunk( "metabase/admin/app/DISABLE_NOTICE", - async (_, { dispatch }) => { + async (_, { getState, dispatch }) => { const setting = { key: "deprecation-notice-version", - value: Settings.currentVersion(), + value: getCurrentVersion(getState()), }; await dispatch(updateSetting(setting)); }, diff --git a/frontend/src/metabase/admin/databases/components/DatabaseEditApp/Sidebar/ModelCachingControl/ModelCachingControl.tsx b/frontend/src/metabase/admin/databases/components/DatabaseEditApp/Sidebar/ModelCachingControl/ModelCachingControl.tsx index 4fd689abcb2..30601632dcf 100644 --- a/frontend/src/metabase/admin/databases/components/DatabaseEditApp/Sidebar/ModelCachingControl/ModelCachingControl.tsx +++ b/frontend/src/metabase/admin/databases/components/DatabaseEditApp/Sidebar/ModelCachingControl/ModelCachingControl.tsx @@ -5,13 +5,12 @@ import { PERSIST_DATABASE, UNPERSIST_DATABASE, } from "metabase/admin/databases/database"; -import { useDocsUrl } from "metabase/common/hooks"; +import { useDocsUrl, useSetting } from "metabase/common/hooks"; import ActionButton from "metabase/components/ActionButton"; import TippyPopover from "metabase/components/Popover/TippyPopover"; import ExternalLink from "metabase/core/components/ExternalLink"; import ButtonsS from "metabase/css/components/buttons.module.css"; import { useDispatch } from "metabase/lib/redux"; -import MetabaseSettings from "metabase/lib/settings"; import { MetabaseApi } from "metabase/services"; import type Database from "metabase-lib/v1/metadata/Database"; import { getModelCacheSchemaName } from "metabase-lib/v1/metadata/utils/models"; @@ -66,8 +65,8 @@ function ModelCachingControl({ database }: Props) { ? t`Turn model persistence off` : t`Turn model persistence on`; - const siteUUID = MetabaseSettings.get("site-uuid") || ""; - const cacheSchemaName = getModelCacheSchemaName(databaseId, siteUUID); + const siteUUID = useSetting("site-uuid"); + const cacheSchemaName = getModelCacheSchemaName(databaseId, siteUUID || ""); const handleCachingChange = async () => { setError(null); diff --git a/frontend/src/metabase/admin/databases/containers/DatabaseListApp.jsx b/frontend/src/metabase/admin/databases/containers/DatabaseListApp.jsx index 205e491596d..f7ec9761f6e 100644 --- a/frontend/src/metabase/admin/databases/containers/DatabaseListApp.jsx +++ b/frontend/src/metabase/admin/databases/containers/DatabaseListApp.jsx @@ -3,9 +3,9 @@ import _ from "underscore"; import LoadingAndGenericErrorWrapper from "metabase/components/LoadingAndGenericErrorWrapper"; import Database from "metabase/entities/databases"; -import MetabaseSettings from "metabase/lib/settings"; import { isSyncInProgress } from "metabase/lib/syncing"; import { PLUGIN_FEATURE_LEVEL_PERMISSIONS } from "metabase/plugins"; +import { getSetting } from "metabase/selectors/settings"; import { getUserIsAdmin } from "metabase/selectors/user"; import DatabaseList from "../components/DatabaseList"; @@ -41,8 +41,8 @@ const mapStateToProps = (state, props) => ({ created: props.location.query.created, createdDbId: props.location.query.createdDbId, - engines: MetabaseSettings.get("engines"), - showSyncingModal: MetabaseSettings.get("show-database-syncing-modal"), + engines: getSetting(state, "engines"), + showSyncingModal: getSetting(state, "show-database-syncing-modal"), deletes: getDeletes(state), deletionError: getDeletionError(state), diff --git a/frontend/src/metabase/admin/datamodel/containers/DataModelApp/ModelEducationalModal.jsx b/frontend/src/metabase/admin/datamodel/containers/DataModelApp/ModelEducationalModal.jsx index 23ae4436b61..5c71fdc7996 100644 --- a/frontend/src/metabase/admin/datamodel/containers/DataModelApp/ModelEducationalModal.jsx +++ b/frontend/src/metabase/admin/datamodel/containers/DataModelApp/ModelEducationalModal.jsx @@ -6,7 +6,7 @@ import Modal from "metabase/components/Modal"; import ModalContent from "metabase/components/ModalContent"; import ButtonsS from "metabase/css/components/buttons.module.css"; import CS from "metabase/css/core/index.css"; -import MetabaseSettings from "metabase/lib/settings"; +import { getLearnUrl } from "metabase/selectors/settings"; import { ButtonLink, @@ -20,7 +20,7 @@ ModelEducationalModal.propTypes = { onClose: PropTypes.func.isRequired, }; -const EDUCATION_URL = MetabaseSettings.learnUrl("getting-started/models"); +const EDUCATION_URL = getLearnUrl("getting-started/models"); export function ModelEducationalModal({ isOpen, onClose }) { return ( diff --git a/frontend/src/metabase/admin/people/containers/UserSuccessModal.jsx b/frontend/src/metabase/admin/people/containers/UserSuccessModal.jsx index 60d2ccf901c..250a3d0b1c5 100644 --- a/frontend/src/metabase/admin/people/containers/UserSuccessModal.jsx +++ b/frontend/src/metabase/admin/people/containers/UserSuccessModal.jsx @@ -12,7 +12,7 @@ import Button from "metabase/core/components/Button"; import Link from "metabase/core/components/Link"; import CS from "metabase/css/core/index.css"; import Users from "metabase/entities/users"; -import MetabaseSettings from "metabase/lib/settings"; +import { getSetting, isSsoEnabled } from "metabase/selectors/settings"; import { clearTemporaryPassword } from "../people"; import { getUserTemporaryPassword } from "../selectors"; @@ -25,10 +25,14 @@ class UserSuccessModal extends Component { } render() { - const { onClose, user, temporaryPassword } = this.props; - const isSsoEnabled = - MetabaseSettings.isSsoEnabled() && - !MetabaseSettings.isPasswordLoginEnabled(); + const { + onClose, + user, + temporaryPassword, + isSsoEnabled, + isPasswordLoginEnabled, + } = this.props; + const ssoEnabled = isSsoEnabled && !isPasswordLoginEnabled; return ( <ModalContent title={t`${user.common_name} has been added`} @@ -38,7 +42,7 @@ class UserSuccessModal extends Component { {temporaryPassword ? ( <PasswordSuccess user={user} temporaryPassword={temporaryPassword} /> ) : ( - <EmailSuccess isSsoEnabled={isSsoEnabled} user={user} /> + <EmailSuccess isSsoEnabled={ssoEnabled} user={user} /> )} </ModalContent> ); @@ -86,7 +90,6 @@ const PasswordSuccess = ({ user, temporaryPassword }) => ( </div> </div> ); - export default _.compose( Users.load({ id: (state, props) => props.params.userId, @@ -96,6 +99,8 @@ export default _.compose( temporaryPassword: getUserTemporaryPassword(state, { userId: props.params.userId, }), + isSsoEnabled: isSsoEnabled(state), + isPasswordLoginEnabled: getSetting(state, "enable-password-login"), }), { onClose: () => push("/admin/people"), diff --git a/frontend/src/metabase/admin/settings/components/SettingsUpdatesForm/SettingsUpdatesForm.jsx b/frontend/src/metabase/admin/settings/components/SettingsUpdatesForm/SettingsUpdatesForm.jsx index 0efc2f43f7a..e7bc0ab453e 100644 --- a/frontend/src/metabase/admin/settings/components/SettingsUpdatesForm/SettingsUpdatesForm.jsx +++ b/frontend/src/metabase/admin/settings/components/SettingsUpdatesForm/SettingsUpdatesForm.jsx @@ -2,8 +2,8 @@ import cx from "classnames"; import PropTypes from "prop-types"; import { UpsellHostingUpdates } from "metabase/admin/upsells"; +import { useSetting } from "metabase/common/hooks"; import CS from "metabase/css/core/index.css"; -import MetabaseSettings from "metabase/lib/settings"; import { Flex } from "metabase/ui"; import { SettingsSetting } from "../SettingsSetting"; @@ -18,16 +18,17 @@ export default function SettingsUpdatesForm({ elements, updateSetting }) { autoFocus={index === 0} /> )); + const isHosted = useSetting("is-hosted"); return ( <Flex justify="space-between"> <div style={{ width: "585px" }}> - {!MetabaseSettings.isHosted() && <ul>{settings}</ul>} + {!isHosted && <ul>{settings}</ul>} <div className={CS.px2}> <div className={cx(CS.pt3, { - [CS.borderTop]: !MetabaseSettings.isHosted(), + [CS.borderTop]: !isHosted, })} > <VersionUpdateNotice /> diff --git a/frontend/src/metabase/admin/settings/components/SettingsUpdatesForm/SettingsUpdatesForm.unit.spec.js b/frontend/src/metabase/admin/settings/components/SettingsUpdatesForm/SettingsUpdatesForm.unit.spec.js index 922a53f3742..48234a37ec6 100644 --- a/frontend/src/metabase/admin/settings/components/SettingsUpdatesForm/SettingsUpdatesForm.unit.spec.js +++ b/frontend/src/metabase/admin/settings/components/SettingsUpdatesForm/SettingsUpdatesForm.unit.spec.js @@ -51,17 +51,17 @@ function setup({ } describe("SettingsUpdatesForm", () => { - it("shows custom message for Cloud installations", () => { + it("shows custom message for Cloud installations", async () => { setup({ isHosted: true }); expect( - screen.getByText(/Metabase Cloud keeps your instance up-to-date/), + await screen.findByText(/Metabase Cloud keeps your instance up-to-date/), ).toBeInTheDocument(); }); - it("shows correct message when latest version is installed", () => { + it("shows correct message when latest version is installed", async () => { setup({ currentVersion: "v1.0.0", latestVersion: "v1.0.0" }); expect( - screen.getByText(/You're running Metabase 1.0.0/), + await screen.findByText(/You're running Metabase 1.0.0/), ).toBeInTheDocument(); }); diff --git a/frontend/src/metabase/admin/settings/components/SettingsUpdatesForm/VersionUpdateNotice/VersionUpdateNotice.jsx b/frontend/src/metabase/admin/settings/components/SettingsUpdatesForm/VersionUpdateNotice/VersionUpdateNotice.jsx index 843aa6d61e1..4d8b087433d 100644 --- a/frontend/src/metabase/admin/settings/components/SettingsUpdatesForm/VersionUpdateNotice/VersionUpdateNotice.jsx +++ b/frontend/src/metabase/admin/settings/components/SettingsUpdatesForm/VersionUpdateNotice/VersionUpdateNotice.jsx @@ -2,10 +2,17 @@ import cx from "classnames"; import PropTypes from "prop-types"; import { t } from "ttag"; +import { + getCurrentVersion, + getLatestVersion, +} from "metabase/admin/settings/selectors"; import ExternalLink from "metabase/core/components/ExternalLink"; import ButtonsS from "metabase/css/components/buttons.module.css"; import CS from "metabase/css/core/index.css"; +import { useSelector } from "metabase/lib/redux"; import MetabaseSettings from "metabase/lib/settings"; +import { newVersionAvailable, versionIsLatest } from "metabase/lib/utils"; +import { getIsHosted } from "metabase/setup/selectors"; import { NewVersionContainer, @@ -13,18 +20,22 @@ import { } from "./VersionUpdateNotice.styled"; export default function VersionUpdateNotice() { - const currentVersion = formatVersion(MetabaseSettings.currentVersion()); + const currentVersion = useSelector(getCurrentVersion); + const latestVersion = useSelector(getLatestVersion); + const isHosted = useSelector(getIsHosted); - if (MetabaseSettings.isHosted()) { + if (isHosted) { return <CloudCustomers currentVersion={currentVersion} />; } - if (MetabaseSettings.versionIsLatest()) { - return <OnLatestVersion currentVersion={currentVersion} />; + const displayVersion = formatVersion(currentVersion); + + if (versionIsLatest({ currentVersion, latestVersion })) { + return <OnLatestVersion currentVersion={displayVersion} />; } - if (MetabaseSettings.newVersionAvailable()) { - return <NewVersionAvailable currentVersion={currentVersion} />; + if (newVersionAvailable({ currentVersion, latestVersion })) { + return <NewVersionAvailable currentVersion={displayVersion} />; } return <div>{t`No successful checks yet.`}</div>; diff --git a/frontend/src/metabase/admin/settings/selectors.js b/frontend/src/metabase/admin/settings/selectors.js index f0ad8ed62e5..ef158dbfd62 100644 --- a/frontend/src/metabase/admin/settings/selectors.js +++ b/frontend/src/metabase/admin/settings/selectors.js @@ -6,6 +6,7 @@ import { SMTPConnectionForm } from "metabase/admin/settings/components/Email/SMT import Breadcrumbs from "metabase/components/Breadcrumbs"; import { DashboardSelector } from "metabase/components/DashboardSelector"; import MetabaseSettings from "metabase/lib/settings"; +import { newVersionAvailable } from "metabase/lib/utils"; import { PLUGIN_ADMIN_SETTINGS_AUTH_TABS, PLUGIN_ADMIN_SETTINGS_UPDATES, @@ -620,9 +621,30 @@ export const getSettingValues = createSelector(getSettings, settings => { return settingValues; }); -export const getNewVersionAvailable = createSelector(getSettings, settings => { - return MetabaseSettings.newVersionAvailable(settings); -}); +export const getCurrentVersion = createSelector( + getDerivedSettingValues, + settings => { + return settings.version?.tag; + }, +); + +export const getLatestVersion = createSelector( + getDerivedSettingValues, + settings => { + return settings["version-info"]?.latest?.version; + }, +); + +export const getNewVersionAvailable = createSelector( + getCurrentVersion, + getLatestVersion, + (currentVersion, latestVersion) => { + return newVersionAvailable({ + currentVersion, + latestVersion, + }); + }, +); export const getSections = createSelector( getSettings, diff --git a/frontend/src/metabase/home/components/HomeHelpCard/HomeHelpCard.tsx b/frontend/src/metabase/home/components/HomeHelpCard/HomeHelpCard.tsx index ddf7fba8e71..0d93824c845 100644 --- a/frontend/src/metabase/home/components/HomeHelpCard/HomeHelpCard.tsx +++ b/frontend/src/metabase/home/components/HomeHelpCard/HomeHelpCard.tsx @@ -2,7 +2,7 @@ import { t } from "ttag"; import { useUniqueId } from "metabase/hooks/use-unique-id"; import { useSelector } from "metabase/lib/redux"; -import MetabaseSettings from "metabase/lib/settings"; +import { getLearnUrl } from "metabase/selectors/settings"; import { getApplicationName, getShowMetabaseLinks, @@ -20,7 +20,7 @@ export const HomeHelpCard = (): JSX.Element | null => { } return ( - <CardRoot href={MetabaseSettings.learnUrl()} aria-labelledby={cardTitleId}> + <CardRoot href={getLearnUrl()} aria-labelledby={cardTitleId}> <CardIcon name="reference" /> <CardTitle id={cardTitleId}>{t`${applicationName} tips`}</CardTitle> </CardRoot> diff --git a/frontend/src/metabase/lib/settings.ts b/frontend/src/metabase/lib/settings.ts index 29a59e405a8..a32c1628b42 100644 --- a/frontend/src/metabase/lib/settings.ts +++ b/frontend/src/metabase/lib/settings.ts @@ -3,7 +3,7 @@ import { msgid, ngettext, t } from "ttag"; import _ from "underscore"; import { parseTimestamp } from "metabase/lib/time"; -import { compareVersions, numberToWord } from "metabase/lib/utils"; +import { numberToWord } from "metabase/lib/utils"; import type { PasswordComplexity, SettingKey, @@ -145,13 +145,6 @@ class MetabaseSettings { return this.get("cloud-gateway-ips") || []; } - /** - * @deprecated use getSetting(state, "has-user-setup") - */ - hasUserSetup() { - return this.get("has-user-setup"); - } - /** * @deprecated use getSetting(state, "hide-embed-branding?") */ @@ -187,17 +180,6 @@ class MetabaseSettings { return this.get("other-sso-enabled?"); } - /** - * @deprecated use getSetting(state, ...) - */ - isSsoEnabled() { - return ( - this.isLdapEnabled() || - this.isGoogleAuthEnabled() || - this.isOtherSsoEnabled() - ); - } - /** * @deprecated use getSetting(state, "enable-password-login") */ @@ -273,35 +255,6 @@ class MetabaseSettings { } } - /** - * @deprecated use getLearnUrl - */ - learnUrl(path = "") { - // eslint-disable-next-line no-unconditional-metabase-links-render -- This is the implementation of MetabaseSettings.learnUrl() - return `https://www.metabase.com/learn/${path}`; - } - - /** - * @deprecated use getStoreUrl - */ - storeUrl(path = "") { - return `https://store.metabase.com/${path}`; - } - - migrateToCloudGuideUrl() { - return "https://www.metabase.com/cloud/docs/migrate/guide"; - } - - newVersionAvailable() { - const result = compareVersions(this.currentVersion(), this.latestVersion()); - return result != null && result < 0; - } - - versionIsLatest() { - const result = compareVersions(this.currentVersion(), this.latestVersion()); - return result != null && result >= 0; - } - /** * @deprecated use getSetting(state, "version-info") */ diff --git a/frontend/src/metabase/lib/utils.ts b/frontend/src/metabase/lib/utils.ts index 4da633ea070..e7434b2cc97 100644 --- a/frontend/src/metabase/lib/utils.ts +++ b/frontend/src/metabase/lib/utils.ts @@ -138,4 +138,26 @@ export function compareVersions( return 0; } +export function newVersionAvailable({ + currentVersion, + latestVersion, +}: { + currentVersion: string; + latestVersion: string; +}) { + const result = compareVersions(currentVersion, latestVersion); + return result != null && result < 0; +} + +export function versionIsLatest({ + currentVersion, + latestVersion, +}: { + currentVersion: string; + latestVersion: string; +}) { + const result = compareVersions(currentVersion, latestVersion); + return result != null && result >= 0; +} + export const isEEBuild = () => PLUGIN_IS_EE_BUILD.isEEBuild(); diff --git a/frontend/src/metabase/models/containers/NewModelOptions/NewModelOptions.tsx b/frontend/src/metabase/models/containers/NewModelOptions/NewModelOptions.tsx index bf9b6cdb5e0..14b1db02c66 100644 --- a/frontend/src/metabase/models/containers/NewModelOptions/NewModelOptions.tsx +++ b/frontend/src/metabase/models/containers/NewModelOptions/NewModelOptions.tsx @@ -9,12 +9,11 @@ import { Grid } from "metabase/components/Grid"; import CS from "metabase/css/core/index.css"; import Databases from "metabase/entities/databases"; import { useSelector } from "metabase/lib/redux"; -import MetabaseSettings from "metabase/lib/settings"; import * as Urls from "metabase/lib/urls"; import NewModelOption from "metabase/models/components/NewModelOption"; import { NoDatabasesEmptyState } from "metabase/reference/databases/NoDatabasesEmptyState"; import { getHasDataAccess, getHasNativeWrite } from "metabase/selectors/data"; -import { getSetting } from "metabase/selectors/settings"; +import { getLearnUrl, getSetting } from "metabase/selectors/settings"; import { getShowMetabaseLinks } from "metabase/selectors/whitelabel"; import { @@ -23,7 +22,7 @@ import { OptionsRoot, } from "./NewModelOptions.styled"; -const EDUCATIONAL_LINK = MetabaseSettings.learnUrl("data-modeling/models"); +const EDUCATIONAL_LINK = getLearnUrl("data-modeling/models"); interface NewModelOptionsProps { location: Location; diff --git a/frontend/src/metabase/nav/components/PaymentBanner/PaymentBanner.tsx b/frontend/src/metabase/nav/components/PaymentBanner/PaymentBanner.tsx index 13bf516c5a5..69dd09c2d2f 100644 --- a/frontend/src/metabase/nav/components/PaymentBanner/PaymentBanner.tsx +++ b/frontend/src/metabase/nav/components/PaymentBanner/PaymentBanner.tsx @@ -3,7 +3,7 @@ import { jt, t } from "ttag"; import Banner from "metabase/components/Banner"; import ExternalLink from "metabase/core/components/ExternalLink"; import CS from "metabase/css/core/index.css"; -import MetabaseSettings from "metabase/lib/settings"; +import { getStoreUrl } from "metabase/selectors/settings"; import type { TokenStatus } from "metabase-types/api"; interface PaymentBannerProps { @@ -19,7 +19,7 @@ export const PaymentBanner = ({ isAdmin, tokenStatus }: PaymentBannerProps) => { <ExternalLink key="payment-past-due" className={CS.link} - href={MetabaseSettings.storeUrl()} + href={getStoreUrl()} > {t`review your payment settings`} </ExternalLink> @@ -33,7 +33,7 @@ export const PaymentBanner = ({ isAdmin, tokenStatus }: PaymentBannerProps) => { <ExternalLink key="payment-unpaid" className={CS.link} - href={MetabaseSettings.storeUrl()} + href={getStoreUrl()} > {t`Review your payment settings`} </ExternalLink> diff --git a/frontend/src/metabase/pulse/components/RecipientPicker.tsx b/frontend/src/metabase/pulse/components/RecipientPicker.tsx index d488ad5c8ae..24ff861deee 100644 --- a/frontend/src/metabase/pulse/components/RecipientPicker.tsx +++ b/frontend/src/metabase/pulse/components/RecipientPicker.tsx @@ -2,12 +2,12 @@ import cx from "classnames"; import { t } from "ttag"; import _ from "underscore"; +import { useSetting } from "metabase/common/hooks"; import TokenField from "metabase/components/TokenField"; import UserAvatar from "metabase/components/UserAvatar"; import CS from "metabase/css/core/index.css"; import { isEmail } from "metabase/lib/email"; import { recipientIsValid } from "metabase/lib/pulse"; -import MetabaseSettings from "metabase/lib/settings"; import { Text } from "metabase/ui"; import type { User } from "metabase-types/api"; @@ -33,7 +33,7 @@ export const RecipientPicker = ({ }; const isValid = recipients.every(r => recipientIsValid(r)); - const domains = MetabaseSettings.subscriptionAllowedDomains().join(", "); + const domains = useSetting("subscription-allowed-domains"); return ( <div> @@ -68,7 +68,9 @@ export const RecipientPicker = ({ updateOnInputBlur /> </div> - {!isValid && <ErrorMessage>{invalidRecipientText(domains)}</ErrorMessage>} + {domains && !isValid && ( + <ErrorMessage>{invalidRecipientText(domains)}</ErrorMessage> + )} </div> ); }; diff --git a/frontend/src/metabase/pulse/components/RecipientPicker.unit.spec.tsx b/frontend/src/metabase/pulse/components/RecipientPicker.unit.spec.tsx index b7afd84971d..c3673f3798e 100644 --- a/frontend/src/metabase/pulse/components/RecipientPicker.unit.spec.tsx +++ b/frontend/src/metabase/pulse/components/RecipientPicker.unit.spec.tsx @@ -1,6 +1,6 @@ -import { render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; +import { renderWithProviders, screen } from "__support__/ui"; import { RecipientPicker } from "metabase/pulse/components/RecipientPicker"; import { createMockUser } from "metabase-types/api/mocks"; @@ -18,7 +18,7 @@ const TEST_USERS = [ describe("recipient picker", () => { describe("focus", () => { it("should be focused if there are no recipients", async () => { - render( + renderWithProviders( <RecipientPicker recipients={[]} users={TEST_USERS} @@ -31,7 +31,7 @@ describe("recipient picker", () => { expect(await screen.findByText("Dustin")).toBeInTheDocument(); }); it("should not be focused if there are existing recipients", () => { - render( + renderWithProviders( <RecipientPicker recipients={[TEST_USERS[0]]} users={TEST_USERS} @@ -49,7 +49,7 @@ describe("recipient picker", () => { it("should track additions", async () => { const onRecipientsChange = jest.fn(); - render( + renderWithProviders( <RecipientPicker recipients={[TEST_USERS[0]]} users={TEST_USERS} @@ -70,7 +70,7 @@ describe("recipient picker", () => { it("should track removals", async () => { const onRecipientsChange = jest.fn(); - render( + renderWithProviders( <RecipientPicker recipients={[TEST_USERS[0], TEST_USERS[1], TEST_USERS[2]]} users={TEST_USERS} @@ -92,7 +92,7 @@ describe("recipient picker", () => { it("should support adding emails", async () => { const onRecipientsChange = jest.fn(); - render( + renderWithProviders( <RecipientPicker recipients={[TEST_USERS[0]]} users={TEST_USERS} diff --git a/frontend/src/metabase/query_builder/components/VisualizationError/VisualizationError.tsx b/frontend/src/metabase/query_builder/components/VisualizationError/VisualizationError.tsx index 814fa52b36a..a1afdc4e552 100644 --- a/frontend/src/metabase/query_builder/components/VisualizationError/VisualizationError.tsx +++ b/frontend/src/metabase/query_builder/components/VisualizationError/VisualizationError.tsx @@ -8,7 +8,7 @@ import CS from "metabase/css/core/index.css"; import QueryBuilderS from "metabase/css/query_builder.module.css"; import { getEngineNativeType } from "metabase/lib/engine"; import { useSelector } from "metabase/lib/redux"; -import MetabaseSettings from "metabase/lib/settings"; +import { getLearnUrl } from "metabase/selectors/settings"; import { getShowMetabaseLinks } from "metabase/selectors/whitelabel"; import * as Lib from "metabase-lib"; import type Question from "metabase-lib/v1/Question"; @@ -115,9 +115,7 @@ export function VisualizationError({ </QueryErrorHeader> <QueryErrorMessage>{processedError}</QueryErrorMessage> {isSql && showMetabaseLinks && ( - <QueryErrorLink - href={MetabaseSettings.learnUrl("debugging-sql/sql-syntax")} - > + <QueryErrorLink href={getLearnUrl("debugging-sql/sql-syntax")}> {t`Learn how to debug SQL errors`} </QueryErrorLink> )} diff --git a/frontend/src/metabase/routes.jsx b/frontend/src/metabase/routes.jsx index 8955d21d482..383d6822af8 100644 --- a/frontend/src/metabase/routes.jsx +++ b/frontend/src/metabase/routes.jsx @@ -26,7 +26,6 @@ import { ModalRoute } from "metabase/hoc/ModalRoute"; import { Route } from "metabase/hoc/Title"; import { HomePage } from "metabase/home/components/HomePage"; import { trackPageView } from "metabase/lib/analytics"; -import MetabaseSettings from "metabase/lib/settings"; import DatabaseMetabotApp from "metabase/metabot/containers/DatabaseMetabotApp"; import ModelMetabotApp from "metabase/metabot/containers/ModelMetabotApp"; import NewModelOptions from "metabase/models/containers/NewModelOptions"; @@ -63,10 +62,13 @@ import { IsAuthenticated, IsNotAuthenticated, } from "./route-guards"; +import { getSetting } from "./selectors/settings"; import { getApplicationName } from "./selectors/whitelabel"; export const getRoutes = store => { const applicationName = getApplicationName(store.getState()); + const hasUserSetup = getSetting(store.getState(), "has-user-setup"); + return ( <Route title={applicationName} component={App}> {/* SETUP */} @@ -74,7 +76,7 @@ export const getRoutes = store => { path="/setup" component={Setup} onEnter={(nextState, replace) => { - if (MetabaseSettings.hasUserSetup()) { + if (hasUserSetup) { replace("/"); } trackPageView(location.pathname); diff --git a/frontend/src/metabase/selectors/settings.ts b/frontend/src/metabase/selectors/settings.ts index 296f8e6156b..e64454b1f9a 100644 --- a/frontend/src/metabase/selectors/settings.ts +++ b/frontend/src/metabase/selectors/settings.ts @@ -27,10 +27,19 @@ export const getSetting = <S extends State, T extends GetSettingKey<S>>( return setting; }; +export const isSsoEnabled = (state: State) => + getSetting(state, "ldap-enabled") || + getSetting(state, "google-auth-enabled") || + getSetting(state, "saml-enabled") || + getSetting(state, "other-sso-enabled?"); + export const getStoreUrl = (path = "") => { return `https://store.metabase.com/${path}`; }; +export const migrateToCloudGuideUrl = () => + "https://www.metabase.com/cloud/docs/migrate/guide"; + export const getLearnUrl = (path = "") => { // eslint-disable-next-line no-unconditional-metabase-links-render -- This is the implementation of getLearnUrl() return `https://www.metabase.com/learn/${path}`; diff --git a/frontend/src/metabase/setup/components/CloudMigrationHelp/CloudMigrationHelp.tsx b/frontend/src/metabase/setup/components/CloudMigrationHelp/CloudMigrationHelp.tsx index c3059d465c6..d7cc366f6ab 100644 --- a/frontend/src/metabase/setup/components/CloudMigrationHelp/CloudMigrationHelp.tsx +++ b/frontend/src/metabase/setup/components/CloudMigrationHelp/CloudMigrationHelp.tsx @@ -2,7 +2,7 @@ import { t } from "ttag"; import HelpCard from "metabase/components/HelpCard"; import { useSelector } from "metabase/lib/redux"; -import MetabaseSettings from "metabase/lib/settings"; +import { migrateToCloudGuideUrl } from "metabase/selectors/settings"; import { getIsHosted } from "../../selectors"; import { useStep } from "../../useStep"; @@ -18,7 +18,7 @@ export const CloudMigrationHelp = () => { <SetupCardContainer isVisible={isVisible}> <HelpCard title={t`Migrating from self-hosted?`} - helpUrl={MetabaseSettings.migrateToCloudGuideUrl()} + helpUrl={migrateToCloudGuideUrl()} >{t`Check out our docs for how to migrate your self-hosted instance to Cloud.`}</HelpCard> </SetupCardContainer> ); -- GitLab