diff --git a/grafana-plugin/src/containers/UserSettings/parts/tabs/PhoneVerification/PhoneVerification.tsx b/grafana-plugin/src/containers/UserSettings/parts/tabs/PhoneVerification/PhoneVerification.tsx index d9f2078b..ed7c11aa 100644 --- a/grafana-plugin/src/containers/UserSettings/parts/tabs/PhoneVerification/PhoneVerification.tsx +++ b/grafana-plugin/src/containers/UserSettings/parts/tabs/PhoneVerification/PhoneVerification.tsx @@ -10,7 +10,6 @@ import { WithPermissionControl } from 'containers/WithPermissionControl/WithPerm import { User } from 'models/user/user.types'; import { AppFeature } from 'state/features'; import { useStore } from 'state/useStore'; -import { openErrorNotification } from 'utils'; import { isUserActionAllowed, UserAction, UserActions } from 'utils/authorization'; import { reCAPTCHA_site_key } from 'utils/consts'; @@ -91,14 +90,9 @@ const PhoneVerification = observer((props: PhoneVerificationProps) => { const onSubmitCallback = useCallback(async () => { if (isCodeSent) { - userStore - .verifyPhone(userPk, code) - .then(() => { - userStore.loadUser(userPk); - }) - .catch((error) => { - openErrorNotification(error.response.data); - }); + userStore.verifyPhone(userPk, code).then(() => { + userStore.loadUser(userPk); + }); } else { window.grecaptcha.ready(function () { window.grecaptcha @@ -110,20 +104,13 @@ const PhoneVerification = observer((props: PhoneVerificationProps) => { unverified_phone_number: phone, }); - userStore - .fetchVerificationCode(userPk, token) - .then(() => { - setState({ isCodeSent: true }); + userStore.fetchVerificationCode(userPk, token).then(() => { + setState({ isCodeSent: true }); - if (codeInputRef.current) { - codeInputRef.current.focus(); - } - }) - .catch(() => { - openErrorNotification( - 'Grafana OnCall is unable to verify your phone number due to incorrect number or verification service being unavailable.' - ); - }); + if (codeInputRef.current) { + codeInputRef.current.focus(); + } + }); }); }); } diff --git a/grafana-plugin/src/models/user/user.ts b/grafana-plugin/src/models/user/user.ts index 101a5f8b..eb5900b9 100644 --- a/grafana-plugin/src/models/user/user.ts +++ b/grafana-plugin/src/models/user/user.ts @@ -8,6 +8,7 @@ import { makeRequest } from 'network'; import { Mixpanel } from 'services/mixpanel'; import { RootStore } from 'state'; import { move } from 'state/helpers'; +import { trottlingError } from 'utils'; import { isUserActionAllowed, UserActions } from 'utils/authorization'; import { getTimezone, prepareForUpdate } from './user.helpers'; @@ -237,14 +238,14 @@ export class UserStore extends BaseStore { await makeRequest(`/users/${userPk}/get_verification_code/`, { method: 'GET', headers: { 'X-OnCall-Recaptcha': recaptchaToken }, - }); + }).catch(trottlingError); } @action async verifyPhone(userPk: User['pk'], token: string) { return await makeRequest(`/users/${userPk}/verify_number/?token=${token}`, { method: 'PUT', - }); + }).catch(trottlingError); } @action diff --git a/grafana-plugin/src/utils/index.ts b/grafana-plugin/src/utils/index.ts index efa429ca..df1d9492 100644 --- a/grafana-plugin/src/utils/index.ts +++ b/grafana-plugin/src/utils/index.ts @@ -47,6 +47,25 @@ export function refreshPageError(error: AxiosError) { throw error; } +export function trottlingError(error: AxiosError) { + if (error.response?.status === 429) { + const seconds = Number(error.response?.headers['retry-after']); + const minutes = Math.floor(seconds / 60); + const text = + 'Too many requests, please try again in ' + + (minutes > 0 ? `${Math.floor(seconds / 60)} minutes.` : `${seconds} seconds.`); + openErrorNotification(text); + } else { + if (error.response?.data === '') { + openErrorNotification( + 'Grafana OnCall is unable to verify your phone number due to incorrect number or verification service being unavailable.' + ); + } else { + openErrorNotification(error.response?.data); + } + } +} + export function openNotification(message: React.ReactNode) { appEvents.emit(AppEvents.alertSuccess, [message]); } diff --git a/grafana-plugin/yarn.lock b/grafana-plugin/yarn.lock index dd418efb..2d2b20d0 100644 --- a/grafana-plugin/yarn.lock +++ b/grafana-plugin/yarn.lock @@ -7477,11 +7477,6 @@ hoist-non-react-statics@3.3.2, hoist-non-react-statics@^3.1.0, hoist-non-react-s dependencies: react-is "^16.7.0" -hoist-non-react-statics@^2.1.1: - version "2.5.5" - resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47" - integrity sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw== - homedir-polyfill@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8" @@ -11370,13 +11365,6 @@ react-calendar@3.9.0: merge-class-names "^1.1.1" prop-types "^15.6.0" -react-click-outside@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/react-click-outside/-/react-click-outside-3.0.1.tgz#6e77e84d2f17afaaac26dbad743cbbf909f5e24c" - integrity sha512-d0KWFvBt+esoZUF15rL2UBB7jkeAqLU8L/Ny35oLK6fW6mIbOv/ChD+ExF4sR9PD26kVx+9hNfD0FTIqRZEyRQ== - dependencies: - hoist-non-react-statics "^2.1.1" - react-colorful@5.5.1: version "5.5.1" resolved "https://registry.yarnpkg.com/react-colorful/-/react-colorful-5.5.1.tgz#29d9c4e496f2ca784dd2bb5053a3a4340cfaf784"