diff --git a/src/routes/v2/authenticated/review.ts b/src/routes/v2/authenticated/review.ts index 6411433e2..8daea9b41 100644 --- a/src/routes/v2/authenticated/review.ts +++ b/src/routes/v2/authenticated/review.ts @@ -24,6 +24,7 @@ import { EditedItemDto, UpdateReviewRequestDto, ReviewRequestDto, + BlobDiffDto, } from "@root/types/dto/review" import ReviewRequestService from "@services/review/ReviewRequestService" // eslint-disable-next-line import/prefer-default-export @@ -947,7 +948,8 @@ export class ReviewsRouter { if (!isReviewer) { logger.error({ - message: "", + message: + "User with insufficient permissions attempted to delete approval", method: "deleteReviewRequestApproval", meta: { userId: userWithSiteSessionData.isomerUserId, @@ -967,6 +969,101 @@ export class ReviewsRouter { return res.status(200).send() } + getBlob: RequestHandler< + { siteName: string; requestId: number }, + BlobDiffDto | ResponseErrorBody, + unknown, + { path: string }, + { userWithSiteSessionData: UserWithSiteSessionData } + > = async (req, res) => { + // Step 1: Check that the site exists + const { siteName, requestId } = req.params + const { path } = req.query + const { userWithSiteSessionData } = res.locals + const site = await this.sitesService.getBySiteName(siteName) + + if (!site) { + logger.error({ + message: "Invalid site requested", + method: "getBlob", + meta: { + userId: userWithSiteSessionData.isomerUserId, + email: userWithSiteSessionData.email, + siteName, + }, + }) + return res.status(404).send({ + message: "Please ensure that the site exists!", + }) + } + + // Step 2: Retrieve review request + const possibleReviewRequest = await this.reviewRequestService.getReviewRequest( + site, + requestId + ) + + if (isIsomerError(possibleReviewRequest)) { + logger.error({ + message: "Invalid review request requested", + method: "getBlob", + meta: { + userId: userWithSiteSessionData.isomerUserId, + email: userWithSiteSessionData.email, + siteName, + requestId, + file: path, + }, + }) + return res.status(404).send({ + message: "Please ensure that the site exists!", + }) + } + + // Step 3: Check if the user is a contributor of the site + const role = await this.collaboratorsService.getRole( + siteName, + userWithSiteSessionData.isomerUserId + ) + + if (!role) { + logger.error({ + message: + "User with insufficient permissions attempted to retrieve blob diff", + method: "getBlob", + meta: { + userId: userWithSiteSessionData.isomerUserId, + email: userWithSiteSessionData.email, + siteName, + }, + }) + return res.status(404).send({ + message: "Please ensure that the site exists!", + }) + } + + // NOTE: Currently, Isomer only allows comparisons between staging and production. + // This might change in the future and in that case, the `getBlob` method call below + // should have the corresponding ref (`master` or `staging`) changed. + const prodPromise = this.reviewRequestService.getBlob( + siteName, + path, + "master" + ) + const stagingPromise = this.reviewRequestService.getBlob( + siteName, + path, + "staging" + ) + + const data = await Promise.all([prodPromise, stagingPromise]) + + return res.status(200).json({ + oldValue: data[0], + newValue: data[1], + }) + } + getRouter() { const router = express.Router({ mergeParams: true }) @@ -1020,7 +1117,7 @@ export class ReviewsRouter { "/:requestId", attachReadRouteHandlerWrapper(this.closeReviewRequest) ) - + router.get("/:requestId/blob", attachReadRouteHandlerWrapper(this.getBlob)) return router } } diff --git a/src/services/db/GitHubService.js b/src/services/db/GitHubService.js index 58ed1eb04..5fe7df9e0 100644 --- a/src/services/db/GitHubService.js +++ b/src/services/db/GitHubService.js @@ -30,6 +30,10 @@ class GitHubService { return ReviewApi.getPullRequest(siteName, pullRequestNumber) } + getBlob(repo, path, ref) { + return ReviewApi.getBlob(repo, path, ref) + } + updatePullRequest(siteName, pullRequestNumber, title, description) { return ReviewApi.updatePullRequest( siteName, diff --git a/src/services/db/review.ts b/src/services/db/review.ts index 16f9467ee..2e5cc8b7a 100644 --- a/src/services/db/review.ts +++ b/src/services/db/review.ts @@ -129,3 +129,16 @@ export const createComment = async ( { body: stringifiedMessage } ) } + +export const getBlob = async ( + repo: string, + path: string, + ref: string +): Promise => + axiosInstance + .get(`${repo}/contents/${path}?ref=${ref}`, { + headers: { + Accept: "application/vnd.github.raw", + }, + }) + .then(({ data }) => data) diff --git a/src/services/review/ReviewRequestService.ts b/src/services/review/ReviewRequestService.ts index 6613a51fb..9809482c0 100644 --- a/src/services/review/ReviewRequestService.ts +++ b/src/services/review/ReviewRequestService.ts @@ -620,4 +620,7 @@ export default class ReviewRequestService { return this.computeCommentData(comments, viewedTime) } + + getBlob = async (repo: string, path: string, ref: string): Promise => + this.apiService.getBlob(repo, path, ref) } diff --git a/src/types/dto/review.ts b/src/types/dto/review.ts index 0f58f25e7..4fba8a4cb 100644 --- a/src/types/dto/review.ts +++ b/src/types/dto/review.ts @@ -58,3 +58,8 @@ export interface GithubCommentData { message: string createdAt: string } + +export interface BlobDiffDto { + oldValue: string + newValue: string +}