Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

5KU error messages mapping #2360

Merged
merged 2 commits into from
Aug 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions spa/src/components/Error/KnownErrors/index.tsx
Original file line number Diff line number Diff line change
@@ -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;}) => <>
<Paragraph>
Can't connect, single sign-on(SSO) required{orgName && <span> for <b>{orgName}</b></span>}.
</Paragraph>
<Paragraph>
1. <StyledLink onClick={() => popup(accessUrl)}>Log into GitHub with SSO</StyledLink>.
</Paragraph>
<Paragraph>
2. <StyledLink onClick={resetCallback}>Retry connection in Jira</StyledLink> (once logged in).
</Paragraph>
</>;

export const ErrorForNonAdmins = ({ orgName }: { orgName?: string; }) => <Paragraph>
Can't connect, you're not the organization owner{orgName && <span> of <b>{orgName}</b></span>}.<br />Ask an owner to complete this step.
</Paragraph>;

export const ErrorForIPBlocked = ({ orgName, resetCallback }: { orgName?: string; resetCallback: () => void }) => <>
<Paragraph>
Can't connect{orgName && <span> to <b>{orgName}</b></span>}, blocked by your IP allow list.
</Paragraph>
<Button
style={{ paddingLeft: 0, paddingRight: 0 }}
appearance="link"
onClick={() => popup("https://github.com/atlassian/github-for-jira/blob/main/docs/ip-allowlist.md")}
>
How to update allowlist
</Button>
<BulletSeparator>&#8226;</BulletSeparator>
<StyledLink onClick={resetCallback}>Retry</StyledLink>
</>;
8 changes: 4 additions & 4 deletions spa/src/components/Error/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ const ErrorWrapper = styled.div<ErrorWrapperType>`
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,
Expand All @@ -42,7 +42,7 @@ const Error = ({
type === "warning" ? <WarningIcon label="warning" primaryColor={token("color.background.warning.bold")} size="medium" /> :
<ErrorIcon label="warning" primaryColor={token("color.background.danger.bold")} size="medium" />
}
<div>{message}</div>
<ErrorContent>{message}</ErrorContent>
</ErrorWrapper>
);
};
Expand Down
45 changes: 5 additions & 40 deletions spa/src/pages/ConfigSteps/OrgsContainer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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,
Expand Down Expand Up @@ -73,42 +65,15 @@ const OrganizationsList = ({
// TODO: Update this to support GHE
const accessUrl = `https://github.com/organizations/${org.account.login}/settings/profile`;

return <>
<Paragraph>
Can't connect, single sign-on(SSO) required.
</Paragraph>
<Paragraph>
1. <StyledLink onClick={() => popup(accessUrl)}>Log into GitHub with SSO</StyledLink>.
</Paragraph>
<Paragraph>
2. <StyledLink onClick={resetToken}>Retry connection in Jira</StyledLink> (once logged in).
</Paragraph>
</>;
return <ErrorForSSO resetCallback={resetToken} accessUrl={accessUrl} />;
}

if (org.isIPBlocked) {
return <>
<Paragraph>
Can't connect, blocked by your IP allow list.
</Paragraph>
<Button
style={{ paddingLeft: 0, paddingRight: 0 }}
appearance="link"
onClick={() => popup("https://github.com/atlassian/github-for-jira/blob/main/docs/ip-allowlist.md")}
>
How to update allowlist
</Button>
<BulletSeparator>&#8226;</BulletSeparator>
<StyledLink onClick={resetToken}>Retry</StyledLink>
</>;
return <ErrorForIPBlocked resetCallback={resetToken} />;
}

if (!org.isAdmin) {
return <>
<Paragraph>
Can't connect, you're not the organization owner.<br />Ask an owner to complete this step.
</Paragraph>
</>;
return <ErrorForNonAdmins />;
}
};

Expand Down
23 changes: 17 additions & 6 deletions spa/src/pages/ConfigSteps/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -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;
Expand All @@ -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);
Expand All @@ -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");
}
Expand All @@ -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);
}
Expand All @@ -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 {
Expand All @@ -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);
Expand Down
58 changes: 18 additions & 40 deletions spa/src/utils/modifyError.tsx
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -28,12 +26,8 @@ export const GENERIC_MESSAGE_WITH_LINK = <>
export const modifyError = (
error: AxiosError<ApiError> | SimpleError | ErrorWithErrorCode,
context: { orgLogin?: string; },
callbacks: { onClearGitHubToken: (e: MouseEvent<HTMLAnchorElement>) => void }
callbacks: { onClearGitHubToken: (e: MouseEvent<HTMLAnchorElement>) => 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";
Expand All @@ -47,50 +41,34 @@ export const modifyError = (

// TODO: map all of the remaining backend errors in frontend
if (errorCode === "IP_BLOCKED") {
return {
...warningObj,
message: <ErrorForIPBlocked resetCallback={callbacks.onRelogin} orgName={context.orgLogin} />
};
} else if (errorCode === "SSO_LOGIN") {
// TODO: Update this to support GHE
const accessUrl = `https://github.com/organizations/${context.orgLogin}/settings/profile`;

return {
...warningObj,
message: <>
<Heading level="h500">GitHub for Jira is blocked by your IP allowlist</Heading>
<Paragraph>
Your GitHub organization only allows access to some IP addresses. To view<br />
development work in Jira, you need to add GitHub for Jira’s IP addresses to<br />
your allowlist.
</Paragraph>
<br />
<a target="_blank" href="https://github.com/atlassian/github-for-jira/blob/main/docs/ip-allowlist.md">Learn how to add GitHub for Jira to your IP allowlist</a>
<ErrorForSSO accessUrl={accessUrl} resetCallback={callbacks.onRelogin} orgName={context.orgLogin} />
</>
};
} 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: <>
<Heading level="h500">SSO Login required</Heading>
<Paragraph>
You cannot connect to this organization because you are not currently logged in through your SSO in GitHub.
<br></br>
Please follow the following steps:
<ol>
<li>
Please go to the <a target="_blank" href={`https://github.com/organizations/${context?.orgLogin}/settings/profile`}> organization settings</a> page and make sure you have admin access there.
</li>
<li>
Please click <a href="" onClick={callbacks.onClearGitHubToken}>this link</a> to reset your token. This will allow you to connect to this organization.
</li>
</ol>
</Paragraph>
</>
message: <ErrorForNonAdmins orgName={context.orgLogin} />
};
} 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: <>
<span>"The GitHub token seems invalid, please <a href="" onClick={callbacks.onClearGitHubToken}>re-authorise</a> and try again."</span>
<span>GitHub token seems invalid, please <a href="" onClick={callbacks.onClearGitHubToken}>login again</a>.</span>
</>
};
} else {
Expand Down