Skip to content

Commit 8de11bd

Browse files
isabellehuanggisabelle huangMaggie Chen
authored
[INTF25] Build Mutator to Create Reviewed Applicant Record (#84)
Co-authored-by: isabelle huang <i24huang@isabelles-MacBook-Air.local> Co-authored-by: Maggie Chen <maggiechen@eduroam-campus-10-36-10-0.campus-dynamic.uwaterloo.ca>
1 parent b1b6fa8 commit 8de11bd

File tree

7 files changed

+344
-0
lines changed

7 files changed

+344
-0
lines changed

backend/typescript/graphql/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import userResolvers from "./resolvers/userResolvers";
1717
import userType from "./types/userType";
1818
import reviewDashboardResolvers from "./resolvers/reviewDashboardResolvers";
1919
import reviewDashboardType from "./types/reviewDashboardType";
20+
import reviewedApplicantRecordTypes from "./types/reviewedApplicantRecordTypes";
21+
import reviewedApplicantRecordResolvers from "./resolvers/reviewedApplicantRecordResolver";
2022
import adminCommentResolvers from "./resolvers/adminCommentsResolvers";
2123
import adminCommentType from "./types/adminCommentsType";
2224
import applicantRecordResolvers from "./resolvers/applicantRecordResolvers";
@@ -45,6 +47,7 @@ const executableSchema = makeExecutableSchema({
4547
simpleEntityType,
4648
userType,
4749
reviewDashboardType,
50+
reviewedApplicantRecordTypes,
4851
adminCommentType,
4952
applicantRecordType,
5053
reviewPageType,
@@ -55,6 +58,7 @@ const executableSchema = makeExecutableSchema({
5558
simpleEntityResolvers,
5659
userResolvers,
5760
reviewDashboardResolvers,
61+
reviewedApplicantRecordResolvers,
5862
adminCommentResolvers,
5963
applicantRecordResolvers,
6064
reviewPageResolvers,
@@ -85,6 +89,10 @@ const graphQLMiddlewares = {
8589
createSimpleEntity: authorizedByAllRoles(),
8690
updateSimpleEntity: authorizedByAllRoles(),
8791
deleteSimpleEntity: authorizedByAllRoles(),
92+
createReviewedApplicantRecord: authorizedByAllRoles(),
93+
bulkCreateReviewedApplicantRecord: authorizedByAllRoles(),
94+
deleteReviewedApplicantRecord: authorizedByAllRoles(),
95+
bulkDeleteReviewedApplicantRecord: authorizedByAllRoles(),
8896
createUser: authorizedByAdmin(),
8997
updateUser: authorizedByAdmin(),
9098
deleteUserById: authorizedByAdmin(),
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import ReviewedApplicantRecordService from "../../services/implementations/reviewedApplicantRecordService";
2+
import {
3+
ReviewedApplicantRecordDTO,
4+
CreateReviewedApplicantRecordDTO,
5+
DeleteReviewedApplicantRecordDTO,
6+
} from "../../types";
7+
import { getErrorMessage } from "../../utilities/errorUtils";
8+
9+
const reviewedApplicantRecordService = new ReviewedApplicantRecordService();
10+
11+
const reviewedApplicantRecordResolvers = {
12+
Mutation: {
13+
createReviewedApplicantRecord: async (
14+
_parent: undefined,
15+
args: { input: CreateReviewedApplicantRecordDTO },
16+
): Promise<ReviewedApplicantRecordDTO> => {
17+
try {
18+
return await reviewedApplicantRecordService.createReviewedApplicantRecord(
19+
args.input,
20+
);
21+
} catch (error) {
22+
throw new Error(getErrorMessage(error));
23+
}
24+
},
25+
26+
bulkCreateReviewedApplicantRecord: async (
27+
_parent: undefined,
28+
args: { inputs: CreateReviewedApplicantRecordDTO[] },
29+
): Promise<ReviewedApplicantRecordDTO[]> => {
30+
try {
31+
return await reviewedApplicantRecordService.bulkCreateReviewedApplicantRecord(
32+
args.inputs,
33+
);
34+
} catch (error) {
35+
throw new Error(getErrorMessage(error));
36+
}
37+
},
38+
39+
deleteReviewedApplicantRecord: async (
40+
_parent: undefined,
41+
args: { input: DeleteReviewedApplicantRecordDTO },
42+
): Promise<ReviewedApplicantRecordDTO> => {
43+
try {
44+
return await reviewedApplicantRecordService.deleteReviewedApplicantRecord(
45+
args.input,
46+
);
47+
} catch (error) {
48+
throw new Error(getErrorMessage(error));
49+
}
50+
},
51+
52+
bulkDeleteReviewedApplicantRecord: async (
53+
_parent: undefined,
54+
args: { inputs: DeleteReviewedApplicantRecordDTO[] },
55+
): Promise<ReviewedApplicantRecordDTO[]> => {
56+
try {
57+
return await reviewedApplicantRecordService.bulkDeleteReviewedApplicantRecord(
58+
args.inputs,
59+
);
60+
} catch (error) {
61+
throw new Error(getErrorMessage(error));
62+
}
63+
},
64+
},
65+
};
66+
67+
export default reviewedApplicantRecordResolvers;
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { gql } from "apollo-server-express";
2+
3+
const reviewedApplicantRecordTypes = gql`
4+
enum SkillCategory {
5+
JUNIOR
6+
INTERMEDIATE
7+
SENIOR
8+
}
9+
10+
type Review {
11+
passionFSG: Int
12+
teamPlayer: Int
13+
desireToLearn: Int
14+
skill: Int
15+
skillCategory: SkillCategory
16+
}
17+
18+
input ReviewInput {
19+
passionFSG: Int
20+
teamPlayer: Int
21+
desireToLearn: Int
22+
skill: Int
23+
skillCategory: SkillCategory
24+
}
25+
26+
type ReviewedApplicantRecord {
27+
applicantRecordId: ID!
28+
reviewerId: Int!
29+
review: Review
30+
status: String
31+
score: Int
32+
reviewerHasConflict: Boolean
33+
}
34+
35+
input CreateReviewedApplicantRecordInput {
36+
applicantRecordId: ID!
37+
reviewerId: Int!
38+
review: ReviewInput
39+
status: String
40+
}
41+
42+
input DeleteReviewedApplicantRecord {
43+
applicantRecordId: ID!
44+
reviewerId: Int!
45+
}
46+
47+
extend type Mutation {
48+
createReviewedApplicantRecord(
49+
input: CreateReviewedApplicantRecordInput!
50+
): ReviewedApplicantRecord!
51+
52+
bulkCreateReviewedApplicantRecord(
53+
inputs: [CreateReviewedApplicantRecordInput!]!
54+
): [ReviewedApplicantRecord!]!
55+
56+
deleteReviewedApplicantRecord(
57+
input: DeleteReviewedApplicantRecord!
58+
): ReviewedApplicantRecord!
59+
60+
bulkDeleteReviewedApplicantRecord(
61+
inputs: [DeleteReviewedApplicantRecord!]!
62+
): [ReviewedApplicantRecord!]!
63+
}
64+
`;
65+
66+
export default reviewedApplicantRecordTypes;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { DataType } from "sequelize-typescript";
2+
import { Migration } from "../umzug";
3+
4+
const TABLE_NAME = "reviewed_applicant_records";
5+
6+
export const up: Migration = async ({ context: sequelize }) => {
7+
await sequelize.getQueryInterface().addColumn(TABLE_NAME, "createdAt", {
8+
type: DataType.DATE,
9+
allowNull: false,
10+
defaultValue: DataType.NOW,
11+
});
12+
13+
await sequelize.getQueryInterface().addColumn(TABLE_NAME, "updatedAt", {
14+
type: DataType.DATE,
15+
allowNull: false,
16+
defaultValue: DataType.NOW,
17+
});
18+
};
19+
20+
export const down: Migration = async ({ context: sequelize }) => {
21+
await sequelize.getQueryInterface().removeColumn(TABLE_NAME, "createdAt");
22+
await sequelize.getQueryInterface().removeColumn(TABLE_NAME, "updatedAt");
23+
};
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import { sequelize } from "../../models";
2+
import ReviewedApplicantRecord from "../../models/reviewedApplicantRecord.model";
3+
import {
4+
ReviewedApplicantRecordDTO,
5+
CreateReviewedApplicantRecordDTO,
6+
DeleteReviewedApplicantRecordDTO,
7+
} from "../../types";
8+
import { getErrorMessage } from "../../utilities/errorUtils";
9+
import logger from "../../utilities/logger";
10+
import IReviewApplicantRecordService from "../interfaces/IReviewedApplicantRecordService";
11+
12+
const Logger = logger(__filename);
13+
14+
class ReviewedApplicantRecordService implements IReviewApplicantRecordService {
15+
/* eslint-disable class-methods-use-this */
16+
async createReviewedApplicantRecord(
17+
dto: CreateReviewedApplicantRecordDTO,
18+
): Promise<ReviewedApplicantRecordDTO> {
19+
try {
20+
const record = await ReviewedApplicantRecord.create(dto);
21+
return record.toJSON() as ReviewedApplicantRecordDTO;
22+
} catch (error: unknown) {
23+
Logger.error(
24+
`Failed to create reviewed applicant record. Reason = ${getErrorMessage(
25+
error,
26+
)}`,
27+
);
28+
throw error;
29+
}
30+
}
31+
32+
async bulkCreateReviewedApplicantRecord(
33+
createReviewedApplicantRecordDTOs: CreateReviewedApplicantRecordDTO[],
34+
): Promise<ReviewedApplicantRecordDTO[]> {
35+
try {
36+
const reviewedApplicantRecords = await sequelize.transaction(
37+
async (t) => {
38+
const records = await ReviewedApplicantRecord.bulkCreate(
39+
createReviewedApplicantRecordDTOs,
40+
{ transaction: t },
41+
);
42+
return records;
43+
},
44+
);
45+
46+
return reviewedApplicantRecords.map(
47+
(record) => record.toJSON() as ReviewedApplicantRecordDTO,
48+
);
49+
} catch (error: unknown) {
50+
Logger.error(
51+
`Failed to bulk create reviewed applicant records. Reason = ${getErrorMessage(
52+
error,
53+
)}`,
54+
);
55+
throw error;
56+
}
57+
}
58+
59+
async deleteReviewedApplicantRecord(
60+
deleteReviewedApplicantRecord: DeleteReviewedApplicantRecordDTO,
61+
): Promise<ReviewedApplicantRecordDTO> {
62+
try {
63+
const { applicantRecordId } = deleteReviewedApplicantRecord;
64+
const { reviewerId } = deleteReviewedApplicantRecord;
65+
const record = await ReviewedApplicantRecord.findOne({
66+
where: { applicantRecordId, reviewerId },
67+
});
68+
69+
if (!record) {
70+
throw new Error("ReviewedApplicantRecord not found, delete failed");
71+
}
72+
73+
await record.destroy();
74+
return record.toJSON() as ReviewedApplicantRecordDTO;
75+
} catch (error: unknown) {
76+
Logger.error(
77+
`Failed to delete reviewed applicant records. Reason = ${getErrorMessage(
78+
error,
79+
)}`,
80+
);
81+
throw error;
82+
}
83+
}
84+
85+
async bulkDeleteReviewedApplicantRecord(
86+
deleteReviewedApplicantRecords: DeleteReviewedApplicantRecordDTO[],
87+
): Promise<ReviewedApplicantRecordDTO[]> {
88+
try {
89+
const deletedRecords = await sequelize.transaction(async (t) => {
90+
const records = await Promise.all(
91+
deleteReviewedApplicantRecords.map(
92+
({ applicantRecordId, reviewerId }) =>
93+
ReviewedApplicantRecord.findOne({
94+
where: { applicantRecordId, reviewerId },
95+
transaction: t,
96+
}),
97+
),
98+
);
99+
100+
if (records.some((r) => !r)) {
101+
throw new Error("Not all records were found, bulk delete failed");
102+
}
103+
104+
const existingRecords = records as ReviewedApplicantRecord[];
105+
await Promise.all(
106+
existingRecords.map((r) => r.destroy({ transaction: t })),
107+
);
108+
109+
return existingRecords;
110+
});
111+
112+
return deletedRecords.map(
113+
(r) => r.toJSON() as ReviewedApplicantRecordDTO,
114+
);
115+
} catch (error: unknown) {
116+
Logger.error(
117+
`Failed to bulk delete reviewed applicant records. Reason = ${getErrorMessage(
118+
error,
119+
)}`,
120+
);
121+
throw error;
122+
}
123+
}
124+
}
125+
126+
export default ReviewedApplicantRecordService;
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import {
2+
ReviewedApplicantRecordDTO,
3+
CreateReviewedApplicantRecordDTO,
4+
DeleteReviewedApplicantRecordDTO,
5+
} from "../../types";
6+
7+
interface IReviewApplicantRecordService {
8+
/**
9+
* Creates a single reviewed applicant record entry
10+
* @Param createReviewedApplicantRecordDTO data to create reviewed applicant record
11+
*/
12+
createReviewedApplicantRecord(
13+
createReviewedApplicantRecordDTO: CreateReviewedApplicantRecordDTO,
14+
): Promise<ReviewedApplicantRecordDTO>;
15+
16+
/**
17+
* Creates multiple reviewed applicant record entries in bulk
18+
* @Param createReviewedApplicantRecordDTOs array of data to create reviewed applicant records
19+
*/
20+
bulkCreateReviewedApplicantRecord(
21+
createReviewedApplicantRecordDTOs: CreateReviewedApplicantRecordDTO[],
22+
): Promise<ReviewedApplicantRecordDTO[]>;
23+
24+
/**
25+
* Deletes a single reviewed applicant record entry
26+
* @Param applicantRecordId the ID of applicant record to delete
27+
* @Param reviewerId the ID of the reviewer
28+
*/
29+
deleteReviewedApplicantRecord(
30+
deleteReviewedApplicantRecord: DeleteReviewedApplicantRecordDTO,
31+
): Promise<ReviewedApplicantRecordDTO>;
32+
33+
/**
34+
* Deletes multiple reviewed applicant record entries in bulk
35+
* @Param deleteReviewedApplicantRecord array of data to delete reviewed applicant records
36+
*/
37+
bulkDeleteReviewedApplicantRecord(
38+
deleteReviewedApplicantRecords: DeleteReviewedApplicantRecordDTO[],
39+
): Promise<ReviewedApplicantRecordDTO[]>;
40+
}
41+
42+
export default IReviewApplicantRecordService;

backend/typescript/types.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,18 @@ export type ReviewedApplicantRecordDTO = {
209209
reviewerHasConflict: boolean;
210210
};
211211

212+
export type CreateReviewedApplicantRecordDTO = {
213+
applicantRecordId: string;
214+
reviewerId: number;
215+
review?: Review;
216+
reviewerHasConflict?: boolean;
217+
};
218+
219+
export type DeleteReviewedApplicantRecordDTO = {
220+
applicantRecordId: string;
221+
reviewerId: number;
222+
};
223+
212224
export type AdminCommentDTO = {
213225
id: string;
214226
userId: number;

0 commit comments

Comments
 (0)