From fd75cb8fd942cb39043af0fde1e234f0b0fb4692 Mon Sep 17 00:00:00 2001 From: kAy Date: Tue, 22 Aug 2023 14:41:47 +1000 Subject: [PATCH] 5KU error messages mapping (#2360) - Error messages mapped for any action - Cleanup & optimization --- .../components/Error/KnownErrors/index.tsx | 48 +++++++++++++++ spa/src/components/Error/index.tsx | 8 +-- .../pages/ConfigSteps/OrgsContainer/index.tsx | 45 ++------------ spa/src/pages/ConfigSteps/index.tsx | 23 ++++++-- spa/src/utils/modifyError.tsx | 58 ++++++------------- 5 files changed, 92 insertions(+), 90 deletions(-) create mode 100644 spa/src/components/Error/KnownErrors/index.tsx diff --git a/spa/src/components/Error/KnownErrors/index.tsx b/spa/src/components/Error/KnownErrors/index.tsx new file mode 100644 index 0000000000..8c63ab2137 --- /dev/null +++ b/spa/src/components/Error/KnownErrors/index.tsx @@ -0,0 +1,48 @@ +import styled from "@emotion/styled"; +import { token } from "@atlaskit/tokens"; +import { popup } from "../../../utils"; +import Button from "@atlaskit/button"; + +const Paragraph = styled.div` + color: ${token("color.text.subtle")}; +`; +const BulletSeparator = styled.span` + padding: 0 ${token("space.100")}; +`; +const StyledLink = styled.a` + cursor: pointer; +`; + +/************************************************************************ + * UI view for the 3 known errors + ************************************************************************/ +export const ErrorForSSO = ({ orgName, accessUrl, resetCallback }: { orgName?: string; accessUrl: string; resetCallback: () => void;}) => <> + + Can't connect, single sign-on(SSO) required{orgName && for {orgName}}. + + + 1. popup(accessUrl)}>Log into GitHub with SSO. + + + 2. Retry connection in Jira (once logged in). + +; + +export const ErrorForNonAdmins = ({ orgName }: { orgName?: string; }) => + Can't connect, you're not the organization owner{orgName && of {orgName}}.
Ask an owner to complete this step. +
; + +export const ErrorForIPBlocked = ({ orgName, resetCallback }: { orgName?: string; resetCallback: () => void }) => <> + + Can't connect{orgName && to {orgName}}, blocked by your IP allow list. + + + + Retry +; diff --git a/spa/src/components/Error/index.tsx b/spa/src/components/Error/index.tsx index 950f6a8a42..63aeb401b9 100644 --- a/spa/src/components/Error/index.tsx +++ b/spa/src/components/Error/index.tsx @@ -19,13 +19,13 @@ const ErrorWrapper = styled.div` background: ${props => props.type === "warning" ? token("color.background.warning") : token("color.background.danger") }; border-radius: 3px; align-items: center; - div { - padding-left: ${token("space.200")}; - } span { align-self: start; } `; +const ErrorContent = styled.div` + padding-left: ${token("space.200")}; +`; const Error = ({ type, @@ -42,7 +42,7 @@ const Error = ({ type === "warning" ? : } -
{message}
+ {message} ); }; diff --git a/spa/src/pages/ConfigSteps/OrgsContainer/index.tsx b/spa/src/pages/ConfigSteps/OrgsContainer/index.tsx index 1e5b6b3f17..7a811a7294 100644 --- a/spa/src/pages/ConfigSteps/OrgsContainer/index.tsx +++ b/spa/src/pages/ConfigSteps/OrgsContainer/index.tsx @@ -4,8 +4,8 @@ import styled from "@emotion/styled"; import { token } from "@atlaskit/tokens"; import { useState } from "react"; import WarningIcon from "@atlaskit/icon/glyph/warning"; -import { popup } from "../../../utils"; import OauthManager from "../../../services/oauth-manager"; +import { ErrorForIPBlocked, ErrorForNonAdmins, ErrorForSSO } from "../../../components/Error/KnownErrors"; type OrgDivType = { key: number; @@ -29,18 +29,10 @@ const OrgName = styled.span` color: ${token("color.text")}; font-weight: 590; `; -const Paragraph = styled.div` - color: ${token("color.text.subtle")}; -`; const IconWrapper = styled.div` padding-top: ${token("space.150")}; `; -const BulletSeparator = styled.span` - padding: 0 ${token("space.100")}; -`; -const StyledLink = styled.a` - cursor: pointer; -`; + const OrganizationsList = ({ organizations, @@ -73,42 +65,15 @@ const OrganizationsList = ({ // TODO: Update this to support GHE const accessUrl = `https://github.com/organizations/${org.account.login}/settings/profile`; - return <> - - Can't connect, single sign-on(SSO) required. - - - 1. popup(accessUrl)}>Log into GitHub with SSO. - - - 2. Retry connection in Jira (once logged in). - - ; + return ; } if (org.isIPBlocked) { - return <> - - Can't connect, blocked by your IP allow list. - - - - Retry - ; + return ; } if (!org.isAdmin) { - return <> - - Can't connect, you're not the organization owner.
Ask an owner to complete this step. -
- ; + return ; } }; diff --git a/spa/src/pages/ConfigSteps/index.tsx b/spa/src/pages/ConfigSteps/index.tsx index b1da4b668b..77fe7c80f0 100644 --- a/spa/src/pages/ConfigSteps/index.tsx +++ b/spa/src/pages/ConfigSteps/index.tsx @@ -19,6 +19,7 @@ import { reportError } from "../../utils"; import { GitHubInstallationType } from "../../../../src/rest-interfaces"; import OrganizationsList from "../ConfigSteps/OrgsContainer"; import SkeletonForLoading from "../ConfigSteps/SkeletonForLoading"; +import OauthManager from "../../services/oauth-manager"; type GitHubOptionType = { selectedOption: number; @@ -161,7 +162,7 @@ const ConfigSteps = () => { const response = await AppManager.fetchOrgs(); setLoaderForOrgFetching(false); if (response instanceof AxiosError) { - showError(modifyError(response, {}, { onClearGitHubToken: clearGitHubToken })); + showError(modifyError(response, {}, { onClearGitHubToken: clearGitHubToken, onRelogin: reLogin })); return { success: false, orgs: [] }; } else { setOrganizations(response.orgs); @@ -180,7 +181,7 @@ const ConfigSteps = () => { }); } catch (e) { setLoaderForLogin(false); - showError(modifyError(e as AxiosError, {}, { onClearGitHubToken: clearGitHubToken })); + showError(modifyError(e as AxiosError, {}, { onClearGitHubToken: clearGitHubToken, onRelogin: reLogin })); reportError(e); } break; @@ -199,6 +200,16 @@ const ConfigSteps = () => { clearLogin(); }; + const reLogin = async () => { + // Clearing the errors + showError(undefined); + await OauthManager.clear(); + // This resets the token validity check in the parent component and resets the UI + setIsLoggedIn(false); + // Restart the whole auth flow + await OauthManager.authenticateInGitHub(() => {}); + }; + const clearLogin = () => { setIsLoggedIn(false); setLoaderForLogin(false); @@ -212,7 +223,7 @@ const ConfigSteps = () => { const connected = await AppManager.connectOrg(gitHubInstallationId); analyticsClient.sendTrackEvent({ actionSubject: "organisationConnectResponse", action: connected ? "success" : "fail"}, { mode }); if (connected instanceof AxiosError) { - showError(modifyError(connected, { orgLogin }, { onClearGitHubToken: clearGitHubToken })); + showError(modifyError(connected, { orgLogin }, { onClearGitHubToken: clearGitHubToken, onRelogin: reLogin })); } else { navigate("/spa/connected"); } @@ -238,7 +249,7 @@ const ConfigSteps = () => { } }); } catch (e) { - showError(modifyError(e as AxiosError, { }, { onClearGitHubToken: clearGitHubToken })); + showError(modifyError(e as AxiosError, { }, { onClearGitHubToken: clearGitHubToken, onRelogin: reLogin })); analyticsClient.sendTrackEvent({ actionSubject: "installNewOrgInGithubResponse", action: "fail"}, { mode }); reportError(e); } @@ -252,7 +263,7 @@ const ConfigSteps = () => { const response = await OAuthManager.finishOAuthFlow(event.data?.code, event.data?.state); setLoaderForLogin(false); if (response instanceof AxiosError) { - showError(modifyError(response, {}, { onClearGitHubToken: clearGitHubToken })); + showError(modifyError(response, {}, { onClearGitHubToken: clearGitHubToken, onRelogin: reLogin })); analyticsClient.sendTrackEvent({ actionSubject: "finishOAuthFlow", action: "fail" }); return; } else { @@ -272,7 +283,7 @@ const ConfigSteps = () => { const recheckValidity = async () => { const status: boolean | AxiosError = await OAuthManager.checkValidity(); if (status instanceof AxiosError) { - showError(modifyError(status, {}, { onClearGitHubToken: clearGitHubToken })); + showError(modifyError(status, {}, { onClearGitHubToken: clearGitHubToken, onRelogin: reLogin })); return; } setLoggedInUser(OAuthManager.getUserDetails().username); diff --git a/spa/src/utils/modifyError.tsx b/spa/src/utils/modifyError.tsx index 834f4ac929..8491db3472 100644 --- a/spa/src/utils/modifyError.tsx +++ b/spa/src/utils/modifyError.tsx @@ -1,9 +1,7 @@ import { AxiosError } from "axios"; import { ErrorType, ApiError, ErrorCode } from "rest-interfaces"; import React, { MouseEvent } from "react"; -import Heading from "@atlaskit/heading"; -import styled from "@emotion/styled"; -import { token } from "@atlaskit/tokens"; +import { ErrorForIPBlocked, ErrorForNonAdmins, ErrorForSSO } from "../components/Error/KnownErrors"; export type ErrorObjType = { type: ErrorType, @@ -28,12 +26,8 @@ export const GENERIC_MESSAGE_WITH_LINK = <> export const modifyError = ( error: AxiosError | SimpleError | ErrorWithErrorCode, context: { orgLogin?: string; }, - callbacks: { onClearGitHubToken: (e: MouseEvent) => void } + callbacks: { onClearGitHubToken: (e: MouseEvent) => void; onRelogin: () => void } ): ErrorObjType => { - - const Paragraph = styled.p` - color: ${token("color.text.subtle")}; - `; const errorObj = { type: "error" as ErrorType }; const warningObj = { type: "warning" as ErrorType }; let errorCode: ErrorCode = "UNKNOWN"; @@ -47,50 +41,34 @@ export const modifyError = ( // TODO: map all of the remaining backend errors in frontend if (errorCode === "IP_BLOCKED") { + return { + ...warningObj, + message: + }; + } else if (errorCode === "SSO_LOGIN") { + // TODO: Update this to support GHE + const accessUrl = `https://github.com/organizations/${context.orgLogin}/settings/profile`; + return { ...warningObj, message: <> - GitHub for Jira is blocked by your IP allowlist - - Your GitHub organization only allows access to some IP addresses. To view
- development work in Jira, you need to add GitHub for Jira’s IP addresses to
- your allowlist. -
-
- Learn how to add GitHub for Jira to your IP allowlist + }; } else if (errorCode === "INSUFFICIENT_PERMISSION") { - return { ...errorObj, message: `You are not Admin of the target org ${context.orgLogin || ""}. Please make sure you are admin of the org and try again.` }; //TODO: Better message - } else if (errorCode === "TIMEOUT") { - return { ...errorObj, message: "Request timeout. Please try again later." }; //TODO: Better message - } else if (errorCode === "RATELIMIT") { - return { ...errorObj, message: "GitHub rate limit exceeded. Please try again later." }; //TODO: Better message - } else if (errorCode === "SSO_LOGIN") { return { ...warningObj, - message: <> - SSO Login required - - You cannot connect to this organization because you are not currently logged in through your SSO in GitHub. -

- Please follow the following steps: -
    -
  1. - Please go to the organization settings page and make sure you have admin access there. -
  2. -
  3. - Please click this link to reset your token. This will allow you to connect to this organization. -
  4. -
-
- + message: }; + } else if (errorCode === "TIMEOUT") { + return { ...errorObj, message: "Request timeout. Please try again later." }; + } else if (errorCode === "RATELIMIT") { + return { ...errorObj, message: "GitHub rate limit exceeded. Please try again later." }; } else if (errorCode === "INVALID_TOKEN") { return { - ...warningObj, + ...errorObj, message: <> - "The GitHub token seems invalid, please re-authorise and try again." + GitHub token seems invalid, please login again. }; } else {