Skip to content

Commit 62d0450

Browse files
authored
fix: comment logic (#1460)
* fix: comment lgoic * fix: update logic for construcvtion of reviewMeta * chore: add tests + minor cleanpu * chore: fix broken test
1 parent c65e098 commit 62d0450

File tree

3 files changed

+106
-13
lines changed

3 files changed

+106
-13
lines changed

src/integration/NotificationOnEditHandler.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ describe("Notifications Router", () => {
298298
await ReviewMeta.create({
299299
reviewId: 1,
300300
pullRequestNumber: 1,
301-
reviewLink: "test",
301+
reviewLink: `/${mockSiteName}/review/1`,
302302
})
303303
mockGithubService.getComments.mockResolvedValueOnce([])
304304
mockGithubService.getCommitDiff.mockResolvedValueOnce({

src/integration/Reviews.spec.ts

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,11 @@ import {
3939
MOCK_GITHUB_PULL_REQUEST_NUMBER,
4040
MOCK_GITHUB_RAWCOMMENT_ONE,
4141
MOCK_GITHUB_RAWCOMMENT_TWO,
42-
MOCK_GITHUB_FRONTMATTER,
4342
MOCK_PAGE_PERMALINK,
4443
} from "@fixtures/github"
4544
import { MOCK_GITHUB_DATE_ONE } from "@fixtures/identity"
4645
import {
4746
MOCK_PULL_REQUEST_BODY_ONE,
48-
MOCK_PULL_REQUEST_CHANGED_FILES_ONE,
4947
MOCK_PULL_REQUEST_ONE,
5048
MOCK_PULL_REQUEST_TITLE_ONE,
5149
} from "@fixtures/review"
@@ -1764,11 +1762,71 @@ describe("Review Requests Integration Tests", () => {
17641762
await Reviewer.destroy({
17651763
where: {},
17661764
})
1765+
await ReviewComment.destroy({ where: {} })
17671766
await ReviewRequest.destroy({
17681767
where: {},
17691768
})
17701769
})
17711770

1771+
it("should not retrieve comments for review requests with the same number across different sites", async () => {
1772+
// Arrange
1773+
const FAKE_COMMENT = "This is a comment that should not appear for site 1"
1774+
const duplicatedReviewRequest = await ReviewRequest.create({
1775+
requestorId: MOCK_USER_ID_ONE,
1776+
siteId: MOCK_SITE_ID_TWO,
1777+
})
1778+
await ReviewMeta.create({
1779+
reviewId: duplicatedReviewRequest?.id,
1780+
pullRequestNumber: MOCK_GITHUB_PULL_REQUEST_NUMBER,
1781+
// NOTE: the only relevant difference here is that the `siteName` is different
1782+
// but importantly, the pr number is the same across both this and the one we created
1783+
// earlier in the `beforeAll` block
1784+
reviewLink: `cms.isomer.gov.sg/sites/${MOCK_REPO_NAME_TWO}/review/${MOCK_GITHUB_PULL_REQUEST_NUMBER}`,
1785+
})
1786+
1787+
await ReviewComment.create({
1788+
reviewId: duplicatedReviewRequest.id,
1789+
reviewerId: MOCK_USER_ID_ONE,
1790+
comment: FAKE_COMMENT,
1791+
createdAt: new Date().getTime(),
1792+
updatedAt: new Date().getTime(),
1793+
})
1794+
1795+
const app = generateRouterForUserWithSite(
1796+
subrouter,
1797+
MOCK_USER_SESSION_DATA_TWO,
1798+
MOCK_REPO_NAME_ONE
1799+
)
1800+
1801+
mockGenericAxios.get.mockResolvedValueOnce({
1802+
data: [MOCK_GITHUB_RAWCOMMENT_ONE, MOCK_GITHUB_RAWCOMMENT_TWO],
1803+
})
1804+
1805+
const expected = [
1806+
{
1807+
user: MOCK_USER_EMAIL_ONE,
1808+
message: MOCK_GITHUB_COMMENT_BODY_ONE,
1809+
createdAt: new Date(MOCK_GITHUB_COMMIT_DATE_ONE).getTime(),
1810+
isRead: false,
1811+
},
1812+
{
1813+
user: MOCK_USER_EMAIL_TWO,
1814+
message: MOCK_GITHUB_COMMENT_BODY_TWO,
1815+
createdAt: new Date(MOCK_GITHUB_COMMIT_DATE_THREE).getTime(),
1816+
isRead: false,
1817+
},
1818+
]
1819+
1820+
// Act
1821+
const actual = await request(app).get(
1822+
`/${MOCK_REPO_NAME_ONE}/${MOCK_GITHUB_PULL_REQUEST_NUMBER}/comments`
1823+
)
1824+
1825+
// Assert
1826+
expect(actual.statusCode).toEqual(200)
1827+
expect(actual.body).toEqual(expected)
1828+
})
1829+
17721830
it("should retrieve the comments for the review request", async () => {
17731831
// Arrange
17741832
const app = generateRouterForUserWithSite(

src/services/review/ReviewRequestService.ts

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import _, { sortBy, unionBy, zipObject } from "lodash"
1+
import _, { pull, sortBy, unionBy, zipObject } from "lodash"
22
import { err, errAsync, ok, okAsync, Result, ResultAsync } from "neverthrow"
33
import { Op, ModelStatic } from "sequelize"
44
import { Sequelize } from "sequelize-typescript"
@@ -1083,28 +1083,47 @@ export default class ReviewRequestService {
10831083
const { siteName, isomerUserId } = sessionData
10841084

10851085
logger.info(
1086-
`Creating comment for PR ${pullRequestNumber}, site: ${siteName}`
1086+
`Creating comment for PR with pullRequestNumber: ${pullRequestNumber}, site: ${siteName}`
10871087
)
1088-
// get id of review request
1088+
// NOTE: We need to do this to ensure that users are creating comments
1089+
// for a review request that exists.
1090+
// Without this check, users can ping our backend
1091+
// to create comments for any review request,
1092+
// even if the review request doesn't exist
10891093
const reviewMeta = await this.reviewMeta.findOne({
1090-
where: { pullRequestNumber },
1094+
where: {
1095+
pullRequestNumber,
1096+
reviewLink: {
1097+
// NOTE: The `/${requestId}/` that the frontend
1098+
// sends across is not actually the `reviewId`.
1099+
// This is actually the GITHUB `pullRequestNumber`
1100+
// and hence, we have to artifically construct
1101+
// the `reviewLink`.
1102+
// We need the starting `/` to prevent cases where `siteName`
1103+
// is potentially a substring of another site (eg: `moe-ny` and `moe-moe-ny`)
1104+
[Op.endsWith]: `/${siteName}/review/${pullRequestNumber}`,
1105+
},
1106+
},
10911107
})
10921108

10931109
if (reviewMeta?.reviewId) {
10941110
try {
10951111
return await this.reviewCommentService.createCommentForReviewRequest(
1096-
reviewMeta?.reviewId,
1112+
reviewMeta.reviewId,
10971113
isomerUserId,
10981114
message
10991115
)
11001116
} catch (e) {
11011117
logger.error(
1102-
`Error creating comment in DB for PR ${pullRequestNumber}, site: ${siteName}`
1118+
`Error creating comment in DB for PR ${reviewMeta.reviewId}, site: ${siteName}`
11031119
)
11041120
throw new DatabaseError("Error creating comment in DB")
11051121
}
11061122
}
1107-
logger.info(`No review request found for PR ${pullRequestNumber}`)
1123+
1124+
logger.info(
1125+
`No review request found for PR with reviewId: ${reviewMeta?.reviewId}, pullRequestNumber: ${pullRequestNumber}, site: ${siteName}`
1126+
)
11081127
throw new RequestNotFoundError("Review Request not found")
11091128
}
11101129

@@ -1115,17 +1134,33 @@ export default class ReviewRequestService {
11151134
): Promise<CommentItem[]> => {
11161135
const { siteName, isomerUserId: userId } = sessionData
11171136

1118-
// get review request id
1137+
// NOTE: We need to do this to ensure that users are creating comments
1138+
// for a review request that exists.
1139+
// Without this check, users can ping our backend
1140+
// to create comments for any review request,
1141+
// even if the review request doesn't exist
11191142
const reviewMeta = await this.reviewMeta.findOne({
1120-
where: { pullRequestNumber },
1143+
where: {
1144+
pullRequestNumber,
1145+
// NOTE: The `/${requestId}/` that the frontend
1146+
// sends across is not actually the `reviewId`.
1147+
// This is actually the GITHUB `pullRequestNumber`
1148+
// and hence, we have to artifically construct
1149+
// the `reviewLink`.
1150+
// We need the starting `/` to prevent cases where `siteName`
1151+
// is potentially a substring of another site (eg: `moe-ny` and `moe-moe-ny`)
1152+
reviewLink: {
1153+
[Op.endsWith]: `/${siteName}/review/${pullRequestNumber}`,
1154+
},
1155+
},
11211156
})
11221157
if (!reviewMeta || !reviewMeta.reviewId) {
11231158
throw new RequestNotFoundError("Review Request not found")
11241159
}
11251160

11261161
const comments = await this.apiService.getComments(
11271162
siteName,
1128-
pullRequestNumber
1163+
reviewMeta.pullRequestNumber
11291164
)
11301165

11311166
const requestsView = await this.reviewRequestView.findOne({

0 commit comments

Comments
 (0)