diff --git a/src/resolvers/profileResolver.ts b/src/resolvers/profileResolver.ts index 32f82831..b4c5bdc9 100644 --- a/src/resolvers/profileResolver.ts +++ b/src/resolvers/profileResolver.ts @@ -7,6 +7,8 @@ import { sendEmail } from '../utils/sendEmail' import Cohort from '../models/cohort.model' import Team from '../models/team.model' import mongoose, { ObjectId } from 'mongoose' +import { fetchTraineeAttendance } from './attendance.resolvers' +import Ticket from '../models/ticket.model' const profileResolvers: any = { Query: { @@ -163,7 +165,23 @@ const profileResolvers: any = { }) .exec() - return traineesInSameTeam + const traineeWithAttendance = await Promise.all( + traineesInSameTeam.map(async (trainee) => { + const objectId = new mongoose.Types.ObjectId(trainee.id) + const traineeTickets = await Ticket.find({ + assignee: objectId, + status: 'open', + }) + const { allPhasesAverage } = await fetchTraineeAttendance(trainee.id) + return { + traineeInfo: trainee, + attendance: allPhasesAverage, + numOfTickets: traineeTickets.length, + } + }) + ) + + return traineeWithAttendance }, }, Profile: { diff --git a/src/resolvers/userResolver.ts b/src/resolvers/userResolver.ts index a4d3e6b2..694250aa 100644 --- a/src/resolvers/userResolver.ts +++ b/src/resolvers/userResolver.ts @@ -37,7 +37,7 @@ import { EmailPattern } from '../utils/validation.utils' import { Context } from './../context' import { UserInputError } from 'apollo-server' import { encodeOtpToToken, generateOtp } from '../utils/2WayAuthentication' -import jwt from 'jsonwebtoken' +import jwt from 'jsonwebtoken' import nonTraineeTemplate from '../utils/templates/nonTraineeTemplate' const octokit = new Octokit({ auth: `${process.env.Org_Repo_Access}` }) @@ -115,7 +115,7 @@ export async function loginsCount(organizationName: any, recentLocation: any) { const resolvers: any = { Query: { async getOrganizations(_: any, __: any, context: Context) { - ; (await checkUserLoggedIn(context))([RoleOfUser.SUPER_ADMIN]) + ;(await checkUserLoggedIn(context))([RoleOfUser.SUPER_ADMIN]) return Organization.find() }, @@ -169,7 +169,7 @@ const resolvers: any = { { organisation, username }: any, context: Context ) { - ; (await checkUserLoggedIn(context))([ + ;(await checkUserLoggedIn(context))([ RoleOfUser.ADMIN, RoleOfUser.COORDINATOR, 'trainee', @@ -181,7 +181,7 @@ const resolvers: any = { name: organisation, }) if (!organisationExists) - throw new Error('This Organization doesn\'t exist') + throw new Error("This Organization doesn't exist") organisation = organisationExists.gitHubOrganisation @@ -329,9 +329,16 @@ const resolvers: any = { invitation.status = 'accepted' await invitation.save() } - if(user.role !=='trainee'){ - const content = nonTraineeTemplate( user.email,password,org.name) - sendEmail(user.email,'Login Details',content,process.env.FRONTEND_LINK, process.env.ADMIN_EMAIL, process.env.ADMIN_PASS) + if (user.role !== 'trainee') { + const content = nonTraineeTemplate(user.email, password, org.name) + sendEmail( + user.email, + 'Login Details', + content, + process.env.FRONTEND_LINK, + process.env.ADMIN_EMAIL, + process.env.ADMIN_PASS + ) } const newProfile = await Profile.create({ @@ -377,14 +384,14 @@ const resolvers: any = { context: any ) { // Check organization validity - const org = await checkLoggedInOrganization(orgToken); - const { clientIpAdress } = context; + const org = await checkLoggedInOrganization(orgToken) + const { clientIpAdress } = context if (!org) { throw new GraphQLError('Organization not found', { extensions: { code: 'InvalidOrganization' }, - }); + }) } - + // Find user with populated fields const user: any = await User.findOne({ email }).populate({ path: 'cohort', @@ -400,15 +407,15 @@ const resolvers: any = { strictPopulate: false, }, }, - }); - + }) + // Check if user exists if (!user) { throw new GraphQLError('Invalid credentials', { extensions: { code: 'AccountNotFound' }, - }); + }) } - + // Check if account is active if (user.status?.status !== 'active') { throw new GraphQLError( @@ -416,14 +423,14 @@ const resolvers: any = { { extensions: { code: 'AccountInactive' }, } - ); + ) } - + // Check if two-factor authentication is enabled if (user.twoFactorAuth) { - const otp = generateOtp(); // Generate OTP - const TwoWayVerificationToken = encodeOtpToToken(otp, email); // Encode OTP - + const otp = generateOtp() // Generate OTP + const TwoWayVerificationToken = encodeOtpToToken(otp, email) // Encode OTP + // Send email with OTP await sendEmail( email, @@ -432,55 +439,55 @@ const resolvers: any = { null, process.env.ADMIN_EMAIL, process.env.ADMIN_PASS - ); - + ) + // Save the Two-Way Verification Token to the database - user.TwoWayVerificationToken = TwoWayVerificationToken; - await user.save(); - + user.TwoWayVerificationToken = TwoWayVerificationToken + await user.save() + // Return a response without exposing the token return { message: 'Check your email for the OTP code.', otpRequired: true, user: { id: user._id }, - }; + } } else { // Verify password if 2FA is not enabled - const passwordMatch = await user?.checkPass(password); + const passwordMatch = await user?.checkPass(password) if (!passwordMatch) { throw new GraphQLError('Invalid credentials', { extensions: { code: 'InvalidCredential' }, - }); + }) } - + // Generate token for authenticated user const token = jwt.sign( { userId: user._id, role: user._doc?.role || 'user' }, SECRET, { expiresIn: '2h' } - ); - - const geoData = await logGeoActivity(user, clientIpAdress); // Log activity - - const organizationName = user.organizations[0]; + ) + + const geoData = await logGeoActivity(user, clientIpAdress) // Log activity + + const organizationName = user.organizations[0] if (organizationName) { const location = geoData && geoData.city && geoData.country_name ? `${geoData.city}-${geoData.country_name}` - : null; - await loginsCount(organizationName, location); + : null + await loginsCount(organizationName, location) } - + // Return token and user data return { token, user: user.toJSON(), geoData, otpRequired: false, - }; + } } }, - + async deleteUser(_: any, { input }: any, context: { userId: any }) { const requester = await User.findById(context.userId) if (!requester) { @@ -563,9 +570,9 @@ const resolvers: any = { ] const org = await checkLoggedInOrganization(orgToken) const roleExists = allRoles.includes(name) - if (!roleExists) throw new Error('This role doesn\'t exist') + if (!roleExists) throw new Error("This role doesn't exist") const userExists = await User.findById(id) - if (!userExists) throw new Error('User doesn\'t exist') + if (!userExists) throw new Error("User doesn't exist") const getAllUsers = await User.find({ role: RoleOfUser.ADMIN, @@ -802,7 +809,7 @@ const resolvers: any = { context: Context ) { // check if requester is super admin - ; (await checkUserLoggedIn(context))([RoleOfUser.SUPER_ADMIN]) + ;(await checkUserLoggedIn(context))([RoleOfUser.SUPER_ADMIN]) const orgExists = await Organization.findOne({ name: name }) if (action == 'approve') { if (!orgExists) { @@ -872,7 +879,7 @@ const resolvers: any = { context: Context ) { // the below commented line help to know if the user is an superAdmin to perform an action of creating an organization - ; (await checkUserLoggedIn(context))([RoleOfUser.SUPER_ADMIN]) + ;(await checkUserLoggedIn(context))([RoleOfUser.SUPER_ADMIN]) if (action == 'new') { const orgExists = await Organization.findOne({ name: name }) if (orgExists) { @@ -937,7 +944,7 @@ const resolvers: any = { { name, gitHubOrganisation }: any, context: Context ) { - ; (await checkUserLoggedIn(context))([ + ;(await checkUserLoggedIn(context))([ RoleOfUser.ADMIN, RoleOfUser.SUPER_ADMIN, ]) @@ -1030,7 +1037,7 @@ const resolvers: any = { }, async deleteOrganization(_: any, { id }: any, context: Context) { - ; (await checkUserLoggedIn(context))([ + ;(await checkUserLoggedIn(context))([ RoleOfUser.ADMIN, RoleOfUser.SUPER_ADMIN, ]) @@ -1038,7 +1045,7 @@ const resolvers: any = { const organizationExists = await Organization.findOne({ _id: id }) if (!organizationExists) - throw new Error('This Organization doesn\'t exist') + throw new Error("This Organization doesn't exist") await Cohort.deleteMany({ organization: id }) await Team.deleteMany({ organization: id }) await Phase.deleteMany({ organization: id }) @@ -1116,7 +1123,7 @@ const resolvers: any = { if (password === confirmPassword) { const user: any = await User.findOne({ email }) if (!user) { - throw new Error('User doesn\'t exist! ') + throw new Error("User doesn't exist! ") } user.password = password await user.save() @@ -1136,7 +1143,7 @@ const resolvers: any = { if (newPassword === confirmPassword) { const user: any = await User.findById(userId) if (!user) { - throw new Error('User doesn\'t exist! ') + throw new Error("User doesn't exist! ") } if (bcrypt.compareSync(currentPassword, user.password)) { diff --git a/src/schema/index.ts b/src/schema/index.ts index 203ffae9..448e04f1 100644 --- a/src/schema/index.ts +++ b/src/schema/index.ts @@ -80,8 +80,8 @@ const Schema = gql` emailNotifications: Boolean! status: StatusType ratings: [Rating] - twoFactorAuth:Boolean! - TwoWayVerificationToken:String + twoFactorAuth: Boolean! + TwoWayVerificationToken: String } input RegisterInput { email: String! @@ -149,16 +149,15 @@ const Schema = gql` user: User } type LoginResponse { - token: String! - user: User! - message: String! -} + token: String! + user: User! + message: String! + } type Login { token: String user: User - message:String - otpRequired:Boolean - + message: String + otpRequired: Boolean } type OrgLogin { token: String @@ -192,7 +191,7 @@ const Schema = gql` description: String } - type Rating { + type Rating { id: ID! user: User! sprint: Int! @@ -271,10 +270,16 @@ const Schema = gql` createdAt: String } + type TTLTrainee { + traineeInfo: User + attendance: String + numOfTickets: Int + } + type Query { getAllUsers(orgToken: String): [User] getAllTTLUsers(orgToken: String): [User] - getTTLTrainees(orgToken: String): [User] + getTTLTrainees(orgToken: String): [TTLTrainee] getUsers(orgToken: String): [User] getAllCoordinators(orgToken: String): [User] getProfile: Profile @@ -314,10 +319,13 @@ const Schema = gql` } type Mutation { - enableTwoFactorAuth(email: String!): String + enableTwoFactorAuth(email: String!): String # //TwoWayVerificationToken: String! - disableTwoFactorAuth(email: String!): String - loginWithTwoFactorAuthentication(email: String!, otp: String!): LoginResponse! + disableTwoFactorAuth(email: String!): String + loginWithTwoFactorAuthentication( + email: String! + otp: String! + ): LoginResponse! createUserRole(name: String!): UserRole! uploadResume(userId: ID!, resume: String!): Profile dropTTLUser(email: String!, reason: String!): String!