From e02f7e5ac2c861195e8fb30b25804f19d832d8d6 Mon Sep 17 00:00:00 2001 From: simeng-li Date: Wed, 28 Dec 2022 15:33:31 +0800 Subject: [PATCH] refactor(ui): replace continue api with patchProfile (#2744) --- .../user-identity-verification.test.ts | 2 +- .../user-identity-verification.ts | 2 +- packages/phrases/src/locales/en/errors.ts | 2 +- packages/phrases/src/locales/pt-br/errors.ts | 2 +- packages/phrases/src/locales/zh-cn/errors.ts | 2 +- packages/ui/src/apis/continue.test.ts | 64 ------------ packages/ui/src/apis/continue.ts | 24 ----- packages/ui/src/apis/interaction.ts | 13 +++ ...-continue-set-email-passcode-validation.ts | 2 +- ...se-continue-set-sms-passcode-validation.ts | 11 +-- ...rgot-password-email-passcode-validation.ts | 2 +- ...forgot-password-sms-passcode-validation.ts | 2 +- .../use-identifier-error-alert.ts | 2 +- ...-sign-in-with-email-passcode-validation.ts | 2 +- ...se-sign-in-with-sms-passcode-validation.ts | 2 +- .../UsernameForm/SetUsername/index.test.tsx | 8 +- .../SetUsername/use-set-username.ts | 8 +- .../use-required-profile-error-handler.ts | 97 ++++++++++--------- .../pages/Continue/SetPassword/index.test.tsx | 8 +- .../Continue/SetPassword/use-set-password.ts | 8 +- .../pages/Continue/SetUsername/index.test.tsx | 8 +- packages/ui/src/types/guard.ts | 14 ++- 22 files changed, 110 insertions(+), 175 deletions(-) delete mode 100644 packages/ui/src/apis/continue.test.ts delete mode 100644 packages/ui/src/apis/continue.ts diff --git a/packages/core/src/routes/interaction/verifications/user-identity-verification.test.ts b/packages/core/src/routes/interaction/verifications/user-identity-verification.test.ts index e6f389f7f..dbbb27041 100644 --- a/packages/core/src/routes/interaction/verifications/user-identity-verification.test.ts +++ b/packages/core/src/routes/interaction/verifications/user-identity-verification.test.ts @@ -153,7 +153,7 @@ describe('verifyUserAccount', () => { }; await expect(verifyUserAccount(interaction)).rejects.toMatchError( - new RequestError({ code: 'user.user_not_exist', status: 404 }, { identity: 'email' }) + new RequestError({ code: 'user.user_not_exist', status: 404 }, { identifier: 'email' }) ); expect(findUserByIdentifierMock).toBeCalledWith({ email: 'email' }); diff --git a/packages/core/src/routes/interaction/verifications/user-identity-verification.ts b/packages/core/src/routes/interaction/verifications/user-identity-verification.ts index fc9d514ef..f1c31a28b 100644 --- a/packages/core/src/routes/interaction/verifications/user-identity-verification.ts +++ b/packages/core/src/routes/interaction/verifications/user-identity-verification.ts @@ -26,7 +26,7 @@ const identifyUserByVerifiedEmailOrPhone = async ( assertThat( user, - new RequestError({ code: 'user.user_not_exist', status: 404 }, { identity: identifier.value }) + new RequestError({ code: 'user.user_not_exist', status: 404 }, { identifier: identifier.value }) ); const { id, isSuspended } = user; diff --git a/packages/phrases/src/locales/en/errors.ts b/packages/phrases/src/locales/en/errors.ts index 904ddc169..01d524be4 100644 --- a/packages/phrases/src/locales/en/errors.ts +++ b/packages/phrases/src/locales/en/errors.ts @@ -62,7 +62,7 @@ const errors = { email_or_phone_required_in_profile: 'You need to add an email address or phone number before signing-in.', suspended: 'This account is suspended.', - user_not_exist: 'User with {{ identity }} does not exist.', + user_not_exist: 'User with {{ identifier }} does not exist.', missing_profile: 'You need to provide additional info before signing-in.', }, password: { diff --git a/packages/phrases/src/locales/pt-br/errors.ts b/packages/phrases/src/locales/pt-br/errors.ts index e84922fab..de6e8a8d7 100644 --- a/packages/phrases/src/locales/pt-br/errors.ts +++ b/packages/phrases/src/locales/pt-br/errors.ts @@ -62,7 +62,7 @@ const errors = { email_or_phone_required_in_profile: 'Você precisa adicionar um endereço de e-mail ou número de telefone antes de fazer login.', suspended: 'Esta conta está suspensa.', - user_not_exist: 'O usuário com {{ identity }} não existe', + user_not_exist: 'O usuário com {{ identifier }} não existe', missing_profile: 'Você precisa fornecer informações adicionais antes de fazer login.', }, password: { diff --git a/packages/phrases/src/locales/zh-cn/errors.ts b/packages/phrases/src/locales/zh-cn/errors.ts index ca835aefa..1e0cba2ea 100644 --- a/packages/phrases/src/locales/zh-cn/errors.ts +++ b/packages/phrases/src/locales/zh-cn/errors.ts @@ -59,7 +59,7 @@ const errors = { phone_exists_in_profile: '当前用户已绑定手机号,无需重复操作。', email_or_phone_required_in_profile: '请绑定邮箱地址或手机号码。', suspended: '账号已被禁用。', - user_not_exist: '未找到与 {{ identity }} 相关联的用户。', + user_not_exist: '未找到与 {{ identifier }} 相关联的用户。', missing_profile: '请于登录时提供必要的用户补充信息。', }, password: { diff --git a/packages/ui/src/apis/continue.test.ts b/packages/ui/src/apis/continue.test.ts deleted file mode 100644 index be9ba0caa..000000000 --- a/packages/ui/src/apis/continue.test.ts +++ /dev/null @@ -1,64 +0,0 @@ -import ky from 'ky'; - -import { continueApi } from './continue'; - -jest.mock('ky', () => ({ - extend: () => ky, - post: jest.fn(() => ({ - json: jest.fn(), - })), -})); - -describe('continue API', () => { - const mockKyPost = ky.post as jest.Mock; - - beforeEach(() => { - mockKyPost.mockReturnValueOnce({ - json: () => ({ - redirectTo: '/', - }), - }); - }); - - afterEach(() => { - mockKyPost.mockClear(); - }); - - it('continue with password', async () => { - await continueApi('password', 'password'); - expect(ky.post).toBeCalledWith('/api/session/sign-in/continue/password', { - json: { - password: 'password', - }, - }); - }); - - it('continue with username', async () => { - await continueApi('username', 'username'); - expect(ky.post).toBeCalledWith('/api/session/sign-in/continue/username', { - json: { - username: 'username', - }, - }); - }); - - it('continue with email', async () => { - await continueApi('email', 'email'); - - expect(ky.post).toBeCalledWith('/api/session/sign-in/continue/email', { - json: { - email: 'email', - }, - }); - }); - - it('continue with phone', async () => { - await continueApi('phone', 'phone'); - - expect(ky.post).toBeCalledWith('/api/session/sign-in/continue/sms', { - json: { - phone: 'phone', - }, - }); - }); -}); diff --git a/packages/ui/src/apis/continue.ts b/packages/ui/src/apis/continue.ts deleted file mode 100644 index 52294532b..000000000 --- a/packages/ui/src/apis/continue.ts +++ /dev/null @@ -1,24 +0,0 @@ -import api from './api'; -import { bindSocialAccount } from './social'; - -type Response = { - redirectTo: string; -}; - -const continueApiPrefix = '/api/session/sign-in/continue'; - -type ContinueKey = 'password' | 'username' | 'email' | 'phone'; - -export const continueApi = async (key: ContinueKey, value: string, socialToBind?: string) => { - const result = await api - .post(`${continueApiPrefix}/${key === 'phone' ? 'sms' : key}`, { - json: { [key]: value }, - }) - .json(); - - if (result.redirectTo && socialToBind) { - await bindSocialAccount(socialToBind); - } - - return result; -}; diff --git a/packages/ui/src/apis/interaction.ts b/packages/ui/src/apis/interaction.ts index 7abba3c5a..9cc1723f7 100644 --- a/packages/ui/src/apis/interaction.ts +++ b/packages/ui/src/apis/interaction.ts @@ -152,3 +152,16 @@ export const registerWithVerifiedIdentifier = async (payload: SendPasscodePayloa return api.post(`${interactionPrefix}/submit`).json(); }; + +export const addProfile = async ( + payload: { username: string } | { password: string }, + socialToBind?: string +) => { + await api.patch(`${interactionPrefix}/profile`, { json: payload }); + + if (socialToBind) { + // TODO: bind social account + } + + return api.post(`${interactionPrefix}/submit`).json(); +}; diff --git a/packages/ui/src/containers/PasscodeValidation/use-continue-set-email-passcode-validation.ts b/packages/ui/src/containers/PasscodeValidation/use-continue-set-email-passcode-validation.ts index c4a00fc2e..4d27c4844 100644 --- a/packages/ui/src/containers/PasscodeValidation/use-continue-set-email-passcode-validation.ts +++ b/packages/ui/src/containers/PasscodeValidation/use-continue-set-email-passcode-validation.ts @@ -24,7 +24,7 @@ const useContinueSetEmailPasscodeValidation = (email: string, errorCallback?: () const verifyPasscodeErrorHandlers: ErrorHandlers = useMemo( () => ({ - 'user.email_not_exist': identifierNotExistErrorHandler, + 'user.email_already_in_use': identifierNotExistErrorHandler, ...requiredProfileErrorHandler, ...sharedErrorHandlers, callback: errorCallback, diff --git a/packages/ui/src/containers/PasscodeValidation/use-continue-set-sms-passcode-validation.ts b/packages/ui/src/containers/PasscodeValidation/use-continue-set-sms-passcode-validation.ts index c5848a7f8..5456a6870 100644 --- a/packages/ui/src/containers/PasscodeValidation/use-continue-set-sms-passcode-validation.ts +++ b/packages/ui/src/containers/PasscodeValidation/use-continue-set-sms-passcode-validation.ts @@ -16,7 +16,7 @@ const useContinueSetSmsPasscodeValidation = (phone: string, errorCallback?: () = const requiredProfileErrorHandler = useRequiredProfileErrorHandler(true); - const identifierNotExistErrorHandler = useIdentifierErrorAlert( + const identifierExistErrorHandler = useIdentifierErrorAlert( UserFlow.continue, SignInIdentifier.Sms, phone @@ -24,17 +24,12 @@ const useContinueSetSmsPasscodeValidation = (phone: string, errorCallback?: () = const verifyPasscodeErrorHandlers: ErrorHandlers = useMemo( () => ({ - 'user.phone_not_exist': identifierNotExistErrorHandler, + 'user.phone_already_in_use': identifierExistErrorHandler, ...requiredProfileErrorHandler, ...sharedErrorHandlers, callback: errorCallback, }), - [ - errorCallback, - identifierNotExistErrorHandler, - requiredProfileErrorHandler, - sharedErrorHandlers, - ] + [errorCallback, identifierExistErrorHandler, requiredProfileErrorHandler, sharedErrorHandlers] ); const { run: verifyPasscode } = useApi( diff --git a/packages/ui/src/containers/PasscodeValidation/use-forgot-password-email-passcode-validation.ts b/packages/ui/src/containers/PasscodeValidation/use-forgot-password-email-passcode-validation.ts index b51da3254..e16cdfdb9 100644 --- a/packages/ui/src/containers/PasscodeValidation/use-forgot-password-email-passcode-validation.ts +++ b/packages/ui/src/containers/PasscodeValidation/use-forgot-password-email-passcode-validation.ts @@ -22,7 +22,7 @@ const useForgotPasswordEmailPasscodeValidation = (email: string, errorCallback?: const errorHandlers: ErrorHandlers = useMemo( () => ({ - 'user.email_not_exist': identifierNotExistErrorHandler, + 'user.user_not_exist': identifierNotExistErrorHandler, 'user.new_password_required_in_profile': () => { navigate(`/${UserFlow.forgotPassword}/reset`, { replace: true }); }, diff --git a/packages/ui/src/containers/PasscodeValidation/use-forgot-password-sms-passcode-validation.ts b/packages/ui/src/containers/PasscodeValidation/use-forgot-password-sms-passcode-validation.ts index ad8a064ee..f6f704183 100644 --- a/packages/ui/src/containers/PasscodeValidation/use-forgot-password-sms-passcode-validation.ts +++ b/packages/ui/src/containers/PasscodeValidation/use-forgot-password-sms-passcode-validation.ts @@ -22,7 +22,7 @@ const useForgotPasswordSmsPasscodeValidation = (phone: string, errorCallback?: ( const errorHandlers: ErrorHandlers = useMemo( () => ({ - 'user.phone_not_exist': identifierNotExistErrorHandler, + 'user.user_not_exist': identifierNotExistErrorHandler, 'user.new_password_required_in_profile': () => { navigate(`/${UserFlow.forgotPassword}/reset`, { replace: true }); }, diff --git a/packages/ui/src/containers/PasscodeValidation/use-identifier-error-alert.ts b/packages/ui/src/containers/PasscodeValidation/use-identifier-error-alert.ts index 296b7a9a6..6f9c63e0c 100644 --- a/packages/ui/src/containers/PasscodeValidation/use-identifier-error-alert.ts +++ b/packages/ui/src/containers/PasscodeValidation/use-identifier-error-alert.ts @@ -20,7 +20,7 @@ const useIdentifierErrorAlert = ( await show({ type: 'alert', ModalContent: t( - flow === UserFlow.register + flow === UserFlow.register || flow === UserFlow.continue ? 'description.create_account_id_exists_alert' : 'description.sign_in_id_does_not_exist_alert', { diff --git a/packages/ui/src/containers/PasscodeValidation/use-sign-in-with-email-passcode-validation.ts b/packages/ui/src/containers/PasscodeValidation/use-sign-in-with-email-passcode-validation.ts index a3229a7fb..15e063ba2 100644 --- a/packages/ui/src/containers/PasscodeValidation/use-sign-in-with-email-passcode-validation.ts +++ b/packages/ui/src/containers/PasscodeValidation/use-sign-in-with-email-passcode-validation.ts @@ -62,7 +62,7 @@ const useSignInWithEmailPasscodeValidation = (email: string, errorCallback?: () const errorHandlers = useMemo( () => ({ - 'user.email_not_exist': + 'user.user_not_exist': // Block user auto register if is bind social or sign-in only flow signInMode === SignInMode.SignIn || socialToBind ? identifierNotExistErrorHandler diff --git a/packages/ui/src/containers/PasscodeValidation/use-sign-in-with-sms-passcode-validation.ts b/packages/ui/src/containers/PasscodeValidation/use-sign-in-with-sms-passcode-validation.ts index 8e8c3abbb..8698314f0 100644 --- a/packages/ui/src/containers/PasscodeValidation/use-sign-in-with-sms-passcode-validation.ts +++ b/packages/ui/src/containers/PasscodeValidation/use-sign-in-with-sms-passcode-validation.ts @@ -62,7 +62,7 @@ const useSignInWithSmsPasscodeValidation = (phone: string, errorCallback?: () => const errorHandlers = useMemo( () => ({ - 'user.phone_not_exist': + 'user.user_not_exist': // Block user auto register if is bind social or sign-in only flow signInMode === SignInMode.SignIn || socialToBind ? identifierNotExistErrorHandler diff --git a/packages/ui/src/containers/UsernameForm/SetUsername/index.test.tsx b/packages/ui/src/containers/UsernameForm/SetUsername/index.test.tsx index b1bf35959..52069546b 100644 --- a/packages/ui/src/containers/UsernameForm/SetUsername/index.test.tsx +++ b/packages/ui/src/containers/UsernameForm/SetUsername/index.test.tsx @@ -1,7 +1,7 @@ import { fireEvent, act, waitFor } from '@testing-library/react'; import renderWithPageContext from '@/__mocks__/RenderWithPageContext'; -import { continueApi } from '@/apis/continue'; +import { addProfile } from '@/apis/interaction'; import SetUsername from '.'; @@ -12,8 +12,8 @@ jest.mock('react-router-dom', () => ({ useNavigate: () => mockedNavigate, })); -jest.mock('@/apis/continue', () => ({ - continueApi: jest.fn(async () => ({})), +jest.mock('@/apis/interaction', () => ({ + addProfile: jest.fn(async () => ({})), })); describe('', () => { @@ -37,7 +37,7 @@ describe('', () => { }); await waitFor(() => { - expect(continueApi).toBeCalledWith('username', 'username', undefined); + expect(addProfile).toBeCalledWith({ username: 'username' }, undefined); }); }); }); diff --git a/packages/ui/src/containers/UsernameForm/SetUsername/use-set-username.ts b/packages/ui/src/containers/UsernameForm/SetUsername/use-set-username.ts index d558a0af3..9d8fe19fc 100644 --- a/packages/ui/src/containers/UsernameForm/SetUsername/use-set-username.ts +++ b/packages/ui/src/containers/UsernameForm/SetUsername/use-set-username.ts @@ -1,7 +1,7 @@ import { useState, useCallback, useMemo, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; -import { continueApi } from '@/apis/continue'; +import { addProfile } from '@/apis/interaction'; import useApi from '@/hooks/use-api'; import type { ErrorHandlers } from '@/hooks/use-api'; import useRequiredProfileErrorHandler from '@/hooks/use-required-profile-error-handler'; @@ -28,14 +28,14 @@ const useSetUsername = () => { [requiredProfileErrorHandler] ); - const { result, run: setUsername } = useApi(continueApi, errorHandlers); + const { result, run: asyncAddProfile } = useApi(addProfile, errorHandlers); const onSubmit = useCallback( async (username: string) => { const socialToBind = getSearchParameters(location.search, SearchParameters.bindWithSocial); - await setUsername('username', username, socialToBind); + await asyncAddProfile({ username }, socialToBind); }, - [setUsername] + [asyncAddProfile] ); useEffect(() => { diff --git a/packages/ui/src/hooks/use-required-profile-error-handler.ts b/packages/ui/src/hooks/use-required-profile-error-handler.ts index 4c1079d2e..c5969cd62 100644 --- a/packages/ui/src/hooks/use-required-profile-error-handler.ts +++ b/packages/ui/src/hooks/use-required-profile-error-handler.ts @@ -1,60 +1,63 @@ -import { useMemo } from 'react'; +import { MissingProfile } from '@logto/schemas'; +import { useMemo, useContext } from 'react'; import { useNavigate } from 'react-router-dom'; +import { validate } from 'superstruct'; import { UserFlow } from '@/types'; +import { missingProfileErrorDataGuard } from '@/types/guard'; + +import type { ErrorHandlers } from './use-api'; +import { PageContext } from './use-page-context'; const useRequiredProfileErrorHandler = (replace?: boolean) => { const navigate = useNavigate(); + const { setToast } = useContext(PageContext); - const requiredProfileErrorHandler = useMemo( + const requiredProfileErrorHandler = useMemo( () => ({ - 'user.password_required_in_profile': () => { - navigate( - { - pathname: `/${UserFlow.continue}/password`, - search: location.search, - }, - { replace } - ); - }, - 'user.username_required_in_profile': () => { - navigate( - { - pathname: `/${UserFlow.continue}/username`, - search: location.search, - }, - { replace } - ); - }, - 'user.email_required_in_profile': () => { - navigate( - { - pathname: `/${UserFlow.continue}/email`, - search: location.search, - }, - { replace } - ); - }, - 'user.phone_required_in_profile': () => { - navigate( - { - pathname: `/${UserFlow.continue}/sms`, - search: location.search, - }, - { replace } - ); - }, - 'user.email_or_phone_required_in_profile': () => { - navigate( - { - pathname: `/${UserFlow.continue}/email-or-sms/email`, - search: location.search, - }, - { replace } - ); + 'user.missing_profile': (error) => { + const [, data] = validate(error.data, missingProfileErrorDataGuard); + const missingProfile = data?.missingProfile[0]; + + switch (missingProfile) { + case MissingProfile.password: + case MissingProfile.username: + case MissingProfile.email: + navigate( + { + pathname: `/${UserFlow.continue}/${missingProfile}`, + search: location.search, + }, + { replace } + ); + break; + case MissingProfile.phone: + navigate( + { + pathname: `/${UserFlow.continue}/sms`, + search: location.search, + }, + { replace } + ); + break; + case MissingProfile.emailOrPhone: + navigate( + { + pathname: `/${UserFlow.continue}/email-or-sms/email`, + search: location.search, + }, + { replace } + ); + break; + + default: { + setToast(error.message); + break; + } + } }, }), - [navigate, replace] + [navigate, replace, setToast] ); return requiredProfileErrorHandler; diff --git a/packages/ui/src/pages/Continue/SetPassword/index.test.tsx b/packages/ui/src/pages/Continue/SetPassword/index.test.tsx index 863c110d9..73949adc3 100644 --- a/packages/ui/src/pages/Continue/SetPassword/index.test.tsx +++ b/packages/ui/src/pages/Continue/SetPassword/index.test.tsx @@ -2,7 +2,7 @@ import { act, waitFor, fireEvent } from '@testing-library/react'; import renderWithPageContext from '@/__mocks__/RenderWithPageContext'; import SettingsProvider from '@/__mocks__/RenderWithPageContext/SettingsProvider'; -import { continueApi } from '@/apis/continue'; +import { addProfile } from '@/apis/interaction'; import SetPassword from '.'; @@ -13,8 +13,8 @@ jest.mock('react-router-dom', () => ({ useNavigate: () => mockedNavigate, })); -jest.mock('@/apis/continue', () => ({ - continueApi: jest.fn(async () => ({ redirectTo: '/' })), +jest.mock('@/apis/interaction', () => ({ + addProfile: jest.fn(async () => ({ redirectTo: '/' })), })); describe('SetPassword', () => { @@ -52,7 +52,7 @@ describe('SetPassword', () => { }); await waitFor(() => { - expect(continueApi).toBeCalledWith('password', '123456', undefined); + expect(addProfile).toBeCalledWith({ password: '123456' }, undefined); }); }); }); diff --git a/packages/ui/src/pages/Continue/SetPassword/use-set-password.ts b/packages/ui/src/pages/Continue/SetPassword/use-set-password.ts index 88eb11023..1e01c47ec 100644 --- a/packages/ui/src/pages/Continue/SetPassword/use-set-password.ts +++ b/packages/ui/src/pages/Continue/SetPassword/use-set-password.ts @@ -1,7 +1,7 @@ import { useMemo, useEffect, useCallback } from 'react'; import { useNavigate } from 'react-router-dom'; -import { continueApi } from '@/apis/continue'; +import { addProfile } from '@/apis/interaction'; import type { ErrorHandlers } from '@/hooks/use-api'; import useApi from '@/hooks/use-api'; import { useConfirmModal } from '@/hooks/use-confirm-modal'; @@ -26,14 +26,14 @@ const useSetPassword = () => { [navigate, requiredProfileErrorHandler, show] ); - const { result, run: asyncSetPassword } = useApi(continueApi, errorHandlers); + const { result, run: asyncAddProfile } = useApi(addProfile, errorHandlers); const setPassword = useCallback( async (password: string) => { const socialToBind = getSearchParameters(location.search, SearchParameters.bindWithSocial); - await asyncSetPassword('password', password, socialToBind); + await asyncAddProfile({ password }, socialToBind); }, - [asyncSetPassword] + [asyncAddProfile] ); useEffect(() => { diff --git a/packages/ui/src/pages/Continue/SetUsername/index.test.tsx b/packages/ui/src/pages/Continue/SetUsername/index.test.tsx index bb4096960..a754a4c65 100644 --- a/packages/ui/src/pages/Continue/SetUsername/index.test.tsx +++ b/packages/ui/src/pages/Continue/SetUsername/index.test.tsx @@ -2,7 +2,7 @@ import { act, waitFor, fireEvent } from '@testing-library/react'; import renderWithPageContext from '@/__mocks__/RenderWithPageContext'; import SettingsProvider from '@/__mocks__/RenderWithPageContext/SettingsProvider'; -import { continueApi } from '@/apis/continue'; +import { addProfile } from '@/apis/interaction'; import SetUsername from '.'; @@ -13,8 +13,8 @@ jest.mock('react-router-dom', () => ({ useNavigate: () => mockedNavigate, })); -jest.mock('@/apis/continue', () => ({ - continueApi: jest.fn(async () => ({ redirectTo: '/' })), +jest.mock('@/apis/interaction', () => ({ + addProfile: jest.fn(async () => ({ redirectTo: '/' })), })); describe('SetPassword', () => { @@ -46,7 +46,7 @@ describe('SetPassword', () => { }); await waitFor(() => { - expect(continueApi).toBeCalledWith('username', 'username', undefined); + expect(addProfile).toBeCalledWith({ username: 'username' }, undefined); }); }); }); diff --git a/packages/ui/src/types/guard.ts b/packages/ui/src/types/guard.ts index 59673129c..b9785321a 100644 --- a/packages/ui/src/types/guard.ts +++ b/packages/ui/src/types/guard.ts @@ -1,4 +1,4 @@ -import { SignInIdentifier } from '@logto/schemas'; +import { SignInIdentifier, MissingProfile } from '@logto/schemas'; import * as s from 'superstruct'; export const bindSocialStateGuard = s.object({ @@ -38,3 +38,15 @@ export const continueMethodGuard = s.union([ export const usernameGuard = s.object({ username: s.string(), }); + +export const missingProfileErrorDataGuard = s.object({ + missingProfile: s.array( + s.union([ + s.literal(MissingProfile.password), + s.literal(MissingProfile.email), + s.literal(MissingProfile.phone), + s.literal(MissingProfile.username), + s.literal(MissingProfile.emailOrPhone), + ]) + ), +});