diff --git a/src/helpers/user.helpers.ts b/src/helpers/user.helpers.ts index de9a7339..00576dd1 100644 --- a/src/helpers/user.helpers.ts +++ b/src/helpers/user.helpers.ts @@ -27,7 +27,7 @@ export const emailExpression = export async function checkUserLoggedIn( org: InstanceType, context: Context -): Promise<(a?: Array) => UserInterface> { +): Promise<(a?: Array) => { user: UserInterface, orgUserData: OrgUserDataInterface }> { const { userId, role } = context if (!userId) { @@ -76,6 +76,6 @@ export async function checkUserLoggedIn( ) } - return user + return { user, orgUserData } } } diff --git a/src/index.ts b/src/index.ts index 690c9a7e..c3d4743c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -51,6 +51,7 @@ import invitationSchema from './schema/invitation.schema' // import TableViewInvitationResolver from './resolvers/TableViewInvitationResolver' import eventSchema from './schema/event.schema' import './utils/cron-jobs/team-jobs' +import userSchema from './schema/user.schema' const PORT: number = parseInt(process.env.PORT!) || 4000 @@ -66,6 +67,7 @@ export const typeDefs = mergeTypeDefs([ notificationSchema, statisticsSchema, eventSchema, + userSchema ]) export const resolvers = mergeResolvers([ diff --git a/src/resolvers/userResolver.ts b/src/resolvers/userResolver.ts index 56c2478e..9aaa56d0 100644 --- a/src/resolvers/userResolver.ts +++ b/src/resolvers/userResolver.ts @@ -4,7 +4,6 @@ import bcrypt, { compareSync, hashSync } from 'bcryptjs' import { GraphQLError } from 'graphql' // import * as jwt from 'jsonwebtoken' import { JwtPayload, verify } from 'jsonwebtoken' -import mongoose, { Error } from 'mongoose' import generateRandomPassword from '../helpers/generateRandomPassword' import isAssigned from '../helpers/isAssignedToProgramOrCohort' import { checkloginAttepmts } from '../helpers/logintracker' @@ -36,6 +35,7 @@ import registrationRequest from '../utils/templates/registrationRequestTemplate' import { EmailPattern } from '../utils/validation.utils' import { Context } from './../context' import { validateEmail, validatePasswordField, validateStringField, validateURLField } from '../validations' +import { use } from 'chai' const octokit = new Octokit({ auth: `${process.env.Org_Repo_Access}` }) const SECRET: string = process.env.SECRET as string @@ -118,7 +118,7 @@ const resolvers: any = { return organization }, - async getSignupOrganization(_: any, { orgToken }: {orgToken: string}, context: Context) { + async getCurrentOrganization(_: any, { orgToken }: {orgToken: string}, context: Context) { const org: InstanceType = await checkLoggedInOrganization(orgToken) ;(await checkUserLoggedIn(org, context))(Object.values(RoleOfUser)) @@ -133,104 +133,85 @@ const resolvers: any = { // } // }, -// async gitHubActivity( -// _: any, -// { organisation, username }: any, -// context: Context -// ) { -// ;(await checkUserLoggedIn(context))([ -// RoleOfUser.ADMIN, -// RoleOfUser.COORDINATOR, -// 'trainee', -// RoleOfUser.MANAGER, -// 'ttl', -// ]) + async gitHubActivity( + _: any, + { orgToken}: { orgToken: string}, + context: Context + ) { + const org = await checkLoggedInOrganization(orgToken) + const {user, orgUserData} = (await checkUserLoggedIn(org, context))([ + RoleOfUser.ADMIN, + RoleOfUser.COORDINATOR, + RoleOfUser.MANAGER, + RoleOfUser.TTL, + RoleOfUser.TRAINEE, + ]) -// const organisationExists = await Organization.findOne({ -// name: organisation, -// }) -// if (!organisationExists) -// throw new Error("This Organization doesn't exist") - -// organisation = organisationExists.gitHubOrganisation - -// const { data: checkOrg } = await octokit.orgs.get({ org: organisation }) -// if (!checkOrg) { -// throw new GraphQLError('Organization Not found On GitHub', { -// extensions: { -// code: 'UserInputError', -// }, -// }) -// } -// const { data: checkUser } = await octokit.users.getByUsername({ -// username: username, -// }) -// if (!checkUser) { -// throw new GraphQLError('User Not found On Github', { -// extensions: { -// code: 'UserInputError', -// }, -// }) -// } + await orgUserData.populate('profile') -// let allRepos: any = [] - -// allRepos = organisationExists.activeRepos - -// let pullRequestOpen = 0 -// let pullRequestClosed = 0 -// let pullRequestMerged = 0 -// let pullRequestTotal = 0 -// let allCommits = 0 - -// try { -// for (const repo of allRepos) { -// try { -// const response = await octokit.pulls.list({ -// owner: organisation, -// repo: repo, -// state: 'all', -// sort: 'created', -// direction: 'desc', -// per_page: 200, -// }) - -// const pullRequests = response.data.filter( -// (pullRequest: any) => pullRequest.user.login === username -// ) -// pullRequestTotal += pullRequests.length -// pullRequestOpen += pullRequests.filter( -// (pullRequest: any) => pullRequest.state === 'open' -// ).length -// pullRequestClosed += pullRequests.filter( -// (pullRequest: any) => pullRequest.state === 'closed' -// ).length -// pullRequestMerged += pullRequests.filter( -// (pullRequest: any) => pullRequest.merged_at != null -// ).length -// } catch (error) { -// console.error( -// `Error retrieving commits for repository ${repo.name}:`, -// error -// ) -// throw new GraphQLError( -// 'Error retrieving commits for repository ${repo.name}:' -// ) -// } -// } -// } catch (error) { -// console.error('Error retrieving repositories:', error) -// } -// return { -// totalCommits: pullRequestMerged, -// pullRequest: { -// merged: pullRequestMerged, -// closed: pullRequestClosed, -// opened: pullRequestOpen, -// }, -// } -// }, -// }, + const { data: checkOrg } = await octokit.orgs.get({ org: org.gitHubOrganisation || "" }) + if (!checkOrg) { + throw new GraphQLError('Organization Not found On GitHub', { + extensions: { + code: 'ORG_NOT_FOUND', + }, + }) + } + const { data: checkUser } = await octokit.users.getByUsername({ + username: (orgUserData.profile as any).githubUsername || "", + }) + if (!checkUser) { + throw new GraphQLError('User Not found On Github', { + extensions: { + code: 'USER_NOT_FOUND', + }, + }) + } + + let pullRequestOpen = 0 + let pullRequestClosed = 0 + let pullRequestMerged = 0 + let pullRequestTotal = 0 + try { + for (const repo of org.activeRepos) { + const response = await octokit.pulls.list({ + owner: org.gitHubOrganisation || "", + repo: repo, + state: 'all', + sort: 'created', + direction: 'desc', + per_page: 200, + }) + const pullRequests = response.data.filter( + (pullRequest: any) => pullRequest.user.login === (orgUserData.profile as any).githubUsername + ) + pullRequestTotal += pullRequests.length + pullRequestOpen += pullRequests.filter( + (pullRequest: any) => pullRequest.state === 'open' + ).length + pullRequestClosed += pullRequests.filter( + (pullRequest: any) => pullRequest.state === 'closed' + ).length + pullRequestMerged += pullRequests.filter( + (pullRequest: any) => pullRequest.merged_at != null + ).length + } + } catch (error) { + throw new GraphQLError("Error retrieving repository information", { + extensions: { + code: "SERVER_ERROR" + } + }) + } + return { + totalCommits: pullRequestMerged, + pullRequest: { + merged: pullRequestMerged, + closed: pullRequestClosed, + opened: pullRequestOpen, + }, + } + }, // Login: { // user: async (parent: any) => { // const user = await User.findById(parent.user.id) @@ -359,7 +340,7 @@ const resolvers: any = { validateStringField(githubUsername, "please enter a valid githubUsername") validateURLField(resume, "Please enter a valid resume url") const org = await checkLoggedInOrganization(orgToken) - const user = (await checkUserLoggedIn(org, context))(Object.values(RoleOfUser)) + const {user} = (await checkUserLoggedIn(org, context))(Object.values(RoleOfUser)) const profile = await Profile.findOneAndUpdate( { user: user._id }, @@ -443,8 +424,8 @@ const resolvers: any = { async deleteUser(_: any, { userId, reason , orgToken}: {userId: string, reason?: string, orgToken: string}, context: Context) { const org = await checkLoggedInOrganization(orgToken) - const requester = (await checkUserLoggedIn(org,context))([RoleOfUser.ADMIN, RoleOfUser.SUPER_ADMIN]) - if (!requester) { + const {user} = (await checkUserLoggedIn(org,context))([RoleOfUser.ADMIN, RoleOfUser.SUPER_ADMIN]) + if (!user) { throw new GraphQLError('Requester does not exist',{ extensions: { code: 'USER_NOT_FOUND' @@ -476,9 +457,9 @@ const resolvers: any = { cohort.coordinator = undefined await cohort.save() await pushNotification( - requester._id, + user._id, `The coordinator of ${cohort.name} has been suspended. Please assign a new coordinator.`, - requester._id, + user._id, org._id ) } @@ -490,9 +471,9 @@ const resolvers: any = { hasTeam.ttl = undefined await hasTeam.save() await pushNotification( - requester._id, + user._id, `The TTL of ${hasTeam.name} has been suspended. Please assign a new TTL.`, - requester._id, + user._id, org._id ) } @@ -508,23 +489,25 @@ const resolvers: any = { await pushNotification( userToSuspend._id, 'Your account has been suspended and will no longer be able to access the system.', - requester._id, + user._id, org._id ) // Send confirmation notification to the requester/admin await pushNotification( - requester._id, + user._id, `You have successfully suspended the user with email: ${userToSuspend.email}.`, - requester._id, + user._id, org._id, ) - return { message: 'User suspended successfully' } + return { + message: 'User suspended successfully' + } }, async updateUserRole(_: any, { userId, role, orgToken }: {userId: string, role: RoleOfUser, orgToken: string}, context: Context) { const org = await checkLoggedInOrganization(orgToken) - const user = (await checkUserLoggedIn(org, context))([RoleOfUser.ADMIN, RoleOfUser.SUPER_ADMIN]) + const { user }= (await checkUserLoggedIn(org, context))([RoleOfUser.ADMIN, RoleOfUser.SUPER_ADMIN]) if (!Object.values(RoleOfUser).includes(role)) { throw new GraphQLError("This role doesn't exist", { extensions: { @@ -586,7 +569,7 @@ const resolvers: any = { }, //This section is to make org name login to be case insensitive - async loginOrg(_: any, { orgInput: { name } }: any) { + async loginOrg(_: any, { name }: { name: String }) { const organization: any = await Organization.findOne({ name: { $regex: new RegExp('^' + name + '$', 'i') }, }) @@ -885,7 +868,7 @@ const resolvers: any = { return org }, - async addsActiveRepostoOrganization(_: any, { repoUrl, orgToken}: {repoUrl: string, orgToken: string}, context: Context) { + async addActiveRepostoOrganization(_: any, { repoUrl, orgToken}: {repoUrl: string, orgToken: string}, context: Context) { validateURLField(repoUrl, "Please provide a valid repoUrl") const org = await checkLoggedInOrganization(orgToken) ;(await checkUserLoggedIn(org, context))([RoleOfUser.ADMIN, RoleOfUser.SUPER_ADMIN]) @@ -906,6 +889,7 @@ const resolvers: any = { }, async deleteActiveRepostoOrganization(_: any, { repoUrl, orgToken}: {repoUrl: string, orgToken: string}, context: Context) { + validateURLField(repoUrl, "Please provide a valid repo URL") const org = await checkLoggedInOrganization(orgToken) ;(await checkUserLoggedIn(org,context))([RoleOfUser.ADMIN,RoleOfUser.SUPER_ADMIN]); @@ -986,7 +970,7 @@ const resolvers: any = { async updateEmailNotifications(_: any, { orgToken }: { orgToken: string}, context: Context) { const org = await checkLoggedInOrganization(orgToken) - const user = (await checkUserLoggedIn(org, context))(Object.values(RoleOfUser)) + const { user } = (await checkUserLoggedIn(org, context))(Object.values(RoleOfUser)) const orgUserData = isPartOfOrganization(user, org) orgUserData.emailNotifications = !orgUserData.emailNotifications @@ -999,7 +983,7 @@ const resolvers: any = { async updatePushNotifications(_: any, { orgToken}: {orgToken: string}, context: Context) { const org = await checkLoggedInOrganization(orgToken) - const user = (await checkUserLoggedIn(org, context))(Object.values(RoleOfUser)) + const { user } = (await checkUserLoggedIn(org, context))(Object.values(RoleOfUser)) const orgUserData = isPartOfOrganization(user, org) orgUserData.pushNotifications = !orgUserData.pushNotifications @@ -1012,11 +996,11 @@ const resolvers: any = { async resetUserPassword( _: any, - { password, confirmPassword, token }: { password: string, confirmPassword: string, token: string} + { password, confirmPassword, resetToken }: { password: string, confirmPassword: string, resetToken: string} ) { validatePasswordField(password, "Please enter a valid new password") validatePasswordField(confirmPassword, "Please enter a valid confirmation password") - const { email } = verify(token, SECRET) as JwtPayload + const { email } = verify(resetToken, SECRET) as JwtPayload if (password !== confirmPassword) { throw new GraphQLError("Passwords do not match",{ extensions: { @@ -1048,7 +1032,7 @@ const resolvers: any = { validatePasswordField(newPassword, "Please enter a valid new password") validatePasswordField(confirmPassword, "Please enter a valid confirmation password") const org = await checkLoggedInOrganization(orgToken) - const user = (await checkUserLoggedIn(org, context))(Object.values(RoleOfUser)) + const { user } = (await checkUserLoggedIn(org, context))(Object.values(RoleOfUser)) if (newPassword !== confirmPassword) { throw new GraphQLError("Passwords do not match",{ extensions: { diff --git a/src/schema/index.ts b/src/schema/index.ts index 0a220125..ebdd8a5e 100644 --- a/src/schema/index.ts +++ b/src/schema/index.ts @@ -85,26 +85,6 @@ const Schema = gql` password: String! role: String } - input LoginInput { - email: String - password: String - orgToken: String - } - input OrgInput { - name: String - } - - type DeleteUserPayload { - message: String! - } - - type Mutation { - deleteUser( - userId: String! - reason: String - orgToken: String! - ): DeleteUserPayload! - } type Profile { id: ID! @@ -134,18 +114,10 @@ const Schema = gql` id: ID! name: String! } - type RegisteredUser { - token: String - user: User - } type Login { token: String user: User } - type OrgLogin { - token: String - organization: Organization - } type Organization { id: ID! @@ -172,23 +144,6 @@ const Schema = gql` emailNotifications: Boolean! } - type GitHubActivity { - totalCommits: String! - pullRequest: pullRequest! - } - - type pullRequest { - merged: String! - closed: String! - opened: String! - } - - input OrganizationInput { - email: String! - name: String! - description: String! - } - type Rating { id: ID! user: User! @@ -275,9 +230,6 @@ const Schema = gql` getProfile: Profile getAllRoles: [UserRole] getRole(id: ID!): UserRole - getOrganizations: [Organization]! - getOrganization(name: String!): Organization - getSignupOrganization(orgToken: String!): Organization fetchRatings(orgToken: String): [Rating] fetchTrainees: [Cohort] fetchRatingsForAdmin(orgToken: String): [FetchRatingForAdmin] @@ -289,7 +241,7 @@ const Schema = gql` getTTLTeams(orgToken: String): [Team!]! getAllTeams(orgToken: String): [Team!]! getAllTeamInCohort(orgToken: String, cohort: String): [Team!] - gitHubActivity(organisation: String!, username: String!): GitHubActivity! + gitHubActivity(orgToken: String!): GitHubActivity! getRatingsByCohort(cohortId: String!, orgToken: String!): [Rating]! getTeamsByCohort(cohortId: String!,orgToken: String!): [Team]! } @@ -308,9 +260,8 @@ const Schema = gql` RejectedRatings: [RejectedRows]! } - type RequestedOrganization{ - message: String!, - org: Organization! + type Message{ + message: String! } type Mutation { @@ -318,41 +269,6 @@ const Schema = gql` uploadResume(userId: ID!, resume: String!): Profile dropTTLUser(email: String!, reason: String!): String! undropTTLUser(email: String!): String! - createUser( - firstName: String! - lastName: String! - dateOfBirth: DateTime! - gender: String! - email: String! - password: String! - orgToken: String! - role: String - ): RegisteredUser! - loginUser(loginInput: LoginInput): Login! - loginOrg(orgInput: OrgInput): OrgLogin! - requestOrganization(organizationInput: OrganizationInput!): RequestedOrganization! - addOrganization( - organizationInput: OrganizationInput - orgToken: String! - ): Organization! - RegisterNewOrganization( - organizationInput: OrganizationInput - action: String, - orgToken: String!, - ): Organization! - updateProfile( - lastName: String - firstName: String - address: String - city: String - country: String - phoneNumber: String - biography: String - fileName: String - cover: String - githubUsername: String - ): Profile - createProfile( lastName: String firstName: String @@ -367,12 +283,6 @@ const Schema = gql` ): Profile updateAvatar(avatar: String): Profile updateCoverImage(cover: String): Profile - updateUserRole(userId: ID!, role: String!, orgToken: String!): User! - deleteOrganization(id: ID!): Organization - updateGithubOrganisation( - name: String! - gitHubOrganisation: String! - ): Organization addRatings( user: String! sprint: Int! @@ -410,28 +320,8 @@ const Schema = gql` user: String content: String ): RatingMessageTemp - approveRating(user: String!, sprint: Int!): ApproveRating rejectRating(user: String!, sprint: Int!): String! - forgotPassword(email: String!): String! - resetUserPassword( - password: String! - confirmPassword: String! - token: String! - ): String! - changeUserPassword( - currentPassword: String! - newPassword: String! - confirmPassword: String! - token: String! - ): String! - - addActiveRepostoOrganization(name: String!, repoUrl: String!): Organization! - - deleteActiveRepostoOrganization( - name: String! - repoUrl: String! - ): Organization! addTeam( name: String! cohortName: String! @@ -570,10 +460,6 @@ const Schema = gql` description: String! ): Documentation! } - type Mutation { - updatePushNotifications(id: ID!): String - updateEmailNotifications(id: ID!): String - } type Query { getUpdatedEmailNotifications(id: ID!): Boolean! getUpdatedPushNotifications(id: ID!): Boolean! diff --git a/src/schema/user.schema.ts b/src/schema/user.schema.ts new file mode 100644 index 00000000..5b007ec7 --- /dev/null +++ b/src/schema/user.schema.ts @@ -0,0 +1,140 @@ +import gql from "graphql-tag"; + +const userSchema = gql` + +type PullRequest { + merged: String! + closed: String! + opened: String! +} + +type GitHubActivity { + totalCommits: String! + pullRequest: PullRequest! +} + +type RegisteredUser { + token: String + user: User + } + +type Query { + getOrganizations(orgToken: String!): [Organization]! + getOrganization(name: String!, orgToken: String!): Organization + getCurrentOrganization(orgToken: String!): Organization + gitHubActivity(orgToken: String!): GitHubActivity! +} + +input LoginInput { + email: String + password: String + orgToken: String +} + +type OrgLogin { + token: String + organization: Organization +} + +input OrganizationInput { + email: String! + name: String! + description: String! +} + +type RequestedOrganization{ + message: String!, + org: Organization! +} + +type Mutation { + createUser( + firstName: String! + lastName: String! + dateOfBirth: DateTime! + gender: String! + email: String! + password: String! + orgToken: String! + role: String + ): RegisteredUser! + + updateProfile( + biography: String + cover: String + avatar: String + githubUsername: String + resume: String + orgToken: String! + ): Profile + + loginUser(loginInput: LoginInput): Login! + + deleteUser( + userId: String! + reason: String + orgToken: String! + ): Message! + + updateUserRole( + userId: ID!, + role: String!, + orgToken: String! + ): User! + + loginOrg(name: String!): OrgLogin! + + requestOrganization(organizationInput: OrganizationInput!): RequestedOrganization! + + RegisterNewOrganization( + organizationInput: OrganizationInput + action: String, + orgToken: String!, + ): Organization! + + addOrganization( + organizationInput: OrganizationInput + orgToken: String! + ): Organization! + + updateGithubOrganisation( + gitHubOrganisation: String! + orgToken: String! + ): Organization + + deleteOrganization( + orgId: ID! + orgToken: String! + ): Organization + + addActiveRepostoOrganization( + repoUrl: String! + orgToken: String! + ): Organization! + + deleteActiveRepostoOrganization( + repoUrl: String! + orgToken: String! + ): Organization! + + updatePushNotifications(orgToken: String!): Message! + updateEmailNotifications(orgToken: String!): Message! + + forgotPassword(email: String!): Message! + + resetUserPassword( + password: String! + confirmPassword: String! + resetToken: String! + ): Message! + + changeUserPassword( + currentPassword: String! + newPassword: String! + confirmPassword: String! + orgToken: String! + ): Message! +} + +` +export default userSchema \ No newline at end of file