Skip to content

Commit e89a06e

Browse files
author
ruiichen
committed
- added relationships between the table models ❤️
- made new type for table row - made new query interface, service, resolver - wrote some joins to build a row
1 parent 83b1a51 commit e89a06e

File tree

10 files changed

+190
-3
lines changed

10 files changed

+190
-3
lines changed

backend/typescript/graphql/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import userType from "./types/userType";
1818
import dashboardType from "./types/dashboardType";
1919
import dashboardResolvers from "./resolvers/dashboardResolvers";
2020
import reviewType from "./types/reviewType";
21+
import reviewDashboardResolvers from "./resolvers/reviewDashboardResolvers";
22+
import reviewDashboardType from "./types/reviewDashboardType";
2123

2224
const query = gql`
2325
type Query {
@@ -41,13 +43,15 @@ const executableSchema = makeExecutableSchema({
4143
simpleEntityType,
4244
userType,
4345
dashboardType,
46+
reviewDashboardType,
4447
],
4548
resolvers: merge(
4649
authResolvers,
4750
entityResolvers,
4851
simpleEntityResolvers,
4952
userResolvers,
5053
dashboardResolvers,
54+
reviewDashboardResolvers,
5155
),
5256
});
5357

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import ReviewDashboardService from "../../services/implementations/reviewDashboardService";
2+
import { ReviewDashoardRowDTO } from "../../types";
3+
import { getErrorMessage } from "../../utilities/errorUtils";
4+
5+
const reviewDashboardService = new ReviewDashboardService();
6+
7+
const reviewDashboardResolvers = {
8+
Query: {
9+
reviewDashboard: async (
10+
_parent: undefined,
11+
{ pageNumber }: { pageNumber: number },
12+
{ resultsPerPage }: { resultsPerPage: number },
13+
): Promise<ReviewDashoardRowDTO[]> => {
14+
try {
15+
return await reviewDashboardService.getReviewDashboard(
16+
pageNumber,
17+
resultsPerPage,
18+
);
19+
} catch (error) {
20+
throw new Error(getErrorMessage(error));
21+
}
22+
},
23+
},
24+
};
25+
26+
export default reviewDashboardResolvers;
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { gql } from "apollo-server-express";
2+
import { ApplicationStatus, PositionTitle, ReviewerDTO } from "../../types";
3+
4+
const reviewDashboardType = gql`
5+
type ReviewerDTO {
6+
firstName: String!
7+
lastName: String!
8+
}
9+
10+
type ReviewDashboardRowDTO {
11+
firstName: String!
12+
lastName: String!
13+
position: Int!
14+
timesApplied: String!
15+
applicationStatus: String!
16+
choice: Int!
17+
reviewers: [ReviewerDTO!]!
18+
totalScore: Int
19+
}
20+
21+
extend type Query {
22+
reviewDashboard(
23+
pageNumber: Int
24+
resultsPerPage: Int
25+
): [ReviewDashboardRowDTO!]!
26+
}
27+
`;
28+
29+
export default reviewDashboardType;

backend/typescript/models/applicant.model.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
/* eslint import/no-cycle: 0 */
22

3-
import { Column, DataType, Model, Table } from "sequelize-typescript";
3+
import { Column, DataType, HasMany, Model, Table } from "sequelize-typescript";
4+
import { NonAttribute } from "sequelize";
5+
import ApplicantRecord from "./applicantRecord.model";
46

57
@Table({ tableName: "applicants" })
68
export default class Applicant extends Model {
@@ -10,7 +12,7 @@ export default class Applicant extends Model {
1012
unique: true,
1113
autoIncrement: true,
1214
})
13-
id!: string;
15+
id!: number;
1416

1517
@Column({ type: DataType.STRING })
1618
academicOrCoop!: string;
@@ -53,4 +55,10 @@ export default class Applicant extends Model {
5355

5456
@Column({ type: DataType.DATE })
5557
submittedAt!: Date;
58+
59+
@HasMany(() => ApplicantRecord, {
60+
foreignKey: "id",
61+
as: "applicant",
62+
})
63+
applicantRecords?: NonAttribute<ApplicantRecord[]>;
5664
}

backend/typescript/models/applicantRecord.model.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
11
/* eslint import/no-cycle: 0 */
22

33
import {
4+
BelongsTo,
45
Column,
56
DataType,
67
ForeignKey,
8+
HasMany,
79
Model,
810
Table,
911
} from "sequelize-typescript";
12+
import { NonAttribute } from "sequelize";
1013
import {
1114
ApplicantRecordExtraInfo,
1215
ApplicationStatus,
1316
SkillCategory,
1417
} from "../types";
1518
import Applicant from "./applicant.model";
1619
import Position from "./position.model";
20+
import ReviewedApplicantRecord from "./reviewedApplicantRecord.model";
1721

1822
@Table({ tableName: "applicant_records" })
1923
export default class ApplicantRecord extends Model {
@@ -23,7 +27,7 @@ export default class ApplicantRecord extends Model {
2327
unique: true,
2428
autoIncrement: true,
2529
})
26-
id!: string;
30+
id!: number;
2731

2832
@ForeignKey(() => Applicant)
2933
@Column({ type: DataType.STRING })
@@ -47,4 +51,10 @@ export default class ApplicantRecord extends Model {
4751

4852
@Column({ type: DataType.JSONB, allowNull: true })
4953
extraInfo!: ApplicantRecordExtraInfo;
54+
55+
@BelongsTo(() => Applicant, "applicantId")
56+
applicant?: NonAttribute<Applicant>;
57+
58+
@HasMany(() => ReviewedApplicantRecord, "applicantRecordId")
59+
reviewedApplicantRecords?: NonAttribute<ReviewedApplicantRecord[]>;
5060
}

backend/typescript/models/reviewedApplicantRecord.model.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import {
66
ForeignKey,
77
Model,
88
Table,
9+
BelongsTo,
910
} from "sequelize-typescript";
11+
import { NonAttribute } from "sequelize";
1012
import User from "./user.model";
1113
import { Review, ReviewStatus, ReviewStatusEnum } from "../types";
1214
import ApplicantRecord from "./applicantRecord.model";
@@ -29,4 +31,7 @@ export default class ReviewedApplicantRecord extends Model {
2931
defaultValue: ReviewStatusEnum.TODO,
3032
})
3133
status!: ReviewStatus;
34+
35+
@BelongsTo(() => User, { foreignKey: "reviewerId", targetKey: "id" })
36+
user?: NonAttribute<User>;
3237
}

backend/typescript/models/user.model.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ import {
88
Model,
99
Table,
1010
} from "sequelize-typescript";
11+
import { NonAttribute } from "sequelize";
1112
import { PositionTitle, PositionTitles, Role } from "../types";
1213
import ApplicationDashboardTable from "./applicationDashboard.model";
1314
import Position from "./position.model";
15+
import ReviewedApplicantRecord from "./reviewedApplicantRecord.model";
1416

1517
@Table({ tableName: "users" })
1618
export default class User extends Model {
@@ -38,4 +40,10 @@ export default class User extends Model {
3840

3941
@HasMany(() => ApplicationDashboardTable)
4042
applicationDashboards?: ApplicationDashboardTable[];
43+
44+
@HasMany(() => ReviewedApplicantRecord, {
45+
foreignKey: "reviewerId",
46+
as: "user",
47+
})
48+
reviewedApplicantRecords?: NonAttribute<ReviewedApplicantRecord[]>;
4149
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { PositionTitle, Review, ReviewDashoardRowDTO } from "../../types";
2+
import IReviewDashboardService from "../interfaces/IReviewDashboardService";
3+
import { getErrorMessage } from "../../utilities/errorUtils";
4+
import logger from "../../utilities/logger";
5+
import ApplicantRecord from "../../models/applicantRecord.model";
6+
7+
const Logger = logger(__filename);
8+
9+
function toDTO(model: ApplicantRecord): ReviewDashoardRowDTO {
10+
return {
11+
firstName: model.applicant!.firstName,
12+
lastName: model.applicant!.lastName,
13+
position: model.position as PositionTitle,
14+
timesApplied: model.applicant!.timesApplied.toString(),
15+
applicationStatus: model.status,
16+
choice: model.choice,
17+
reviewers: model.reviewedApplicantRecords!.map((r) => ({
18+
firstName: r.user!.first_name,
19+
lastName: r.user!.last_name,
20+
})),
21+
totalScore: null,
22+
};
23+
}
24+
25+
class ReviewDashboardService implements IReviewDashboardService {
26+
/* eslint-disable class-methods-use-this */
27+
async getReviewDashboard(
28+
page: number,
29+
resultsPerPage: number,
30+
): Promise<ReviewDashoardRowDTO[]> {
31+
try {
32+
// get applicant_record
33+
// JOIN applicant ON applicant_id
34+
// JOIN reviewed_applicant_record ON applicant_record_id
35+
// JOIN user ON reviewer_id
36+
const applicants: Array<ApplicantRecord> | null =
37+
await ApplicantRecord.findAll({
38+
attributes: { exclude: ["createdAt", "updatedAt"] },
39+
include: [
40+
{
41+
attributes: { exclude: ["createdAt", "updatedAt"] },
42+
association: "reviewedApplicantRecords",
43+
include: [
44+
{
45+
attributes: { exclude: ["createdAt", "updatedAt"] },
46+
association: "user",
47+
},
48+
],
49+
},
50+
{
51+
attributes: { exclude: ["createdAt", "updatedAt"] },
52+
association: "applicant",
53+
},
54+
],
55+
});
56+
return applicants.map(toDTO);
57+
} catch (error: unknown) {
58+
Logger.error(
59+
`Failed to get dashboard. Reason = ${getErrorMessage(error)}`,
60+
);
61+
throw error;
62+
}
63+
}
64+
}
65+
66+
export default ReviewDashboardService;
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { ReviewDashoardRowDTO } from "../../types";
2+
3+
interface IReviewDashboardService {
4+
/**
5+
* Pagination-supporting viewing of the virtual review dashboard
6+
* @Param page the page the viewer is on
7+
* @Param resultsPerPage the number of results per page
8+
*/
9+
getReviewDashboard(
10+
page: number,
11+
resultsPerPage: number,
12+
): Promise<ReviewDashoardRowDTO[]>;
13+
}
14+
15+
export default IReviewDashboardService;

backend/typescript/types.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,22 @@ export type ApplicationDashboardRowDTO = {
127127
reviewers: UserDTO[];
128128
};
129129

130+
export type ReviewerDTO = {
131+
firstName: string;
132+
lastName: string;
133+
};
134+
135+
export type ReviewDashoardRowDTO = {
136+
firstName: string;
137+
lastName: string;
138+
position: PositionTitle;
139+
timesApplied: string;
140+
applicationStatus: ApplicationStatus;
141+
choice: number;
142+
reviewers: ReviewerDTO[];
143+
totalScore: number | null;
144+
};
145+
130146
export type CreateUserDTO = Omit<UserDTO, "id" | "firstName" | "lastName">;
131147

132148
export type UpdateUserDTO = Omit<UserDTO, "id">;

0 commit comments

Comments
 (0)