Skip to content

Commit fb8d36d

Browse files
committed
Merge branch 'develop' into bugfix-2672/address-test-warnings
2 parents e9037c4 + 661855b commit fb8d36d

File tree

9 files changed

+155
-81
lines changed

9 files changed

+155
-81
lines changed

server/src/main/java/com/objectcomputing/checkins/notifications/email/MailJetSender.java

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,6 @@ public void sendEmail(String fromName, String fromAddress, String subject, Strin
9090
return;
9191
}
9292

93-
List<JSONArray> emailBatches = getEmailBatches(recipients);
94-
List<JSONArray> failedBatches = new ArrayList<>();
9593
JSONObject sender = new JSONObject()
9694
.put("Email", fromAddress)
9795
.put("Name", fromName);
@@ -106,27 +104,33 @@ public void sendEmail(String fromName, String fromAddress, String subject, Strin
106104
final String localEmailFormat = modifiedEmailFormat;
107105
final String localContent = content;
108106

109-
emailBatches.forEach((recipientList) -> {
110-
MailjetRequest request = new MailjetRequest(Emailv31.resource)
111-
.property(Emailv31.MESSAGES, new JSONArray()
112-
.put(new JSONObject()
113-
.put(Emailv31.Message.FROM, sender)
114-
.put(Emailv31.Message.TO, new JSONArray().put(sender))
115-
.put(Emailv31.Message.BCC, recipientList)
116-
.put(Emailv31.Message.SUBJECT, subject)
117-
.put(localEmailFormat, localContent)));
107+
if (recipients.length == 1) {
108+
final JSONArray to = new JSONArray()
109+
.put(new JSONObject().put("Email", recipients[0]));
118110
try {
119-
MailjetResponse response = client.post(request);
120-
LOG.info("Mailjet response status: {}", response.getStatus());
121-
LOG.info("Mailjet response data: {}", response.getData());
111+
send(sender, to, null, subject, localEmailFormat, localContent);
122112
} catch (MailjetException e) {
123-
LOG.error("An unexpected error occurred while sending the upload notification: {}", e.getLocalizedMessage(), e);
124-
failedBatches.add(recipientList);
113+
throw new BadArgException("Failed to send email for " + to);
114+
}
115+
} else {
116+
List<JSONArray> emailBatches = getEmailBatches(recipients);
117+
List<JSONArray> failedBatches = new ArrayList<>();
118+
final JSONArray to =
119+
new JSONArray()
120+
.put(new JSONObject().put("Email", this.fromAddress));
121+
emailBatches.forEach((recipientList) -> {
122+
try {
123+
send(sender, to, recipientList,
124+
subject, localEmailFormat, localContent);
125+
} catch (MailjetException e) {
126+
LOG.error("An unexpected error occurred while sending the upload notification: {}", e.getLocalizedMessage(), e);
127+
failedBatches.add(recipientList);
128+
}
129+
});
130+
131+
if (!failedBatches.isEmpty()) {
132+
throw new BadArgException("Failed to send emails for " + failedBatches);
125133
}
126-
});
127-
128-
if (!failedBatches.isEmpty()) {
129-
throw new BadArgException("Failed to send emails for " + failedBatches);
130134
}
131135
}
132136

@@ -154,4 +158,22 @@ public void setEmailFormat(String format) {
154158
throw new BadArgException(String.format("Email format must be either HTMLPART, MJMLPART or TEXTPART, got %s", format));
155159
}
156160
}
161+
162+
private void send(JSONObject from, JSONArray to, JSONArray bcc,
163+
String subject, String emailFormat, String content) throws MailjetException {
164+
JSONObject values = new JSONObject()
165+
.put(Emailv31.Message.FROM, from)
166+
.put(Emailv31.Message.TO, to)
167+
.put(Emailv31.Message.SUBJECT, subject)
168+
.put(emailFormat, content);
169+
if (bcc != null) {
170+
values.put(Emailv31.Message.BCC, bcc);
171+
}
172+
173+
MailjetRequest request = new MailjetRequest(Emailv31.resource)
174+
.property(Emailv31.MESSAGES, new JSONArray().put(values));
175+
MailjetResponse response = client.post(request);
176+
LOG.info("Mailjet response status: {}", response.getStatus());
177+
LOG.info("Mailjet response data: {}", response.getData());
178+
}
157179
}

server/src/main/resources/db/dev/R__Load_testing_data.sql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1291,12 +1291,12 @@ VALUES
12911291
INSERT INTO review_periods
12921292
(id, name, review_status, review_template_id, self_review_template_id, launch_date, self_review_close_date, close_date, period_start_date, period_end_date)
12931293
VALUES
1294-
('12345678-e29c-4cf4-9ea4-6baa09405c57', 'Review Period 1', 'PLANNING', 'd1e94b60-47c4-4945-87d1-4dc88f088e57', '926a37a4-4ded-4633-8900-715b0383aecc', '2024-09-01 06:00:00', '2024-09-02 06:00:00', '2024-09-03 06:00:00', '2024-01-01 06:00:00', '2024-08-31 06:00:00');
1294+
('12345678-e29c-4cf4-9ea4-6baa09405c57', 'Review Period 1', 'PLANNING', 'd1e94b60-47c4-4945-87d1-4dc88f088e57', '926a37a4-4ded-4633-8900-715b0383aecc', CURRENT_DATE + TIME '06:00:00', CURRENT_DATE + INTERVAL '1' DAY + TIME '06:00:00', CURRENT_DATE + INTERVAL '2' DAY + TIME '06:00:00', date_trunc('year', CURRENT_DATE) + TIME '06:00:00', CURRENT_DATE + INTERVAL '-1' DAY + TIME '06:00:00');
12951295

12961296
INSERT INTO review_periods
12971297
(id, name, review_status, review_template_id, self_review_template_id, launch_date, self_review_close_date, close_date, period_start_date, period_end_date)
12981298
VALUES
1299-
('12345678-e29c-4cf4-9ea4-6baa09405c58', 'Review Period 2', 'PLANNING', 'd1e94b60-47c4-4945-87d1-4dc88f088e57', '926a37a4-4ded-4633-8900-715b0383aecc', '2024-09-10 06:00:00', '2024-09-11 06:00:00', '2024-09-12 06:00:00', '2024-01-01 06:00:00', '2024-08-31 06:00:00');
1299+
('12345678-e29c-4cf4-9ea4-6baa09405c58', 'Review Period 2', 'PLANNING', 'd1e94b60-47c4-4945-87d1-4dc88f088e57', '926a37a4-4ded-4633-8900-715b0383aecc', CURRENT_DATE + TIME '06:00:00', CURRENT_DATE + INTERVAL '1' DAY + TIME '06:00:00', CURRENT_DATE + INTERVAL '2' DAY + TIME '06:00:00', date_trunc('year', CURRENT_DATE) + TIME '06:00:00', CURRENT_DATE + INTERVAL '-1' DAY + TIME '06:00:00');
13001300

13011301
INSERT INTO review_periods
13021302
(id, name, review_status, review_template_id, self_review_template_id, launch_date, self_review_close_date, close_date, period_start_date, period_end_date)

web-ui/src/components/feedback_submit_form/FeedbackSubmitForm.jsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ const FeedbackSubmitForm = ({
7878
const { state, dispatch } = useContext(AppContext);
7979
const csrf = selectCsrfToken(state);
8080
const [isLoading, setIsLoading] = useState(false);
81+
const [isSubmitting, setIsSubmitting] = useState(false);
8182
const [isReviewing, setIsReviewing] = useState(reviewOnly);
8283
const history = useHistory();
8384
const [questionAnswerPairs, setQuestionAnswerPairs] = useState([]);
@@ -117,6 +118,7 @@ const FeedbackSubmitForm = ({
117118
}
118119

119120
const onSubmitHandler = () => {
121+
setIsSubmitting(true);
120122
updateAllAnswersSubmit()
121123
.then(res => {
122124
for (let i = 0; i < res.length; ++i) {
@@ -135,9 +137,11 @@ const FeedbackSubmitForm = ({
135137
})
136138
.then(resTwo => {
137139
if (resTwo === false) {
140+
setIsSubmitting(false);
138141
return;
139142
}
140143
updateRequestSubmit().then(res => {
144+
setIsSubmitting(false);
141145
if (res && res.payload && res.payload.data && !res.error) {
142146
history.push(`/feedback/submit/confirmation/?request=${requestId}`);
143147
} else {
@@ -225,7 +229,7 @@ const FeedbackSubmitForm = ({
225229
<React.Fragment>
226230
<Button
227231
className={classes.coloredButton}
228-
disabled={isLoading}
232+
disabled={isLoading || isSubmitting}
229233
onClick={() => setIsReviewing(false)}
230234
variant="contained"
231235
color="secondary"
@@ -234,7 +238,7 @@ const FeedbackSubmitForm = ({
234238
</Button>
235239
<Button
236240
className={classes.button}
237-
disabled={isLoading}
241+
disabled={isLoading || isSubmitting}
238242
onClick={onSubmitHandler}
239243
variant="contained"
240244
color="primary"

web-ui/src/components/reviews/periods/ReviewPeriods.jsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -389,11 +389,14 @@ const ReviewPeriods = ({ onPeriodSelected, mode }) => {
389389
) : periods.length > 0 ? (
390390
periods
391391
.sort((a, b) => {
392+
const aName = (a.name || '');
392393
return a.reviewStatus === b.reviewStatus
393-
? (a.name || '').localeCompare(b.name)
394+
? aName.localeCompare(b.name)
394395
: a.reviewStatus === ReviewStatus.OPEN
395396
? -1
396-
: 1;
397+
: (b.reviewStatus === ReviewStatus.OPEN
398+
? 1
399+
: aName.localeCompare(b.name));
397400
})
398401
.map((period) => (
399402
<div>

web-ui/src/context/AppContext.jsx

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,17 @@ import {getCertifications} from "../api/certification.js";
3434

3535
const AppContext = React.createContext();
3636

37+
function getSessionCookieValue(name) {
38+
const cookies = document?.cookie?.split(';');
39+
for (let i = 0; i < cookies.length; i++) {
40+
const cookie = cookies[i].trim();
41+
if (cookie.startsWith(name + '=')) {
42+
return decodeURIComponent(cookie.substring(name.length + 1));
43+
}
44+
}
45+
return null;
46+
}
47+
3748
const AppContextProvider = props => {
3849
const [state, dispatch] = useReducer(
3950
reducer,
@@ -64,12 +75,17 @@ const AppContextProvider = props => {
6475
useEffect(() => {
6576
const getCsrf = async () => {
6677
if (!csrf) {
67-
const res = await fetch(url, {
68-
responseType: 'text',
69-
credentials: 'include'
70-
});
71-
if (res && res.ok) {
72-
dispatch({ type: SET_CSRF, payload: await res.text() });
78+
const payload = getSessionCookieValue('_csrf');
79+
if (payload) {
80+
dispatch({ type: SET_CSRF, payload });
81+
} else {
82+
const res = await fetch(url, {
83+
responseType: 'text',
84+
credentials: 'include'
85+
});
86+
if (res && res.ok) {
87+
dispatch({ type: SET_CSRF, payload: await res.text() });
88+
}
7389
}
7490
}
7591
};

web-ui/src/pages/AnniversaryReportPage.test.jsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,28 @@ const userStateWithPermission = {
1414

1515
it('renders correctly', () => {
1616
const mockDate = new Date(2022, 1, 1);
17-
const spy = vi.spyOn(global, 'Date').mockImplementation(() => mockDate);
17+
vi.useFakeTimers();
18+
vi.setSystemTime(mockDate);
1819

1920
snapshot(
2021
<AppContextProvider value={userStateWithPermission}>
2122
<AnniversaryReportPage />
2223
</AppContextProvider>
2324
);
2425

25-
spy.mockRestore();
26+
vi.useRealTimers();
2627
});
2728

2829
it('renders an error if user does not have appropriate permission', () => {
2930
const mockDate = new Date(2022, 1, 1);
30-
const spy = vi.spyOn(global, 'Date').mockImplementation(() => mockDate);
31+
vi.useFakeTimers();
32+
vi.setSystemTime(mockDate);
3133

3234
snapshot(
3335
<AppContextProvider>
3436
<AnniversaryReportPage />
3537
</AppContextProvider>
3638
);
3739

38-
spy.mockRestore();
40+
vi.useRealTimers();
3941
});

web-ui/src/pages/BirthdayReportPage.test.jsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React from 'react';
2+
import { vi } from 'vitest';
23
import BirthdayReportPage from './BirthdayReportPage';
34
import { AppContextProvider } from '../context/AppContext';
45

@@ -14,26 +15,28 @@ const userStateWithPermission = {
1415

1516
it('renders correctly', () => {
1617
const mockDate = new Date(2022, 1, 1);
17-
const spy = vi.spyOn(global, 'Date').mockImplementation(() => mockDate);
18+
vi.useFakeTimers();
19+
vi.setSystemTime(mockDate);
1820

1921
snapshot(
2022
<AppContextProvider value={userStateWithPermission}>
2123
<BirthdayReportPage />
2224
</AppContextProvider>
2325
);
2426

25-
spy.mockRestore();
27+
vi.useRealTimers();
2628
});
2729

2830
it('renders an error if user does not have appropriate permission', () => {
2931
const mockDate = new Date(2022, 1, 1);
30-
const spy = vi.spyOn(global, 'Date').mockImplementation(() => mockDate);
32+
vi.useFakeTimers();
33+
vi.setSystemTime(mockDate);
3134

3235
snapshot(
3336
<AppContextProvider>
3437
<BirthdayReportPage />
3538
</AppContextProvider>
3639
);
3740

38-
spy.mockRestore();
41+
vi.useRealTimers();
3942
});

web-ui/src/pages/HomePage.jsx

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import MyBirthday from '../components/celebrations/MyBirthday';
99
import { AppContext } from '../context/AppContext';
1010
import { selectCsrfToken, selectCurrentUser } from '../context/selectors';
1111
import { sortAnniversaries, sortBirthdays } from '../context/util';
12-
import { Button } from '@mui/material';
12+
import { Button, Grid } from '@mui/material';
1313

1414
import './HomePage.css';
1515

@@ -76,6 +76,13 @@ export default function HomePage() {
7676
return document.cookie.indexOf("OJWT=") != -1;
7777
}
7878

79+
// This width matches the birthdays-card and anniversaries-card style.
80+
// However, we do not want to set this width on the PublicKudos css as it is
81+
// used elsewhere and does not need to have it's width restricted. This only
82+
// applies if if we have birthdays or anniversaries to display on this page.
83+
const kudosStyle = birthdays.length == 0 &&
84+
anniversaries.length == 0 ? {} : { width: '450px' };
85+
7986
return (
8087
<div className="home-page">
8188
<div className="celebrations">
@@ -87,11 +94,19 @@ export default function HomePage() {
8794
myAnniversary={myAnniversaryData}
8895
/>
8996
) : (
90-
<>
91-
{ anniversaries.length > 0 && (<Anniversaries anniversaries={anniversaries} />) }
92-
{ birthdays.length > 0 && (<Birthdays birthdays={birthdays} />) }
93-
<PublicKudos />
94-
</>
97+
<Grid container spacing={2} style={{ padding: '0 20px 0 20px' }}>
98+
{ anniversaries.length > 0 && (
99+
<Grid item>
100+
<Anniversaries anniversaries={anniversaries} />
101+
</Grid>) }
102+
{ birthdays.length > 0 && (
103+
<Grid item>
104+
<Birthdays birthdays={birthdays} />
105+
</Grid>) }
106+
<Grid item style={kudosStyle}>
107+
<PublicKudos />
108+
</Grid>
109+
</Grid>
95110
)}
96111
</div>
97112
{checkForImpersonation() &&

0 commit comments

Comments
 (0)