From f5608c6da14dd1fe89f97cb451a01761a4a8c50d Mon Sep 17 00:00:00 2001 From: Alexander Polyankin <alexander.polyankin@metabase.com> Date: Thu, 17 Nov 2022 15:54:02 +0200 Subject: [PATCH] Migrate GoogleSettingsForm to formik and add unit tests (#26293) --- .../src/metabase-enterprise/auth/index.js | 26 ---- .../src/metabase-types/api/mocks/settings.ts | 36 +++++- frontend/src/metabase-types/api/settings.ts | 25 +++- .../GoogleAuthForm/GoogleAuthForm.styled.tsx | 16 +++ .../GoogleAuthForm/GoogleAuthForm.tsx | 121 ++++++++++++++++++ .../GoogleAuthForm.unit.spec.tsx | 80 ++++++++++++ .../auth/components/GoogleAuthForm/index.ts | 1 + .../metabase/admin/settings/auth/constants.ts | 23 +++- .../GoogleAuthForm/GoogleAuthForm.tsx | 15 +++ .../auth/containers/GoogleAuthForm/index.ts | 1 + .../SettingsGoogleForm.styled.tsx | 21 --- .../SettingsGoogleForm/SettingsGoogleForm.tsx | 107 ---------------- .../components/SettingsGoogleForm/index.ts | 1 - .../core/hooks/use-form-submit/types.ts | 2 +- .../hooks/use-form-submit/use-form-submit.ts | 16 ++- .../metabase/plugins/builtin/auth/google.js | 18 +-- src/metabase/api/google.clj | 2 +- 17 files changed, 328 insertions(+), 183 deletions(-) create mode 100644 frontend/src/metabase/admin/settings/auth/components/GoogleAuthForm/GoogleAuthForm.styled.tsx create mode 100644 frontend/src/metabase/admin/settings/auth/components/GoogleAuthForm/GoogleAuthForm.tsx create mode 100644 frontend/src/metabase/admin/settings/auth/components/GoogleAuthForm/GoogleAuthForm.unit.spec.tsx create mode 100644 frontend/src/metabase/admin/settings/auth/components/GoogleAuthForm/index.ts create mode 100644 frontend/src/metabase/admin/settings/auth/containers/GoogleAuthForm/GoogleAuthForm.tsx create mode 100644 frontend/src/metabase/admin/settings/auth/containers/GoogleAuthForm/index.ts delete mode 100644 frontend/src/metabase/admin/settings/components/SettingsGoogleForm/SettingsGoogleForm.styled.tsx delete mode 100644 frontend/src/metabase/admin/settings/components/SettingsGoogleForm/SettingsGoogleForm.tsx delete mode 100644 frontend/src/metabase/admin/settings/components/SettingsGoogleForm/index.ts diff --git a/enterprise/frontend/src/metabase-enterprise/auth/index.js b/enterprise/frontend/src/metabase-enterprise/auth/index.js index 7bce7a15dcb..43e30dd81e4 100644 --- a/enterprise/frontend/src/metabase-enterprise/auth/index.js +++ b/enterprise/frontend/src/metabase-enterprise/auth/index.js @@ -14,7 +14,6 @@ import GroupMappingsWidget from "metabase/admin/settings/components/widgets/Grou import SecretKeyWidget from "metabase/admin/settings/components/widgets/SecretKeyWidget"; import SessionTimeoutSetting from "metabase-enterprise/auth/components/SessionTimeoutSetting"; -import SettingsGoogleForm from "metabase/admin/settings/components/SettingsGoogleForm"; import { createSessionMiddleware } from "../auth/middleware/session-middleware"; import SettingsSAMLForm from "./components/SettingsSAMLForm"; import SettingsJWTForm from "./components/SettingsJWTForm"; @@ -264,29 +263,4 @@ PLUGIN_ADMIN_SETTINGS_UPDATES.push(sections => ]), ); -PLUGIN_ADMIN_SETTINGS_UPDATES.push(sections => ({ - ...sections, - "authentication/google": { - component: SettingsGoogleForm, - settings: [ - { - key: "google-auth-client-id", - required: true, - autoFocus: true, - }, - { - // Default to OSS fields if enterprise SSO is not enabled - ...sections["authentication/google"].settings.find( - setting => setting.key === "google-auth-auto-create-accounts-domain", - ), - ...(hasPremiumFeature("sso") && { - placeholder: "mycompany.com, example.com.br, otherdomain.co.uk", - description: - "Allow users to sign up on their own if their Google account email address is from one of the domains you specify here:", - }), - }, - ], - }, -})); - PLUGIN_REDUX_MIDDLEWARES.push(createSessionMiddleware([LOGIN, LOGIN_GOOGLE])); diff --git a/frontend/src/metabase-types/api/mocks/settings.ts b/frontend/src/metabase-types/api/mocks/settings.ts index 8e58d7c5a17..2690f43845f 100644 --- a/frontend/src/metabase-types/api/mocks/settings.ts +++ b/frontend/src/metabase-types/api/mocks/settings.ts @@ -1,4 +1,11 @@ -import { Engine, FontFile, Settings, Version } from "metabase-types/api"; +import { + Engine, + FontFile, + SettingDefinition, + Settings, + TokenFeatures, + Version, +} from "metabase-types/api"; export const createMockEngine = (opts?: Partial<Engine>): Engine => ({ "driver-name": "PostgreSQL", @@ -58,6 +65,30 @@ export const createMockTokenStatus = () => ({ "valid-thru": "2022-12-30T23:00:00Z", }); +export const createMockTokenFeatures = ( + opts?: Partial<TokenFeatures>, +): TokenFeatures => ({ + advanced_config: false, + advanced_permissions: false, + audit_app: false, + content_management: false, + embedding: false, + hosting: false, + sandboxes: false, + sso: false, + whitelabel: false, + ...opts, +}); + +export const createMockSettingDefinition = ( + opts?: Partial<SettingDefinition>, +): SettingDefinition => ({ + key: "key", + env_name: "", + is_env_setting: false, + ...opts, +}); + export const createMockSettings = (opts?: Partial<Settings>): Settings => ({ "application-font": "Lato", "application-font-files": [], @@ -92,7 +123,8 @@ export const createMockSettings = (opts?: Partial<Settings>): Settings => ({ "slack-files-channel": null, "slack-token": null, "slack-token-valid?": false, - "token-status": createMockTokenStatus(), + "token-features": createMockTokenFeatures(), + "token-status": null, engines: createMockEngines(), version: createMockVersion(), ...opts, diff --git a/frontend/src/metabase-types/api/settings.ts b/frontend/src/metabase-types/api/settings.ts index d717dabb6d6..2fc8d57a4a1 100644 --- a/frontend/src/metabase-types/api/settings.ts +++ b/frontend/src/metabase-types/api/settings.ts @@ -58,9 +58,27 @@ export type LoadingMessage = export type TokenStatusStatus = "unpaid" | "past-due" | string; -export type TokenStatus = { +export interface TokenStatus { status?: TokenStatusStatus; -}; +} + +export interface TokenFeatures { + advanced_config: boolean; + advanced_permissions: boolean; + audit_app: boolean; + content_management: boolean; + embedding: boolean; + hosting: boolean; + sandboxes: boolean; + sso: boolean; + whitelabel: boolean; +} + +export interface SettingDefinition { + key: string; + env_name: string; + is_env_setting: boolean; +} export interface Settings { "application-font": string; @@ -96,7 +114,8 @@ export interface Settings { "slack-files-channel": string | null; "slack-token": string | null; "slack-token-valid?": boolean; - "token-status": TokenStatus | undefined; + "token-features": TokenFeatures; + "token-status": TokenStatus | null; engines: Record<string, Engine>; version: Version; } diff --git a/frontend/src/metabase/admin/settings/auth/components/GoogleAuthForm/GoogleAuthForm.styled.tsx b/frontend/src/metabase/admin/settings/auth/components/GoogleAuthForm/GoogleAuthForm.styled.tsx new file mode 100644 index 00000000000..d049cc0d7b9 --- /dev/null +++ b/frontend/src/metabase/admin/settings/auth/components/GoogleAuthForm/GoogleAuthForm.styled.tsx @@ -0,0 +1,16 @@ +import styled from "@emotion/styled"; +import Form from "metabase/core/components/Form"; +import { color } from "metabase/lib/colors"; + +export const GoogleForm = styled(Form)` + margin: 0 1rem; + max-width: 32.5rem; +`; + +export const GoogleFormHeader = styled.h2` + margin-top: 1rem; +`; + +export const GoogleFormCaption = styled.p` + color: ${color("text-medium")}; +`; diff --git a/frontend/src/metabase/admin/settings/auth/components/GoogleAuthForm/GoogleAuthForm.tsx b/frontend/src/metabase/admin/settings/auth/components/GoogleAuthForm/GoogleAuthForm.tsx new file mode 100644 index 00000000000..4c165cff514 --- /dev/null +++ b/frontend/src/metabase/admin/settings/auth/components/GoogleAuthForm/GoogleAuthForm.tsx @@ -0,0 +1,121 @@ +import React, { useMemo } from "react"; +import { jt, t } from "ttag"; +import _ from "underscore"; +import MetabaseSettings from "metabase/lib/settings"; +import ExternalLink from "metabase/core/components/ExternalLink"; +import FormProvider from "metabase/core/components/FormProvider"; +import FormInput from "metabase/core/components/FormInput"; +import FormSubmitButton from "metabase/core/components/FormSubmitButton"; +import FormErrorMessage from "metabase/core/components/FormErrorMessage"; +import Breadcrumbs from "metabase/components/Breadcrumbs"; +import { SettingDefinition, Settings } from "metabase-types/api"; +import { GOOGLE_SCHEMA } from "../../constants"; +import { + GoogleForm, + GoogleFormCaption, + GoogleFormHeader, +} from "./GoogleAuthForm.styled"; + +const ENABLED_KEY = "google-auth-enabled"; +const CLIENT_ID_KEY = "google-auth-client-id"; +const DOMAIN_KEY = "google-auth-auto-create-accounts-domain"; + +const BREADCRUMBS = [ + [t`Authentication`, "/admin/settings/authentication"], + [t`Google Sign-In`], +]; + +export interface GoogleAuthFormProps { + elements?: SettingDefinition[]; + settingValues?: Partial<Settings>; + isEnabled: boolean; + isSsoEnabled: boolean; + onSubmit: (settingValues: Partial<Settings>) => void; +} + +const GoogleAuthForm = ({ + elements = [], + settingValues = {}, + isEnabled, + isSsoEnabled, + onSubmit, +}: GoogleAuthFormProps): JSX.Element => { + const settings = useMemo(() => { + return _.indexBy(elements, "key"); + }, [elements]); + + const initialValues = useMemo(() => { + const values = GOOGLE_SCHEMA.cast(settingValues, { stripUnknown: true }); + return { ...values, [ENABLED_KEY]: true }; + }, [settingValues]); + + return ( + <FormProvider + initialValues={initialValues} + enableReinitialize + validationSchema={GOOGLE_SCHEMA} + validationContext={settings} + onSubmit={onSubmit} + > + {({ dirty }) => ( + <GoogleForm disabled={!dirty}> + <Breadcrumbs crumbs={BREADCRUMBS} /> + <GoogleFormHeader>{t`Sign in with Google`}</GoogleFormHeader> + <GoogleFormCaption> + {t`Allows users with existing Metabase accounts to login with a Google account that matches their email address in addition to their Metabase username and password.`} + </GoogleFormCaption> + <GoogleFormCaption> + {jt`To allow users to sign in with Google you'll need to give Metabase a Google Developers console application client ID. It only takes a few steps and instructions on how to create a key can be found ${( + <ExternalLink key="link" href={getDocsLink()}> + {t`here`} + </ExternalLink> + )}.`} + </GoogleFormCaption> + <FormInput + name={CLIENT_ID_KEY} + title={t`Client ID`} + placeholder={t`{your-client-id}.apps.googleusercontent.com`} + {...getFormFieldProps(settings[CLIENT_ID_KEY])} + /> + <FormInput + name={DOMAIN_KEY} + title={t`Domain`} + description={ + isSsoEnabled + ? t`Allow users to sign up on their own if their Google account email address is from one of the domains you specify here:` + : t`Allow users to sign up on their own if their Google account email address is from:` + } + placeholder={ + isSsoEnabled + ? "mycompany.com, example.com.br, otherdomain.co.uk" + : "mycompany.com" + } + nullable + {...getFormFieldProps(settings[DOMAIN_KEY])} + /> + <FormSubmitButton + title={isEnabled ? t`Save changes` : t`Save and enable`} + primary + disabled={!dirty} + /> + <FormErrorMessage /> + </GoogleForm> + )} + </FormProvider> + ); +}; + +const getFormFieldProps = (setting?: SettingDefinition) => { + if (setting?.is_env_setting) { + return { placeholder: t`Using ${setting.env_name}`, readOnly: true }; + } +}; + +const getDocsLink = (): string => { + return MetabaseSettings.docsUrl( + "people-and-groups/google-and-ldap", + "enabling-google-sign-in", + ); +}; + +export default GoogleAuthForm; diff --git a/frontend/src/metabase/admin/settings/auth/components/GoogleAuthForm/GoogleAuthForm.unit.spec.tsx b/frontend/src/metabase/admin/settings/auth/components/GoogleAuthForm/GoogleAuthForm.unit.spec.tsx new file mode 100644 index 00000000000..8224901bb35 --- /dev/null +++ b/frontend/src/metabase/admin/settings/auth/components/GoogleAuthForm/GoogleAuthForm.unit.spec.tsx @@ -0,0 +1,80 @@ +import React from "react"; +import { render, screen, waitFor } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { createMockSettingDefinition } from "metabase-types/api/mocks"; +import GoogleAuthForm, { GoogleAuthFormProps } from "./GoogleAuthForm"; + +describe("GoogleAuthForm", () => { + it("should submit the form", async () => { + const props = getProps(); + + render(<GoogleAuthForm {...props} />); + userEvent.type(screen.getByLabelText("Client ID"), "id.test"); + await waitFor(() => expect(screen.getByText(/Save/)).toBeEnabled()); + screen.getByText("Save and enable").click(); + + await waitFor(() => { + expect(props.onSubmit).toHaveBeenCalledWith( + { + "google-auth-enabled": true, + "google-auth-client-id": "id.test", + "google-auth-auto-create-accounts-domain": null, + }, + expect.anything(), + ); + }); + }); + + it("should not submit the form without required fields", () => { + const props = getProps({ + isEnabled: true, + elements: [ + createMockSettingDefinition({ + key: "google-auth-client-id", + is_env_setting: false, + }), + ], + }); + + render(<GoogleAuthForm {...props} />); + userEvent.type(screen.getByLabelText("Domain"), "domain.test"); + + expect(screen.getByText("Save changes")).toBeDisabled(); + }); + + it("should submit the form when required fields set by env vars", async () => { + const props = getProps({ + isEnabled: true, + elements: [ + createMockSettingDefinition({ + key: "google-auth-client-id", + is_env_setting: true, + }), + ], + }); + + render(<GoogleAuthForm {...props} />); + userEvent.type(screen.getByLabelText("Domain"), "domain.test"); + screen.getByText("Save changes").click(); + + await waitFor(() => { + expect(props.onSubmit).toHaveBeenCalledWith( + { + "google-auth-enabled": true, + "google-auth-client-id": null, + "google-auth-auto-create-accounts-domain": "domain.test", + }, + expect.anything(), + ); + }); + }); +}); + +const getProps = ( + opts?: Partial<GoogleAuthFormProps>, +): GoogleAuthFormProps => ({ + isEnabled: false, + isSsoEnabled: false, + onSubmit: jest.fn(), + ...opts, +}); diff --git a/frontend/src/metabase/admin/settings/auth/components/GoogleAuthForm/index.ts b/frontend/src/metabase/admin/settings/auth/components/GoogleAuthForm/index.ts new file mode 100644 index 00000000000..34832e7400e --- /dev/null +++ b/frontend/src/metabase/admin/settings/auth/components/GoogleAuthForm/index.ts @@ -0,0 +1 @@ +export { default } from "./GoogleAuthForm"; diff --git a/frontend/src/metabase/admin/settings/auth/constants.ts b/frontend/src/metabase/admin/settings/auth/constants.ts index fdb15c25684..025af06b677 100644 --- a/frontend/src/metabase/admin/settings/auth/constants.ts +++ b/frontend/src/metabase/admin/settings/auth/constants.ts @@ -1,18 +1,29 @@ import * as Yup from "yup"; +import * as Errors from "metabase/core/utils/errors"; +import { SettingDefinition } from "metabase-types/api"; + +const REQUIRED_SCHEMA = { + is: (isEnabled: boolean, setting?: SettingDefinition) => + isEnabled && !setting?.is_env_setting, + then: (schema: Yup.AnySchema) => schema.required(Errors.required), +}; export const GOOGLE_SCHEMA = Yup.object({ - "google-auth-enabled": Yup.boolean().default(false), - "google-auth-client-id": Yup.string().nullable().default(null), + "google-auth-enabled": Yup.boolean().nullable().default(false), + "google-auth-client-id": Yup.string() + .nullable() + .default(null) + .when(["google-auth-enabled", "$google-auth-client-id"], REQUIRED_SCHEMA), "google-auth-auto-create-accounts-domain": Yup.string() .nullable() .default(null), }); export const LDAP_SCHEMA = Yup.object({ - "ldap-enabled": Yup.boolean().default(false), + "ldap-enabled": Yup.boolean().nullable().default(false), "ldap-host": Yup.string().nullable().default(null), "ldap-port": Yup.number().nullable().default(null), - "ldap-security": Yup.string().default("none"), + "ldap-security": Yup.string().nullable().default("none"), "ldap-bind-dn": Yup.string().nullable().default(null), "ldap-password": Yup.string().nullable().default(null), "ldap-user-base": Yup.string().nullable().default(null), @@ -20,7 +31,7 @@ export const LDAP_SCHEMA = Yup.object({ "ldap-attribute-email": Yup.string().nullable().default(null), "ldap-attribute-firstname": Yup.string().nullable().default(null), "ldap-attribute-lastname": Yup.string().nullable().default(null), - "ldap-group-sync": Yup.boolean().default(false), + "ldap-group-sync": Yup.boolean().nullable().default(false), "ldap-group-base": Yup.string().nullable().default(null), - "ldap-group-mappings": Yup.object().default(null), + "ldap-group-mappings": Yup.object().nullable().default(null), }); diff --git a/frontend/src/metabase/admin/settings/auth/containers/GoogleAuthForm/GoogleAuthForm.tsx b/frontend/src/metabase/admin/settings/auth/containers/GoogleAuthForm/GoogleAuthForm.tsx new file mode 100644 index 00000000000..2406e91ba25 --- /dev/null +++ b/frontend/src/metabase/admin/settings/auth/containers/GoogleAuthForm/GoogleAuthForm.tsx @@ -0,0 +1,15 @@ +import { connect } from "react-redux"; +import { State } from "metabase-types/store"; +import GoogleAuthForm from "../../components/GoogleAuthForm"; +import { updateGoogleSettings } from "../../../settings"; + +const mapStateToProps = (state: State) => ({ + isEnabled: state.settings.values["google-auth-enabled"], + isSsoEnabled: state.settings.values["token-features"].sso, +}); + +const mapDispatchToProps = { + onSubmit: updateGoogleSettings, +}; + +export default connect(mapStateToProps, mapDispatchToProps)(GoogleAuthForm); diff --git a/frontend/src/metabase/admin/settings/auth/containers/GoogleAuthForm/index.ts b/frontend/src/metabase/admin/settings/auth/containers/GoogleAuthForm/index.ts new file mode 100644 index 00000000000..34832e7400e --- /dev/null +++ b/frontend/src/metabase/admin/settings/auth/containers/GoogleAuthForm/index.ts @@ -0,0 +1 @@ +export { default } from "./GoogleAuthForm"; diff --git a/frontend/src/metabase/admin/settings/components/SettingsGoogleForm/SettingsGoogleForm.styled.tsx b/frontend/src/metabase/admin/settings/components/SettingsGoogleForm/SettingsGoogleForm.styled.tsx deleted file mode 100644 index 35ad46b6fed..00000000000 --- a/frontend/src/metabase/admin/settings/components/SettingsGoogleForm/SettingsGoogleForm.styled.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import styled from "@emotion/styled"; -import Form from "metabase/containers/FormikForm"; -import { color } from "metabase/lib/colors"; - -export const FormRoot = styled(Form)` - margin: 0 1rem; - max-width: 32.5rem; -`; - -export const FormHeader = styled.h2` - margin-top: 1rem; -`; - -export const FormCaption = styled.p` - color: ${color("text-medium")}; -`; - -export const FormSection = styled.div` - display: flex; - gap: 0.5rem; -`; diff --git a/frontend/src/metabase/admin/settings/components/SettingsGoogleForm/SettingsGoogleForm.tsx b/frontend/src/metabase/admin/settings/components/SettingsGoogleForm/SettingsGoogleForm.tsx deleted file mode 100644 index 7dfcd355b33..00000000000 --- a/frontend/src/metabase/admin/settings/components/SettingsGoogleForm/SettingsGoogleForm.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import React, { useCallback } from "react"; -import { connect } from "react-redux"; -import { jt, t } from "ttag"; -import MetabaseSettings from "metabase/lib/settings"; -import ExternalLink from "metabase/core/components/ExternalLink"; -import Breadcrumbs from "metabase/components/Breadcrumbs"; -import { - FormField, - FormMessage, - FormSubmit, -} from "metabase/containers/FormikForm"; -import { updateGoogleSettings } from "metabase/admin/settings/settings"; -import { settingToFormField } from "metabase/admin/settings/utils"; -import { - FormCaption, - FormSection, - FormHeader, - FormRoot, -} from "./SettingsGoogleForm.styled"; - -export interface SettingElement { - key: string; -} - -export interface SettingsGoogleFormProps { - elements?: SettingElement[]; - settingValues?: Record<string, unknown>; - onSubmit: (settingValues: Record<string, unknown>) => void; -} - -const SettingsGoogleForm = ({ - elements = [], - settingValues = {}, - onSubmit, -}: SettingsGoogleFormProps) => { - const isEnabled = Boolean(settingValues["google-auth-enabled"]); - - const handleSubmit = useCallback( - (values: Record<string, unknown>) => { - return onSubmit({ ...values, "google-auth-enabled": true }); - }, - [onSubmit], - ); - - return ( - <FormRoot - initialValues={settingValues} - disablePristineSubmit - overwriteOnInitialValuesChange - onSubmit={handleSubmit} - > - <Breadcrumbs - crumbs={[ - [t`Authentication`, "/admin/settings/authentication"], - [t`Google Sign-In`], - ]} - /> - <FormHeader>{t`Sign in with Google`}</FormHeader> - <FormCaption> - {t`Allows users with existing Metabase accounts to login with a Google account that matches their email address in addition to their Metabase username and password.`} - </FormCaption> - <FormCaption> - {jt`To allow users to sign in with Google you'll need to give Metabase a Google Developers console application client ID. It only takes a few steps and instructions on how to create a key can be found ${( - <ExternalLink key="link" href={getDocsLink()}>{t`here`}</ExternalLink> - )}.`} - </FormCaption> - <FormField - {...getField("google-auth-client-id", elements)} - title={t`Client ID`} - description="" - placeholder={t`{your-client-id}.apps.googleusercontent.com`} - required - autoFocus - /> - <FormField - {...getField("google-auth-auto-create-accounts-domain", elements)} - title={t`Domain`} - /> - <FormSection> - <FormMessage /> - </FormSection> - <FormSection> - <FormSubmit> - {isEnabled ? t`Save changes` : t`Save and enable`} - </FormSubmit> - </FormSection> - </FormRoot> - ); -}; - -const getField = (name: string, elements: SettingElement[]) => { - const setting = elements.find(e => e.key === name) ?? { key: name }; - return settingToFormField(setting); -}; - -const getDocsLink = () => { - return MetabaseSettings.docsUrl( - "people-and-groups/google-and-ldap", - "enabling-google-sign-in", - ); -}; - -const mapDispatchToProps = { - onSubmit: updateGoogleSettings, -}; - -export default connect(null, mapDispatchToProps)(SettingsGoogleForm); diff --git a/frontend/src/metabase/admin/settings/components/SettingsGoogleForm/index.ts b/frontend/src/metabase/admin/settings/components/SettingsGoogleForm/index.ts deleted file mode 100644 index f3c8fda7595..00000000000 --- a/frontend/src/metabase/admin/settings/components/SettingsGoogleForm/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from "./SettingsGoogleForm"; diff --git a/frontend/src/metabase/core/hooks/use-form-submit/types.ts b/frontend/src/metabase/core/hooks/use-form-submit/types.ts index 35c22de0280..a9c5958abf7 100644 --- a/frontend/src/metabase/core/hooks/use-form-submit/types.ts +++ b/frontend/src/metabase/core/hooks/use-form-submit/types.ts @@ -1,7 +1,7 @@ import type { FormikErrors } from "formik"; export interface FormError<T> extends FormErrorData<T> { - data?: FormErrorData<T>; + data?: string | FormErrorData<T>; } export interface FormErrorData<T> { diff --git a/frontend/src/metabase/core/hooks/use-form-submit/use-form-submit.ts b/frontend/src/metabase/core/hooks/use-form-submit/use-form-submit.ts index fd3e9ede5fa..915ea928e44 100644 --- a/frontend/src/metabase/core/hooks/use-form-submit/use-form-submit.ts +++ b/frontend/src/metabase/core/hooks/use-form-submit/use-form-submit.ts @@ -42,11 +42,23 @@ const isFormError = <T>(error: unknown): error is FormError<T> => { }; const getFormErrors = (error: unknown) => { - return isFormError(error) ? error.data?.errors ?? error.errors ?? {} : {}; + if (isFormError(error)) { + if (typeof error.data !== "string") { + return error.data?.errors ?? error.errors ?? {}; + } + } + + return {}; }; const getFormMessage = (error: unknown) => { - return isFormError(error) ? error.data?.message ?? error.message : undefined; + if (isFormError(error)) { + if (typeof error.data !== "string") { + return error.data?.message ?? error.message; + } else { + return error.data; + } + } }; export default useFormSubmit; diff --git a/frontend/src/metabase/plugins/builtin/auth/google.js b/frontend/src/metabase/plugins/builtin/auth/google.js index 1350ce71dc1..75eba2180fa 100644 --- a/frontend/src/metabase/plugins/builtin/auth/google.js +++ b/frontend/src/metabase/plugins/builtin/auth/google.js @@ -8,8 +8,9 @@ import { import MetabaseSettings from "metabase/lib/settings"; -import SettingsGoogleForm from "metabase/admin/settings/components/SettingsGoogleForm"; +import FormikForm from "metabase/containers/FormikForm"; import GoogleAuthCard from "metabase/admin/settings/auth/containers/GoogleAuthCard"; +import GoogleSettingsForm from "metabase/admin/settings/auth/containers/GoogleAuthForm"; PLUGIN_AUTH_PROVIDERS.push(providers => { const googleProvider = { @@ -38,19 +39,10 @@ PLUGIN_ADMIN_SETTINGS_UPDATES.push(sections => PLUGIN_ADMIN_SETTINGS_UPDATES.push(sections => ({ ...sections, "authentication/google": { - component: SettingsGoogleForm, + component: GoogleSettingsForm ?? FormikForm, settings: [ - { - key: "google-auth-client-id", - required: true, - autoFocus: true, - }, - { - key: "google-auth-auto-create-accounts-domain", - description: - "Allow users to sign up on their own if their Google account email address is from:", - placeholder: "mycompany.com", - }, + { key: "google-auth-client-id" }, + { key: "google-auth-auto-create-accounts-domain" }, ], }, })); diff --git a/src/metabase/api/google.clj b/src/metabase/api/google.clj index c07bdb43bf2..925f263937c 100644 --- a/src/metabase/api/google.clj +++ b/src/metabase/api/google.clj @@ -11,7 +11,7 @@ (api/defendpoint PUT "/settings" "Update Google Sign-In related settings. You must be a superuser or have `setting` permission to do this." [:as {{:keys [google-auth-client-id google-auth-enabled google-auth-auto-create-accounts-domain]} :body}] - {google-auth-client-id s/Str + {google-auth-client-id (s/maybe s/Str) google-auth-enabled (s/maybe s/Bool) google-auth-auto-create-accounts-domain (s/maybe s/Str)} (validation/check-has-application-permission :setting) -- GitLab