Skip to content

Commit 473c982

Browse files
authored
Merge branch 'main' into website-add-andy-to-maintainers
2 parents 78778ca + 5c4329c commit 473c982

File tree

16 files changed

+578
-103
lines changed

16 files changed

+578
-103
lines changed

src/db/file/pushes.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ export const authorise = async (id: string, attestation: any): Promise<{ message
111111
return { message: `authorised ${id}` };
112112
};
113113

114-
export const reject = async (id: string, attestation: any): Promise<{ message: string }> => {
114+
export const reject = async (id: string, rejection: any): Promise<{ message: string }> => {
115115
const action = await getPush(id);
116116
if (!action) {
117117
throw new Error(`push ${id} not found`);
@@ -120,7 +120,7 @@ export const reject = async (id: string, attestation: any): Promise<{ message: s
120120
action.authorised = false;
121121
action.canceled = false;
122122
action.rejected = true;
123-
action.attestation = attestation;
123+
action.rejection = rejection;
124124
await writeAudit(action);
125125
return { message: `reject ${id}` };
126126
};

src/db/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,8 @@ export const deletePush = (id: string): Promise<void> => start().deletePush(id);
167167
export const authorise = (id: string, attestation: any): Promise<{ message: string }> =>
168168
start().authorise(id, attestation);
169169
export const cancel = (id: string): Promise<{ message: string }> => start().cancel(id);
170-
export const reject = (id: string, attestation: any): Promise<{ message: string }> =>
171-
start().reject(id, attestation);
170+
export const reject = (id: string, rejection: any): Promise<{ message: string }> =>
171+
start().reject(id, rejection);
172172
export const getRepos = (query?: Partial<RepoQuery>): Promise<Repo[]> => start().getRepos(query);
173173
export const getRepo = (name: string): Promise<Repo | null> => start().getRepo(name);
174174
export const getRepoByUrl = (url: string): Promise<Repo | null> => start().getRepoByUrl(url);

src/db/mongo/pushes.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,15 +77,15 @@ export const authorise = async (id: string, attestation: any): Promise<{ message
7777
return { message: `authorised ${id}` };
7878
};
7979

80-
export const reject = async (id: string, attestation: any): Promise<{ message: string }> => {
80+
export const reject = async (id: string, rejection: any): Promise<{ message: string }> => {
8181
const action = await getPush(id);
8282
if (!action) {
8383
throw new Error(`push ${id} not found`);
8484
}
8585
action.authorised = false;
8686
action.canceled = false;
8787
action.rejected = true;
88-
action.attestation = attestation;
88+
action.rejection = rejection;
8989
await writeAudit(action);
9090
return { message: `reject ${id}` };
9191
};

src/db/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ export interface Sink {
9898
deletePush: (id: string) => Promise<void>;
9999
authorise: (id: string, attestation: any) => Promise<{ message: string }>;
100100
cancel: (id: string) => Promise<{ message: string }>;
101-
reject: (id: string, attestation: any) => Promise<{ message: string }>;
101+
reject: (id: string, rejection: any) => Promise<{ message: string }>;
102102
getRepos: (query?: Partial<RepoQuery>) => Promise<Repo[]>;
103103
getRepo: (name: string) => Promise<Repo | null>;
104104
getRepoByUrl: (url: string) => Promise<Repo | null>;

src/proxy/actions/Action.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { processGitURLForNameAndOrg, processUrlPath } from '../routes/helper';
22
import { Step } from './Step';
3-
import { Attestation, CommitData } from '../processors/types';
3+
import { Attestation, CommitData, Rejection } from '../processors/types';
44

55
/**
66
* Class representing a Push.
@@ -34,6 +34,7 @@ class Action {
3434
user?: string;
3535
userEmail?: string;
3636
attestation?: Attestation;
37+
rejection?: Rejection;
3738
lastStep?: Step;
3839
proxyGitPath?: string;
3940
newIdxFiles?: string[];

src/proxy/processors/types.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,15 @@ export type Attestation = {
1919
questions: Question[];
2020
};
2121

22+
export type Rejection = {
23+
reviewer: {
24+
username: string;
25+
reviewerEmail: string;
26+
};
27+
timestamp: string | Date;
28+
reason: string;
29+
};
30+
2231
export type CommitContent = {
2332
item: number;
2433
type: number;

src/service/routes/push.ts

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,14 @@ router.post('/:id/reject', async (req: Request<{ id: string }>, res: Response) =
4545

4646
const id = req.params.id;
4747
const { username } = req.user as { username: string };
48+
const { reason } = req.body;
49+
50+
if (!reason || !reason.trim()) {
51+
res.status(400).send({
52+
message: 'Rejection reason is required',
53+
});
54+
return;
55+
}
4856

4957
// Get the push request
5058
const push = await getValidPushOrRespond(id, res);
@@ -71,8 +79,29 @@ router.post('/:id/reject', async (req: Request<{ id: string }>, res: Response) =
7179
const isAllowed = await db.canUserApproveRejectPush(id, username);
7280

7381
if (isAllowed) {
74-
const result = await db.reject(id, null);
75-
console.log(`User ${username} rejected push request for ${id}`);
82+
const reviewerList = await db.getUsers({ username });
83+
const reviewerEmail = reviewerList[0].email;
84+
85+
if (!reviewerEmail) {
86+
res.status(404).send({
87+
message: `There was no registered email address for the reviewer: ${username}`,
88+
});
89+
return;
90+
}
91+
92+
const rejection = {
93+
reason,
94+
timestamp: new Date(),
95+
reviewer: {
96+
username,
97+
reviewerEmail,
98+
},
99+
};
100+
101+
const result = await db.reject(id, rejection);
102+
console.log(
103+
`User ${username} rejected push request for ${id}${reason ? ` with reason: ${reason}` : ''}`,
104+
);
76105
res.send(result);
77106
} else {
78107
res.status(403).send({

src/ui/services/git-push.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,12 +99,13 @@ const rejectPush = async (
9999
id: string,
100100
setMessage: (message: string) => void,
101101
setUserAllowedToReject: (userAllowedToReject: boolean) => void,
102+
reason?: string,
102103
): Promise<void> => {
103104
const apiV1Base = await getApiV1BaseUrl();
104105
const url = `${apiV1Base}/push/${id}/reject`;
105106
let errorMsg = '';
106107
let isUserAllowedToReject = true;
107-
await axios.post(url, {}, getAxiosConfig()).catch((error: any) => {
108+
await axios.post(url, { reason }, getAxiosConfig()).catch((error: any) => {
108109
if (error.response && error.response.status === 401) {
109110
errorMsg = 'You are not authorised to reject...';
110111
isUserAllowedToReject = false;

src/ui/views/PushDetails/PushDetails.tsx

Lines changed: 14 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ import CardFooter from '../../components/Card/CardFooter';
1212
import Button from '../../components/CustomButtons/Button';
1313
import Diff from './components/Diff';
1414
import Attestation from './components/Attestation';
15-
import AttestationView from './components/AttestationView';
15+
import AttestationInfo from './components/AttestationInfo';
16+
import RejectionInfo from './components/RejectionInfo';
17+
import Reject from './components/Reject';
1618
import Table from '@material-ui/core/Table';
1719
import TableBody from '@material-ui/core/TableBody';
1820
import TableHead from '@material-ui/core/TableHead';
@@ -21,11 +23,9 @@ import TableCell from '@material-ui/core/TableCell';
2123
import { getPush, authorisePush, rejectPush, cancelPush } from '../../services/git-push';
2224
import { CheckCircle, Visibility, Cancel, Block } from '@material-ui/icons';
2325
import Snackbar from '@material-ui/core/Snackbar';
24-
import Tooltip from '@material-ui/core/Tooltip';
25-
import { AttestationFormData, PushActionView } from '../../types';
26+
import { PushActionView } from '../../types';
2627
import { trimPrefixRefsHeads, trimTrailingDotGit } from '../../../db/helper';
2728
import { generateEmailLink, getGitProvider } from '../../utils';
28-
import UserLink from '../../components/UserLink/UserLink';
2929

3030
const Dashboard: React.FC = () => {
3131
const { id } = useParams<{ id: string }>();
@@ -62,9 +62,9 @@ const Dashboard: React.FC = () => {
6262
}
6363
};
6464

65-
const reject = async () => {
65+
const reject = async (reason: string) => {
6666
if (!id) return;
67-
await rejectPush(id, setMessage, setUserAllowedToReject);
67+
await rejectPush(id, setMessage, setUserAllowedToReject, reason);
6868
if (isUserAllowedToReject) {
6969
navigate('/dashboard/push/');
7070
}
@@ -153,91 +153,17 @@ const Dashboard: React.FC = () => {
153153
<Button color='warning' onClick={cancel}>
154154
Cancel
155155
</Button>
156-
<Button color='danger' onClick={reject}>
157-
Reject
158-
</Button>
156+
<Reject rejectFn={reject} />
159157
<Attestation approveFn={authorise} />
160158
</div>
161159
)}
162-
{push.attestation && push.authorised && (
163-
<div
164-
style={{
165-
background: '#eee',
166-
padding: '10px 20px 10px 20px',
167-
borderRadius: '10px',
168-
color: 'black',
169-
marginTop: '15px',
170-
float: 'right',
171-
position: 'relative',
172-
textAlign: 'left',
173-
}}
174-
>
175-
<span style={{ position: 'absolute', top: 0, right: 0 }}>
176-
<CheckCircle
177-
style={{
178-
cursor: push.autoApproved ? 'default' : 'pointer',
179-
transform: 'scale(0.65)',
180-
opacity: push.autoApproved ? 0.5 : 1,
181-
}}
182-
onClick={() => {
183-
if (!push.autoApproved) {
184-
setAttestation(true);
185-
}
186-
}}
187-
htmlColor='green'
188-
/>
189-
</span>
190-
191-
{push.autoApproved ? (
192-
<div style={{ paddingTop: '15px' }}>
193-
<p>
194-
<strong>Auto-approved by system</strong>
195-
</p>
196-
</div>
197-
) : (
198-
<>
199-
{isGitHub && (
200-
<UserLink username={push.attestation.reviewer.username}>
201-
<img
202-
style={{ width: '45px', borderRadius: '20px' }}
203-
src={`https://github.com/${push.attestation.reviewer.gitAccount}.png`}
204-
/>
205-
</UserLink>
206-
)}
207-
<div>
208-
<p>
209-
{isGitHub && (
210-
<UserLink username={push.attestation.reviewer.username}>
211-
{push.attestation.reviewer.gitAccount}
212-
</UserLink>
213-
)}
214-
{!isGitHub && <UserLink username={push.attestation.reviewer.username} />}{' '}
215-
approved this contribution
216-
</p>
217-
</div>
218-
</>
219-
)}
220-
221-
<Tooltip
222-
title={moment(push.attestation.timestamp).format(
223-
'dddd, MMMM Do YYYY, h:mm:ss a',
224-
)}
225-
arrow
226-
>
227-
<kbd style={{ color: 'black', float: 'right' }}>
228-
{moment(push.attestation.timestamp).fromNow()}
229-
</kbd>
230-
</Tooltip>
231-
232-
{!push.autoApproved && (
233-
<AttestationView
234-
data={push.attestation as AttestationFormData}
235-
attestation={attestation}
236-
setAttestation={setAttestation}
237-
/>
238-
)}
239-
</div>
240-
)}
160+
<AttestationInfo
161+
push={push}
162+
isGitHub={isGitHub}
163+
attestation={attestation}
164+
setAttestation={setAttestation}
165+
/>
166+
<RejectionInfo push={push} />
241167
</CardHeader>
242168
<CardBody>
243169
<GridContainer>
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import React from 'react';
2+
import moment from 'moment';
3+
import { CheckCircle } from '@material-ui/icons';
4+
import Tooltip from '@material-ui/core/Tooltip';
5+
import UserLink from '../../../components/UserLink/UserLink';
6+
import AttestationView from './AttestationView';
7+
import { AttestationFormData, PushActionView } from '../../../types';
8+
9+
interface AttestationInfoProps {
10+
push: PushActionView;
11+
isGitHub: boolean;
12+
attestation: boolean;
13+
setAttestation: (value: boolean) => void;
14+
}
15+
16+
const AttestationInfo: React.FC<AttestationInfoProps> = ({
17+
push,
18+
isGitHub,
19+
attestation,
20+
setAttestation,
21+
}) => {
22+
if (!push.attestation || !push.authorised) {
23+
return null;
24+
}
25+
26+
return (
27+
<div
28+
style={{
29+
background: '#eee',
30+
padding: '10px 20px 10px 20px',
31+
borderRadius: '10px',
32+
color: 'black',
33+
marginTop: '15px',
34+
float: 'right',
35+
position: 'relative',
36+
textAlign: 'left',
37+
}}
38+
>
39+
<span style={{ position: 'absolute', top: 0, right: 0 }}>
40+
<CheckCircle
41+
style={{
42+
cursor: push.autoApproved ? 'default' : 'pointer',
43+
transform: 'scale(0.65)',
44+
opacity: push.autoApproved ? 0.5 : 1,
45+
}}
46+
onClick={() => {
47+
if (!push.autoApproved) {
48+
setAttestation(true);
49+
}
50+
}}
51+
htmlColor='green'
52+
/>
53+
</span>
54+
55+
{push.autoApproved ? (
56+
<div style={{ paddingTop: '15px' }}>
57+
<p>
58+
<strong>Auto-approved by system</strong>
59+
</p>
60+
</div>
61+
) : (
62+
<>
63+
{isGitHub && (
64+
<UserLink username={push.attestation.reviewer.username}>
65+
<img
66+
style={{ width: '45px', borderRadius: '20px' }}
67+
src={`https://github.com/${push.attestation.reviewer.gitAccount}.png`}
68+
/>
69+
</UserLink>
70+
)}
71+
<div>
72+
<p>
73+
{isGitHub && (
74+
<UserLink username={push.attestation.reviewer.username}>
75+
{push.attestation.reviewer.gitAccount}
76+
</UserLink>
77+
)}
78+
{!isGitHub && <UserLink username={push.attestation.reviewer.username} />} approved
79+
this contribution
80+
</p>
81+
</div>
82+
</>
83+
)}
84+
85+
<Tooltip
86+
title={moment(push.attestation.timestamp).format('dddd, MMMM Do YYYY, h:mm:ss a')}
87+
arrow
88+
>
89+
<kbd style={{ color: 'black', float: 'right' }}>
90+
{moment(push.attestation.timestamp).fromNow()}
91+
</kbd>
92+
</Tooltip>
93+
94+
{!push.autoApproved && (
95+
<AttestationView
96+
data={push.attestation as AttestationFormData}
97+
attestation={attestation}
98+
setAttestation={setAttestation}
99+
/>
100+
)}
101+
</div>
102+
);
103+
};
104+
105+
export default AttestationInfo;

0 commit comments

Comments
 (0)