Skip to content

Commit 8f21ce4

Browse files
authored
Merge branch 'develop' into feature-2647/volunteer-tracking-bugfixes
2 parents b550167 + 437283b commit 8f21ce4

File tree

7 files changed

+87
-80
lines changed

7 files changed

+87
-80
lines changed

server/src/main/java/com/objectcomputing/checkins/services/reviews/ReviewAssignmentServicesImpl.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,12 @@ public ReviewAssignment save(ReviewAssignment reviewAssignment) {
4343
return newAssignment;
4444
}
4545

46+
// Now that uniqueness constraints have been placed on the
47+
// reviewee-reviewer-reviewPeriod, this method needs to be synchronized
48+
// to avoid multiple calls from the client-side overlapping and attempting
49+
// to create the same review assignments multiple times.
4650
@Override
47-
public List<ReviewAssignment> saveAll(UUID reviewPeriodId, List<ReviewAssignment> reviewAssignments, boolean deleteExisting) {
51+
public synchronized List<ReviewAssignment> saveAll(UUID reviewPeriodId, List<ReviewAssignment> reviewAssignments, boolean deleteExisting) {
4852

4953
if(deleteExisting) {
5054
LOG.warn("Deleting all review assignments for review period {}", reviewPeriodId);

server/src/main/java/com/objectcomputing/checkins/services/role/member_roles/MemberRoleController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import java.util.List;
1212
import java.util.UUID;
1313

14-
@Secured(SecurityRule.IS_ANONYMOUS)
14+
@Secured(SecurityRule.IS_AUTHENTICATED)
1515
@ExecuteOn(TaskExecutors.BLOCKING)
1616
@Controller("/services/roles/members")
1717
public class MemberRoleController {
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
DELETE FROM review_assignments o USING review_assignments n
2+
WHERE o.id < n.id AND (o.reviewee_id = n.reviewee_id AND
3+
o.reviewer_id = n.reviewer_id AND
4+
o.review_period_id = n.review_period_id);
5+
6+
ALTER TABLE review_assignments
7+
ADD CONSTRAINT unique_assignment UNIQUE (reviewee_id, reviewer_id, review_period_id)

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', '2024-09-02', '2024-09-03', '2024-01-01', '2024-12-31');
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');
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', '2024-09-11', '2024-09-12', '2024-01-01', '2024-12-31');
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');
13001300

13011301
-- CAE - Self-Review feedback request, Creator: Big Boss
13021302
INSERT INTO feedback_requests

server/src/test/java/com/objectcomputing/checkins/services/reviews/ReviewAssignmentControllerTest.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,11 @@ void testGETFindAssignmentsByPeriodIdDefaultAssignments() {
146146

147147
final HttpResponse<Set<ReviewAssignment>> response = client.toBlocking().exchange(request, Argument.setOf(ReviewAssignment.class));
148148

149+
// A review period only has default review assignments added to it when
150+
// the review period is created through the controller. And, they are
151+
// no longer added to the review period when retrieving the review
152+
// assignments for a specific review period. Therefore, this review
153+
// period should have zero review assignments associated with it.
149154
assertNotNull(response.body());
150155
assertEquals(0, Objects.requireNonNull(response.body()).size());
151156
assertEquals(HttpStatus.OK, response.getStatus());
@@ -284,4 +289,4 @@ void deleteReviewAssignmentWithoutPermissions() {
284289
assertNotNull(responseException.getResponse());
285290
assertEquals(HttpStatus.FORBIDDEN, responseException.getStatus());
286291
}
287-
}
292+
}

web-ui/src/api/reviewassignments.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { resolve } from './api.js';
2+
3+
const reviewAssignmentsUrl = '/services/review-assignments';
4+
5+
export const getReviewAssignments = async (id, cookie) => {
6+
return resolve({
7+
url: `${reviewAssignmentsUrl}/period/${id}`,
8+
headers: { 'X-CSRF-Header': cookie, Accept: 'application/json' }
9+
});
10+
};
11+
12+
export const createReviewAssignments = async (id, assignments, cookie) => {
13+
return resolve({
14+
method: 'POST',
15+
url: `${reviewAssignmentsUrl}/${id}`,
16+
data: assignments,
17+
headers: {
18+
'X-CSRF-Header': cookie,
19+
Accept: 'application/json',
20+
'Content-Type': 'application/json;charset=UTF-8'
21+
}
22+
});
23+
};
24+
25+
export const updateReviewAssignment = async (assignment, cookie) => {
26+
return resolve({
27+
method: assignment.id === null ? 'POST' : 'PUT',
28+
url: reviewAssignmentsUrl,
29+
data: assignment,
30+
headers: {
31+
'X-CSRF-Header': cookie,
32+
Accept: 'application/json',
33+
'Content-Type': 'application/json;charset=UTF-8'
34+
}
35+
});
36+
};
37+
38+
export const removeReviewAssignment = async (id, cookie) => {
39+
return resolve({
40+
method: 'DELETE',
41+
url: `${reviewAssignmentsUrl}/${id}`,
42+
headers: { 'X-CSRF-Header': cookie }
43+
});
44+
};

web-ui/src/components/reviews/TeamReviews.jsx

Lines changed: 22 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,12 @@ import {
3434
import { styled } from '@mui/material/styles';
3535

3636
import ConfirmationDialog from '../dialogs/ConfirmationDialog';
37-
import { resolve } from '../../api/api.js';
37+
import {
38+
getReviewAssignments,
39+
createReviewAssignments,
40+
updateReviewAssignment,
41+
removeReviewAssignment,
42+
} from '../../api/reviewassignments.js';
3843
import {
3944
findReviewRequestsByPeriod,
4045
findSelfReviewRequestsByPeriodAndTeamMembers
@@ -158,8 +163,6 @@ const TeamReviews = ({ onBack, periodId }) => {
158163
const isAdmin = selectIsAdmin(state);
159164
const period = selectReviewPeriod(state, periodId);
160165

161-
const reviewAssignmentsUrl = '/services/review-assignments';
162-
163166
useEffect(() => {
164167
loadAssignments();
165168
}, [currentMembers]);
@@ -192,16 +195,7 @@ const TeamReviews = ({ onBack, periodId }) => {
192195
};
193196

194197
const loadAssignments = async () => {
195-
const myId = currentUser?.id;
196-
const res = await resolve({
197-
method: 'GET',
198-
url: `${reviewAssignmentsUrl}/period/${periodId}`,
199-
headers: {
200-
'X-CSRF-Header': csrf,
201-
Accept: 'application/json',
202-
'Content-Type': 'application/json;charset=UTF-8'
203-
}
204-
});
198+
const res = await getReviewAssignments(periodId, csrf);
205199
if (res.error) return;
206200

207201
const assignments = res.payload.data;
@@ -210,12 +204,6 @@ const TeamReviews = ({ onBack, periodId }) => {
210204
};
211205

212206
const loadTeamMembers = () => {
213-
// If we already have a list of team members, we should not overwrite the
214-
// list with the original list of team members.
215-
if (teamMembers.length > 0) {
216-
return;
217-
}
218-
219207
let source;
220208
if (!approvalMode || (isAdmin && showAll)) {
221209
source = currentMembers;
@@ -230,38 +218,31 @@ const TeamReviews = ({ onBack, periodId }) => {
230218
// Always filter the members down to existing selected assignments.
231219
// We do not want to add members that were not already selected.
232220
const memberIds = assignments.map(a => a.revieweeId);
233-
let members = source.filter(m => memberIds.includes(m.id));
221+
const members = source.filter(m => memberIds.includes(m.id));
234222
setTeamMembers(members);
235223
};
236224

237225
const updateTeamMembers = async teamMembers => {
226+
// First, create a set of team members, each with a default reviewer.
238227
const data = teamMembers.map(tm => ({
239228
revieweeId: tm.id,
240229
reviewerId: tm.supervisorid,
241230
reviewPeriodId: periodId,
242231
approved: false
243232
}));
244233

245-
const res = await resolve({
246-
method: 'POST',
247-
url: reviewAssignmentsUrl + '/' + periodId,
248-
data,
249-
headers: {
250-
'X-CSRF-Header': csrf,
251-
Accept: 'application/json',
252-
'Content-Type': 'application/json;charset=UTF-8'
253-
}
254-
});
234+
// Set those on the server as the review assignments.
235+
let res = await createReviewAssignments(periodId, data, csrf);
255236
if (res.error) return;
256237

257-
setTeamMembers(teamMembers);
258-
addAssignmentForMemberWithNone(teamMembers);
238+
// Get the list of review assignments from the server to ensure that we are
239+
// reflecting what was actually created.
240+
res = await getReviewAssignments(periodId, csrf);
241+
const assignments = res.error ? [] : res.payload.data;
259242

260-
// Now that teamMembers has been updated, we need to make sure that the
261-
// assignments reflects the set of team members.
262-
const ids = teamMembers.map(m => m.id);
263-
const newAssignments = assignments.filter(a => a.revieweeId && ids.includes(a.revieweeId));
264-
setAssignments(newAssignments);
243+
// Update our reactive assignment and member lists.
244+
setAssignments(assignments);
245+
setTeamMembers(teamMembers);
265246
};
266247

267248
const addAssignmentForMemberWithNone = async (members) => {
@@ -605,11 +586,7 @@ const TeamReviews = ({ onBack, periodId }) => {
605586

606587
const { id, revieweeId, reviewerId } = assignment;
607588
if (id) {
608-
const res = await resolve({
609-
method: 'DELETE',
610-
url: `${reviewAssignmentsUrl}/${id}`,
611-
headers: { 'X-CSRF-Header': csrf }
612-
});
589+
const res = await removeReviewAssignment(id, csrf);
613590

614591
if (res.error) {
615592
console.error('Error deleting assignment:', res.error);
@@ -641,19 +618,7 @@ const TeamReviews = ({ onBack, periodId }) => {
641618
};
642619

643620
const updateReviewPeriodStatus = async reviewStatus => {
644-
const res = await resolve({
645-
method: 'PUT',
646-
url: '/services/review-periods',
647-
headers: {
648-
Accept: 'application/json',
649-
'Content-Type': 'application/json;charset=UTF-8',
650-
'X-CSRF-Header': csrf
651-
},
652-
data: {
653-
...period,
654-
reviewStatus
655-
}
656-
});
621+
const res = await updateReviewPeriod({ ...period, reviewStatus }, csrf);
657622
if (res.error) return;
658623

659624
onBack();
@@ -721,16 +686,7 @@ const TeamReviews = ({ onBack, periodId }) => {
721686
}
722687
}
723688

724-
const res = await resolve({
725-
method: 'POST',
726-
url: `${reviewAssignmentsUrl}/${periodId}`,
727-
data: newAssignments,
728-
headers: {
729-
'X-CSRF-Header': csrf,
730-
Accept: 'application/json',
731-
'Content-Type': 'application/json;charset=UTF-8'
732-
}
733-
});
689+
const res = await createReviewAssignments(periodId, newAssignments, csrf);
734690
if (res.error) return;
735691

736692
newAssignments = sortMembers(res.payload.data);
@@ -940,16 +896,7 @@ const TeamReviews = ({ onBack, periodId }) => {
940896
};
941897

942898
const approveReviewAssignment = async (assignment, approved) => {
943-
const res = await resolve({
944-
method: assignment.id === null ? 'POST' : 'PUT',
945-
url: '/services/review-assignments',
946-
headers: {
947-
Accept: 'application/json',
948-
'Content-Type': 'application/json;charset=UTF-8',
949-
'X-CSRF-Header': csrf
950-
},
951-
data: { ...assignment, approved }
952-
});
899+
await updateReviewAssignment({ ...assignment, approved }, csrf);
953900
};
954901

955902
const visibleTeamMembers = () => {

0 commit comments

Comments
 (0)