Skip to content

Commit 5d31541

Browse files
authored
Merge branch 'main' into remove-stale-msg-flag
2 parents fc3a57d + 1e5043d commit 5d31541

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+1830
-1249
lines changed

.nvmrc

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
14.21.1
1+
18.18.1

CONTRIBUTING.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Please note that this project has a [Contributor Code of Conduct](CODE_OF_CONDUC
1616

1717
## Getting Started
1818

19-
This app is written in [Typescript](https://www.typescriptlang.org/) and runs on [Node.js](https://nodejs.org/) **v14.x**.
19+
This app is written in [Typescript](https://www.typescriptlang.org/) and runs on [Node.js](https://nodejs.org/) **v18.x**.
2020

2121
Please install [Docker](https://www.docker.com/get-started) and [Docker Compose](https://docs.docker.com/compose/install/) to easily run the project locally.
2222

Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM node:14.21-alpine3.16 as build
1+
FROM node:18-alpine3.18 as build
22

33
# adding python for node-gyp
44
RUN apk add g++ make python3

Dockerfile.prod

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM node:14.21-alpine3.16 as build
1+
FROM node:18-alpine3.18 as build
22

33
# adding python for node-gyp
44
RUN apk add g++ make python3
@@ -28,7 +28,7 @@ RUN yarn run build:release
2828
# https://stackoverflow.com/questions/57108751/docker-build-failed-when-copying-in-multi-step-build
2929
RUN chown root:root -R /app
3030

31-
FROM node:14.21-alpine3.16
31+
FROM node:18-alpine3.18
3232

3333
# adding to solve vuln
3434
RUN apk add --update --upgrade busybox

package.json

+9-7
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"description": "GitHub integration for Atlassian Jira",
55
"repository": "https://github.com/atlassian/github-for-jira.git",
66
"engines": {
7-
"node": ">= 14.21 <15",
7+
"node": ">= 18.18.1",
88
"yarn": "^1.22.0"
99
},
1010
"main": "src/main",
@@ -28,7 +28,7 @@
2828
"lint:ts": "eslint . --ext .ts,.tsx",
2929
"lint:yaml": "yamllint github-for-jira.sd.yml",
3030
"pretest": "NODE_ENV=test run-p clean setup db:test",
31-
"test": "yarn spa:test && jest --runInBand --forceExit --coverage",
31+
"test": "yarn spa:test && jest --forceExit --coverage --runInBand --logHeapUsage --workerIdleMemoryLimit='2GB'",
3232
"test:watch": "jest --runInBand --watchAll --coverage=false",
3333
"db": "run-s db:create db:migrate",
3434
"db:create": "run-p db:create:dev db:create:test",
@@ -95,6 +95,7 @@
9595
"helmet": "^3.21.2",
9696
"hot-shots": "^8.3.2",
9797
"http-proxy": "^1.18.1",
98+
"http-proxy-agent": "^7.0.0",
9899
"https-proxy-agent": "5.0.0",
99100
"ioredis": "^4.16.3",
100101
"is-base64": "^1.1.0",
@@ -157,13 +158,14 @@
157158
"eslint-import-resolver-typescript": "^2.5.0",
158159
"eslint-plugin-import": "^2.25.4",
159160
"eslint-plugin-jest": "^27.4.0",
160-
"eslint-plugin-jsdoc": "^37.9.3",
161+
"eslint-plugin-jsdoc": "^46.8.2",
161162
"eslint-plugin-no-only-tests": "^2.6.0",
162163
"eslint-plugin-node": "^11.1.0",
163164
"eslint-plugin-promise": "^6.0.0",
164165
"husky": "^6.0.0",
165-
"jest": "^27.5.1",
166-
"jest-diff": "^27.5.1",
166+
"jest": "^29.7.0",
167+
"jest-diff": "^29.7",
168+
"jest-environment-jsdom": "^29.7.0",
167169
"jest-when": "^3.5.1",
168170
"keygrip": "^1.0.2",
169171
"nock": "^13.2.4",
@@ -175,7 +177,7 @@
175177
"smee-client": "^1.2.2",
176178
"sqlite3": "^5.1.5",
177179
"supertest": "^6.2.2",
178-
"ts-jest": "~27.1.3",
180+
"ts-jest": "^29.1.1",
179181
"ts-node": "^10.5.0",
180182
"ts-node-dev": "^2.0.0",
181183
"yaml-lint": "^1.2.4"
@@ -184,7 +186,7 @@
184186
"@atlassian/sqs-queue-dlq-service": "^2.1.1"
185187
},
186188
"volta": {
187-
"node": "14.21.1",
189+
"node": "18.18.1",
188190
"yarn": "1.22.18"
189191
}
190192
}

spa/jest.config.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@
44
"^.+\\.tsx?$": "ts-jest"
55
},
66
"setupFilesAfterEnv": [
7-
"@testing-library/jest-dom/extend-expect"
7+
"./setup.ts"
88
]
99
}

spa/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
"simplebar-react": "^3.2.4"
4949
},
5050
"devDependencies": {
51-
"@testing-library/jest-dom": "^5.16.5",
51+
"@testing-library/jest-dom": "^6.1.4",
5252
"@testing-library/react": "^14.0.0",
5353
"@testing-library/user-event": "^14.4.3",
5454
"@types/jest": "^29.5.2",

spa/setup.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import "@testing-library/jest-dom";

spa/src/components/Error/KnownErrors/index.tsx

+10
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import Modal, {
1717
import TextArea from "@atlaskit/textarea";
1818
import Spinner from "@atlaskit/spinner";
1919
import Button from "@atlaskit/button";
20+
import WarningIcon from "@atlaskit/icon/glyph/warning";
2021

2122
const olStyle = css`
2223
padding-left: 1.2em;
@@ -35,6 +36,11 @@ const linkStyle = css`
3536
const textAreaStyle = css`
3637
margin-top: 20px;
3738
`;
39+
const expireTextWrapperStyle = css`
40+
display: flex;
41+
align-items: center;
42+
margin-top: ${token("space.100")};
43+
`;
3844

3945
/************************************************************************
4046
* UI view for the 3 known errors
@@ -137,6 +143,10 @@ export const ErrorForNonAdmins = ({ orgName, adminOrgsUrl, onPopupBlocked, defer
137143
Find an organization owner
138144
</a>
139145
</div>
146+
<div css={expireTextWrapperStyle}>
147+
<WarningIcon label="warning" primaryColor={token("color.background.warning.bold")} />
148+
<b>Note that the following link will expire after 2 days.</b>
149+
</div>
140150
<TextArea
141151
onCopy={() => {
142152
analyticsClient.sendUIEvent({ actionSubject: "copiedDeferredInstallationUrl", action: "clicked"}, { type: "cloud" });
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/** @jsxImportSource @emotion/react */
2+
import Step from "../../../components/Step";
3+
import { css } from "@emotion/react";
4+
import { token } from "@atlaskit/tokens";
5+
import { popup } from "../../../utils";
6+
import analyticsClient from "../../../analytics";
7+
8+
const paragraphStyle = css`
9+
color: ${token("color.text.subtle")};
10+
margin: ${token("space.0")};
11+
`;
12+
const noAdminDivStyle = css`
13+
margin-top: ${token("space.200")};
14+
`;
15+
const linkStyle = css`
16+
cursor: pointer;
17+
padding-left: 0;
18+
padding-right: 0;
19+
`;
20+
21+
const ForbiddenState = ({ orgName, requestId } : { orgName: string; requestId: string; }) => {
22+
const getOrgOwnerUrl = async () => {
23+
// TODO: Need to get this URL for Enterprise users too, this is only for Cloud users
24+
popup(`https://github.com/orgs/${orgName}/people?query=role%3Aowner`);
25+
analyticsClient.sendUIEvent({ actionSubject: "checkOrgAdmin", action: "clicked"}, { type: "cloud", from: "DeferredInstallationFailedScreen" }, requestId);
26+
};
27+
28+
return (
29+
<Step title="Can’t connect this organization because you don’t have owner permissions">
30+
<div css={noAdminDivStyle}>
31+
<p css={paragraphStyle}>
32+
The GitHub account you’ve used doesn’t have owner permissions
33+
for organization {orgName}.
34+
</p>
35+
<br/>
36+
<p css={paragraphStyle}>
37+
Let the person who sent you the request know to{" "}
38+
<a css={linkStyle} onClick={getOrgOwnerUrl}>
39+
find an owner for that organization.
40+
</a>
41+
</p>
42+
</div>
43+
</Step>
44+
);
45+
};
46+
47+
export default ForbiddenState;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/** @jsxImportSource @emotion/react */
2+
import { Box, xcss } from "@atlaskit/primitives";
3+
import InfoIcon from "@atlaskit/icon/glyph/info";
4+
import Button from "@atlaskit/button";
5+
import { css } from "@emotion/react";
6+
import { token } from "@atlaskit/tokens";
7+
import Step from "../../../components/Step";
8+
import { useEffect, useState } from "react";
9+
import { AxiosError } from "axios";
10+
import OAuthManager from "../../../services/oauth-manager";
11+
import DeferralManager from "../../../services/deferral-manager";
12+
import analyticsClient from "../../../analytics";
13+
import { useNavigate } from "react-router-dom";
14+
import { StateCallbacksForDefaultDeferredState } from "rest-interfaces";
15+
16+
const boxStyles = xcss({
17+
borderBlockWidth: token("space.500"),
18+
borderStyle: "solid",
19+
borderColor: "color.border",
20+
borderRadius: "border.radius.050",
21+
borderWidth: "border.width",
22+
marginTop: token("space.200"),
23+
marginBottom: token("space.200"),
24+
display: "flex",
25+
alignItems: "center",
26+
});
27+
const paragraphStyle = css`
28+
color: ${token("color.text.subtle")};
29+
margin: ${token("space.0")};
30+
`;
31+
const infoParaStyle = css`
32+
padding-left: 16px;
33+
`;
34+
35+
const DefaultState = ({
36+
orgName,
37+
jiraHost,
38+
requestId,
39+
callbacks,
40+
}: {
41+
orgName: string;
42+
jiraHost: string;
43+
requestId: string;
44+
callbacks: StateCallbacksForDefaultDeferredState;
45+
}) => {
46+
const navigate = useNavigate();
47+
const { setIsLoading, setForbidden, onPopupBlocked } = callbacks;
48+
const [isLoggedIn, setIsLoggedIn] = useState(false);
49+
50+
// Finish the OAuth dance if authenticated
51+
useEffect(() => {
52+
const handler = async (event: MessageEvent) => {
53+
if (event.data?.type === "oauth-callback" && event.data?.code) {
54+
const response: boolean | AxiosError = await OAuthManager.finishOAuthFlow(event.data?.code, event.data?.state);
55+
setIsLoading(false);
56+
if (response instanceof AxiosError) {
57+
return;
58+
}
59+
setIsLoggedIn(true);
60+
}
61+
};
62+
window.addEventListener("message", handler);
63+
return () => {
64+
window.removeEventListener("message", handler);
65+
};
66+
// eslint-disable-next-line react-hooks/exhaustive-deps
67+
}, []);
68+
69+
// Start the deferral connection if authenticated
70+
useEffect(() => {
71+
const connectDeferredOrgOrg = async () => {
72+
if (requestId) {
73+
setIsLoading(true);
74+
const status: boolean | AxiosError = await DeferralManager.connectOrgByDeferral(requestId);
75+
if (status instanceof AxiosError) {
76+
setForbidden(true);
77+
analyticsClient.sendScreenEvent({ name: "DeferredInstallationFailedScreen" }, { type: "cloud" }, requestId);
78+
}
79+
else if (status) {
80+
setForbidden(false);
81+
navigate("/spa/connected", { state: { orgLogin: orgName, requestId } });
82+
} else {
83+
setForbidden(true);
84+
}
85+
setIsLoading(false);
86+
}
87+
};
88+
89+
isLoggedIn && connectDeferredOrgOrg();
90+
// eslint-disable-next-line react-hooks/exhaustive-deps
91+
}, [ isLoggedIn ]);
92+
93+
// Authenticate if no token/username is set
94+
const authenticate = async () => {
95+
setIsLoading(true);
96+
try {
97+
analyticsClient.sendUIEvent({ actionSubject: "signInAndConnectThroughDeferredInstallationStartScreen", action: "clicked"}, { type: "cloud" }, requestId);
98+
await OAuthManager.authenticateInGitHub({
99+
onWinClosed: () => {
100+
setIsLoading(false);
101+
}, onPopupBlocked
102+
});
103+
} catch (e) {
104+
// TODO: print alert error
105+
console.error("check error", e);
106+
} finally {
107+
setIsLoading(false);
108+
}
109+
};
110+
111+
return (
112+
<Step
113+
title={`Connect GitHub organization ${orgName} to Jira Software`}
114+
>
115+
<>
116+
<p css={paragraphStyle}>
117+
A Jira administrator has asked for approval to connect the
118+
GitHub organization {orgName} to the Jira site {jiraHost}.
119+
</p>
120+
<Box padding="space.200" xcss={boxStyles}>
121+
<InfoIcon label="differed-installation-info" size="small"/>
122+
<p css={[paragraphStyle, infoParaStyle]}>
123+
This will make all repositories in {orgName} available to all projects in {jiraHost}. Import work from those
124+
GitHub repositories into Jira.
125+
</p>
126+
</Box>
127+
<Button appearance="primary" onClick={authenticate}>
128+
Sign in & connect
129+
</Button>
130+
</>
131+
</Step>
132+
);
133+
};
134+
135+
export default DefaultState;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { ErrorObjType } from "../../../utils/modifyError";
2+
import ErrorUI from "../../../components/Error";
3+
import Step from "../../../components/Step";
4+
5+
const ErrorState = ({ error }: { error: ErrorObjType}) => <>
6+
<ErrorUI type={error.type} message={error.message} />
7+
<Step title="Connect a GitHub organization to Jira software">
8+
<div>
9+
Please inform the person who sent you the link that the link <br/>
10+
has expired and send a new link.
11+
</div>
12+
</Step>
13+
</>;
14+
15+
export default ErrorState;

0 commit comments

Comments
 (0)