Skip to content

Commit 0801124

Browse files
committed
onboarding task
1 parent c5ef398 commit 0801124

File tree

9 files changed

+2526
-2134
lines changed

9 files changed

+2526
-2134
lines changed

backend/typescript/graphql/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ import applicantRecordResolvers from "./resolvers/applicantRecordResolvers";
2525
import applicantRecordType from "./types/applicantRecordType";
2626
import reviewPageType from "./types/reviewPageType";
2727
import reviewPageResolvers from "./resolvers/reviewPageResolvers";
28+
import teamMemberType from "./types/teamMemberType";
29+
import teamMemberResolvers from "./resolvers/teamMemberResolvers";
2830

2931
const query = gql`
3032
type Query {
@@ -51,6 +53,7 @@ const executableSchema = makeExecutableSchema({
5153
adminCommentType,
5254
applicantRecordType,
5355
reviewPageType,
56+
teamMemberType,
5457
],
5558
resolvers: merge(
5659
authResolvers,
@@ -62,6 +65,7 @@ const executableSchema = makeExecutableSchema({
6265
adminCommentResolvers,
6366
applicantRecordResolvers,
6467
reviewPageResolvers,
68+
teamMemberResolvers,
6569
),
6670
});
6771

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import TeamMemberService from "../../services/implementations/teamMemberService";
2+
import ITeamMemberService from "../../services/interfaces/ITeamMemberService";
3+
import { CreateTeamMemberDTO, TeamMemberDTO } from "../../types";
4+
import { getErrorMessage } from "../../utilities/errorUtils";
5+
6+
const teamMemberService: ITeamMemberService = new TeamMemberService();
7+
8+
const teamMemberResolvers = {
9+
Query: {
10+
teamMembers: async (): Promise<TeamMemberDTO[]> => {
11+
try {
12+
return await teamMemberService.getTeamMembers();
13+
} catch (error) {
14+
throw new Error(getErrorMessage(error));
15+
}
16+
},
17+
},
18+
Mutation: {
19+
createTeamMember: async (
20+
_parent: unknown,
21+
{ teamMember }: { teamMember: CreateTeamMemberDTO },
22+
): Promise<TeamMemberDTO> => {
23+
try {
24+
return await teamMemberService.createTeamMember(teamMember);
25+
} catch (error) {
26+
throw new Error(getErrorMessage(error));
27+
}
28+
},
29+
},
30+
};
31+
32+
export default teamMemberResolvers;
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { gql } from "apollo-server-express";
2+
import { TeamRole } from "../../types";
3+
4+
const teamMemberType = gql`
5+
enum TeamRole {
6+
PM
7+
DESIGNER
8+
PL
9+
DEVELOPER
10+
}
11+
12+
type TeamMemberDTO {
13+
id: ID!
14+
firstName: String!
15+
lastName: String!
16+
teamRole: TeamRole!
17+
}
18+
19+
input CreateTeamMemberDTO {
20+
firstName: String!
21+
lastName: String!
22+
teamRole: TeamRole!
23+
}
24+
25+
extend type Query {
26+
teamMembers: [TeamMemberDTO!]!
27+
}
28+
29+
extend type Mutation {
30+
createTeamMember(teamMember: CreateTeamMemberDTO!): TeamMemberDTO!
31+
}
32+
`;
33+
34+
export default teamMemberType;
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { DataType } from "sequelize-typescript";
2+
import { Migration } from "../umzug";
3+
import { teamRoleValues } from "../types";
4+
5+
const TABLE_NAME = "team_members";
6+
7+
export const up: Migration = async ({ context: sequelize }) => {
8+
await sequelize.getQueryInterface().createTable(TABLE_NAME, {
9+
id: {
10+
type: DataType.UUID,
11+
defaultValue: DataType.UUIDV4,
12+
allowNull: false,
13+
primaryKey: true,
14+
},
15+
firstName: {
16+
type: DataType.STRING,
17+
allowNull: false,
18+
},
19+
lastName: {
20+
type: DataType.STRING,
21+
allowNull: false,
22+
},
23+
teamRole: {
24+
type: DataType.ENUM(...teamRoleValues),
25+
allowNull: false,
26+
},
27+
createdAt: {
28+
type: DataType.DATE,
29+
allowNull: false,
30+
defaultValue: DataType.NOW,
31+
},
32+
updatedAt: {
33+
type: DataType.DATE,
34+
allowNull: false,
35+
defaultValue: DataType.NOW,
36+
},
37+
});
38+
};
39+
40+
export const down: Migration = async ({ context: sequelize }) => {
41+
await sequelize.getQueryInterface().dropTable(TABLE_NAME);
42+
await sequelize
43+
.getQueryInterface()
44+
.sequelize.query(`DROP TYPE IF EXISTS "enum_team_members_teamRole";`);
45+
};
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/* eslint import/no-cycle: 0 */
2+
3+
import { Column, DataType, Model, Table } from "sequelize-typescript";
4+
import { TeamRole, teamRoleValues } from "../types";
5+
6+
@Table({ tableName: "team_members" })
7+
export default class TeamMember extends Model {
8+
@Column({
9+
type: DataType.UUID,
10+
defaultValue: DataType.UUIDV4,
11+
allowNull: false,
12+
primaryKey: true,
13+
})
14+
id!: string;
15+
16+
@Column({ type: DataType.STRING, allowNull: false })
17+
firstName!: string;
18+
19+
@Column({ type: DataType.STRING, allowNull: false })
20+
lastName!: string;
21+
22+
@Column({ type: DataType.ENUM(...teamRoleValues), allowNull: false })
23+
teamRole!: TeamRole;
24+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import ITeamMemberService from "../interfaces/ITeamMemberService";
2+
import logger from "../../utilities/logger";
3+
import TeamMember from "../../models/teamMember.model";
4+
import { CreateTeamMemberDTO, TeamMemberDTO } from "../../types";
5+
import { getErrorMessage } from "../../utilities/errorUtils";
6+
7+
const Logger = logger(__filename);
8+
9+
function toDTO(model: TeamMember): TeamMemberDTO {
10+
return {
11+
id: String(model.id),
12+
firstName: model.firstName,
13+
lastName: model.lastName,
14+
teamRole: model.teamRole,
15+
};
16+
}
17+
18+
class TeamMemberService implements ITeamMemberService {
19+
/* eslint-disable class-methods-use-this */
20+
async getTeamMembers(): Promise<TeamMemberDTO[]> {
21+
let teamMembers: TeamMember[] | null;
22+
try {
23+
teamMembers = await TeamMember.findAll();
24+
} catch (error) {
25+
Logger.error(
26+
`Failed to get team members. Reason = ${getErrorMessage(error)}`,
27+
);
28+
throw error;
29+
}
30+
31+
return teamMembers.map(toDTO);
32+
}
33+
34+
async createTeamMember(
35+
teamMember: CreateTeamMemberDTO,
36+
): Promise<TeamMemberDTO> {
37+
let newTeamMember: TeamMember | null;
38+
try {
39+
newTeamMember = await TeamMember.create(teamMember);
40+
} catch (error) {
41+
Logger.error(`Failed to create team member. Reason = ${error}`);
42+
throw error;
43+
}
44+
45+
return toDTO(newTeamMember);
46+
}
47+
}
48+
49+
export default TeamMemberService;
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { TeamMemberDTO, CreateTeamMemberDTO } from "../../types";
2+
3+
interface ITeamMemberService {
4+
/**
5+
* Get all team members
6+
* @returns an array of TeamMemberDTOs
7+
* @throws Error if team member retrieval fails
8+
*/
9+
getTeamMembers(): Promise<TeamMemberDTO[]>;
10+
11+
/**
12+
* Create a team member
13+
* @param teamMember the team member data to create
14+
* @returns the created TeamMemberDTO
15+
* @throws Error if team member creation fails
16+
*/
17+
createTeamMember(teamMember: CreateTeamMemberDTO): Promise<TeamMemberDTO>;
18+
}
19+
20+
export default ITeamMemberService;

backend/typescript/types.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ export type ApplicantRecordDTO = {
8888
isApplicantFlagged: boolean;
8989
};
9090

91+
export const teamRoleValues = ["PM", "DESIGNER", "PL", "DEVELOPER"] as const;
92+
export type TeamRole = (typeof teamRoleValues)[number];
93+
9194
export type ApplicationStatus =
9295
| "Applied"
9396
| "InReview"
@@ -127,6 +130,15 @@ export type RegisterUserDTO = Omit<CreateUserDTO, "role">;
127130

128131
export type AuthDTO = Token & UserDTO;
129132

133+
export type TeamMemberDTO = {
134+
id: string;
135+
firstName: string;
136+
lastName: string;
137+
teamRole: TeamRole;
138+
};
139+
140+
export type CreateTeamMemberDTO = Omit<TeamMemberDTO, "id">;
141+
130142
export type Letters = "A" | "B" | "C" | "D";
131143

132144
export type NodemailerConfig = {

0 commit comments

Comments
 (0)