Skip to content
Closed
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
4 changes: 4 additions & 0 deletions backend/typescript/graphql/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import applicantRecordResolvers from "./resolvers/applicantRecordResolvers";
import applicantRecordType from "./types/applicantRecordType";
import reviewPageType from "./types/reviewPageType";
import reviewPageResolvers from "./resolvers/reviewPageResolvers";
import teamMemberType from "./types/teamMemberType";
import teamMemberResolvers from "./resolvers/teamMemberResolvers";

const query = gql`
type Query {
Expand All @@ -51,6 +53,7 @@ const executableSchema = makeExecutableSchema({
adminCommentType,
applicantRecordType,
reviewPageType,
teamMemberType,
],
resolvers: merge(
authResolvers,
Expand All @@ -62,6 +65,7 @@ const executableSchema = makeExecutableSchema({
adminCommentResolvers,
applicantRecordResolvers,
reviewPageResolvers,
teamMemberResolvers,
),
});

Expand Down
32 changes: 32 additions & 0 deletions backend/typescript/graphql/resolvers/teamMemberResolvers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import TeamMemberService from "../../services/implementations/teamMemberService";
import ITeamMemberService from "../../services/interfaces/ITeamMemberService";
import { CreateTeamMemberDTO, TeamMemberDTO } from "../../types";
import { getErrorMessage } from "../../utilities/errorUtils";

const teamMemberService: ITeamMemberService = new TeamMemberService();

const teamMemberResolvers = {
Query: {
teamMembers: async (): Promise<TeamMemberDTO[]> => {
try {
return await teamMemberService.getTeamMembers();
} catch (error) {
throw new Error(getErrorMessage(error));
}
},
},
Mutation: {
createTeamMember: async (
_parent: unknown,
{ teamMember }: { teamMember: CreateTeamMemberDTO },
): Promise<TeamMemberDTO> => {
try {
return await teamMemberService.createTeamMember(teamMember);
} catch (error) {
throw new Error(getErrorMessage(error));
}
},
},
};

export default teamMemberResolvers;
34 changes: 34 additions & 0 deletions backend/typescript/graphql/types/teamMemberType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { gql } from "apollo-server-express";
import { TeamRole } from "../../types";

Check warning on line 2 in backend/typescript/graphql/types/teamMemberType.ts

View workflow job for this annotation

GitHub Actions / run-lint

'TeamRole' is defined but never used

const teamMemberType = gql`
enum TeamRole {
PM
DESIGNER
PL
DEVELOPER
}

type TeamMemberDTO {
id: ID!
firstName: String!
lastName: String!
teamRole: TeamRole!
}

input CreateTeamMemberDTO {
firstName: String!
lastName: String!
teamRole: TeamRole!
}

extend type Query {
teamMembers: [TeamMemberDTO!]!
}

extend type Mutation {
createTeamMember(teamMember: CreateTeamMemberDTO!): TeamMemberDTO!
}
`;

export default teamMemberType;
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { DataType } from "sequelize-typescript";
import { Migration } from "../umzug";
import { teamRoleValues } from "../types";

const TABLE_NAME = "team_members";

export const up: Migration = async ({ context: sequelize }) => {
await sequelize.getQueryInterface().createTable(TABLE_NAME, {
id: {
type: DataType.UUID,
defaultValue: DataType.UUIDV4,
allowNull: false,
primaryKey: true,
},
firstName: {
type: DataType.STRING,
allowNull: false,
},
lastName: {
type: DataType.STRING,
allowNull: false,
},
teamRole: {
type: DataType.ENUM(...teamRoleValues),
allowNull: false,
},
createdAt: {
type: DataType.DATE,
allowNull: false,
defaultValue: DataType.NOW,
},
updatedAt: {
type: DataType.DATE,
allowNull: false,
defaultValue: DataType.NOW,
},
});
};

export const down: Migration = async ({ context: sequelize }) => {
await sequelize.getQueryInterface().dropTable(TABLE_NAME);
await sequelize
.getQueryInterface()
.sequelize.query(`DROP TYPE IF EXISTS "enum_team_members_teamRole";`);
};
24 changes: 24 additions & 0 deletions backend/typescript/models/teamMember.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/* eslint import/no-cycle: 0 */

import { Column, DataType, Model, Table } from "sequelize-typescript";
import { TeamRole, teamRoleValues } from "../types";

@Table({ tableName: "team_members" })
export default class TeamMember extends Model {
@Column({
type: DataType.UUID,
defaultValue: DataType.UUIDV4,
allowNull: false,
primaryKey: true,
})
id!: string;

@Column({ type: DataType.STRING, allowNull: false })
firstName!: string;

@Column({ type: DataType.STRING, allowNull: false })
lastName!: string;

@Column({ type: DataType.ENUM(...teamRoleValues), allowNull: false })
teamRole!: TeamRole;
}
49 changes: 49 additions & 0 deletions backend/typescript/services/implementations/teamMemberService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import ITeamMemberService from "../interfaces/ITeamMemberService";
import logger from "../../utilities/logger";
import TeamMember from "../../models/teamMember.model";
import { CreateTeamMemberDTO, TeamMemberDTO } from "../../types";
import { getErrorMessage } from "../../utilities/errorUtils";

const Logger = logger(__filename);

function toDTO(model: TeamMember): TeamMemberDTO {
return {
id: String(model.id),
firstName: model.firstName,
lastName: model.lastName,
teamRole: model.teamRole,
};
}

class TeamMemberService implements ITeamMemberService {
/* eslint-disable class-methods-use-this */
async getTeamMembers(): Promise<TeamMemberDTO[]> {
let teamMembers: TeamMember[] | null;
try {
teamMembers = await TeamMember.findAll();
} catch (error) {
Logger.error(
`Failed to get team members. Reason = ${getErrorMessage(error)}`,
);
throw error;
}

return teamMembers.map(toDTO);
}

async createTeamMember(
teamMember: CreateTeamMemberDTO,
): Promise<TeamMemberDTO> {
let newTeamMember: TeamMember | null;
try {
newTeamMember = await TeamMember.create(teamMember);
} catch (error) {
Logger.error(`Failed to create team member. Reason = ${error}`);
throw error;
}

return toDTO(newTeamMember);
}
}

export default TeamMemberService;
20 changes: 20 additions & 0 deletions backend/typescript/services/interfaces/ITeamMemberService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { TeamMemberDTO, CreateTeamMemberDTO } from "../../types";

interface ITeamMemberService {
/**
* Get all team members
* @returns an array of TeamMemberDTOs
* @throws Error if team member retrieval fails
*/
getTeamMembers(): Promise<TeamMemberDTO[]>;

/**
* Create a team member
* @param teamMember the team member data to create
* @returns the created TeamMemberDTO
* @throws Error if team member creation fails
*/
createTeamMember(teamMember: CreateTeamMemberDTO): Promise<TeamMemberDTO>;
}

export default ITeamMemberService;
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ApplicantRecordDTO } from "../../types";
import { ApplicationStatus, ApplicantRecordDTO } from "../../types";

interface IApplicantRecordService {
updateApplicantStatus(
Expand All @@ -8,7 +8,7 @@ interface IApplicantRecordService {
bulkUpdateApplicantStatus(
applicantRecordIds: string[],
status: ApplicationStatus,
): Promise<ApplicantRecordDto[]>;
): Promise<ApplicantRecordDTO[]>;
setApplicantRecordFlag(
applicantRecordId: string,
flagValue: boolean,
Expand Down
12 changes: 12 additions & 0 deletions backend/typescript/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ export type ApplicantRecordDTO = {
isApplicantFlagged: boolean;
};

export const teamRoleValues = ["PM", "DESIGNER", "PL", "DEVELOPER"] as const;
export type TeamRole = (typeof teamRoleValues)[number];

export type ApplicationStatus =
| "Applied"
| "InReview"
Expand Down Expand Up @@ -127,6 +130,15 @@ export type RegisterUserDTO = Omit<CreateUserDTO, "role">;

export type AuthDTO = Token & UserDTO;

export type TeamMemberDTO = {
id: string;
firstName: string;
lastName: string;
teamRole: TeamRole;
};

export type CreateTeamMemberDTO = Omit<TeamMemberDTO, "id">;

export type Letters = "A" | "B" | "C" | "D";

export type NodemailerConfig = {
Expand Down
Loading