Skip to content

Commit

Permalink
Adds sendSignInLink mutation
Browse files Browse the repository at this point in the history
  • Loading branch information
HeetShah committed Nov 12, 2023
1 parent f7580fd commit 6700258
Show file tree
Hide file tree
Showing 12 changed files with 189 additions and 58 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ backend/typescript/serviceAccount.json
package-lock.json
.gitignore
package.json
backend/typescript/graphql/sampleData/users.json
1 change: 1 addition & 0 deletions backend/typescript/graphql/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ const graphQLMiddlewares = {
deleteUserByEmail: authorizedByAdmin(),
logout: isAuthorizedByUserId("userId"),
resetPassword: isAuthorizedByEmail("email"),
sendSignInLink: authorizedByAllRoles(),
},
};

Expand Down
27 changes: 19 additions & 8 deletions backend/typescript/graphql/resolvers/authResolvers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { CookieOptions, Request, Response } from "express";

// import { CookieOptions } from "express";
import * as firebaseAdmin from "firebase-admin";
import nodemailerConfig from "../../nodemailer.config";
import AuthService from "../../services/implementations/authService";
Expand All @@ -15,15 +14,18 @@ import IReviewService from "../../services/interfaces/reviewService";
import ReviewService from "../../services/implementations/reviewService";

const userService: IUserService = new UserService();
const emailService: IEmailService = new EmailService(nodemailerConfig);
const emailService: IEmailService = new EmailService(
nodemailerConfig,
"UW Blueprint Internal Tools Team",
);
const authService: IAuthService = new AuthService(userService, emailService);
const reviewService: IReviewService = new ReviewService();

const cookieOptions: CookieOptions = {
httpOnly: true,
sameSite: process.env.PREVIEW_DEPLOY ? "none" : "strict",
secure: process.env.NODE_ENV === "production",
};
// const cookieOptions: CookieOptions = {
// httpOnly: true,
// sameSite: process.env.PREVIEW_DEPLOY ? "none" : "strict",
// secure: process.env.NODE_ENV === "production",
// };

// Object to pass back when frontend queries a login request
// eslint-disable-next-line @typescript-eslint/no-unused-vars
Expand Down Expand Up @@ -142,6 +144,15 @@ const authResolvers = {
await authService.resetPassword(email);
return true;
},
sendSignInLink: async (
_parent: undefined,
{ email }: { email: string },
): Promise<boolean> => {
await authService.sendSignInLink(email).catch((err) => {
throw err;
});
return true;
},
},
};

Expand Down
7 changes: 3 additions & 4 deletions backend/typescript/graphql/resolvers/dashboardResolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,12 @@ const dashboardResolvers = {
firstChoice,
);
return applications;
},applicationsById: async (
},
applicationsById: async (
_parent: undefined,
{ id }: { id: number },
): Promise<ApplicationDTO> => {
const application = await dashboardService.getApplicationsById(
id,
);
const application = await dashboardService.getApplicationsById(id);
return application;
},
dashboardsByApplicationId: async (
Expand Down
1 change: 1 addition & 0 deletions backend/typescript/graphql/types/authType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const authType = gql`
refresh(refreshToken: String!): String!
logout(userId: ID!): ID
resetPassword(email: String!): Boolean!
sendSignInLink(email: String!): Boolean!
}
`;

Expand Down
19 changes: 9 additions & 10 deletions backend/typescript/migrations/2023.04.05T17.43.42.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { DataType } from "sequelize-typescript";
// import ApplicationDashboardTable from "../models/applicationDashboard.model";
// import User from "../models/user.model";

import { DataTypes } from "sequelize";
import { Migration } from "../umzug";
import allApplications from "./applicationlist.json";
import { DataTypes } from "sequelize";
import { statusType, secondChoiceStatusType } from "../types";
import { StatusType, SecondChoiceStatusType } from "../types";

const TABLE_NAME = "applicantresponse";

Expand Down Expand Up @@ -36,10 +36,10 @@ const importApplicationData = () => {
status: currApplication.status,
term: currApplication.term,
timesApplied: currApplication.timesApplied,
timestamp: currApplication.timestamp
timestamp: currApplication.timestamp,
};
});

return seededData;
};

Expand Down Expand Up @@ -98,7 +98,7 @@ export const up: Migration = async ({ context: sequelize }) => {
},
resumeUrl: {
type: DataType.STRING(4000),
allowNull: true,
allowNull: true,
},
roleSpecificQuestions: {
type: DataType.ARRAY(DataType.STRING(4000)),
Expand All @@ -113,14 +113,14 @@ export const up: Migration = async ({ context: sequelize }) => {
allowNull: true,
},
status: {
type: DataType.ENUM(...Object.values(statusType)),
type: DataType.ENUM(...Object.values(StatusType)),
allowNull: false,
defaultValue: statusType.PENDING
defaultValue: StatusType.PENDING,
},
secondChoiceStatus: {
type: DataTypes.ENUM(...Object.values(secondChoiceStatusType)),
type: DataTypes.ENUM(...Object.values(SecondChoiceStatusType)),
allowNull: false,
defaultValue: secondChoiceStatusType.NOT_APPLICABLE
defaultValue: SecondChoiceStatusType.NOT_APPLICABLE,
},
term: {
type: DataType.STRING(4000),
Expand All @@ -141,7 +141,6 @@ export const up: Migration = async ({ context: sequelize }) => {
const SEEDED_DATA = importApplicationData();

await sequelize.getQueryInterface().bulkInsert(TABLE_NAME, SEEDED_DATA);

};

export const down: Migration = async ({ context: sequelize }) => {
Expand Down
19 changes: 9 additions & 10 deletions backend/typescript/models/application.model.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
/* eslint import/no-cycle: 0 */

import { Column, DataType, HasMany, Model, Table } from "sequelize-typescript";
import { DataTypes } from "sequelize";
import ApplicationDashboardTable from "./applicationDashboard.model";
import { DataTypes, Sequelize } from "sequelize";
import { statusType, secondChoiceStatusType } from "../types";

import { StatusType, SecondChoiceStatusType } from "../types";

@Table({ tableName: "applicantresponse" })
export default class Application extends Model {
Expand Down Expand Up @@ -56,17 +55,17 @@ export default class Application extends Model {
@Column({ type: DataType.ARRAY(DataType.STRING) })
shortAnswerQuestions!: string[];

@Column({
type: DataType.ENUM(...Object.values(statusType)),
defaultValue: statusType.PENDING
@Column({
type: DataType.ENUM(...Object.values(StatusType)),
defaultValue: StatusType.PENDING,
})
status!: statusType;
status!: StatusType;

@Column({
type: DataTypes.ENUM(...Object.values(secondChoiceStatusType)),
defaultValue: secondChoiceStatusType.NOT_APPLICABLE
type: DataTypes.ENUM(...Object.values(SecondChoiceStatusType)),
defaultValue: SecondChoiceStatusType.NOT_APPLICABLE,
})
secondChoiceStatus!: secondChoiceStatusType;
secondChoiceStatus!: SecondChoiceStatusType;

@Column({ type: DataType.STRING })
term!: string;
Expand Down
83 changes: 73 additions & 10 deletions backend/typescript/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import { ApolloServer } from "apollo-server-express";
import { sequelize } from "./models";
import schema from "./graphql";
import Application from "./models/application.model";

import memeberData from "./graphql/sampleData/members.json";
import firebaseAuthUsers from "./graphql/sampleData/users.json";

const CORS_ALLOW_LIST = [
"http://localhost:3000",
Expand Down Expand Up @@ -64,16 +65,79 @@ admin.initializeApp({
const db = admin.database();
const ref = db.ref("studentApplications");

app.get("/diff", async (req, res) => {
const currentTerm = memeberData.term;
const currentTermMembers: string[] = [];

// const teamToMembers : Record<string, string[]> = {};
memeberData.members.forEach((member) => {
if (member.term === currentTerm) {
currentTermMembers.push(member.name);
// if (teamToMembers[member.teams[0]]) {
// teamToMembers[member.teams[0]].push(member.name);
// } else {
// teamToMembers[member.teams[0]] = [member.name];
// }
}
});

// const teamToMemberSize : Record<string, number> = {};
// (Object.keys(teamToMembers)).forEach((team) => {
// teamToMemberSize[team] = teamToMembers[team].length;
// }
// )

const firebaseUsers: Record<string, string | undefined> = {};
firebaseAuthUsers.forEach((user) => {
firebaseUsers[user.uid] = user.displayName;
});

// see if all currentTermMembers have their name in firebase_users
const missingMembersFromFirebaseAuth: string[] = [];

currentTermMembers.forEach((member) => {
if (!Object.values(firebaseUsers).includes(member)) {
missingMembersFromFirebaseAuth.push(member);
}
});

res.status(200).json({
currentTerm,
currentTermMembers,
firebaseUsers,
missingMembersFromFirebaseAuth,
});
});

app.get("/authUsers", async (req, res) => {
try {
admin
.auth()
.listUsers()
.then((data) => {
res.status(200).json(data.users);
});
} catch (error) {
res
.status(500)
.send("An error occurred while retrieving the applications.");
}
});

app.get("/termApplications", async (req, res) => {
ref.orderByChild("term").equalTo("Fall 2023").once("value", function (snapshot) {
const applications: Application[] = [];
snapshot.forEach((childSnapshot) => {
applications.push(childSnapshot.val());
ref
.orderByChild("term")
.equalTo("Fall 2023")
// eslint-disable-next-line func-names
.once("value", function (snapshot) {
const applications: Application[] = [];
snapshot.forEach((childSnapshot) => {
applications.push(childSnapshot.val());
});
res.status(200).json(applications);
});
res.status(200).json(applications);
})});

});

app.get("/applications", async (req, res) => {
try {
const snapshot = await ref.once("value");
Expand All @@ -83,7 +147,6 @@ app.get("/applications", async (req, res) => {
});
res.status(200).json(applications);
} catch (error) {
console.error(error);
res
.status(500)
.send("An error occurred while retrieving the applications.");
Expand All @@ -101,13 +164,13 @@ app.get("/applications/:id", async (req, res) => {
res.status(404).send("Student application not found.");
}
} catch (error) {
console.error(error);
res
.status(500)
.send("An error occurred while retrieving the student application.");
}
});

app.listen({ port: process.env.PORT || 5000 }, () => {
// eslint-disable-next-line no-console
console.info(`Server is listening on port ${process.env.PORT || 5000}!`);
});
20 changes: 11 additions & 9 deletions backend/typescript/services/implementations/appDashboardService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ class AppDashboardService implements IAppDashboardService {
secondChoiceStatus: application.secondChoiceStatus,
term: application.term,
timesApplied: application.timesApplied,
timestamp: application.timestamp
timestamp: application.timestamp,
};
});
} catch (error: unknown) {
Expand All @@ -99,20 +99,22 @@ class AppDashboardService implements IAppDashboardService {
return applicationsByRoleDTO;
}

//Takes in an application id and returns an array of applicants with same id
async getApplicationsById(id: number): Promise<ApplicationDTO> {
// Takes in an application id and returns an array of applicants with same id
async getApplicationsById(id: number): Promise<ApplicationDTO> {
let applications: Array<Application> | null;
let applicationById: Application |undefined;
let applicationById: Application | undefined;
let applicationByIdDTO: ApplicationDTO;
try {
applications = await Application.findAll();
applicationById = applications.find(application => application.id == id);
applicationById = applications.find(
(application) => application.id === id,
);

if (applicationById === undefined) {
// Handle the case when no application is found
throw new Error(`Application with id ${id} not found`);
}

applicationByIdDTO = {
id: applicationById.id,
academicOrCoop: applicationById.academicOrCoop,
Expand All @@ -134,8 +136,8 @@ async getApplicationsById(id: number): Promise<ApplicationDTO> {
status: applicationById.status,
term: applicationById.term,
timesApplied: applicationById.timesApplied,
timestamp: applicationById.timestamp
};
timestamp: applicationById.timestamp,
};
} catch (error: unknown) {
Logger.error(
`Failed to get applications by id = ${id}. Reason = ${getErrorMessage(
Expand All @@ -144,7 +146,7 @@ async getApplicationsById(id: number): Promise<ApplicationDTO> {
);
throw error;
}
return applicationByIdDTO;
return applicationByIdDTO;
}

async getDashboardsByApplicationId(
Expand Down
Loading

0 comments on commit 6700258

Please sign in to comment.