Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion backend/typescript/graphql/resolvers/reviewPageResolvers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { ApplicationDTO, ReviewedApplicantsDTO } from "../../types";
import {
ApplicationDTO,
ReviewedApplicantRecordDTO,
ReviewedApplicantsDTO,
} from "../../types";
import ReviewPageService from "../../services/implementations/reviewPageService";
import { getErrorMessage } from "../../utilities/errorUtils";

Expand Down Expand Up @@ -29,6 +33,21 @@ const reviewPageResolvers = {
}
},
},
Mutation: {
reportReviewConflict: async (
_parent: undefined,
args: { applicantRecordId: string; reviewerId: number },
): Promise<ReviewedApplicantRecordDTO> => {
try {
return await reviewPageService.reportReviewConflict(
args.applicantRecordId,
args.reviewerId,
);
} catch (error) {
throw new Error(getErrorMessage(error));
}
},
},
};

export default reviewPageResolvers;
16 changes: 16 additions & 0 deletions backend/typescript/graphql/types/reviewPageType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,22 @@ const reviewPageType = gql`
applicantLastName: String!
}

type ReviewedApplicantRecordDTO {
applicantRecordId: String!
reviewerId: Int!
review: Review!
status: String!
score: Int
reviewerHasConflict: Boolean!
}

extend type Mutation {
reportReviewConflict(
applicantRecordId: String!
reviewerId: Int!
): ReviewedApplicantRecordDTO!
}

extend type Query {
reviewApplicantPage(applicantRecordId: String!): ApplicationDTO!
getReviewedApplicantsByUserId(userId: Int!): [ReviewedApplicantsDTO!]!
Expand Down
64 changes: 56 additions & 8 deletions backend/typescript/services/implementations/reviewPageService.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { ApplicationDTO, ReviewedApplicantsDTO } from "../../types";
import {
ApplicationDTO,
ReviewedApplicantRecordDTO,
ReviewedApplicantsDTO,
} from "../../types";
import IReviewPageService from "../interfaces/IReviewPageService";
import { getErrorMessage } from "../../utilities/errorUtils";
import logger from "../../utilities/logger";
import ApplicantRecord from "../../models/applicantRecord.model";
import type ReviewedApplicantRecord from "../../models/reviewedApplicantRecord.model";
import ReviewedApplicantRecord from "../../models/reviewedApplicantRecord.model";
import Applicant from "../../models/applicant.model";
import User from "../../models/user.model";

Expand Down Expand Up @@ -39,7 +43,7 @@ function toApplicationDTO(model: Applicant): ApplicationDTO {
};
}

function toReviewedApplicantRecordDTO(
function toReviewedApplicantsDTO(
model: ReviewedApplicantRecord,
): ReviewedApplicantsDTO {
/* eslint-disable @typescript-eslint/no-non-null-assertion */
Expand All @@ -51,6 +55,18 @@ function toReviewedApplicantRecordDTO(
};
}

function toReviewedApplicantRecordDTO(
model: ReviewedApplicantRecord,
): ReviewedApplicantRecordDTO {
return {
applicantRecordId: model.applicantRecordId,
reviewerId: model.reviewerId,
review: model.review,
status: model.status,
reviewerHasConflict: model.reviewerHasConflict,
};
}

class ReviewPageService implements IReviewPageService {
/* eslint-disable class-methods-use-this */
async getReviewPage(applicantRecordId: string): Promise<ApplicationDTO> {
Expand All @@ -60,8 +76,9 @@ class ReviewPageService implements IReviewPageService {
where: { id: applicantRecordId },
attributes: { exclude: ["createdAt", "updatedAt"] },
});
if (!applicantRecord)
if (!applicantRecord) {
throw new Error(`Database integrity has been violated`);
}

const applicant: Applicant | null = await Applicant.findOne({
where: { id: applicantRecord.applicantId },
Expand All @@ -73,7 +90,9 @@ class ReviewPageService implements IReviewPageService {
},
],
});
if (!applicant) throw new Error(`Database integrity has been violated`);
if (!applicant) {
throw new Error(`Database integrity has been violated`);
}

return toApplicationDTO(applicant);
} catch (error: unknown) {
Expand Down Expand Up @@ -108,14 +127,43 @@ class ReviewPageService implements IReviewPageService {
},
],
});
if (!user) throw new Error(`No user with ${userId} found.`);
if (!user.reviewedApplicantRecords) return [];
return user.reviewedApplicantRecords.map(toReviewedApplicantRecordDTO);
if (!user) {
throw new Error(`No user with ${userId} found.`);
}
if (!user.reviewedApplicantRecords) {
return [];
}
return user.reviewedApplicantRecords.map(toReviewedApplicantsDTO);
} catch (error: unknown) {
Logger.error(`Failed to fetch. Reason = ${getErrorMessage(error)}`);
throw error;
}
}

async reportReviewConflict(
applicantRecordId: string,
reviewerId: number,
): Promise<ReviewedApplicantRecordDTO> {
try {
const reviewedApplicantRecord: ReviewedApplicantRecord | null =
await ReviewedApplicantRecord.findOne({
where: { applicantRecordId, reviewerId },
});
if (!reviewedApplicantRecord) {
throw new Error(
`No reviewed applicant record with ${applicantRecordId} and ${reviewerId} found.`,
);
}
reviewedApplicantRecord.reviewerHasConflict = true;
await reviewedApplicantRecord.save();
return toReviewedApplicantRecordDTO(reviewedApplicantRecord);
} catch (error: unknown) {
Logger.error(
`Failed to report conflict. Reason = ${getErrorMessage(error)}`,
);
throw error;
}
}
}

export default ReviewPageService;
16 changes: 15 additions & 1 deletion backend/typescript/services/interfaces/IReviewPageService.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { ApplicationDTO, ReviewedApplicantsDTO } from "../../types";
import {
ApplicationDTO,
ReviewedApplicantRecordDTO,
ReviewedApplicantsDTO,
} from "../../types";

interface IReviewPageService {
/**
Expand All @@ -14,6 +18,16 @@ interface IReviewPageService {
getReviewedApplicantsByUserId(
userId: number,
): Promise<ReviewedApplicantsDTO[]>;

/**
* Update the reviewerHasConflict column of a ReviewedApplicantRecord entry to indicate a conflict.
* @param applicantRecordId the id of the applicant record that the reviewer is interested in
* @param reviewerId the id of the reviewer that is reporting the conflict
*/
reportReviewConflict(
applicantRecordId: string,
reviewerId: number,
): Promise<ReviewedApplicantRecordDTO>;
}

export default IReviewPageService;
Loading