From f480cebdfb9c34fe3959aa675d1916e3eb10f6de Mon Sep 17 00:00:00 2001 From: Eshaan Aggarwal <96648934+EshaanAgg@users.noreply.github.com> Date: Thu, 29 Jun 2023 22:24:16 +0530 Subject: [PATCH] Improved Event Management: Relevant backend changes (#1349) * Make changes for attendees in the Event model * Migrate tests and resolvers (init) * Migrate all tests (final) * Add field level resolver to get all registered users for an event * Add volunteers table and field level resolver for volunteers on a task * Introduce the Event Project schema * Update package-lock.json for clean installs * Add additonal field level resolvers * Change resolvers according to frontend requirements * Allow superadmin access in all event related resolvers and update their deletion resolvers * Add auth check in createTask resolver * Make changes according to the UI requirements * Add field level resolver for organization on event model * Add setTaskVolunteers mutation with tests * Add setVolunteers mutation * Add attendee resolvers * Add CheckIn detail * Init CheckIn related changes * Add checkIn schema and basic resolvers * Add testing for all checkin resolvers * Fix installation error * Fix usage of any in resolvers * Pull from dev --- codegen.ts | 6 +- package-lock.json | 96 ++++-- schema.graphql | 80 +++-- src/constants.ts | 34 ++ src/models/CheckIn.ts | 53 +++ src/models/Event.ts | 51 +-- src/models/EventAttendee.ts | 40 +++ src/models/EventProject.ts | 10 +- src/models/Task.ts | 21 +- src/models/TaskVolunteer.ts | 41 +++ src/models/index.ts | 3 + src/resolvers/CheckIn/event.ts | 12 + src/resolvers/CheckIn/index.ts | 8 + src/resolvers/CheckIn/user.ts | 12 + src/resolvers/Event/attendees.ts | 13 + src/resolvers/Event/attendeesCheckInStatus.ts | 18 + src/resolvers/Event/index.ts | 12 + src/resolvers/Event/organization.ts | 8 + src/resolvers/Event/projects.ts | 8 + src/resolvers/EventProject/index.ts | 6 + src/resolvers/EventProject/tasks.ts | 8 + src/resolvers/Mutation/addEventAttendee.ts | 79 +++++ src/resolvers/Mutation/checkIn.ts | 103 ++++++ src/resolvers/Mutation/createEvent.ts | 20 +- src/resolvers/Mutation/createEventProject.ts | 97 +++--- src/resolvers/Mutation/createTask.ts | 43 +-- src/resolvers/Mutation/index.ts | 14 + src/resolvers/Mutation/registerForEvent.ts | 103 ++---- src/resolvers/Mutation/removeEvent.ts | 48 ++- src/resolvers/Mutation/removeEventAttendee.ts | 76 +++++ src/resolvers/Mutation/removeEventProject.ts | 105 +++--- src/resolvers/Mutation/removeTask.ts | 25 +- src/resolvers/Mutation/setTaskVolunteers.ts | 117 +++++++ .../Mutation/unregisterForEventByUser.ts | 52 +-- src/resolvers/Mutation/updateEvent.ts | 9 +- src/resolvers/Mutation/updateEventProject.ts | 91 ++--- src/resolvers/Mutation/updateTask.ts | 13 +- src/resolvers/Query/eventsByOrganization.ts | 9 - .../Query/eventsByOrganizationConnection.ts | 10 +- src/resolvers/Query/index.ts | 8 - src/resolvers/Query/isUserRegister.ts | 51 --- src/resolvers/Query/registrantsByEvent.ts | 43 --- src/resolvers/Query/tasksByEvent.ts | 24 -- src/resolvers/Query/tasksByUser.ts | 23 -- src/resolvers/Task/index.ts | 6 + src/resolvers/Task/volunteers.ts | 11 + src/resolvers/User/assignedTasks.ts | 12 + src/resolvers/User/index.ts | 2 + src/resolvers/index.ts | 8 + src/typeDefs/inputs.ts | 35 +- src/typeDefs/mutations.ts | 19 +- src/typeDefs/queries.ts | 6 - src/typeDefs/types.ts | 49 +-- src/types/generatedGraphQLTypes.ts | 222 ++++++++---- tests/helpers/checkIn.ts | 44 +++ tests/helpers/donation.ts | 1 - tests/helpers/events.ts | 14 +- tests/helpers/eventsWithRegistrants.ts | 68 ++-- tests/helpers/task.ts | 89 +++-- tests/resolvers/CheckIn/event.spec.ts | 38 +++ tests/resolvers/CheckIn/user.spec.ts | 38 +++ tests/resolvers/Event/attendees.spec.ts | 36 ++ .../Event/attendeesCheckInStatus.spec.ts | 43 +++ tests/resolvers/Event/organization.spec.ts | 36 ++ tests/resolvers/Event/projects.spec.ts | 38 +++ tests/resolvers/EventProject/tasks.spec.ts | 39 +++ .../Mutation/addEventAttendee.spec.ts | 212 ++++++++++++ tests/resolvers/Mutation/checkIn.spec.ts | 247 ++++++++++++++ tests/resolvers/Mutation/createEvent.spec.ts | 15 +- .../Mutation/createEventProject.spec.ts | 22 +- tests/resolvers/Mutation/createTask.spec.ts | 63 ++-- .../Mutation/registerForEvent.spec.ts | 108 +----- .../Mutation/removeEventAttendee.spec.ts | 214 ++++++++++++ .../Mutation/removeEventProject.spec.ts | 15 +- tests/resolvers/Mutation/removeTask.spec.ts | 113 +------ .../Mutation/setTaskVolunteers.spec.ts | 219 ++++++++++++ .../Mutation/unregisterForEventByUser.spec.ts | 75 +---- .../Mutation/updateEventProject.spec.ts | 9 +- tests/resolvers/Mutation/updateTask.spec.ts | 49 +-- .../Mutation/updateUserPassword.spec.ts | 1 - tests/resolvers/Query/event.spec.ts | 4 +- .../eventsByOrganizationConnection.spec.ts | 33 +- tests/resolvers/Query/isUserRegister.spec.ts | 172 ---------- .../Query/registrantsByEvent.spec.ts | 73 ---- tests/resolvers/Query/tasksByEvent.spec.ts | 316 ------------------ tests/resolvers/Query/tasksByUser.spec.ts | 297 ---------------- tests/resolvers/Task/volunteers.spec.ts | 35 ++ tests/resolvers/User/assignedTasks.spec.ts | 35 ++ 88 files changed, 2921 insertions(+), 1963 deletions(-) create mode 100644 src/models/CheckIn.ts create mode 100644 src/models/EventAttendee.ts create mode 100644 src/models/TaskVolunteer.ts create mode 100644 src/resolvers/CheckIn/event.ts create mode 100644 src/resolvers/CheckIn/index.ts create mode 100644 src/resolvers/CheckIn/user.ts create mode 100644 src/resolvers/Event/attendees.ts create mode 100644 src/resolvers/Event/attendeesCheckInStatus.ts create mode 100644 src/resolvers/Event/index.ts create mode 100644 src/resolvers/Event/organization.ts create mode 100644 src/resolvers/Event/projects.ts create mode 100644 src/resolvers/EventProject/index.ts create mode 100644 src/resolvers/EventProject/tasks.ts create mode 100644 src/resolvers/Mutation/addEventAttendee.ts create mode 100644 src/resolvers/Mutation/checkIn.ts create mode 100644 src/resolvers/Mutation/removeEventAttendee.ts create mode 100644 src/resolvers/Mutation/setTaskVolunteers.ts delete mode 100644 src/resolvers/Query/isUserRegister.ts delete mode 100644 src/resolvers/Query/registrantsByEvent.ts delete mode 100644 src/resolvers/Query/tasksByEvent.ts delete mode 100644 src/resolvers/Query/tasksByUser.ts create mode 100644 src/resolvers/Task/index.ts create mode 100644 src/resolvers/Task/volunteers.ts create mode 100644 src/resolvers/User/assignedTasks.ts create mode 100644 tests/helpers/checkIn.ts create mode 100644 tests/resolvers/CheckIn/event.spec.ts create mode 100644 tests/resolvers/CheckIn/user.spec.ts create mode 100644 tests/resolvers/Event/attendees.spec.ts create mode 100644 tests/resolvers/Event/attendeesCheckInStatus.spec.ts create mode 100644 tests/resolvers/Event/organization.spec.ts create mode 100644 tests/resolvers/Event/projects.spec.ts create mode 100644 tests/resolvers/EventProject/tasks.spec.ts create mode 100644 tests/resolvers/Mutation/addEventAttendee.spec.ts create mode 100644 tests/resolvers/Mutation/checkIn.spec.ts create mode 100644 tests/resolvers/Mutation/removeEventAttendee.spec.ts create mode 100644 tests/resolvers/Mutation/setTaskVolunteers.spec.ts delete mode 100644 tests/resolvers/Query/isUserRegister.spec.ts delete mode 100644 tests/resolvers/Query/registrantsByEvent.spec.ts delete mode 100644 tests/resolvers/Query/tasksByEvent.spec.ts delete mode 100644 tests/resolvers/Query/tasksByUser.spec.ts create mode 100644 tests/resolvers/Task/volunteers.spec.ts create mode 100644 tests/resolvers/User/assignedTasks.spec.ts diff --git a/codegen.ts b/codegen.ts index 0cf56e2804..4eccc61652 100644 --- a/codegen.ts +++ b/codegen.ts @@ -25,6 +25,8 @@ const config: CodegenConfig = { // functionality is useful because what we retrieve from the database and what we choose to return from a graphql server // could be completely different fields. Address to models here is relative to the location of generated types. mappers: { + CheckIn: "../models/CheckIn#InterfaceCheckIn", + MessageChat: "../models/MessageChat#InterfaceMessageChat", Comment: "../models/Comment#InterfaceComment", @@ -38,7 +40,9 @@ const config: CodegenConfig = { Event: "../models/Event#InterfaceEvent", - // EventProject: '../models/EventProject#InterfaceEventProject' + EventAttendee: "../models/EventAttendee#InterfaceEventAttendee", + + EventProject: "../models/EventProject#InterfaceEventProject", // File: '../models/File#InterfaceFile', diff --git a/package-lock.json b/package-lock.json index aac49df29b..ecefd162dc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4523,12 +4523,12 @@ } }, "node_modules/@types/express": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.15.tgz", - "integrity": "sha512-Yv0k4bXGOH+8a+7bELd2PqHQsuiANB+A8a4gnQrkRWzrkKlb6KHaVvyXhqs04sVW/OWlbPyYxRgYlIXLfrufMQ==", + "version": "4.17.17", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", + "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", "dependencies": { "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.31", + "@types/express-serve-static-core": "^4.17.33", "@types/qs": "*", "@types/serve-static": "*" } @@ -4543,13 +4543,14 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "4.17.32", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.32.tgz", - "integrity": "sha512-aI5h/VOkxOF2Z1saPy0Zsxs5avets/iaiAJYznQFm5By/pamU31xWKL//epiF4OfUA2qTOc9PV6tCUjhO8wlZA==", + "version": "4.17.35", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.35.tgz", + "integrity": "sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==", "dependencies": { "@types/node": "*", "@types/qs": "*", - "@types/range-parser": "*" + "@types/range-parser": "*", + "@types/send": "*" } }, "node_modules/@types/fs-capacitor": { @@ -4664,9 +4665,9 @@ } }, "node_modules/@types/lodash": { - "version": "4.14.191", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.191.tgz", - "integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==", + "version": "4.14.195", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.195.tgz", + "integrity": "sha512-Hwx9EUgdwf2GLarOjQp5ZH8ZmblzcbTBC2wtQWNKARBSxM9ezRIAUpeDTgoQRAFB0+8CNWXVA9+MaSOzOF3nPg==", "dev": true }, "node_modules/@types/long": { @@ -4778,6 +4779,20 @@ "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", "dev": true }, + "node_modules/@types/send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.1.tgz", + "integrity": "sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/send/node_modules/@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" + }, "node_modules/@types/serve-static": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", @@ -6461,12 +6476,12 @@ "integrity": "sha512-Ylo+MAo5BDUq1KA3f3R/MFhh+g8cnHmo8bz3YPGhI1znrMaf77ol1sfvYJzsw3nTE+Y2GryfDxBaR+AqpAkEHQ==" }, "node_modules/body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", "dependencies": { "bytes": "3.1.2", - "content-type": "~1.0.4", + "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", @@ -6474,7 +6489,7 @@ "iconv-lite": "0.4.24", "on-finished": "2.4.1", "qs": "6.11.0", - "raw-body": "2.5.1", + "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" }, @@ -7213,9 +7228,9 @@ } }, "node_modules/content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "engines": { "node": ">= 0.6" } @@ -8487,6 +8502,29 @@ "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-5.5.1.tgz", "integrity": "sha512-MTjE2eIbHv5DyfuFz4zLYWxpqVhEhkTiwFGuB74Q9CSou2WHO52nlE5y3Zlg6SIsiYUIPj6ifFxnkPz6O3sIUg==" }, + "node_modules/express/node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, "node_modules/express/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -8500,6 +8538,20 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/express/node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -12981,9 +13033,9 @@ } }, "node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", diff --git a/schema.graphql b/schema.graphql index 7fb9120159..80074329ab 100644 --- a/schema.graphql +++ b/schema.graphql @@ -26,6 +26,28 @@ type AuthData { user: User! } +type CheckIn { + _id: ID! + allotedRoom: String + allotedSeat: String + event: Event! + time: DateTime! + user: User! +} + +input CheckInInput { + allotedRoom: String + allotedSeat: String + eventId: ID! + userId: ID! +} + +type CheckInStatus { + _id: ID! + checkIn: CheckIn + user: User! +} + type Comment { _id: ID createdAt: DateTime @@ -121,6 +143,8 @@ type Event { _id: ID! admins(adminId: ID): [User] allDay: Boolean! + attendees: [User!]! + attendeesCheckInStatus: [CheckInStatus!]! creator: User! description: String! endDate: Date! @@ -131,16 +155,20 @@ type Event { location: String longitude: Longitude organization: Organization + projects: [EventProject] recurrance: Recurrance recurring: Boolean! - registrants: [UserAttende] startDate: Date! startTime: Time status: Status! - tasks: [Task] title: String! } +input EventAttendeeInput { + eventId: ID! + userId: ID! +} + input EventInput { allDay: Boolean! description: String! @@ -182,9 +210,18 @@ enum EventOrderByInput { title_DESC } -type EventRegistrants { +type EventProject { + _id: ID! + description: String! event: Event! - isRegistered: Boolean! + tasks: [Task] + title: String! +} + +input EventProjectInput { + description: String! + eventId: ID! + title: String! } input EventWhereInput { @@ -355,6 +392,7 @@ type MinimumValueError implements FieldError { type Mutation { acceptAdmin(id: ID!): Boolean! acceptMembershipRequest(membershipRequestId: ID!): MembershipRequest! + addEventAttendee(data: EventAttendeeInput!): User! addLanguageTranslation(data: LanguageInput!): Language! addOrganizationImage(file: String!, organizationId: String!): Organization! addUserImage(file: String!): User! @@ -365,18 +403,20 @@ type Mutation { blockPluginCreationBySuperadmin(blockUser: Boolean!, userId: ID!): User! blockUser(organizationId: ID!, userId: ID!): User! cancelMembershipRequest(membershipRequestId: ID!): MembershipRequest! + checkIn(data: CheckInInput!): CheckIn! createAdmin(data: UserAndOrganizationInput!): User! createComment(data: CommentInput!, postId: ID!): Comment createDirectChat(data: createChatInput!): DirectChat! createDonation(amount: Float!, nameOfOrg: String!, nameOfUser: String!, orgId: ID!, payPalId: ID!, userId: ID!): Donation! createEvent(data: EventInput): Event! + createEventProject(data: EventProjectInput!): EventProject! createGroupChat(data: createGroupChatInput!): GroupChat! createMember(input: UserAndOrganizationInput!): Organization! createMessageChat(data: MessageChatInput!): MessageChat! createOrganization(data: OrganizationInput, file: String): Organization! createPlugin(installedOrgs: [ID!], pluginCreatedBy: String!, pluginDesc: String!, pluginInstallStatus: Boolean!, pluginName: String!): Plugin! createPost(data: PostInput!, file: String): Post - createTask(data: TaskInput, eventId: ID!): Task! + createTask(data: TaskInput!, eventProjectId: ID!): Task! createUserTag(input: CreateUserTagInput!): UserTag deleteDonationById(id: ID!): DeletePayload! forgotPassword(data: ForgotPasswordData!): Boolean! @@ -396,6 +436,8 @@ type Mutation { removeComment(id: ID!): Comment removeDirectChat(chatId: ID!, organizationId: ID!): DirectChat! removeEvent(id: ID!): Event! + removeEventAttendee(data: EventAttendeeInput!): User! + removeEventProject(id: ID!): EventProject! removeGroupChat(chatId: ID!): GroupChat! removeMember(data: UserAndOrganizationInput!): Organization! removeOrganization(id: ID!): User! @@ -410,6 +452,7 @@ type Mutation { sendMembershipRequest(organizationId: ID!): MembershipRequest! sendMessageToDirectChat(chatId: ID!, messageContent: String!): DirectChatMessage! sendMessageToGroupChat(chatId: ID!, messageContent: String!): GroupChatMessage! + setTaskVolunteers(id: ID!, volunteers: [ID]!): Task signUp(data: UserInput!, file: String): AuthData! togglePostPin(id: ID!): Post! unassignUserTag(input: ToggleUserTagAssignInput!): User @@ -418,12 +461,13 @@ type Mutation { unlikePost(id: ID!): Post unregisterForEventByUser(id: ID!): Event! updateEvent(data: UpdateEventInput, id: ID!): Event! + updateEventProject(data: UpdateEventProjectInput!, id: ID!): EventProject! updateLanguage(languageCode: String!): User! updateOrganization(data: UpdateOrganizationInput, file: String, id: ID!): Organization! updatePluginInstalledOrgs(id: ID!, orgId: ID!): Plugin! updatePluginStatus(id: ID!, status: Boolean!): Plugin! updatePost(data: PostUpdateInput, id: ID!): Post! - updateTask(data: UpdateTaskInput, id: ID!): Task + updateTask(data: UpdateTaskInput!, id: ID!): Task updateUserPassword(data: UpdateUserPasswordInput!): User! updateUserProfile(data: UpdateUserInput, file: String): User! updateUserTag(input: UpdateUserTagInput!): UserTag @@ -667,7 +711,6 @@ type Query { getDonationByOrgIdConnection(first: Int, orgId: ID!, skip: Int, where: DonationWhereInput): [Donation!]! getPlugins: [Plugin] getlanguage(lang_code: String!): [Translation] - isUserRegister(eventId: ID!): EventRegistrants joinedOrganizations(id: ID): [Organization] me: User! myLanguage: String @@ -680,8 +723,6 @@ type Query { postsByOrganizationConnection(first: Int, id: ID!, orderBy: PostOrderByInput, skip: Int, where: PostWhereInput): PostConnection registeredEventsByUser(id: ID, orderBy: EventOrderByInput): [Event] registrantsByEvent(id: ID!): [User] - tasksByEvent(id: ID!, orderBy: TaskOrderByInput): [Task] - tasksByUser(id: ID!, orderBy: TaskOrderByInput): [Task] user(id: ID!): User! userLanguage(userId: ID!): String users(orderBy: UserOrderByInput, where: UserWhereInput): [User] @@ -714,17 +755,19 @@ type Subscription { type Task { _id: ID! + completed: Boolean createdAt: DateTime! creator: User! deadline: DateTime description: String event: Event! title: String! + volunteers: [User] } input TaskInput { - deadline: DateTime - description: String + deadline: DateTime! + description: String! title: String! } @@ -787,6 +830,11 @@ input UpdateEventInput { title: String } +input UpdateEventProjectInput { + description: String + title: String +} + input UpdateOrganizationInput { description: String isPublic: Boolean @@ -796,6 +844,7 @@ input UpdateOrganizationInput { } input UpdateTaskInput { + completed: Boolean deadline: DateTime description: String title: String @@ -828,6 +877,7 @@ type User { adminApproved: Boolean adminFor: [Organization] appLanguageCode: String! + assignedTasks: [Task] createdAt: DateTime createdEvents: [Event] createdOrganizations: [Organization] @@ -852,14 +902,6 @@ input UserAndOrganizationInput { userId: ID! } -type UserAttende { - _id: ID! - createdAt: DateTime - status: Status! - user: User! - userId: String! -} - type UserConnection { aggregate: AggregateUser! edges: [User]! diff --git a/src/constants.ts b/src/constants.ts index b29dc0b32c..c412ba7049 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -134,6 +134,18 @@ export const USER_NOT_AUTHORIZED_ADMIN = { PARAM: "roleValidationAdmin", }; +export const USER_ALREADY_REGISTERED_FOR_EVENT = { + MESSAGE: "The user has already been registered for the event", + CODE: "user.alreadyRegistered", + PARAM: "user.alreadyRegistered", +}; + +export const USER_NOT_REGISTERED_FOR_EVENT = { + MESSAGE: "The user is not registered for the event", + CODE: "user.notRegistered", + PARAM: "user.notRegistered", +}; + export const USER_NOT_ORGANIZATION_ADMIN = { MESSAGE: "Error: User must be an ADMIN", CODE: "role.notValid.admin", @@ -295,12 +307,14 @@ export const INVALID_REFRESH_TOKEN_ERROR = { MESSAGE: "invalid.refreshToken", PARAM: "refreshToken", }; + export const USER_PROFILE_IMAGE_NOT_FOUND_ERROR = { DESC: "User profile image not found", CODE: "user.profileImage.notFound", MESSAGE: "user.profileImage.notFound", PARAM: "userProfileImage", }; + export const EMAIL_ALREADY_EXISTS_ERROR = { DESC: "Email already exists", CODE: "email.alreadyExists", @@ -308,6 +322,26 @@ export const EMAIL_ALREADY_EXISTS_ERROR = { PARAM: "email", }; +export const VOLUNTEER_NOT_FOUND_ERROR = { + DESC: "Volunteer not found", + CODE: "volunteer.notFound", + MESSAGE: "volunteer.notFound", + PARAM: "volunteers", +}; + +export const VOLUNTEER_NOT_MEMBER_ERROR = { + DESC: "Volunteer is not member of the organization.", + CODE: "volunteer.notMember", + MESSAGE: "volunteer.notMember", + PARAM: "volunteers", +}; + +export const USER_ALREADY_CHECKED_IN = { + MESSAGE: "The user has already been checked for this event.", + CODE: "user.alreadyCheckedIn", + PARAM: "user.alreadyCheckedIn", +}; + export const MAXIMUM_FETCH_LIMIT = 100; export const BASE_URL = `http://localhost:${process.env.port || 4000}/`; diff --git a/src/models/CheckIn.ts b/src/models/CheckIn.ts new file mode 100644 index 0000000000..419fa35f29 --- /dev/null +++ b/src/models/CheckIn.ts @@ -0,0 +1,53 @@ +import { + Schema, + model, + type PopulatedDoc, + type Types, + type Document, + models, + type Model, +} from "mongoose"; +import { type InterfaceEventAttendee } from "./EventAttendee"; + +export interface InterfaceCheckIn { + _id: Types.ObjectId; + eventAttendeeId: PopulatedDoc; + time: Date; + allotedRoom: string; + allotedSeat: string; +} + +const checkInSchema = new Schema({ + eventAttendeeId: { + type: Schema.Types.ObjectId, + ref: "EventAttendee", + required: true, + }, + time: { + type: Date, + required: true, + default: Date.now, + }, + allotedRoom: { + type: String, + required: false, + }, + allotedSeat: { + type: String, + required: false, + }, +}); + +// We will also create an index here for faster database querying +checkInSchema.index({ + eventAttendeeId: 1, +}); + +const checkInModel = (): Model => + model("CheckIn", checkInSchema); + +// This syntax is needed to prevent Mongoose OverwriteModelError while running tests. + +export const CheckIn = (models.CheckIn || checkInModel()) as ReturnType< + typeof checkInModel +>; diff --git a/src/models/Event.ts b/src/models/Event.ts index 1efe6d8e3a..df2efbb17f 100644 --- a/src/models/Event.ts +++ b/src/models/Event.ts @@ -1,45 +1,8 @@ import type { Types, PopulatedDoc, Document, Model } from "mongoose"; import { Schema, model, models } from "mongoose"; import type { InterfaceOrganization } from "./Organization"; -import type { InterfaceTask } from "./Task"; import type { InterfaceUser } from "./User"; -/** - * This is an interface representing a document for a user attendee in the database(MongoDB). - */ -export interface InterfaceUserAttende { - userId: string; - user: PopulatedDoc; - status: string; - createdAt: Date; -} -/** - * This is the Structure of the User Attendee - * @param userId - User-id - * @param user - User details - * @param status - whether the User is active, blocked, or delete - * @param createdAt - Creation Date - */ -const userAttendeSchema = new Schema({ - userId: { - type: String, - required: true, - }, - user: { - type: Schema.Types.ObjectId, - ref: "User", - required: true, - }, - status: { - type: String, - required: true, - enum: ["ACTIVE", "BLOCKED", "DELETED"], - default: "ACTIVE", - }, - createdAt: { - type: Date, - default: Date.now, - }, -}); + /** * This is an interface representing a document for an event in the database(MongoDB). */ @@ -61,12 +24,11 @@ export interface InterfaceEvent { isPublic: boolean; isRegisterable: boolean; creator: PopulatedDoc; - registrants: PopulatedDoc[]; admins: PopulatedDoc[]; organization: PopulatedDoc; - tasks: PopulatedDoc[]; status: string; } + /** * This is the Structure of the Event * @param title - Title of the event @@ -87,9 +49,9 @@ export interface InterfaceEvent { * @param creator - Creator of the event * @param admins - Admins * @param organization - Organization - * @param tasks - Tasks * @param status - whether the event is active, blocked, or deleted. */ + const eventSchema = new Schema({ title: { type: String, @@ -170,7 +132,6 @@ const eventSchema = new Schema({ ref: "User", required: true, }, - registrants: [userAttendeSchema], admins: [ { type: Schema.Types.ObjectId, @@ -183,12 +144,6 @@ const eventSchema = new Schema({ ref: "Organization", required: true, }, - tasks: [ - { - type: Schema.Types.ObjectId, - ref: "Task", - }, - ], status: { type: String, required: true, diff --git a/src/models/EventAttendee.ts b/src/models/EventAttendee.ts new file mode 100644 index 0000000000..0e60088f17 --- /dev/null +++ b/src/models/EventAttendee.ts @@ -0,0 +1,40 @@ +import type { PopulatedDoc, Document, Model } from "mongoose"; +import { Schema, model, models } from "mongoose"; +import type { InterfaceUser } from "./User"; +import type { InterfaceEvent } from "./Event"; +import type { InterfaceCheckIn } from "./CheckIn"; + +export interface InterfaceEventAttendee { + _id: Schema.Types.ObjectId; + userId: PopulatedDoc; + eventId: PopulatedDoc; + checkInId: PopulatedDoc | null; +} + +const eventAttendeeSchema = new Schema({ + userId: { + type: Schema.Types.ObjectId, + ref: "User", + required: true, + }, + eventId: { + type: Schema.Types.ObjectId, + ref: "Event", + required: true, + }, + checkInId: { + type: Schema.Types.ObjectId, + required: false, + default: null, + ref: "CheckIn", + }, +}); + +eventAttendeeSchema.index({ userId: 1, eventId: 1 }, { unique: true }); + +const eventAttendeeModel = (): Model => + model("EventAttendee", eventAttendeeSchema); + +// This syntax is needed to prevent Mongoose OverwriteModelError while running tests. +export const EventAttendee = (models.EventAttendee || + eventAttendeeModel()) as ReturnType; diff --git a/src/models/EventProject.ts b/src/models/EventProject.ts index 78321f0da4..be0d98f30b 100644 --- a/src/models/EventProject.ts +++ b/src/models/EventProject.ts @@ -1,8 +1,8 @@ import type { Types, PopulatedDoc, Document, Model } from "mongoose"; import { Schema, model, models } from "mongoose"; import type { InterfaceEvent } from "./Event"; -import type { InterfaceTask } from "./Task"; import type { InterfaceUser } from "./User"; + /** * This is an interface representing a document for an event project in the database(MongoDB). */ @@ -13,9 +13,9 @@ export interface InterfaceEventProject { createdAt: Date; event: PopulatedDoc; creator: PopulatedDoc; - tasks: PopulatedDoc[]; status: string; } + /** * This is the Structure of the event project * @param title - Title @@ -49,12 +49,6 @@ const eventProjectSchema = new Schema({ ref: "User", required: true, }, - tasks: [ - { - type: Schema.Types.ObjectId, - ref: "Task", - }, - ], status: { type: String, required: true, diff --git a/src/models/Task.ts b/src/models/Task.ts index f9b92a3df5..03640a6a03 100644 --- a/src/models/Task.ts +++ b/src/models/Task.ts @@ -1,10 +1,12 @@ import type { PopulatedDoc, Types, Document, Model } from "mongoose"; import { Schema, model, models } from "mongoose"; -import type { InterfaceEvent } from "./Event"; import type { InterfaceUser } from "./User"; +import type { InterfaceEventProject } from "./EventProject"; + /** * This is an interface that represents a database(MongoDB) document for Task. */ + export interface InterfaceTask { _id: Types.ObjectId; title: string; @@ -12,9 +14,11 @@ export interface InterfaceTask { status: string; createdAt: Date; deadline: Date | undefined; - event: PopulatedDoc; + eventProjectId: PopulatedDoc; creator: PopulatedDoc; + completed: boolean; } + /** * This describes the schema for a `Task` that corresponds to `InterfaceTask` document. * @param title - Task title. @@ -22,9 +26,11 @@ export interface InterfaceTask { * @param status - Status. * @param createdAt - Time stamp of data creation. * @param deadline - Task deadline. - * @param event - Event object for which task is added, refer to `Event` model. + * @param eventProjectId - Event Project object for which task is added. * @param creator - Task creator, refer to `User` model. + * @param completed - Has the task been completed */ + const taskSchema = new Schema({ title: { type: String, @@ -46,9 +52,9 @@ const taskSchema = new Schema({ deadline: { type: Date, }, - event: { + eventProjectId: { type: Schema.Types.ObjectId, - ref: "Event", + ref: "EventProject", required: true, }, creator: { @@ -56,6 +62,11 @@ const taskSchema = new Schema({ ref: "User", required: true, }, + completed: { + type: Boolean, + default: false, + required: true, + }, }); const taskModel = (): Model => diff --git a/src/models/TaskVolunteer.ts b/src/models/TaskVolunteer.ts new file mode 100644 index 0000000000..ea50986067 --- /dev/null +++ b/src/models/TaskVolunteer.ts @@ -0,0 +1,41 @@ +import type { Types, PopulatedDoc, Document, Model } from "mongoose"; +import { Schema, model, models } from "mongoose"; +import type { InterfaceTask } from "./Task"; +import type { InterfaceUser } from "./User"; + +/** + * This is an interface representing a document for an event in the database(MongoDB). + */ +export interface InterfaceTaskVolunteer { + _id: Types.ObjectId; + userId: PopulatedDoc; + taskId: PopulatedDoc; +} + +/** + * This is the Structure of the Task Volunteer + * @param userId - The user who was assgined the task + * @param taskId - The task which was assigned + */ + +const taskVolunteerSchema = new Schema({ + userId: { + type: Schema.Types.ObjectId, + ref: "User", + required: true, + }, + taskId: { + type: Schema.Types.ObjectId, + ref: "Task", + required: true, + }, +}); + +taskVolunteerSchema.index({ userId: 1, taskId: 1 }, { unique: true }); + +const taskVolunteerModel = (): Model => + model("TaskVolunteer", taskVolunteerSchema); + +// This syntax is needed to prevent Mongoose OverwriteModelError while running tests. +export const TaskVolunteer = (models.TaskVolunteer || + taskVolunteerModel()) as ReturnType; diff --git a/src/models/index.ts b/src/models/index.ts index 5c617fddac..fbf5e4ee1a 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -1,9 +1,11 @@ +export * from "./CheckIn"; export * from "./MessageChat"; export * from "./Comment"; export * from "./DirectChat"; export * from "./DirectChatMessage"; export * from "./Donation"; export * from "./Event"; +export * from "./EventAttendee"; export * from "./EventProject"; export * from "./File"; export * from "./Group"; @@ -19,5 +21,6 @@ export * from "./Plugin"; export * from "./PluginField"; export * from "./Post"; export * from "./Task"; +export * from "./TaskVolunteer"; export * from "./User"; export * from "./TagUser"; diff --git a/src/resolvers/CheckIn/event.ts b/src/resolvers/CheckIn/event.ts new file mode 100644 index 0000000000..b6a619a5ee --- /dev/null +++ b/src/resolvers/CheckIn/event.ts @@ -0,0 +1,12 @@ +import type { CheckInResolvers } from "../../types/generatedGraphQLTypes"; +import { EventAttendee } from "../../models"; + +export const event: CheckInResolvers["event"] = async (parent) => { + const attendeeObject = await EventAttendee.findOne({ + _id: parent.eventAttendeeId, + }) + .populate("eventId") + .lean(); + + return attendeeObject!.eventId; +}; diff --git a/src/resolvers/CheckIn/index.ts b/src/resolvers/CheckIn/index.ts new file mode 100644 index 0000000000..17a4a1285e --- /dev/null +++ b/src/resolvers/CheckIn/index.ts @@ -0,0 +1,8 @@ +import type { CheckInResolvers } from "../../types/generatedGraphQLTypes"; +import { event } from "./event"; +import { user } from "./user"; + +export const CheckIn: CheckInResolvers = { + event, + user, +}; diff --git a/src/resolvers/CheckIn/user.ts b/src/resolvers/CheckIn/user.ts new file mode 100644 index 0000000000..35d3b74db8 --- /dev/null +++ b/src/resolvers/CheckIn/user.ts @@ -0,0 +1,12 @@ +import type { CheckInResolvers } from "../../types/generatedGraphQLTypes"; +import { EventAttendee } from "../../models"; + +export const user: CheckInResolvers["user"] = async (parent) => { + const attendeeObject = await EventAttendee.findOne({ + _id: parent.eventAttendeeId, + }) + .populate("userId") + .lean(); + + return attendeeObject!.userId; +}; diff --git a/src/resolvers/Event/attendees.ts b/src/resolvers/Event/attendees.ts new file mode 100644 index 0000000000..ca500fc4fa --- /dev/null +++ b/src/resolvers/Event/attendees.ts @@ -0,0 +1,13 @@ +import type { EventResolvers } from "../../types/generatedGraphQLTypes"; +import { EventAttendee } from "../../models"; + +export const attendees: EventResolvers["attendees"] = async (parent) => { + const eventAttendeeObjects = await EventAttendee.find({ + eventId: parent._id, + }) + .populate("userId") + .lean(); + return eventAttendeeObjects.map( + (eventAttendeeObject) => eventAttendeeObject.userId + ); +}; diff --git a/src/resolvers/Event/attendeesCheckInStatus.ts b/src/resolvers/Event/attendeesCheckInStatus.ts new file mode 100644 index 0000000000..25ebb3f1d7 --- /dev/null +++ b/src/resolvers/Event/attendeesCheckInStatus.ts @@ -0,0 +1,18 @@ +import type { EventResolvers } from "../../types/generatedGraphQLTypes"; +import { EventAttendee } from "../../models"; + +export const attendeesCheckInStatus: EventResolvers["attendeesCheckInStatus"] = + async (parent) => { + const eventAttendeeObjects = await EventAttendee.find({ + eventId: parent._id, + }) + .populate("userId") + .populate("checkInId") + .lean(); + + return eventAttendeeObjects.map((obj) => ({ + user: obj.userId, + _id: obj._id.toString(), + checkIn: obj.checkInId, + })); + }; diff --git a/src/resolvers/Event/index.ts b/src/resolvers/Event/index.ts new file mode 100644 index 0000000000..bf07fb4e70 --- /dev/null +++ b/src/resolvers/Event/index.ts @@ -0,0 +1,12 @@ +import type { EventResolvers } from "../../types/generatedGraphQLTypes"; +import { attendees } from "./attendees"; +import { attendeesCheckInStatus } from "./attendeesCheckInStatus"; +import { organization } from "./organization"; +import { projects } from "./projects"; + +export const Event: EventResolvers = { + attendees, + attendeesCheckInStatus, + organization, + projects, +}; diff --git a/src/resolvers/Event/organization.ts b/src/resolvers/Event/organization.ts new file mode 100644 index 0000000000..1bc86dfc8e --- /dev/null +++ b/src/resolvers/Event/organization.ts @@ -0,0 +1,8 @@ +import type { EventResolvers } from "../../types/generatedGraphQLTypes"; +import { Organization } from "../../models"; + +export const organization: EventResolvers["organization"] = async (parent) => { + return Organization.findOne({ + _id: parent.organization, + }).lean(); +}; diff --git a/src/resolvers/Event/projects.ts b/src/resolvers/Event/projects.ts new file mode 100644 index 0000000000..297b569acc --- /dev/null +++ b/src/resolvers/Event/projects.ts @@ -0,0 +1,8 @@ +import type { EventResolvers } from "../../types/generatedGraphQLTypes"; +import { EventProject } from "../../models"; + +export const projects: EventResolvers["projects"] = async (parent) => { + return await EventProject.find({ + event: parent._id, + }).lean(); +}; diff --git a/src/resolvers/EventProject/index.ts b/src/resolvers/EventProject/index.ts new file mode 100644 index 0000000000..ca9192a0ee --- /dev/null +++ b/src/resolvers/EventProject/index.ts @@ -0,0 +1,6 @@ +import type { EventProjectResolvers } from "../../types/generatedGraphQLTypes"; +import { tasks } from "./tasks"; + +export const EventProject: EventProjectResolvers = { + tasks, +}; diff --git a/src/resolvers/EventProject/tasks.ts b/src/resolvers/EventProject/tasks.ts new file mode 100644 index 0000000000..5b5b980699 --- /dev/null +++ b/src/resolvers/EventProject/tasks.ts @@ -0,0 +1,8 @@ +import type { EventProjectResolvers } from "../../types/generatedGraphQLTypes"; +import { Task } from "../../models"; + +export const tasks: EventProjectResolvers["tasks"] = async (parent) => { + return await Task.find({ + eventProjectId: parent._id, + }).lean(); +}; diff --git a/src/resolvers/Mutation/addEventAttendee.ts b/src/resolvers/Mutation/addEventAttendee.ts new file mode 100644 index 0000000000..7cf5a632e6 --- /dev/null +++ b/src/resolvers/Mutation/addEventAttendee.ts @@ -0,0 +1,79 @@ +import { + EVENT_NOT_FOUND_ERROR, + USER_NOT_AUTHORIZED_ERROR, + USER_NOT_FOUND_ERROR, + USER_ALREADY_REGISTERED_FOR_EVENT, +} from "../../constants"; +import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; +import { errors, requestContext } from "../../libraries"; +import { User, Event, EventAttendee } from "../../models"; + +export const addEventAttendee: MutationResolvers["addEventAttendee"] = async ( + _parent, + args, + context +) => { + const currentUser = await User.findOne({ + _id: context.userId, + }); + + if (currentUser === null) { + throw new errors.NotFoundError( + requestContext.translate(USER_NOT_FOUND_ERROR.MESSAGE), + USER_NOT_FOUND_ERROR.CODE, + USER_NOT_FOUND_ERROR.PARAM + ); + } + + const currentEvent = await Event.findOne({ + _id: args.data.eventId, + }).lean(); + + if (currentEvent === null) { + throw new errors.NotFoundError( + requestContext.translate(EVENT_NOT_FOUND_ERROR.MESSAGE), + EVENT_NOT_FOUND_ERROR.CODE, + EVENT_NOT_FOUND_ERROR.PARAM + ); + } + + const isUserEventAdmin = currentEvent.admins.some( + (admin) => admin.toString() === context.userId.toString() + ); + + if (!isUserEventAdmin && currentUser.userType !== "SUPERADMIN") { + throw new errors.UnauthorizedError( + requestContext.translate(USER_NOT_AUTHORIZED_ERROR.MESSAGE), + USER_NOT_AUTHORIZED_ERROR.CODE, + USER_NOT_AUTHORIZED_ERROR.PARAM + ); + } + + const requestUser = await User.findOne({ + _id: args.data.userId, + }).lean(); + + if (requestUser === null) { + throw new errors.NotFoundError( + requestContext.translate(USER_NOT_FOUND_ERROR.MESSAGE), + USER_NOT_FOUND_ERROR.CODE, + USER_NOT_FOUND_ERROR.PARAM + ); + } + + const userAlreadyAttendee = await EventAttendee.exists({ + ...args.data, + }); + + if (userAlreadyAttendee) { + throw new errors.ConflictError( + requestContext.translate(USER_ALREADY_REGISTERED_FOR_EVENT.MESSAGE), + USER_ALREADY_REGISTERED_FOR_EVENT.CODE, + USER_ALREADY_REGISTERED_FOR_EVENT.PARAM + ); + } + + await EventAttendee.create({ ...args.data }); + + return requestUser; +}; diff --git a/src/resolvers/Mutation/checkIn.ts b/src/resolvers/Mutation/checkIn.ts new file mode 100644 index 0000000000..ba9ec8f47b --- /dev/null +++ b/src/resolvers/Mutation/checkIn.ts @@ -0,0 +1,103 @@ +import { + EVENT_NOT_FOUND_ERROR, + USER_NOT_AUTHORIZED_ERROR, + USER_NOT_FOUND_ERROR, + USER_NOT_REGISTERED_FOR_EVENT, + USER_ALREADY_CHECKED_IN, +} from "../../constants"; +import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; +import { errors, requestContext } from "../../libraries"; +import { User, Event, EventAttendee, CheckIn } from "../../models"; + +export const checkIn: MutationResolvers["checkIn"] = async ( + _parent, + args, + context +) => { + const currentUser = await User.findOne({ + _id: context.userId, + }); + + if (currentUser === null) { + throw new errors.NotFoundError( + requestContext.translate(USER_NOT_FOUND_ERROR.MESSAGE), + USER_NOT_FOUND_ERROR.CODE, + USER_NOT_FOUND_ERROR.PARAM + ); + } + + const currentEvent = await Event.findOne({ + _id: args.data.eventId, + }).lean(); + + if (currentEvent === null) { + throw new errors.NotFoundError( + requestContext.translate(EVENT_NOT_FOUND_ERROR.MESSAGE), + EVENT_NOT_FOUND_ERROR.CODE, + EVENT_NOT_FOUND_ERROR.PARAM + ); + } + + const isUserEventAdmin = currentEvent.admins.some( + (admin) => admin.toString() === context.userId.toString() + ); + + if (!isUserEventAdmin && currentUser.userType !== "SUPERADMIN") { + throw new errors.UnauthorizedError( + requestContext.translate(USER_NOT_AUTHORIZED_ERROR.MESSAGE), + USER_NOT_AUTHORIZED_ERROR.CODE, + USER_NOT_AUTHORIZED_ERROR.PARAM + ); + } + + const requestUser = await User.findOne({ + _id: args.data.userId, + }).lean(); + + if (requestUser === null) { + throw new errors.NotFoundError( + requestContext.translate(USER_NOT_FOUND_ERROR.MESSAGE), + USER_NOT_FOUND_ERROR.CODE, + USER_NOT_FOUND_ERROR.PARAM + ); + } + + const attendeeData = await EventAttendee.findOne({ + eventId: args.data.eventId, + userId: args.data.userId, + }); + + if (attendeeData === null) { + throw new errors.ConflictError( + requestContext.translate(USER_NOT_REGISTERED_FOR_EVENT.MESSAGE), + USER_NOT_REGISTERED_FOR_EVENT.CODE, + USER_NOT_REGISTERED_FOR_EVENT.PARAM + ); + } + + if (attendeeData.checkInId !== null) { + throw new errors.ConflictError( + requestContext.translate(USER_ALREADY_CHECKED_IN.MESSAGE), + USER_ALREADY_CHECKED_IN.CODE, + USER_ALREADY_CHECKED_IN.PARAM + ); + } + + const checkIn = await CheckIn.create({ + eventAttendeeId: attendeeData!._id, + allotedSeat: args.data.allotedSeat, + allotedRoom: args.data.allotedRoom, + }); + + await EventAttendee.updateOne( + { + eventId: args.data.eventId, + userId: args.data.userId, + }, + { + checkInId: checkIn._id, + } + ); + + return checkIn.toObject(); +}; diff --git a/src/resolvers/Mutation/createEvent.ts b/src/resolvers/Mutation/createEvent.ts index 62f793a0d0..30fdb2b3f6 100644 --- a/src/resolvers/Mutation/createEvent.ts +++ b/src/resolvers/Mutation/createEvent.ts @@ -11,6 +11,7 @@ import admin, { credential } from "firebase-admin"; import { getApps } from "firebase-admin/app"; import { isValidString } from "../../libraries/validators/validateString"; import { compareDates } from "../../libraries/validators/compareDates"; +import { EventAttendee } from "../../models/EventAttendee"; const applicationDefault = credential.applicationDefault; @@ -68,7 +69,13 @@ export const createEvent: MutationResolvers["createEvent"] = async ( ); // Checks whether currentUser neither created nor joined the organization. - if (!(userCreatedOrganization || userJoinedOrganization)) { + if ( + !( + userCreatedOrganization || + userJoinedOrganization || + currentUser.userType == "SUPERADMIN" + ) + ) { throw new errors.UnauthorizedError( requestContext.translate(ORGANIZATION_NOT_AUTHORIZED_ERROR.MESSAGE), ORGANIZATION_NOT_AUTHORIZED_ERROR.CODE, @@ -122,16 +129,15 @@ export const createEvent: MutationResolvers["createEvent"] = async ( const createdEvent = await Event.create({ ...args.data, creator: currentUser._id, - registrants: [ - { - userId: currentUser._id.toString(), - user: currentUser._id, - }, - ], admins: [currentUser._id], organization: organization._id, }); + await EventAttendee.create({ + userId: currentUser._id.toString(), + eventId: createdEvent._id, + }); + /* Adds createdEvent._id to eventAdmin, createdEvents and registeredEvents lists on currentUser's document. diff --git a/src/resolvers/Mutation/createEventProject.ts b/src/resolvers/Mutation/createEventProject.ts index 10fbb0167a..a3ed4e1351 100644 --- a/src/resolvers/Mutation/createEventProject.ts +++ b/src/resolvers/Mutation/createEventProject.ts @@ -1,3 +1,4 @@ +import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; import type { InterfaceEventProject } from "../../models"; import { User, EventProject, Event } from "../../models"; import { errors, requestContext } from "../../libraries"; @@ -6,6 +7,7 @@ import { USER_NOT_AUTHORIZED_ERROR, EVENT_NOT_FOUND_ERROR, } from "../../constants"; + /** * This function enables to create an event project. * @param _parent - parent of current request @@ -17,58 +19,59 @@ import { * 3. If the user is an admin of the event. * @returns Created event project */ -export const createEventProject = async ( - _parent: any, - args: any, - context: any -): Promise => { - const currentUserExists = await User.exists({ - _id: context.userId, - }); - // Checks whether currentUser with _id === context.userId exists. - if (currentUserExists === false) { - throw new errors.NotFoundError( - requestContext.translate(USER_NOT_FOUND_ERROR.MESSAGE), - USER_NOT_FOUND_ERROR.CODE, - USER_NOT_FOUND_ERROR.PARAM - ); - } +export const createEventProject: MutationResolvers["createEventProject"] = + async (_parent, args, context): Promise => { + const currentUser = await User.findOne({ + _id: context.userId, + }); - const event = await Event.findOne({ - _id: args.data.eventId, - }).lean(); + // Checks whether currentUser with _id === context.userId exists. + if (currentUser === null) { + throw new errors.NotFoundError( + requestContext.translate(USER_NOT_FOUND_ERROR.MESSAGE), + USER_NOT_FOUND_ERROR.CODE, + USER_NOT_FOUND_ERROR.PARAM + ); + } - // Checks whether event exists. - if (!event) { - throw new errors.NotFoundError( - requestContext.translate(EVENT_NOT_FOUND_ERROR.MESSAGE), - EVENT_NOT_FOUND_ERROR.CODE, - EVENT_NOT_FOUND_ERROR.PARAM - ); - } + const event = await Event.findOne({ + _id: args.data.eventId, + }).lean(); - const currentUserIsEventAdmin = event.admins.some((admin) => - admin.equals(context.userId) - ); + // Checks whether event exists. + if (!event) { + throw new errors.NotFoundError( + requestContext.translate(EVENT_NOT_FOUND_ERROR.MESSAGE), + EVENT_NOT_FOUND_ERROR.CODE, + EVENT_NOT_FOUND_ERROR.PARAM + ); + } - // Checks whether currentUser with _id === context.userId is an admin of event. - if (currentUserIsEventAdmin === false) { - throw new errors.UnauthorizedError( - requestContext.translate(USER_NOT_AUTHORIZED_ERROR.MESSAGE), - USER_NOT_AUTHORIZED_ERROR.CODE, - USER_NOT_AUTHORIZED_ERROR.PARAM + const currentUserIsEventAdmin = event.admins.some((admin) => + admin.equals(context.userId) ); - } - // Creates new eventProject. - const createdEventProject = await EventProject.create({ - title: args.data.title, - description: args.data.description, - event: args.data.eventId, - creator: context.userId, - }); + // Checks whether currentUser with _id === context.userId is an admin of event. + if ( + currentUserIsEventAdmin === false && + currentUser.userType !== "SUPERADMIN" + ) { + throw new errors.UnauthorizedError( + requestContext.translate(USER_NOT_AUTHORIZED_ERROR.MESSAGE), + USER_NOT_AUTHORIZED_ERROR.CODE, + USER_NOT_AUTHORIZED_ERROR.PARAM + ); + } + + // Creates new eventProject. + const createdEventProject = await EventProject.create({ + title: args.data.title, + description: args.data.description, + event: args.data.eventId, + creator: context.userId, + }); - // Returns createdEventProject. - return createdEventProject.toObject(); -}; + // Returns createdEventProject. + return createdEventProject.toObject(); + }; diff --git a/src/resolvers/Mutation/createTask.ts b/src/resolvers/Mutation/createTask.ts index 6d74704cc5..2cdfe17402 100644 --- a/src/resolvers/Mutation/createTask.ts +++ b/src/resolvers/Mutation/createTask.ts @@ -1,7 +1,11 @@ import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; -import { User, Task, Event } from "../../models"; +import { User, Task, EventProject } from "../../models"; import { errors, requestContext } from "../../libraries"; -import { USER_NOT_FOUND_ERROR, EVENT_NOT_FOUND_ERROR } from "../../constants"; +import { + USER_NOT_FOUND_ERROR, + EVENT_NOT_FOUND_ERROR, + USER_NOT_AUTHORIZED_ERROR, +} from "../../constants"; /** * This function enables to create a task. * @param _parent - parent of current request @@ -12,17 +16,18 @@ import { USER_NOT_FOUND_ERROR, EVENT_NOT_FOUND_ERROR } from "../../constants"; * 2. If the event exists * @returns Created task */ + export const createTask: MutationResolvers["createTask"] = async ( _parent, args, context ) => { - const currentUserExists = await User.exists({ + const currentUser = await User.findOne({ _id: context.userId, }); // Checks whether currentUser with _id == context.userId exists. - if (currentUserExists === false) { + if (currentUser === null) { throw new errors.NotFoundError( requestContext.translate(USER_NOT_FOUND_ERROR.MESSAGE), USER_NOT_FOUND_ERROR.CODE, @@ -30,12 +35,12 @@ export const createTask: MutationResolvers["createTask"] = async ( ); } - const eventExists = await Event.exists({ - _id: args.eventId, + const eventProject = await EventProject.findOne({ + _id: args.eventProjectId, }); // Checks whether event with _id == args.eventId exists. - if (eventExists === false) { + if (eventProject === null) { throw new errors.NotFoundError( requestContext.translate(EVENT_NOT_FOUND_ERROR.MESSAGE), EVENT_NOT_FOUND_ERROR.CODE, @@ -43,25 +48,23 @@ export const createTask: MutationResolvers["createTask"] = async ( ); } + if ( + eventProject.creator.toString() != currentUser._id.toString() && + currentUser.userType !== "SUPERADMIN" + ) { + throw new errors.NotFoundError( + requestContext.translate(USER_NOT_AUTHORIZED_ERROR.MESSAGE), + USER_NOT_AUTHORIZED_ERROR.CODE, + USER_NOT_AUTHORIZED_ERROR.PARAM + ); + } // Creates new task. const createdTask = await Task.create({ ...args.data, - event: args.eventId, + eventProjectId: args.eventProjectId, creator: context.userId, }); - // Adds createdTask._id to tasks list on event's document with _id === args.eventId. - await Event.updateOne( - { - _id: args.eventId, - }, - { - $push: { - tasks: createdTask._id, - }, - } - ); - // Returns createdTask. return createdTask.toObject(); }; diff --git a/src/resolvers/Mutation/index.ts b/src/resolvers/Mutation/index.ts index 6852d6e059..3656b34d4b 100644 --- a/src/resolvers/Mutation/index.ts +++ b/src/resolvers/Mutation/index.ts @@ -1,6 +1,7 @@ import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; import { acceptAdmin } from "./acceptAdmin"; import { acceptMembershipRequest } from "./acceptMembershipRequest"; +import { addEventAttendee } from "./addEventAttendee"; import { addLanguageTranslation } from "./addLanguageTranslation"; import { addOrganizationImage } from "./addOrganizationImage"; import { addUserImage } from "./addUserImage"; @@ -11,12 +12,14 @@ import { assignUserTag } from "./assignUserTag"; import { blockPluginCreationBySuperadmin } from "./blockPluginCreationBySuperadmin"; import { blockUser } from "./blockUser"; import { cancelMembershipRequest } from "./cancelMembershipRequest"; +import { checkIn } from "./checkIn"; import { createMember } from "./createMember"; import { createAdmin } from "./createAdmin"; import { createComment } from "./createComment"; import { createDirectChat } from "./createDirectChat"; import { createDonation } from "./createDonation"; import { createEvent } from "./createEvent"; +import { createEventProject } from "./createEventProject"; import { createGroupChat } from "./createGroupChat"; import { createMessageChat } from "./createMessageChat"; import { createOrganization } from "./createOrganization"; @@ -42,6 +45,8 @@ import { removeAdmin } from "./removeAdmin"; import { removeComment } from "./removeComment"; import { removeDirectChat } from "./removeDirectChat"; import { removeEvent } from "./removeEvent"; +import { removeEventAttendee } from "./removeEventAttendee"; +import { removeEventProject } from "./removeEventProject"; import { removeGroupChat } from "./removeGroupChat"; import { removeMember } from "./removeMember"; import { removeOrganization } from "./removeOrganization"; @@ -56,6 +61,7 @@ import { saveFcmToken } from "./saveFcmToken"; import { sendMembershipRequest } from "./sendMembershipRequest"; import { sendMessageToDirectChat } from "./sendMessageToDirectChat"; import { sendMessageToGroupChat } from "./sendMessageToGroupChat"; +import { setTaskVolunteers } from "./setTaskVolunteers"; import { signUp } from "./signUp"; import { togglePostPin } from "./togglePostPin"; import { unassignUserTag } from "./unassignUserTag"; @@ -64,6 +70,7 @@ import { unlikeComment } from "./unlikeComment"; import { unlikePost } from "./unlikePost"; import { unregisterForEventByUser } from "./unregisterForEventByUser"; import { updateEvent } from "./updateEvent"; +import { updateEventProject } from "./updateEventProject"; import { updateLanguage } from "./updateLanguage"; import { updateOrganization } from "./updateOrganization"; import { updatePluginInstalledOrgs } from "./updatePluginInstalledOrgs"; @@ -78,6 +85,7 @@ import { updateUserType } from "./updateUserType"; export const Mutation: MutationResolvers = { acceptAdmin, acceptMembershipRequest, + addEventAttendee, addLanguageTranslation, addOrganizationImage, addUserImage, @@ -88,12 +96,14 @@ export const Mutation: MutationResolvers = { blockPluginCreationBySuperadmin, blockUser, cancelMembershipRequest, + checkIn, createMember, createAdmin, createComment, createDirectChat, createDonation, createEvent, + createEventProject, createGroupChat, createMessageChat, createOrganization, @@ -119,6 +129,8 @@ export const Mutation: MutationResolvers = { removeComment, removeDirectChat, removeEvent, + removeEventAttendee, + removeEventProject, removeGroupChat, removeMember, removeOrganization, @@ -133,6 +145,7 @@ export const Mutation: MutationResolvers = { sendMembershipRequest, sendMessageToDirectChat, sendMessageToGroupChat, + setTaskVolunteers, signUp, togglePostPin, unassignUserTag, @@ -141,6 +154,7 @@ export const Mutation: MutationResolvers = { unlikePost, unregisterForEventByUser, updateEvent, + updateEventProject, updateLanguage, updateOrganization, updatePluginInstalledOrgs, diff --git a/src/resolvers/Mutation/registerForEvent.ts b/src/resolvers/Mutation/registerForEvent.ts index f556bc03cf..fecfcd3a92 100644 --- a/src/resolvers/Mutation/registerForEvent.ts +++ b/src/resolvers/Mutation/registerForEvent.ts @@ -1,11 +1,12 @@ import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; import { errors, requestContext } from "../../libraries"; -import { User, Event } from "../../models"; +import { User, Event, EventAttendee } from "../../models"; import { USER_NOT_FOUND_ERROR, EVENT_NOT_FOUND_ERROR, REGISTRANT_ALREADY_EXIST_ERROR, } from "../../constants"; + /** * This function enables to register for event. * @param _parent - parent of current request @@ -17,6 +18,7 @@ import { * 2. If the user has already registered for the event * @returns Updated event. */ + export const registerForEvent: MutationResolvers["registerForEvent"] = async ( _parent, args, @@ -48,86 +50,35 @@ export const registerForEvent: MutationResolvers["registerForEvent"] = async ( ); } - const index = event.registrants.findIndex((registrant) => { - return registrant.userId.toString() === context.userId.toString(); + const currentUserIsEventRegistrant = await EventAttendee.exists({ + userId: context.userId, + eventId: args.id, }); - let currentUserIsEventRegistrant = false; - - // Checks whether currentUser with _id === context.userId is already a registrant for event. - if (index !== -1) { - if (event.registrants[index].status === "ACTIVE") { - throw new errors.NotFoundError( - requestContext.translate(REGISTRANT_ALREADY_EXIST_ERROR.MESSAGE), - REGISTRANT_ALREADY_EXIST_ERROR.CODE, - REGISTRANT_ALREADY_EXIST_ERROR.PARAM - ); - } else { - currentUserIsEventRegistrant = true; - } - } - - // Checks whether currentUser with _id === context.userId is not registrant of event. - if (currentUserIsEventRegistrant === false) { - // Adds event._id to registeredEvents list of currentUser with _id === context.userId. - await User.updateOne( - { - _id: context.userId, - }, - { - $push: { - registeredEvents: event._id, - }, - } + if (currentUserIsEventRegistrant) { + throw new errors.InputValidationError( + requestContext.translate(REGISTRANT_ALREADY_EXIST_ERROR.MESSAGE), + REGISTRANT_ALREADY_EXIST_ERROR.CODE, + REGISTRANT_ALREADY_EXIST_ERROR.PARAM ); + } - /* - Adds currentUser with _id === context.userId new registrant to registrants - list of event and returns the updated event. - */ - return await Event.findOneAndUpdate( - { - _id: event._id, - status: "ACTIVE", - }, - { - $push: { - registrants: { - userId: context.userId, - user: context.userId, - }, - }, + // Adds event._id to registeredEvents list of currentUser with _id === context.userId. + await User.updateOne( + { + _id: context.userId, + }, + { + $push: { + registeredEvents: event._id, }, - { - new: true, - } - ).lean(); - } else { - const updatedRegistrants = event.registrants; + } + ); - // Sets registrant.status for user with _id === context.userId of event to ACTIVE. - updatedRegistrants[index] = { - id: updatedRegistrants[index].id, - userId: updatedRegistrants[index].userId, - user: updatedRegistrants[index].user, - status: "ACTIVE", - createdAt: updatedRegistrants[index].createdAt, - }; + await EventAttendee.create({ + userId: context.userId, + eventId: args.id, + }); - // Sets updatedRegistrants as registrants list of event and returns the updated event. - return await Event.findOneAndUpdate( - { - _id: event._id, - status: "ACTIVE", - }, - { - $set: { - registrants: updatedRegistrants, - }, - }, - { - new: true, - } - ).lean(); - } + return event; }; diff --git a/src/resolvers/Mutation/removeEvent.ts b/src/resolvers/Mutation/removeEvent.ts index 3e40824c45..26d4d2c732 100644 --- a/src/resolvers/Mutation/removeEvent.ts +++ b/src/resolvers/Mutation/removeEvent.ts @@ -1,6 +1,6 @@ import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; import { errors, requestContext } from "../../libraries"; -import { User, Event } from "../../models"; +import { User, Event, EventProject, Task, TaskVolunteer } from "../../models"; import { USER_NOT_FOUND_ERROR, EVENT_NOT_FOUND_ERROR, @@ -60,7 +60,13 @@ export const removeEvent: MutationResolvers["removeEvent"] = async ( ); // Checks whether currentUser cannot delete event. - if (!(currentUserIsOrganizationAdmin || currentUserIsEventAdmin)) { + if ( + !( + currentUserIsOrganizationAdmin || + currentUserIsEventAdmin || + currentUser.userType === "SUPERADMIN" + ) + ) { throw new errors.UnauthorizedError( requestContext.translate(USER_NOT_AUTHORIZED_ERROR.MESSAGE), USER_NOT_AUTHORIZED_ERROR.CODE, @@ -99,5 +105,43 @@ export const removeEvent: MutationResolvers["removeEvent"] = async ( } ); + // Fetch and delete all the event projects under the particular event + const eventProjects = await EventProject.find( + { + event: event._id, + }, + { + _id: 1, + } + ).lean(); + const eventProjectIds = eventProjects.map((project) => project._id); + await EventProject.deleteMany({ + event: event._id, + }); + + // Fetch and delete all the event tasks indirectly under the particular event + const eventTasks = await Task.find( + { + eventProjectId: { + $in: eventProjectIds, + }, + }, + { + _id: 1, + } + ).lean(); + const taskIds = eventTasks.map((task) => task._id); + await Task.deleteMany({ + eventProjectId: { + $in: eventProjectIds, + }, + }); + + // Delete all the task volunteer entries indirectly under the particular event + await TaskVolunteer.deleteMany({ + taskId: { + $in: taskIds, + }, + }); return event; }; diff --git a/src/resolvers/Mutation/removeEventAttendee.ts b/src/resolvers/Mutation/removeEventAttendee.ts new file mode 100644 index 0000000000..4fc2b726c5 --- /dev/null +++ b/src/resolvers/Mutation/removeEventAttendee.ts @@ -0,0 +1,76 @@ +import { + EVENT_NOT_FOUND_ERROR, + USER_NOT_AUTHORIZED_ERROR, + USER_NOT_FOUND_ERROR, + USER_NOT_REGISTERED_FOR_EVENT, +} from "../../constants"; +import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; +import { errors, requestContext } from "../../libraries"; +import { User, Event, EventAttendee } from "../../models"; + +export const removeEventAttendee: MutationResolvers["removeEventAttendee"] = + async (_parent, args, context) => { + const currentUser = await User.findOne({ + _id: context.userId, + }); + + if (currentUser === null) { + throw new errors.NotFoundError( + requestContext.translate(USER_NOT_FOUND_ERROR.MESSAGE), + USER_NOT_FOUND_ERROR.CODE, + USER_NOT_FOUND_ERROR.PARAM + ); + } + + const currentEvent = await Event.findOne({ + _id: args.data.eventId, + }).lean(); + + if (currentEvent === null) { + throw new errors.NotFoundError( + requestContext.translate(EVENT_NOT_FOUND_ERROR.MESSAGE), + EVENT_NOT_FOUND_ERROR.CODE, + EVENT_NOT_FOUND_ERROR.PARAM + ); + } + + const isUserEventAdmin = currentEvent.admins.some( + (admin) => admin.toString() === context.userId.toString() + ); + + if (!isUserEventAdmin && currentUser.userType !== "SUPERADMIN") { + throw new errors.UnauthorizedError( + requestContext.translate(USER_NOT_AUTHORIZED_ERROR.MESSAGE), + USER_NOT_AUTHORIZED_ERROR.CODE, + USER_NOT_AUTHORIZED_ERROR.PARAM + ); + } + + const requestUser = await User.findOne({ + _id: args.data.userId, + }).lean(); + + if (requestUser === null) { + throw new errors.NotFoundError( + requestContext.translate(USER_NOT_FOUND_ERROR.MESSAGE), + USER_NOT_FOUND_ERROR.CODE, + USER_NOT_FOUND_ERROR.PARAM + ); + } + + const userAlreadyAttendee = await EventAttendee.exists({ + ...args.data, + }); + + if (!userAlreadyAttendee) { + throw new errors.ConflictError( + requestContext.translate(USER_NOT_REGISTERED_FOR_EVENT.MESSAGE), + USER_NOT_REGISTERED_FOR_EVENT.CODE, + USER_NOT_REGISTERED_FOR_EVENT.PARAM + ); + } + + await EventAttendee.deleteOne({ ...args.data }); + + return requestUser; + }; diff --git a/src/resolvers/Mutation/removeEventProject.ts b/src/resolvers/Mutation/removeEventProject.ts index 8ec3c3c823..14c8a933bd 100644 --- a/src/resolvers/Mutation/removeEventProject.ts +++ b/src/resolvers/Mutation/removeEventProject.ts @@ -1,11 +1,13 @@ +import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; import type { InterfaceEventProject } from "../../models"; -import { User, EventProject } from "../../models"; +import { User, EventProject, Task, TaskVolunteer } from "../../models"; import { errors, requestContext } from "../../libraries"; import { USER_NOT_FOUND_ERROR, EVENT_PROJECT_NOT_FOUND_ERROR, USER_NOT_AUTHORIZED_ERROR, } from "../../constants"; + /** * This function enables to remove an event project. * @param _parent - parent of current request @@ -17,49 +19,70 @@ import { * 3. If the user is the creator of the event project. * @returns Deleted event project. */ -export const removeEventProject = async ( - _parent: any, - args: any, - context: any -): Promise => { - const currentUserExists = await User.exists({ - _id: context.userId, - }); - // Checks if currentUser with _id === context.userId exists. - if (currentUserExists === false) { - throw new errors.NotFoundError( - requestContext.translate(USER_NOT_FOUND_ERROR.MESSAGE), - USER_NOT_FOUND_ERROR.CODE, - USER_NOT_FOUND_ERROR.PARAM - ); - } +export const removeEventProject: MutationResolvers["removeEventProject"] = + async (_parent, args, context): Promise => { + const currentUser = await User.findOne({ + _id: context.userId, + }); + + // Checks if currentUser with _id === context.userId exists. + if (currentUser === null) { + throw new errors.NotFoundError( + requestContext.translate(USER_NOT_FOUND_ERROR.MESSAGE), + USER_NOT_FOUND_ERROR.CODE, + USER_NOT_FOUND_ERROR.PARAM + ); + } + + const eventProject = await EventProject.findOne({ + _id: args.id, + }).lean(); + + // Checks whether eventProject exists. + if (!eventProject) { + throw new errors.NotFoundError( + requestContext.translate(EVENT_PROJECT_NOT_FOUND_ERROR.MESSAGE), + EVENT_PROJECT_NOT_FOUND_ERROR.CODE, + EVENT_PROJECT_NOT_FOUND_ERROR.PARAM + ); + } - const eventProject = await EventProject.findOne({ - _id: args.id, - }).lean(); + // Checks whether currentUser with _id === context.userId is not the creator of eventProject. + if ( + !eventProject.creator.equals(context.userId) && + currentUser.userType !== "SUPERADMIN" + ) { + throw new errors.UnauthorizedError( + requestContext.translate(USER_NOT_AUTHORIZED_ERROR.MESSAGE), + USER_NOT_AUTHORIZED_ERROR.CODE, + USER_NOT_AUTHORIZED_ERROR.PARAM + ); + } - // Checks whether eventProject exists. - if (!eventProject) { - throw new errors.NotFoundError( - requestContext.translate(EVENT_PROJECT_NOT_FOUND_ERROR.MESSAGE), - EVENT_PROJECT_NOT_FOUND_ERROR.CODE, - EVENT_PROJECT_NOT_FOUND_ERROR.PARAM - ); - } + await EventProject.deleteOne({ + _id: args.id, + }); - // Checks whether currentUser with _id === context.userId is not the creator of eventProject. - if (!eventProject.creator.equals(context.userId)) { - throw new errors.UnauthorizedError( - requestContext.translate(USER_NOT_AUTHORIZED_ERROR.MESSAGE), - USER_NOT_AUTHORIZED_ERROR.CODE, - USER_NOT_AUTHORIZED_ERROR.PARAM - ); - } + // Fetch all the tasks associated with the project + const tasks = await Task.find( + { + eventProjectId: args.id, + }, + { + _id: 1, + } + ).lean(); + const taskIds = tasks.map((task) => task._id); - await EventProject.deleteOne({ - _id: args.id, - }); + await Task.deleteMany({ + eventProjectId: args.id, + }); - return eventProject; -}; + await TaskVolunteer.deleteMany({ + taskId: { + $in: taskIds, + }, + }); + return eventProject; + }; diff --git a/src/resolvers/Mutation/removeTask.ts b/src/resolvers/Mutation/removeTask.ts index d5772b053c..c640c1339b 100644 --- a/src/resolvers/Mutation/removeTask.ts +++ b/src/resolvers/Mutation/removeTask.ts @@ -5,7 +5,7 @@ import { } from "../../constants"; import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; import { errors, requestContext } from "../../libraries"; -import { User, Task, Event } from "../../models"; +import { User, Task, TaskVolunteer } from "../../models"; /** * This function enables to remove a task. * @param _parent - parent of current request @@ -22,12 +22,12 @@ export const removeTask: MutationResolvers["removeTask"] = async ( args, context ) => { - const currentUserExists = await User.exists({ + const currentUser = await User.findOne({ _id: context.userId, }); // Checks whether currentUser with _id === context.userId exists. - if (currentUserExists === false) { + if (currentUser === null) { throw new errors.NotFoundError( requestContext.translate(USER_NOT_FOUND_ERROR.MESSAGE), USER_NOT_FOUND_ERROR.CODE, @@ -49,7 +49,10 @@ export const removeTask: MutationResolvers["removeTask"] = async ( } // Checks whether currentUser with _id === context.userId is not the creator of task. - if (!task.creator.equals(context.userId)) { + if ( + !task.creator.equals(context.userId) && + currentUser.userType !== "SUPERADMIN" + ) { throw new errors.UnauthorizedError( requestContext.translate(USER_NOT_AUTHORIZED_ERROR.MESSAGE), USER_NOT_AUTHORIZED_ERROR.CODE, @@ -62,17 +65,9 @@ export const removeTask: MutationResolvers["removeTask"] = async ( _id: task._id, }); - // Removes task._id from tasks list of task.event. - await Event.updateMany( - { - _id: task.event, - }, - { - $pull: { - tasks: task._id, - }, - } - ); + await TaskVolunteer.deleteMany({ + taskId: task._id, + }); // Returns deleted task. return task; diff --git a/src/resolvers/Mutation/setTaskVolunteers.ts b/src/resolvers/Mutation/setTaskVolunteers.ts new file mode 100644 index 0000000000..96e152d3e9 --- /dev/null +++ b/src/resolvers/Mutation/setTaskVolunteers.ts @@ -0,0 +1,117 @@ +import { + TASK_NOT_FOUND_ERROR, + USER_NOT_AUTHORIZED_ERROR, + USER_NOT_FOUND_ERROR, + VOLUNTEER_NOT_FOUND_ERROR, + VOLUNTEER_NOT_MEMBER_ERROR, +} from "../../constants"; +import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; +import { errors, requestContext } from "../../libraries"; +import { User, Task, TaskVolunteer } from "../../models"; +import { type ObjectId } from "mongoose"; + +const verifyUser = async ( + userId: string, + memberIds: string[] +): Promise => { + const user = await User.findOne({ + _id: userId, + }); + + if (user === null) { + throw new errors.NotFoundError( + requestContext.translate(VOLUNTEER_NOT_FOUND_ERROR.MESSAGE), + VOLUNTEER_NOT_FOUND_ERROR.CODE, + VOLUNTEER_NOT_FOUND_ERROR.PARAM + ); + } + + const isOrgMember = memberIds.some( + (memberId) => memberId.toString() == userId.toString() + ); + + if (!isOrgMember) { + throw new errors.NotFoundError( + requestContext.translate(VOLUNTEER_NOT_MEMBER_ERROR.MESSAGE), + VOLUNTEER_NOT_MEMBER_ERROR.CODE, + VOLUNTEER_NOT_MEMBER_ERROR.PARAM + ); + } +}; + +export const setTaskVolunteers: MutationResolvers["setTaskVolunteers"] = async ( + _parent, + args, + context +) => { + const currentUser = await User.findOne({ + _id: context.userId, + }); + + if (currentUser === null) { + throw new errors.NotFoundError( + requestContext.translate(USER_NOT_FOUND_ERROR.MESSAGE), + USER_NOT_FOUND_ERROR.CODE, + USER_NOT_FOUND_ERROR.PARAM + ); + } + + const task = await Task.findOne({ + _id: args.id, + }) + .populate({ + path: "eventProjectId", + populate: { + path: "event", + populate: { + path: "organization", + }, + }, + }) + .lean(); + + if (!task) { + throw new errors.NotFoundError( + requestContext.translate(TASK_NOT_FOUND_ERROR.MESSAGE), + TASK_NOT_FOUND_ERROR.CODE, + TASK_NOT_FOUND_ERROR.PARAM + ); + } + + if ( + task.creator.toString() !== context.userId.toString() && + currentUser.userType !== "SUPERADMIN" + ) { + throw new errors.UnauthorizedError( + requestContext.translate(USER_NOT_AUTHORIZED_ERROR.MESSAGE), + USER_NOT_AUTHORIZED_ERROR.CODE, + USER_NOT_AUTHORIZED_ERROR.PARAM + ); + } + + // Filter the volunteer list to only contain unique ids + const volunteerIds = Array.from(new Set(args.volunteers)); + + const orgMemberIds = task.eventProjectId.event.organization.members.map( + (member: ObjectId) => member.toString() + ); + + // Verify that each of the volunteer id exist and they are an organization member + for (let i = 0; i < volunteerIds.length; i++) + await verifyUser(volunteerIds[i]!, orgMemberIds); + + // Delete all the exisiting volunteers + await TaskVolunteer.deleteMany({ + taskId: args.id, + }); + + // Add the new volunteers + await TaskVolunteer.create( + volunteerIds.map((volunteerId) => ({ + taskId: args.id, + userId: volunteerId, + })) + ); + + return task; +}; diff --git a/src/resolvers/Mutation/unregisterForEventByUser.ts b/src/resolvers/Mutation/unregisterForEventByUser.ts index 10e93ce57a..007fdaaf13 100644 --- a/src/resolvers/Mutation/unregisterForEventByUser.ts +++ b/src/resolvers/Mutation/unregisterForEventByUser.ts @@ -1,11 +1,12 @@ import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; import { errors, requestContext } from "../../libraries"; -import { User, Event } from "../../models"; +import { User, Event, EventAttendee } from "../../models"; import { USER_NOT_FOUND_ERROR, EVENT_NOT_FOUND_ERROR, USER_ALREADY_UNREGISTERED_ERROR, } from "../../constants"; + /** * This function enables a user to unregister from an event. * @param _parent - parent of current request @@ -17,6 +18,7 @@ import { * 3. If the user is a registrant of the event. * @returns Updated event. */ + export const unregisterForEventByUser: MutationResolvers["unregisterForEventByUser"] = async (_parent, args, context) => { const currentUserExists = await User.exists({ @@ -45,49 +47,23 @@ export const unregisterForEventByUser: MutationResolvers["unregisterForEventByUs ); } - // gets position(index) of current user's _id in the registrants list of event - const index = event.registrants.findIndex((element) => { - return String(element.userId) === String(context.userId); + const userRegisteredForEvent = await EventAttendee.exists({ + userId: context.userId, + eventId: args.id, }); - // checks if current user is a registrant of event - if (index === -1) { - throw new errors.NotFoundError( - requestContext.translate(USER_NOT_FOUND_ERROR.MESSAGE), - USER_NOT_FOUND_ERROR.CODE, - USER_NOT_FOUND_ERROR.PARAM - ); - } - - if (event.registrants[index].status === "ACTIVE") { - const updatedRegistrants = event.registrants; - updatedRegistrants[index] = { - id: updatedRegistrants[index].id, - userId: updatedRegistrants[index].userId, - user: updatedRegistrants[index].user, - status: "DELETED", - createdAt: updatedRegistrants[index].createdAt, - }; - - return await Event.findOneAndUpdate( - { - _id: args.id, - status: "ACTIVE", - }, - { - $set: { - registrants: updatedRegistrants, - }, - }, - { - new: true, - } - ).lean(); - } else { + if (!userRegisteredForEvent) { throw new errors.NotFoundError( requestContext.translate(USER_ALREADY_UNREGISTERED_ERROR.MESSAGE), USER_ALREADY_UNREGISTERED_ERROR.CODE, USER_ALREADY_UNREGISTERED_ERROR.PARAM ); } + + await EventAttendee.deleteOne({ + userId: context.userId, + eventId: args.id, + }); + + return event; }; diff --git a/src/resolvers/Mutation/updateEvent.ts b/src/resolvers/Mutation/updateEvent.ts index 7636ce1ebe..31233ccce0 100644 --- a/src/resolvers/Mutation/updateEvent.ts +++ b/src/resolvers/Mutation/updateEvent.ts @@ -24,12 +24,12 @@ export const updateEvent: MutationResolvers["updateEvent"] = async ( args, context ) => { - const currentUserExists = await User.exists({ + const currentUser = await User.findOne({ _id: context.userId, }); // checks if current user exists - if (currentUserExists === false) { + if (currentUser === null) { throw new errors.NotFoundError( requestContext.translate(USER_NOT_FOUND_ERROR.MESSAGE), USER_NOT_FOUND_ERROR.CODE, @@ -55,7 +55,10 @@ export const updateEvent: MutationResolvers["updateEvent"] = async ( ); // checks if current user is an admin of the event with _id === args.id - if (currentUserIsEventAdmin === false) { + if ( + currentUserIsEventAdmin === false && + currentUser.userType !== "SUPERADMIN" + ) { throw new errors.UnauthorizedError( requestContext.translate(USER_NOT_AUTHORIZED_ERROR.MESSAGE), USER_NOT_AUTHORIZED_ERROR.CODE, diff --git a/src/resolvers/Mutation/updateEventProject.ts b/src/resolvers/Mutation/updateEventProject.ts index 8a22d3fcee..3983b3c22a 100644 --- a/src/resolvers/Mutation/updateEventProject.ts +++ b/src/resolvers/Mutation/updateEventProject.ts @@ -1,4 +1,5 @@ import type { InterfaceEventProject } from "../../models"; +import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; import { User, EventProject } from "../../models"; import { errors, requestContext } from "../../libraries"; import { @@ -7,6 +8,7 @@ import { USER_NOT_AUTHORIZED_ERROR, USER_NOT_FOUND_ERROR, } from "../../constants"; + /** * This function enables to update an event project. * @param _parent - parent of current request @@ -17,53 +19,54 @@ import { * 2. If the event project exists. * @returns Updated event project. */ -export const updateEventProject = async ( - _parent: any, - args: any, - context: any -): Promise => { - const currentUserExists = await User.exists({ - _id: context.userId, - }); - if (currentUserExists === false) { - throw new errors.NotFoundError( - requestContext.translate(USER_NOT_FOUND_ERROR.MESSAGE), - USER_NOT_FOUND_ERROR.CODE, - USER_NOT_FOUND_ERROR.PARAM - ); - } +export const updateEventProject: MutationResolvers["updateEventProject"] = + async (_parent, args, context): Promise => { + const currentUser = await User.findOne({ + _id: context.userId, + }); - const eventProject = await EventProject.findOne({ - _id: args.id, - }).lean(); + if (currentUser === null) { + throw new errors.NotFoundError( + requestContext.translate(USER_NOT_FOUND_ERROR.MESSAGE), + USER_NOT_FOUND_ERROR.CODE, + USER_NOT_FOUND_ERROR.PARAM + ); + } - if (!eventProject) { - throw new errors.NotFoundError( - requestContext.translate(EVENT_PROJECT_NOT_FOUND_ERROR.MESSAGE), - EVENT_NOT_FOUND_ERROR.CODE, - EVENT_PROJECT_NOT_FOUND_ERROR.PARAM - ); - } + const eventProject = await EventProject.findOne({ + _id: args.id, + }).lean(); - // toString() method converts mongodb's objectId to a javascript string for comparision - if (eventProject.creator.toString() !== context.userId.toString()) { - throw new errors.UnauthorizedError( - requestContext.translate(USER_NOT_AUTHORIZED_ERROR.MESSAGE), - USER_NOT_AUTHORIZED_ERROR.CODE, - USER_NOT_AUTHORIZED_ERROR.PARAM - ); - } + if (!eventProject) { + throw new errors.NotFoundError( + requestContext.translate(EVENT_PROJECT_NOT_FOUND_ERROR.MESSAGE), + EVENT_NOT_FOUND_ERROR.CODE, + EVENT_PROJECT_NOT_FOUND_ERROR.PARAM + ); + } - return await EventProject.findOneAndUpdate( - { - _id: args.id, - }, - { - ...args.data, - }, - { - new: true, + // toString() method converts mongodb's objectId to a javascript string for comparision + if ( + eventProject.creator.toString() !== context.userId.toString() && + currentUser.userType !== "SUPERADMIN" + ) { + throw new errors.UnauthorizedError( + requestContext.translate(USER_NOT_AUTHORIZED_ERROR.MESSAGE), + USER_NOT_AUTHORIZED_ERROR.CODE, + USER_NOT_AUTHORIZED_ERROR.PARAM + ); } - ).lean(); -}; + + return await EventProject.findOneAndUpdate( + { + _id: args.id, + }, + { + ...(args.data as any), + }, + { + new: true, + } + ).lean(); + }; diff --git a/src/resolvers/Mutation/updateTask.ts b/src/resolvers/Mutation/updateTask.ts index 9dca755d1f..229b190be5 100644 --- a/src/resolvers/Mutation/updateTask.ts +++ b/src/resolvers/Mutation/updateTask.ts @@ -21,11 +21,11 @@ export const updateTask: MutationResolvers["updateTask"] = async ( args, context ) => { - const currentUserExists = await User.exists({ + const currentUser = await User.findOne({ _id: context.userId, }); - if (currentUserExists === false) { + if (currentUser === null) { throw new errors.NotFoundError( requestContext.translate(USER_NOT_FOUND_ERROR.MESSAGE), USER_NOT_FOUND_ERROR.CODE, @@ -45,7 +45,10 @@ export const updateTask: MutationResolvers["updateTask"] = async ( ); } - if (task.creator.toString() !== context.userId.toString()) { + if ( + task.creator.toString() !== context.userId.toString() && + currentUser.userType !== "SUPERADMIN" + ) { throw new errors.UnauthorizedError( requestContext.translate(USER_NOT_AUTHORIZED_ERROR.MESSAGE), USER_NOT_AUTHORIZED_ERROR.CODE, @@ -55,10 +58,10 @@ export const updateTask: MutationResolvers["updateTask"] = async ( const updatedTask = await Task.findOneAndUpdate( { - _id: task._id, + _id: args.id, }, { - ...(args as any), + ...(args.data as any), }, { new: true, diff --git a/src/resolvers/Query/eventsByOrganization.ts b/src/resolvers/Query/eventsByOrganization.ts index 193fed4e10..96ef6034d0 100644 --- a/src/resolvers/Query/eventsByOrganization.ts +++ b/src/resolvers/Query/eventsByOrganization.ts @@ -1,7 +1,5 @@ import type { QueryResolvers } from "../../types/generatedGraphQLTypes"; -import type { InterfaceUserAttende } from "../../models"; import { Event } from "../../models"; -import { STATUS_ACTIVE } from "../../constants"; import { getSort } from "./helperFunctions/getSort"; /** * This query will fetch all events for the organization which have `ACTIVE` status from database. @@ -23,12 +21,5 @@ export const eventsByOrganization: QueryResolvers["eventsByOrganization"] = .populate("admins", "-password") .lean(); - events.forEach((event) => { - event.registrants = event.registrants.filter( - (registrant: InterfaceUserAttende) => - registrant.status === STATUS_ACTIVE - ); - }); - return events; }; diff --git a/src/resolvers/Query/eventsByOrganizationConnection.ts b/src/resolvers/Query/eventsByOrganizationConnection.ts index 532ca84975..fb7d311fac 100644 --- a/src/resolvers/Query/eventsByOrganizationConnection.ts +++ b/src/resolvers/Query/eventsByOrganizationConnection.ts @@ -1,7 +1,6 @@ import type { QueryResolvers } from "../../types/generatedGraphQLTypes"; -import type { InterfaceEvent, InterfaceUserAttende } from "../../models"; +import type { InterfaceEvent } from "../../models"; import { Event } from "../../models"; -import { STATUS_ACTIVE } from "../../constants"; import { getSort } from "./helperFunctions/getSort"; import { getWhere } from "./helperFunctions/getWhere"; @@ -24,12 +23,5 @@ export const eventsByOrganizationConnection: QueryResolvers["eventsByOrganizatio .populate("admins", "-password") .lean(); - events.forEach((event) => { - event.registrants = event.registrants.filter( - (registrant: InterfaceUserAttende) => - registrant.status === STATUS_ACTIVE - ); - }); - return events; }; diff --git a/src/resolvers/Query/index.ts b/src/resolvers/Query/index.ts index 1745d36d71..e9a306a33f 100644 --- a/src/resolvers/Query/index.ts +++ b/src/resolvers/Query/index.ts @@ -10,7 +10,6 @@ import { getDonationByOrgId } from "./getDonationByOrgId"; import { getDonationByOrgIdConnection } from "./getDonationByOrgIdConnection"; import { getlanguage } from "./getlanguage"; import { getPlugins } from "./getPlugins"; -import { isUserRegister } from "./isUserRegister"; import { me } from "./me"; import { myLanguage } from "./myLanguage"; import { organizations } from "./organizations"; @@ -20,9 +19,6 @@ import { post } from "./post"; import { postsByOrganization } from "./postsByOrganization"; import { postsByOrganizationConnection } from "./postsByOrganizationConnection"; import { registeredEventsByUser } from "./registeredEventsByUser"; -import { registrantsByEvent } from "./registrantsByEvent"; -import { tasksByEvent } from "./tasksByEvent"; -import { tasksByUser } from "./tasksByUser"; import { user } from "./user"; import { userLanguage } from "./userLanguage"; import { users } from "./users"; @@ -40,7 +36,6 @@ export const Query: QueryResolvers = { getDonationByOrgIdConnection, getlanguage, getPlugins, - isUserRegister, me, myLanguage, organizations, @@ -50,9 +45,6 @@ export const Query: QueryResolvers = { postsByOrganization, postsByOrganizationConnection, registeredEventsByUser, - registrantsByEvent, - tasksByEvent, - tasksByUser, user, userLanguage, users, diff --git a/src/resolvers/Query/isUserRegister.ts b/src/resolvers/Query/isUserRegister.ts deleted file mode 100644 index c55478da4e..0000000000 --- a/src/resolvers/Query/isUserRegister.ts +++ /dev/null @@ -1,51 +0,0 @@ -import type { QueryResolvers } from "../../types/generatedGraphQLTypes"; -import { Event } from "../../models"; -import { errors } from "../../libraries"; -import { EVENT_NOT_FOUND_ERROR } from "../../constants"; -/** - * This query determines whether or not the user is registered for an event. - * @param _parent - - * @param args - An object that contains `eventId` of an event. - * @param context - An object that contains `userId` of the User. - * @returns An object that contains an `event` object and a boolean property `isRegistered`. - * If the `event` is null or not found then throws `NotFoundError` error. - */ -export const isUserRegister: QueryResolvers["isUserRegister"] = async ( - _parent, - args, - context -) => { - const event = await Event.findOne({ - _id: args.eventId, - status: "ACTIVE", - }) - .populate("creator", "-password") - .populate("tasks") - .populate("admins", "-password") - .lean(); - - if (!event) { - throw new errors.NotFoundError( - EVENT_NOT_FOUND_ERROR.DESC, - EVENT_NOT_FOUND_ERROR.CODE, - EVENT_NOT_FOUND_ERROR.PARAM - ); - } - - let isCurrentUserRegistered = false; - - for (const registrant of event.registrants) { - if ( - registrant.userId === context.userId && - registrant.status === "ACTIVE" - ) { - isCurrentUserRegistered = true; - break; - } - } - - return { - event, - isRegistered: isCurrentUserRegistered, - }; -}; diff --git a/src/resolvers/Query/registrantsByEvent.ts b/src/resolvers/Query/registrantsByEvent.ts deleted file mode 100644 index d2c1dc25f4..0000000000 --- a/src/resolvers/Query/registrantsByEvent.ts +++ /dev/null @@ -1,43 +0,0 @@ -import type { QueryResolvers } from "../../types/generatedGraphQLTypes"; -import type { InterfaceUser, InterfaceUserAttende } from "../../models"; -import { Event } from "../../models"; -import { errors } from "../../libraries"; -import { EVENT_NOT_FOUND_ERROR } from "../../constants"; -/** - * This query will fetch all the registrants of the Event from the database. - * @param _parent- - * @param args - An object that contains `id` of the event. - * @returns An object that contains list of all the Registrants. - * If the event is not found then it throws an `NotFoundError` error. - */ -export const registrantsByEvent: QueryResolvers["registrantsByEvent"] = async ( - _parent, - args -) => { - const event = await Event.findOne({ - _id: args.id, - status: "ACTIVE", - }) - .populate("registrants.user", "-password") - .lean(); - - if (!event) { - throw new errors.NotFoundError( - EVENT_NOT_FOUND_ERROR.DESC, - EVENT_NOT_FOUND_ERROR.CODE, - EVENT_NOT_FOUND_ERROR.PARAM - ); - } - - const registrants: InterfaceUser[] = []; - - if (event.registrants.length > 0) { - event.registrants.map((registrant: InterfaceUserAttende) => { - if (registrant.status === "ACTIVE") { - registrants.push(registrant.user); - } - }); - } - - return registrants; -}; diff --git a/src/resolvers/Query/tasksByEvent.ts b/src/resolvers/Query/tasksByEvent.ts deleted file mode 100644 index 31593a11c8..0000000000 --- a/src/resolvers/Query/tasksByEvent.ts +++ /dev/null @@ -1,24 +0,0 @@ -import type { QueryResolvers } from "../../types/generatedGraphQLTypes"; -import { Task } from "../../models"; -import { getSort } from "./helperFunctions/getSort"; -/** - * This query will fetch the list of tasks for an Event in specified order from database. - * @param _parent- - * @param args - An object that contains `id` of an Event and `orderBy`. - * @returns An object that contains list of the tasks. - * @remarks The query function uses `getSort()` function to sort the data in specified. - */ -export const tasksByEvent: QueryResolvers["tasksByEvent"] = async ( - _parent, - args -) => { - const sort = getSort(args.orderBy); - - return await Task.find({ - event: args.id, - }) - .sort(sort) - .populate("event") - .populate("creator", "-password") - .lean(); -}; diff --git a/src/resolvers/Query/tasksByUser.ts b/src/resolvers/Query/tasksByUser.ts deleted file mode 100644 index d3661f9ac5..0000000000 --- a/src/resolvers/Query/tasksByUser.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { QueryResolvers } from "../../types/generatedGraphQLTypes"; -import { Task } from "../../models"; -import { getSort } from "./helperFunctions/getSort"; -/** - * This query will fetch the list of tasks created by the user in an specified order from the database. - * @param _parent- - * @param args - An object that contains `id` of the user and `orderBy`. - * @returns An object that contains the list of all the task created by the user. - */ -export const tasksByUser: QueryResolvers["tasksByUser"] = async ( - _parent, - args -) => { - const sort = getSort(args.orderBy); - - return await Task.find({ - creator: args.id, - }) - .sort(sort) - .populate("event") - .populate("creator", "-password") - .lean(); -}; diff --git a/src/resolvers/Task/index.ts b/src/resolvers/Task/index.ts new file mode 100644 index 0000000000..e46555d6df --- /dev/null +++ b/src/resolvers/Task/index.ts @@ -0,0 +1,6 @@ +import type { TaskResolvers } from "../../types/generatedGraphQLTypes"; +import { volunteers } from "./volunteers"; + +export const Task: TaskResolvers = { + volunteers, +}; diff --git a/src/resolvers/Task/volunteers.ts b/src/resolvers/Task/volunteers.ts new file mode 100644 index 0000000000..e90387582b --- /dev/null +++ b/src/resolvers/Task/volunteers.ts @@ -0,0 +1,11 @@ +import type { TaskResolvers } from "../../types/generatedGraphQLTypes"; +import { TaskVolunteer } from "../../models"; + +export const volunteers: TaskResolvers["volunteers"] = async (parent) => { + const volunteerObjects = await TaskVolunteer.find({ + taskId: parent._id, + }) + .populate("userId") + .lean(); + return volunteerObjects.map((object) => object.userId); +}; diff --git a/src/resolvers/User/assignedTasks.ts b/src/resolvers/User/assignedTasks.ts new file mode 100644 index 0000000000..19162d4bfa --- /dev/null +++ b/src/resolvers/User/assignedTasks.ts @@ -0,0 +1,12 @@ +import { TaskVolunteer } from "../../models"; +import type { UserResolvers } from "../../types/generatedGraphQLTypes"; + +export const assignedTasks: UserResolvers["assignedTasks"] = async (parent) => { + const taskObjects = await TaskVolunteer.find({ + userId: parent._id, + }) + .populate("taskId") + .lean(); + + return taskObjects.map((object) => object.taskId); +}; diff --git a/src/resolvers/User/index.ts b/src/resolvers/User/index.ts index 8cd8407858..7cd1c6f563 100644 --- a/src/resolvers/User/index.ts +++ b/src/resolvers/User/index.ts @@ -1,6 +1,8 @@ import type { UserResolvers } from "../../types/generatedGraphQLTypes"; +import { assignedTasks } from "./assignedTasks"; // import { tagsAssignedWith } from "./tagsAssignedWith"; export const User: UserResolvers = { + assignedTasks, // tagsAssignedWith, }; diff --git a/src/resolvers/index.ts b/src/resolvers/index.ts index 3cf95b3f10..3bb615454f 100644 --- a/src/resolvers/index.ts +++ b/src/resolvers/index.ts @@ -1,7 +1,10 @@ import type { Resolvers } from "../types/generatedGraphQLTypes"; +import { CheckIn } from "./CheckIn"; import { Comment } from "./Comment"; import { DirectChat } from "./DirectChat"; import { DirectChatMessage } from "./DirectChatMessage"; +import { Event } from "./Event"; +import { EventProject } from "./EventProject"; import { GroupChat } from "./GroupChat"; import { GroupChatMessage } from "./GroupChatMessage"; import { MembershipRequest } from "./MembershipRequest"; @@ -10,6 +13,7 @@ import { Organization } from "./Organization"; import { Post } from "./Post"; import { Query } from "./Query"; import { Subscription } from "./Subscription"; +import { Task } from "./Task"; import { User } from "./User"; import { UserTag } from "./UserTag"; import { @@ -25,9 +29,12 @@ import { } from "graphql-scalars"; export const resolvers: Resolvers = { + CheckIn, Comment, DirectChat, DirectChatMessage, + Event, + EventProject, GroupChat, GroupChatMessage, MembershipRequest, @@ -36,6 +43,7 @@ export const resolvers: Resolvers = { Post, Query, Subscription, + Task, User, UserTag, diff --git a/src/typeDefs/inputs.ts b/src/typeDefs/inputs.ts index 0b7256d392..f59e116384 100644 --- a/src/typeDefs/inputs.ts +++ b/src/typeDefs/inputs.ts @@ -6,6 +6,18 @@ export const inputs = gql` text: String! } + input EventAttendeeInput { + userId: ID! + eventId: ID! + } + + input CheckInInput { + userId: ID! + eventId: ID! + allotedRoom: String + allotedSeat: String + } + input createChatInput { userIds: [ID!]! organizationId: ID! @@ -95,11 +107,11 @@ export const inputs = gql` organization_id: ID } - # input EventProjectInput { - # title: String! - # description: String! - # eventId: String - # } + input EventProjectInput { + title: String! + description: String! + eventId: ID! + } input ForgotPasswordData { userOtp: String! @@ -224,8 +236,8 @@ export const inputs = gql` input TaskInput { title: String! - description: String - deadline: DateTime + description: String! + deadline: DateTime! } input ToggleUserTagAssignInput { @@ -264,10 +276,10 @@ export const inputs = gql` limit: PositiveInt! } - # input UpdateEventProjectInput { - # title: String - # description: String - # } + input UpdateEventProjectInput { + title: String + description: String + } input UpdateOrganizationInput { name: String @@ -286,6 +298,7 @@ export const inputs = gql` title: String description: String deadline: DateTime + completed: Boolean } input UpdateUserInput { diff --git a/src/typeDefs/mutations.ts b/src/typeDefs/mutations.ts index a4b0878832..a17db02a17 100644 --- a/src/typeDefs/mutations.ts +++ b/src/typeDefs/mutations.ts @@ -9,6 +9,8 @@ export const mutations = gql` acceptMembershipRequest(membershipRequestId: ID!): MembershipRequest! @auth + addEventAttendee(data: EventAttendeeInput!): User! @auth + addLanguageTranslation(data: LanguageInput!): Language! @auth addOrganizationImage(file: String!, organizationId: String!): Organization! @@ -32,6 +34,8 @@ export const mutations = gql` cancelMembershipRequest(membershipRequestId: ID!): MembershipRequest! @auth + checkIn(data: CheckInInput!): CheckIn! @auth + createMember(input: UserAndOrganizationInput!): Organization! @auth createAdmin(data: UserAndOrganizationInput!): User! @@ -53,6 +57,8 @@ export const mutations = gql` createEvent(data: EventInput): Event! @auth + createEventProject(data: EventProjectInput!): EventProject! @auth + createGroupChat(data: createGroupChatInput!): GroupChat! @auth createMessageChat(data: MessageChatInput!): MessageChat! @auth @@ -73,7 +79,7 @@ export const mutations = gql` createUserTag(input: CreateUserTagInput!): UserTag @auth - createTask(data: TaskInput, eventId: ID!): Task! @auth + createTask(data: TaskInput!, eventProjectId: ID!): Task! @auth deleteDonationById(id: ID!): DeletePayload! @@ -113,6 +119,10 @@ export const mutations = gql` removeEvent(id: ID!): Event! @auth + removeEventAttendee(data: EventAttendeeInput!): User! @auth + + removeEventProject(id: ID!): EventProject! @auth + removeGroupChat(chatId: ID!): GroupChat! @auth removeMember(data: UserAndOrganizationInput!): Organization! @auth @@ -147,6 +157,8 @@ export const mutations = gql` messageContent: String! ): GroupChatMessage! @auth + setTaskVolunteers(id: ID!, volunteers: [ID]!): Task @auth + signUp(data: UserInput!, file: String): AuthData! togglePostPin(id: ID!): Post! @auth @@ -163,6 +175,9 @@ export const mutations = gql` updateEvent(id: ID!, data: UpdateEventInput): Event! @auth + updateEventProject(id: ID!, data: UpdateEventProjectInput!): EventProject! + @auth + updatePost(id: ID!, data: PostUpdateInput): Post! @auth updateLanguage(languageCode: String!): User! @auth @@ -179,7 +194,7 @@ export const mutations = gql` updateUserTag(input: UpdateUserTagInput!): UserTag @auth - updateTask(id: ID!, data: UpdateTaskInput): Task @auth + updateTask(id: ID!, data: UpdateTaskInput!): Task @auth updateUserProfile(data: UpdateUserInput, file: String): User! @auth diff --git a/src/typeDefs/queries.ts b/src/typeDefs/queries.ts index 30ae346b7a..e811586945 100644 --- a/src/typeDefs/queries.ts +++ b/src/typeDefs/queries.ts @@ -39,8 +39,6 @@ export const queries = gql` getPlugins: [Plugin] - isUserRegister(eventId: ID!): EventRegistrants - joinedOrganizations(id: ID): [Organization] me: User! @auth @@ -82,10 +80,6 @@ export const queries = gql` registrantsByEvent(id: ID!): [User] - tasksByEvent(id: ID!, orderBy: TaskOrderByInput): [Task] - - tasksByUser(id: ID!, orderBy: TaskOrderByInput): [Task] - user(id: ID!): User! @auth userLanguage(userId: ID!): String @auth diff --git a/src/typeDefs/types.ts b/src/typeDefs/types.ts index 610ca538f0..b88376cbfd 100644 --- a/src/typeDefs/types.ts +++ b/src/typeDefs/types.ts @@ -26,6 +26,23 @@ export const types = gql` iosFirebaseOptions: IOSFirebaseOptions! } + # Stores the detail of an check in of an user in an event + type CheckIn { + _id: ID! + time: DateTime! + allotedRoom: String + allotedSeat: String + user: User! + event: Event! + } + + # Used to show whether an user has checked in for an event + type CheckInStatus { + _id: ID! + user: User! + checkIn: CheckIn + } + type Comment { _id: ID text: String! @@ -98,23 +115,20 @@ export const types = gql` longitude: Longitude organization: Organization creator: User! - registrants: [UserAttende] + attendees: [User!]! + # For each attendee, gives information about whether he/she has checked in yet or not + attendeesCheckInStatus: [CheckInStatus!]! admins(adminId: ID): [User] - tasks: [Task] status: Status! + projects: [EventProject] } - # type EventProject { - # _id: ID! - # title:String! - # description: String! - # event: Event! - # tasks: [Task] - # } - - type EventRegistrants { + type EventProject { + _id: ID! + title: String! + description: String! event: Event! - isRegistered: Boolean! + tasks: [Task] } type Group { @@ -316,7 +330,9 @@ export const types = gql` event: Event! creator: User! createdAt: DateTime! + completed: Boolean deadline: DateTime + volunteers: [User] } type Translation { @@ -346,6 +362,7 @@ export const types = gql` organizationUserBelongsTo: Organization pluginCreationAllowed: Boolean adminApproved: Boolean + assignedTasks: [Task] createdAt: DateTime tagsAssignedWith( after: String @@ -356,14 +373,6 @@ export const types = gql` ): UserTagsConnection } - type UserAttende { - _id: ID! - userId: String! - user: User! - status: Status! - createdAt: DateTime - } - type UserConnection { pageInfo: PageInfo! edges: [User]! diff --git a/src/types/generatedGraphQLTypes.ts b/src/types/generatedGraphQLTypes.ts index e6eaad5703..9625d906e5 100644 --- a/src/types/generatedGraphQLTypes.ts +++ b/src/types/generatedGraphQLTypes.ts @@ -1,10 +1,13 @@ import type { GraphQLResolveInfo, GraphQLScalarType, GraphQLScalarTypeConfig } from 'graphql'; +import type { InterfaceCheckIn as InterfaceCheckInModel } from '../models/CheckIn'; import type { InterfaceMessageChat as InterfaceMessageChatModel } from '../models/MessageChat'; import type { InterfaceComment as InterfaceCommentModel } from '../models/Comment'; import type { InterfaceDirectChat as InterfaceDirectChatModel } from '../models/DirectChat'; import type { InterfaceDirectChatMessage as InterfaceDirectChatMessageModel } from '../models/DirectChatMessage'; import type { InterfaceDonation as InterfaceDonationModel } from '../models/Donation'; import type { InterfaceEvent as InterfaceEventModel } from '../models/Event'; +import type { InterfaceEventAttendee as InterfaceEventAttendeeModel } from '../models/EventAttendee'; +import type { InterfaceEventProject as InterfaceEventProjectModel } from '../models/EventProject'; import type { InterfaceGroup as InterfaceGroupModel } from '../models/Group'; import type { InterfaceGroupChat as InterfaceGroupChatModel } from '../models/GroupChat'; import type { InterfaceGroupChatMessage as InterfaceGroupChatMessageModel } from '../models/GroupChatMessage'; @@ -71,6 +74,30 @@ export type AuthData = { user: User; }; +export type CheckIn = { + __typename?: 'CheckIn'; + _id: Scalars['ID']; + allotedRoom?: Maybe; + allotedSeat?: Maybe; + event: Event; + time: Scalars['DateTime']; + user: User; +}; + +export type CheckInInput = { + allotedRoom?: InputMaybe; + allotedSeat?: InputMaybe; + eventId: Scalars['ID']; + userId: Scalars['ID']; +}; + +export type CheckInStatus = { + __typename?: 'CheckInStatus'; + _id: Scalars['ID']; + checkIn?: Maybe; + user: User; +}; + export type Comment = { __typename?: 'Comment'; _id?: Maybe; @@ -167,6 +194,8 @@ export type Event = { _id: Scalars['ID']; admins?: Maybe>>; allDay: Scalars['Boolean']; + attendees: Array; + attendeesCheckInStatus: Array; creator: User; description: Scalars['String']; endDate: Scalars['Date']; @@ -177,13 +206,12 @@ export type Event = { location?: Maybe; longitude?: Maybe; organization?: Maybe; + projects?: Maybe>>; recurrance?: Maybe; recurring: Scalars['Boolean']; - registrants?: Maybe>>; startDate: Scalars['Date']; startTime?: Maybe; status: Status; - tasks?: Maybe>>; title: Scalars['String']; }; @@ -192,6 +220,11 @@ export type EventAdminsArgs = { adminId?: InputMaybe; }; +export type EventAttendeeInput = { + eventId: Scalars['ID']; + userId: Scalars['ID']; +}; + export type EventInput = { allDay: Scalars['Boolean']; description: Scalars['String']; @@ -232,10 +265,19 @@ export type EventOrderByInput = | 'title_ASC' | 'title_DESC'; -export type EventRegistrants = { - __typename?: 'EventRegistrants'; +export type EventProject = { + __typename?: 'EventProject'; + _id: Scalars['ID']; + description: Scalars['String']; event: Event; - isRegistered: Scalars['Boolean']; + tasks?: Maybe>>; + title: Scalars['String']; +}; + +export type EventProjectInput = { + description: Scalars['String']; + eventId: Scalars['ID']; + title: Scalars['String']; }; export type EventWhereInput = { @@ -418,6 +460,7 @@ export type Mutation = { __typename?: 'Mutation'; acceptAdmin: Scalars['Boolean']; acceptMembershipRequest: MembershipRequest; + addEventAttendee: User; addLanguageTranslation: Language; addOrganizationImage: Organization; addUserImage: User; @@ -428,11 +471,13 @@ export type Mutation = { blockPluginCreationBySuperadmin: User; blockUser: User; cancelMembershipRequest: MembershipRequest; + checkIn: CheckIn; createAdmin: User; createComment?: Maybe; createDirectChat: DirectChat; createDonation: Donation; createEvent: Event; + createEventProject: EventProject; createGroupChat: GroupChat; createMember: Organization; createMessageChat: MessageChat; @@ -459,6 +504,8 @@ export type Mutation = { removeComment?: Maybe; removeDirectChat: DirectChat; removeEvent: Event; + removeEventAttendee: User; + removeEventProject: EventProject; removeGroupChat: GroupChat; removeMember: Organization; removeOrganization: User; @@ -473,6 +520,7 @@ export type Mutation = { sendMembershipRequest: MembershipRequest; sendMessageToDirectChat: DirectChatMessage; sendMessageToGroupChat: GroupChatMessage; + setTaskVolunteers?: Maybe; signUp: AuthData; togglePostPin: Post; unassignUserTag?: Maybe; @@ -481,6 +529,7 @@ export type Mutation = { unlikePost?: Maybe; unregisterForEventByUser: Event; updateEvent: Event; + updateEventProject: EventProject; updateLanguage: User; updateOrganization: Organization; updatePluginInstalledOrgs: Plugin; @@ -504,6 +553,11 @@ export type MutationAcceptMembershipRequestArgs = { }; +export type MutationAddEventAttendeeArgs = { + data: EventAttendeeInput; +}; + + export type MutationAddLanguageTranslationArgs = { data: LanguageInput; }; @@ -558,6 +612,11 @@ export type MutationCancelMembershipRequestArgs = { }; +export type MutationCheckInArgs = { + data: CheckInInput; +}; + + export type MutationCreateAdminArgs = { data: UserAndOrganizationInput; }; @@ -589,6 +648,11 @@ export type MutationCreateEventArgs = { }; +export type MutationCreateEventProjectArgs = { + data: EventProjectInput; +}; + + export type MutationCreateGroupChatArgs = { data: CreateGroupChatInput; }; @@ -626,8 +690,8 @@ export type MutationCreatePostArgs = { export type MutationCreateTaskArgs = { - data?: InputMaybe; - eventId: Scalars['ID']; + data: TaskInput; + eventProjectId: Scalars['ID']; }; @@ -722,6 +786,16 @@ export type MutationRemoveEventArgs = { }; +export type MutationRemoveEventAttendeeArgs = { + data: EventAttendeeInput; +}; + + +export type MutationRemoveEventProjectArgs = { + id: Scalars['ID']; +}; + + export type MutationRemoveGroupChatArgs = { chatId: Scalars['ID']; }; @@ -790,6 +864,12 @@ export type MutationSendMessageToGroupChatArgs = { }; +export type MutationSetTaskVolunteersArgs = { + id: Scalars['ID']; + volunteers: Array>; +}; + + export type MutationSignUpArgs = { data: UserInput; file?: InputMaybe; @@ -833,6 +913,12 @@ export type MutationUpdateEventArgs = { }; +export type MutationUpdateEventProjectArgs = { + data: UpdateEventProjectInput; + id: Scalars['ID']; +}; + + export type MutationUpdateLanguageArgs = { languageCode: Scalars['String']; }; @@ -864,7 +950,7 @@ export type MutationUpdatePostArgs = { export type MutationUpdateTaskArgs = { - data?: InputMaybe; + data: UpdateTaskInput; id: Scalars['ID']; }; @@ -1137,7 +1223,6 @@ export type Query = { getDonationByOrgIdConnection: Array; getPlugins?: Maybe>>; getlanguage?: Maybe>>; - isUserRegister?: Maybe; joinedOrganizations?: Maybe>>; me: User; myLanguage?: Maybe; @@ -1150,8 +1235,6 @@ export type Query = { postsByOrganizationConnection?: Maybe; registeredEventsByUser?: Maybe>>; registrantsByEvent?: Maybe>>; - tasksByEvent?: Maybe>>; - tasksByUser?: Maybe>>; user: User; userLanguage?: Maybe; users?: Maybe>>; @@ -1216,11 +1299,6 @@ export type QueryGetlanguageArgs = { }; -export type QueryIsUserRegisterArgs = { - eventId: Scalars['ID']; -}; - - export type QueryJoinedOrganizationsArgs = { id?: InputMaybe; }; @@ -1285,18 +1363,6 @@ export type QueryRegistrantsByEventArgs = { }; -export type QueryTasksByEventArgs = { - id: Scalars['ID']; - orderBy?: InputMaybe; -}; - - -export type QueryTasksByUserArgs = { - id: Scalars['ID']; - orderBy?: InputMaybe; -}; - - export type QueryUserArgs = { id: Scalars['ID']; }; @@ -1346,17 +1412,19 @@ export type Subscription = { export type Task = { __typename?: 'Task'; _id: Scalars['ID']; + completed?: Maybe; createdAt: Scalars['DateTime']; creator: User; deadline?: Maybe; description?: Maybe; event: Event; title: Scalars['String']; + volunteers?: Maybe>>; }; export type TaskInput = { - deadline?: InputMaybe; - description?: InputMaybe; + deadline: Scalars['DateTime']; + description: Scalars['String']; title: Scalars['String']; }; @@ -1416,6 +1484,11 @@ export type UpdateEventInput = { title?: InputMaybe; }; +export type UpdateEventProjectInput = { + description?: InputMaybe; + title?: InputMaybe; +}; + export type UpdateOrganizationInput = { description?: InputMaybe; isPublic?: InputMaybe; @@ -1425,6 +1498,7 @@ export type UpdateOrganizationInput = { }; export type UpdateTaskInput = { + completed?: InputMaybe; deadline?: InputMaybe; description?: InputMaybe; title?: InputMaybe; @@ -1458,6 +1532,7 @@ export type User = { adminApproved?: Maybe; adminFor?: Maybe>>; appLanguageCode: Scalars['String']; + assignedTasks?: Maybe>>; createdAt?: Maybe; createdEvents?: Maybe>>; createdOrganizations?: Maybe>>; @@ -1491,15 +1566,6 @@ export type UserAndOrganizationInput = { userId: Scalars['ID']; }; -export type UserAttende = { - __typename?: 'UserAttende'; - _id: Scalars['ID']; - createdAt?: Maybe; - status: Status; - user: User; - userId: Scalars['String']; -}; - export type UserConnection = { __typename?: 'UserConnection'; aggregate: AggregateUser; @@ -1717,6 +1783,9 @@ export type ResolversTypes = { AndroidFirebaseOptions: ResolverTypeWrapper; AuthData: ResolverTypeWrapper & { user: ResolversTypes['User'] }>; Boolean: ResolverTypeWrapper; + CheckIn: ResolverTypeWrapper; + CheckInInput: CheckInInput; + CheckInStatus: ResolverTypeWrapper & { checkIn?: Maybe, user: ResolversTypes['User'] }>; Comment: ResolverTypeWrapper; CommentInput: CommentInput; ConnectionError: ResolversTypes['InvalidCursor'] | ResolversTypes['MaximumValueError']; @@ -1733,9 +1802,11 @@ export type ResolversTypes = { EmailAddress: ResolverTypeWrapper; Error: ResolversTypes['UnauthenticatedError'] | ResolversTypes['UnauthorizedError']; Event: ResolverTypeWrapper; + EventAttendeeInput: EventAttendeeInput; EventInput: EventInput; EventOrderByInput: EventOrderByInput; - EventRegistrants: ResolverTypeWrapper & { event: ResolversTypes['Event'] }>; + EventProject: ResolverTypeWrapper; + EventProjectInput: EventProjectInput; EventWhereInput: EventWhereInput; ExtendSession: ResolverTypeWrapper; FieldError: ResolversTypes['InvalidCursor'] | ResolversTypes['MaximumLengthError'] | ResolversTypes['MaximumValueError'] | ResolversTypes['MinimumLengthError'] | ResolversTypes['MinimumValueError']; @@ -1801,6 +1872,7 @@ export type ResolversTypes = { UnauthenticatedError: ResolverTypeWrapper; UnauthorizedError: ResolverTypeWrapper; UpdateEventInput: UpdateEventInput; + UpdateEventProjectInput: UpdateEventProjectInput; UpdateOrganizationInput: UpdateOrganizationInput; UpdateTaskInput: UpdateTaskInput; UpdateUserInput: UpdateUserInput; @@ -1809,7 +1881,6 @@ export type ResolversTypes = { UpdateUserTypeInput: UpdateUserTypeInput; User: ResolverTypeWrapper; UserAndOrganizationInput: UserAndOrganizationInput; - UserAttende: ResolverTypeWrapper & { user: ResolversTypes['User'] }>; UserConnection: ResolverTypeWrapper & { edges: Array> }>; UserEdge: ResolverTypeWrapper & { node: ResolversTypes['User'] }>; UserInput: UserInput; @@ -1835,6 +1906,9 @@ export type ResolversParentTypes = { AndroidFirebaseOptions: AndroidFirebaseOptions; AuthData: Omit & { user: ResolversParentTypes['User'] }; Boolean: Scalars['Boolean']; + CheckIn: InterfaceCheckInModel; + CheckInInput: CheckInInput; + CheckInStatus: Omit & { checkIn?: Maybe, user: ResolversParentTypes['User'] }; Comment: InterfaceCommentModel; CommentInput: CommentInput; ConnectionError: ResolversParentTypes['InvalidCursor'] | ResolversParentTypes['MaximumValueError']; @@ -1851,8 +1925,10 @@ export type ResolversParentTypes = { EmailAddress: Scalars['EmailAddress']; Error: ResolversParentTypes['UnauthenticatedError'] | ResolversParentTypes['UnauthorizedError']; Event: InterfaceEventModel; + EventAttendeeInput: EventAttendeeInput; EventInput: EventInput; - EventRegistrants: Omit & { event: ResolversParentTypes['Event'] }; + EventProject: InterfaceEventProjectModel; + EventProjectInput: EventProjectInput; EventWhereInput: EventWhereInput; ExtendSession: ExtendSession; FieldError: ResolversParentTypes['InvalidCursor'] | ResolversParentTypes['MaximumLengthError'] | ResolversParentTypes['MaximumValueError'] | ResolversParentTypes['MinimumLengthError'] | ResolversParentTypes['MinimumValueError']; @@ -1911,6 +1987,7 @@ export type ResolversParentTypes = { UnauthenticatedError: UnauthenticatedError; UnauthorizedError: UnauthorizedError; UpdateEventInput: UpdateEventInput; + UpdateEventProjectInput: UpdateEventProjectInput; UpdateOrganizationInput: UpdateOrganizationInput; UpdateTaskInput: UpdateTaskInput; UpdateUserInput: UpdateUserInput; @@ -1919,7 +1996,6 @@ export type ResolversParentTypes = { UpdateUserTypeInput: UpdateUserTypeInput; User: InterfaceUserModel; UserAndOrganizationInput: UserAndOrganizationInput; - UserAttende: Omit & { user: ResolversParentTypes['User'] }; UserConnection: Omit & { edges: Array> }; UserEdge: Omit & { node: ResolversParentTypes['User'] }; UserInput: UserInput; @@ -1974,6 +2050,23 @@ export type AuthDataResolvers; }; +export type CheckInResolvers = { + _id?: Resolver; + allotedRoom?: Resolver, ParentType, ContextType>; + allotedSeat?: Resolver, ParentType, ContextType>; + event?: Resolver; + time?: Resolver; + user?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + +export type CheckInStatusResolvers = { + _id?: Resolver; + checkIn?: Resolver, ParentType, ContextType>; + user?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + export type CommentResolvers = { _id?: Resolver, ParentType, ContextType>; createdAt?: Resolver, ParentType, ContextType>; @@ -2053,6 +2146,8 @@ export type EventResolvers; admins?: Resolver>>, ParentType, ContextType, Partial>; allDay?: Resolver; + attendees?: Resolver, ParentType, ContextType>; + attendeesCheckInStatus?: Resolver, ParentType, ContextType>; creator?: Resolver; description?: Resolver; endDate?: Resolver; @@ -2063,20 +2158,22 @@ export type EventResolvers, ParentType, ContextType>; longitude?: Resolver, ParentType, ContextType>; organization?: Resolver, ParentType, ContextType>; + projects?: Resolver>>, ParentType, ContextType>; recurrance?: Resolver, ParentType, ContextType>; recurring?: Resolver; - registrants?: Resolver>>, ParentType, ContextType>; startDate?: Resolver; startTime?: Resolver, ParentType, ContextType>; status?: Resolver; - tasks?: Resolver>>, ParentType, ContextType>; title?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; -export type EventRegistrantsResolvers = { +export type EventProjectResolvers = { + _id?: Resolver; + description?: Resolver; event?: Resolver; - isRegistered?: Resolver; + tasks?: Resolver>>, ParentType, ContextType>; + title?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; @@ -2218,6 +2315,7 @@ export type MinimumValueErrorResolvers = { acceptAdmin?: Resolver>; acceptMembershipRequest?: Resolver>; + addEventAttendee?: Resolver>; addLanguageTranslation?: Resolver>; addOrganizationImage?: Resolver>; addUserImage?: Resolver>; @@ -2228,18 +2326,20 @@ export type MutationResolvers>; blockUser?: Resolver>; cancelMembershipRequest?: Resolver>; + checkIn?: Resolver>; createAdmin?: Resolver>; createComment?: Resolver, ParentType, ContextType, RequireFields>; createDirectChat?: Resolver>; createDonation?: Resolver>; createEvent?: Resolver>; + createEventProject?: Resolver>; createGroupChat?: Resolver>; createMember?: Resolver>; createMessageChat?: Resolver>; createOrganization?: Resolver>; createPlugin?: Resolver>; createPost?: Resolver, ParentType, ContextType, RequireFields>; - createTask?: Resolver>; + createTask?: Resolver>; createUserTag?: Resolver, ParentType, ContextType, RequireFields>; deleteDonationById?: Resolver>; forgotPassword?: Resolver>; @@ -2259,6 +2359,8 @@ export type MutationResolvers, ParentType, ContextType, RequireFields>; removeDirectChat?: Resolver>; removeEvent?: Resolver>; + removeEventAttendee?: Resolver>; + removeEventProject?: Resolver>; removeGroupChat?: Resolver>; removeMember?: Resolver>; removeOrganization?: Resolver>; @@ -2273,6 +2375,7 @@ export type MutationResolvers>; sendMessageToDirectChat?: Resolver>; sendMessageToGroupChat?: Resolver>; + setTaskVolunteers?: Resolver, ParentType, ContextType, RequireFields>; signUp?: Resolver>; togglePostPin?: Resolver>; unassignUserTag?: Resolver, ParentType, ContextType, RequireFields>; @@ -2281,12 +2384,13 @@ export type MutationResolvers, ParentType, ContextType, RequireFields>; unregisterForEventByUser?: Resolver>; updateEvent?: Resolver>; + updateEventProject?: Resolver>; updateLanguage?: Resolver>; updateOrganization?: Resolver>; updatePluginInstalledOrgs?: Resolver>; updatePluginStatus?: Resolver>; updatePost?: Resolver>; - updateTask?: Resolver, ParentType, ContextType, RequireFields>; + updateTask?: Resolver, ParentType, ContextType, RequireFields>; updateUserPassword?: Resolver>; updateUserProfile?: Resolver>; updateUserTag?: Resolver, ParentType, ContextType, RequireFields>; @@ -2402,7 +2506,6 @@ export type QueryResolvers, ParentType, ContextType, RequireFields>; getPlugins?: Resolver>>, ParentType, ContextType>; getlanguage?: Resolver>>, ParentType, ContextType, RequireFields>; - isUserRegister?: Resolver, ParentType, ContextType, RequireFields>; joinedOrganizations?: Resolver>>, ParentType, ContextType, Partial>; me?: Resolver; myLanguage?: Resolver, ParentType, ContextType>; @@ -2415,8 +2518,6 @@ export type QueryResolvers, ParentType, ContextType, RequireFields>; registeredEventsByUser?: Resolver>>, ParentType, ContextType, Partial>; registrantsByEvent?: Resolver>>, ParentType, ContextType, RequireFields>; - tasksByEvent?: Resolver>>, ParentType, ContextType, RequireFields>; - tasksByUser?: Resolver>>, ParentType, ContextType, RequireFields>; user?: Resolver>; userLanguage?: Resolver, ParentType, ContextType, RequireFields>; users?: Resolver>>, ParentType, ContextType, Partial>; @@ -2431,12 +2532,14 @@ export type SubscriptionResolvers = { _id?: Resolver; + completed?: Resolver, ParentType, ContextType>; createdAt?: Resolver; creator?: Resolver; deadline?: Resolver, ParentType, ContextType>; description?: Resolver, ParentType, ContextType>; event?: Resolver; title?: Resolver; + volunteers?: Resolver>>, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; @@ -2471,6 +2574,7 @@ export type UserResolvers, ParentType, ContextType>; adminFor?: Resolver>>, ParentType, ContextType>; appLanguageCode?: Resolver; + assignedTasks?: Resolver>>, ParentType, ContextType>; createdAt?: Resolver, ParentType, ContextType>; createdEvents?: Resolver>>, ParentType, ContextType>; createdOrganizations?: Resolver>>, ParentType, ContextType>; @@ -2491,15 +2595,6 @@ export type UserResolvers; }; -export type UserAttendeResolvers = { - _id?: Resolver; - createdAt?: Resolver, ParentType, ContextType>; - status?: Resolver; - user?: Resolver; - userId?: Resolver; - __isTypeOf?: IsTypeOfResolverFn; -}; - export type UserConnectionResolvers = { aggregate?: Resolver; edges?: Resolver>, ParentType, ContextType>; @@ -2558,6 +2653,8 @@ export type Resolvers = { AggregateUser?: AggregateUserResolvers; AndroidFirebaseOptions?: AndroidFirebaseOptionsResolvers; AuthData?: AuthDataResolvers; + CheckIn?: CheckInResolvers; + CheckInStatus?: CheckInStatusResolvers; Comment?: CommentResolvers; ConnectionError?: ConnectionErrorResolvers; ConnectionPageInfo?: ConnectionPageInfoResolvers; @@ -2570,7 +2667,7 @@ export type Resolvers = { EmailAddress?: GraphQLScalarType; Error?: ErrorResolvers; Event?: EventResolvers; - EventRegistrants?: EventRegistrantsResolvers; + EventProject?: EventProjectResolvers; ExtendSession?: ExtendSessionResolvers; FieldError?: FieldErrorResolvers; Group?: GroupResolvers; @@ -2609,7 +2706,6 @@ export type Resolvers = { UnauthenticatedError?: UnauthenticatedErrorResolvers; UnauthorizedError?: UnauthorizedErrorResolvers; User?: UserResolvers; - UserAttende?: UserAttendeResolvers; UserConnection?: UserConnectionResolvers; UserEdge?: UserEdgeResolvers; UserTag?: UserTagResolvers; diff --git a/tests/helpers/checkIn.ts b/tests/helpers/checkIn.ts new file mode 100644 index 0000000000..330f078914 --- /dev/null +++ b/tests/helpers/checkIn.ts @@ -0,0 +1,44 @@ +import { nanoid } from "nanoid"; +import { + CheckIn, + EventAttendee, + type InterfaceCheckIn, +} from "../../src/models"; +import type { Document } from "mongoose"; +import { createTestEventWithRegistrants } from "./eventsWithRegistrants"; +import type { TestOrganizationType, TestUserType } from "./userAndOrg"; +import type { TestEventType } from "./task"; + +export type TestCheckInType = + | (InterfaceCheckIn & Document) + | null; + +export const createEventWithCheckedInUser = async (): Promise< + [TestUserType, TestOrganizationType, TestEventType, TestCheckInType] +> => { + const [testUser, testOrg, testEvent] = await createTestEventWithRegistrants(); + + const eventAttendee = await EventAttendee.findOne({ + userId: testUser!._id, + eventId: testEvent!._id, + }).lean(); + + const checkIn = await CheckIn.create({ + eventAttendeeId: eventAttendee!._id, + allotedRoom: nanoid(), + allotedSeat: nanoid(), + time: new Date(), + }); + + await EventAttendee.updateOne( + { + userId: testUser!._id, + eventId: testEvent!._id, + }, + { + checkInId: checkIn!._id, + } + ); + + return [testUser, testOrg, testEvent, checkIn]; +}; diff --git a/tests/helpers/donation.ts b/tests/helpers/donation.ts index 7d4f1e569d..e74e965dff 100644 --- a/tests/helpers/donation.ts +++ b/tests/helpers/donation.ts @@ -1,6 +1,5 @@ import type { TestOrganizationType, TestUserType } from "./userAndOrg"; import { createTestUserAndOrganization } from "./userAndOrg"; - import type { InterfaceDonation } from "../../src/models"; import { Donation } from "../../src/models"; import type { Document } from "mongoose"; diff --git a/tests/helpers/events.ts b/tests/helpers/events.ts index 0f40931720..3a75b46067 100644 --- a/tests/helpers/events.ts +++ b/tests/helpers/events.ts @@ -1,7 +1,7 @@ import type { TestOrganizationType, TestUserType } from "./userAndOrg"; import { createTestUserAndOrganization } from "./userAndOrg"; import type { InterfaceEvent } from "../../src/models"; -import { Event, User } from "../../src/models"; +import { Event, EventAttendee, User } from "../../src/models"; import type { Document } from "mongoose"; import { nanoid } from "nanoid"; @@ -27,7 +27,6 @@ export const createTestEvent = async (): Promise< isRegisterable: true, creator: testUser._id, admins: [testUser._id], - registrants: [], organization: testOrganization._id, }); @@ -58,12 +57,6 @@ export const createEventWithRegistrant = async ( ): Promise => { const testEvent = await Event.create({ creator: userId, - registrants: [ - { - userId: userId, - user: userId, - }, - ], admins: [userId], organization: organizationId, isRegisterable: true, @@ -79,6 +72,11 @@ export const createEventWithRegistrant = async ( location: `location${nanoid()}`, }); + await EventAttendee.create({ + userId, + eventId: testEvent!._id, + }); + await User.updateOne( { _id: userId, diff --git a/tests/helpers/eventsWithRegistrants.ts b/tests/helpers/eventsWithRegistrants.ts index 23f8622693..068848a4f3 100644 --- a/tests/helpers/eventsWithRegistrants.ts +++ b/tests/helpers/eventsWithRegistrants.ts @@ -1,7 +1,7 @@ import type { TestOrganizationType, TestUserType } from "./userAndOrg"; import { createTestUserAndOrganization } from "./userAndOrg"; import type { InterfaceEvent } from "../../src/models"; -import { Event, User } from "../../src/models"; +import { Event, EventAttendee, User } from "../../src/models"; import type { Document } from "mongoose"; export type TestEventType = @@ -11,45 +11,37 @@ export type TestEventType = export const createTestEventWithRegistrants = async ( isAdmin = true ): Promise<[TestUserType, TestOrganizationType, TestEventType]> => { - const resultsArray = await createTestUserAndOrganization(); - const testUser = resultsArray[0]; - const testOrganization = resultsArray[1]; + const [testUser, testOrganization] = await createTestUserAndOrganization(); - if (testUser && testOrganization) { - const testEvent = await Event.create({ - creator: testUser._id, - registrants: [ - { - userId: testUser._id, - user: testUser._id, - status: "ACTIVE", - }, - ], - admins: [testUser._id], - organization: testOrganization._id, - isRegisterable: true, - isPublic: true, - title: "title", - description: "description", - allDay: true, - startDate: new Date().toString(), - }); + const testEvent = await Event.create({ + creator: testUser!._id, + admins: [testUser!._id], + organization: testOrganization!._id, + isRegisterable: true, + isPublic: true, + title: "title", + description: "description", + allDay: true, + startDate: new Date().toString(), + }); - await User.updateOne( - { - _id: testUser._id, + await EventAttendee.create({ + userId: testUser!._id, + eventId: testEvent!._id, + }); + + await User.updateOne( + { + _id: testUser!._id, + }, + { + $push: { + eventAdmin: isAdmin ? testEvent._id : [], + createdEvents: testEvent._id, + registeredEvents: testEvent._id, }, - { - $push: { - eventAdmin: isAdmin ? testEvent._id : [], - createdEvents: testEvent._id, - registeredEvents: testEvent._id, - }, - } - ); + } + ); - return [testUser, testOrganization, testEvent]; - } else { - return [testUser, testOrganization, null]; - } + return [testUser, testOrganization, testEvent]; }; diff --git a/tests/helpers/task.ts b/tests/helpers/task.ts index efc6cef487..0ef2d7a229 100644 --- a/tests/helpers/task.ts +++ b/tests/helpers/task.ts @@ -1,36 +1,85 @@ -import type { InterfaceEvent, InterfaceTask } from "../../src/models"; -import { Event, Task } from "../../src/models"; +import type { + InterfaceEvent, + InterfaceEventProject, + InterfaceTask, +} from "../../src/models"; +import { EventProject, Task, TaskVolunteer } from "../../src/models"; import type { Document } from "mongoose"; import { nanoid } from "nanoid"; +import { createTestEvent } from "./events"; +import type { TestOrganizationType, TestUserType } from "./userAndOrg"; export type TestEventType = | (InterfaceEvent & Document) | null; +export type TestEventProjectType = + | (InterfaceEventProject & Document) + | null; + export type TestTaskType = | (InterfaceTask & Document) | null; +export const createTestEventProject = async (): Promise< + [TestUserType, TestOrganizationType, TestEventType, TestEventProjectType] +> => { + const [testUser, testOrg, testEvent] = await createTestEvent(); + const testEventProject = await EventProject.create({ + title: `test${nanoid()}`, + description: `testDesc${nanoid()}`, + event: testEvent!._id, + creator: testUser!._id, + }); + + return [testUser, testOrg, testEvent, testEventProject]; +}; + +export const createAndAssignTestTask = async (): Promise< + [ + TestUserType, + TestOrganizationType, + TestEventType, + TestEventProjectType, + TestTaskType + ] +> => { + const [testUser, testOrg, testEvent, testEventProject] = + await createTestEventProject(); + + const testTask = await Task.create({ + title: `test${nanoid()}`, + description: `testDesc${nanoid()}`, + creator: testUser!._id, + eventProjectId: testEventProject!._id, + }); + + // Assign the task to the user + await TaskVolunteer.create({ userId: testUser!._id, taskId: testTask!._id }); + + return [testUser, testOrg, testEvent, testEventProject, testTask]; +}; + export const createTestTask = async ( - eventId: string, - creatorId: string -): Promise => { + eventID: string, + userID: string +): Promise<[TestEventProjectType, TestTaskType]> => { + const testEventProject = await EventProject.create({ + title: `test${nanoid()}`, + description: `testDesc${nanoid()}`, + event: eventID, + creator: userID, + }); + const testTask = await Task.create({ - title: `title${nanoid().toLowerCase()}`, - event: eventId, - creator: creatorId, + title: `test${nanoid()}`, + description: `testDesc${nanoid()}`, + creator: userID, + eventProjectId: testEventProject!._id, }); - await Event.updateOne( - { - _id: eventId, - }, - { - $push: { - tasks: testTask._id, - }, - } - ); - - return testTask; + // Assign the task to the user + await TaskVolunteer.create({ userId: userID, taskId: testTask!._id }); + + return [testEventProject, testTask]; }; diff --git a/tests/resolvers/CheckIn/event.spec.ts b/tests/resolvers/CheckIn/event.spec.ts new file mode 100644 index 0000000000..66b3ea324b --- /dev/null +++ b/tests/resolvers/CheckIn/event.spec.ts @@ -0,0 +1,38 @@ +import "dotenv/config"; +import { event as eventResolver } from "../../../src/resolvers/CheckIn/event"; +import { connect, disconnect } from "../../helpers/db"; +import type mongoose from "mongoose"; +import { beforeAll, afterAll, describe, it, expect } from "vitest"; +import { Event } from "../../../src/models"; +import { + createEventWithCheckedInUser, + type TestCheckInType, +} from "../../helpers/checkIn"; +import type { TestEventType } from "../../helpers/eventsWithRegistrants"; + +let MONGOOSE_INSTANCE: typeof mongoose; +let testEvent: TestEventType; +let testCheckIn: TestCheckInType; + +beforeAll(async () => { + MONGOOSE_INSTANCE = await connect(); + [, , testEvent, testCheckIn] = await createEventWithCheckedInUser(); +}); + +afterAll(async () => { + await disconnect(MONGOOSE_INSTANCE); +}); + +describe("resolvers -> CheckIn -> Event", () => { + it(`returns the event object for parent post`, async () => { + const parent = testCheckIn!.toObject(); + + const eventPayload = await eventResolver?.(parent, {}, {}); + + const eventObject = await Event.findOne({ + _id: testEvent!._id, + }).lean(); + + expect(eventPayload).toEqual(eventObject); + }); +}); diff --git a/tests/resolvers/CheckIn/user.spec.ts b/tests/resolvers/CheckIn/user.spec.ts new file mode 100644 index 0000000000..7215151f38 --- /dev/null +++ b/tests/resolvers/CheckIn/user.spec.ts @@ -0,0 +1,38 @@ +import "dotenv/config"; +import { user as userResolver } from "../../../src/resolvers/CheckIn/user"; +import { connect, disconnect } from "../../helpers/db"; +import type mongoose from "mongoose"; +import { beforeAll, afterAll, describe, it, expect } from "vitest"; +import { User } from "../../../src/models"; +import { + createEventWithCheckedInUser, + type TestCheckInType, +} from "../../helpers/checkIn"; +import type { TestUserType } from "../../helpers/userAndOrg"; + +let MONGOOSE_INSTANCE: typeof mongoose; +let testUser: TestUserType; +let testCheckIn: TestCheckInType; + +beforeAll(async () => { + MONGOOSE_INSTANCE = await connect(); + [testUser, , , testCheckIn] = await createEventWithCheckedInUser(); +}); + +afterAll(async () => { + await disconnect(MONGOOSE_INSTANCE); +}); + +describe("resolvers -> CheckIn -> user", () => { + it(`returns the user object for parent post`, async () => { + const parent = testCheckIn!.toObject(); + + const userPayload = await userResolver?.(parent, {}, {}); + + const userObject = await User.findOne({ + _id: testUser!._id, + }).lean(); + + expect(userPayload).toEqual(userObject); + }); +}); diff --git a/tests/resolvers/Event/attendees.spec.ts b/tests/resolvers/Event/attendees.spec.ts new file mode 100644 index 0000000000..637948d391 --- /dev/null +++ b/tests/resolvers/Event/attendees.spec.ts @@ -0,0 +1,36 @@ +import "dotenv/config"; +import { attendees as attendeesResolver } from "../../../src/resolvers/Event/attendees"; +import { connect, disconnect } from "../../helpers/db"; +import type mongoose from "mongoose"; +import { beforeAll, afterAll, describe, it, expect } from "vitest"; +import { createTestEventWithRegistrants } from "../../helpers/eventsWithRegistrants"; +import type { TestUserType } from "../../helpers/userAndOrg"; +import type { TestEventType } from "../../helpers/events"; +import { User } from "../../../src/models"; + +let MONGOOSE_INSTANCE: typeof mongoose; +let testUser: TestUserType; +let testEvent: TestEventType; + +beforeAll(async () => { + MONGOOSE_INSTANCE = await connect(); + [testUser, , testEvent] = await createTestEventWithRegistrants(); +}); + +afterAll(async () => { + await disconnect(MONGOOSE_INSTANCE); +}); + +describe("resolvers -> Event -> Attendees", () => { + it(`returns the attendee user objects for parent event`, async () => { + const parent = testEvent!.toObject(); + + const attendeesPayload = await attendeesResolver?.(parent, {}, {}); + + const attendeeObject = await User.findOne({ + _id: testUser!._id, + }).lean(); + + expect(attendeesPayload).toEqual([attendeeObject]); + }); +}); diff --git a/tests/resolvers/Event/attendeesCheckInStatus.spec.ts b/tests/resolvers/Event/attendeesCheckInStatus.spec.ts new file mode 100644 index 0000000000..1d0d41a517 --- /dev/null +++ b/tests/resolvers/Event/attendeesCheckInStatus.spec.ts @@ -0,0 +1,43 @@ +import "dotenv/config"; +import { attendeesCheckInStatus as attendeesResolver } from "../../../src/resolvers/Event/attendeesCheckInStatus"; +import { connect, disconnect } from "../../helpers/db"; +import type mongoose from "mongoose"; +import { beforeAll, afterAll, describe, it, expect } from "vitest"; +import { createEventWithCheckedInUser } from "../../helpers/checkIn"; +import type { TestEventType } from "../../helpers/events"; +import { EventAttendee } from "../../../src/models"; + +let MONGOOSE_INSTANCE: typeof mongoose; +let testEvent: TestEventType; + +beforeAll(async () => { + MONGOOSE_INSTANCE = await connect(); + [, , testEvent] = await createEventWithCheckedInUser(); +}); + +afterAll(async () => { + await disconnect(MONGOOSE_INSTANCE); +}); + +describe("resolvers -> Event -> attendeesCheckInStatus", () => { + it(`returns the attendeesCheckInStatus object for parent event`, async () => { + const parent = testEvent!.toObject(); + + const payload = await attendeesResolver?.(parent, {}, {}); + + const eventAttendeeObjects = await EventAttendee.find({ + eventId: testEvent!._id, + }) + .populate("userId") + .populate("checkInId") + .lean(); + + const statusObject = eventAttendeeObjects.map((obj) => ({ + user: obj.userId, + _id: obj._id.toString(), + checkIn: obj.checkInId, + })); + + expect(payload).toEqual(statusObject); + }); +}); diff --git a/tests/resolvers/Event/organization.spec.ts b/tests/resolvers/Event/organization.spec.ts new file mode 100644 index 0000000000..ac5dddd3b4 --- /dev/null +++ b/tests/resolvers/Event/organization.spec.ts @@ -0,0 +1,36 @@ +import "dotenv/config"; +import { organization as attendeesResolver } from "../../../src/resolvers/Event/organization"; +import { connect, disconnect } from "../../helpers/db"; +import type mongoose from "mongoose"; +import { beforeAll, afterAll, describe, it, expect } from "vitest"; +import { createTestEventWithRegistrants } from "../../helpers/eventsWithRegistrants"; +import type { TestEventType } from "../../helpers/events"; +import { Organization } from "../../../src/models"; +import { type TestOrganizationType } from "../../helpers/userAndOrg"; + +let MONGOOSE_INSTANCE: typeof mongoose; +let testOrg: TestOrganizationType; +let testEvent: TestEventType; + +beforeAll(async () => { + MONGOOSE_INSTANCE = await connect(); + [, testOrg, testEvent] = await createTestEventWithRegistrants(); +}); + +afterAll(async () => { + await disconnect(MONGOOSE_INSTANCE); +}); + +describe("resolvers -> Event -> organization", () => { + it(`returns the organization object for parent event`, async () => { + const parent = testEvent!.toObject(); + + const orgPayload = await attendeesResolver?.(parent, {}, {}); + + const orgObject = await Organization.findOne({ + _id: testOrg!._id, + }).lean(); + + expect(orgPayload).toEqual(orgObject); + }); +}); diff --git a/tests/resolvers/Event/projects.spec.ts b/tests/resolvers/Event/projects.spec.ts new file mode 100644 index 0000000000..ee9f19d7df --- /dev/null +++ b/tests/resolvers/Event/projects.spec.ts @@ -0,0 +1,38 @@ +import "dotenv/config"; +import { projects as projectsResolver } from "../../../src/resolvers/Event/projects"; +import { connect, disconnect } from "../../helpers/db"; +import type mongoose from "mongoose"; +import { beforeAll, afterAll, describe, it, expect } from "vitest"; +import type { TestEventType } from "../../helpers/events"; +import { + createAndAssignTestTask, + type TestEventProjectType, +} from "../../helpers/task"; +import { EventProject } from "../../../src/models"; + +let MONGOOSE_INSTANCE: typeof mongoose; +let testEvent: TestEventType; +let testProject: TestEventProjectType; + +beforeAll(async () => { + MONGOOSE_INSTANCE = await connect(); + [, , testEvent, testProject] = await createAndAssignTestTask(); +}); + +afterAll(async () => { + await disconnect(MONGOOSE_INSTANCE); +}); + +describe("resolvers -> Event -> projects", () => { + it(`returns the Event project objects for parent event`, async () => { + const parent = testEvent!.toObject(); + + const projectsPayload = await projectsResolver?.(parent, {}, {}); + const projectObject = await EventProject.find({ + _id: testProject!._id, + }).lean(); + + expect(projectsPayload!.length).toEqual(1); + expect(projectsPayload).toEqual(projectObject); + }); +}); diff --git a/tests/resolvers/EventProject/tasks.spec.ts b/tests/resolvers/EventProject/tasks.spec.ts new file mode 100644 index 0000000000..5d687f48ed --- /dev/null +++ b/tests/resolvers/EventProject/tasks.spec.ts @@ -0,0 +1,39 @@ +import "dotenv/config"; +import { tasks as tasksResolver } from "../../../src/resolvers/EventProject/tasks"; +import { connect, disconnect } from "../../helpers/db"; +import type mongoose from "mongoose"; +import { beforeAll, afterAll, describe, it, expect } from "vitest"; +import { + createAndAssignTestTask, + type TestEventProjectType, + type TestTaskType, +} from "../../helpers/task"; +import { Task } from "../../../src/models"; + +let MONGOOSE_INSTANCE: typeof mongoose; +let testEventProject: TestEventProjectType; +let testTask: TestTaskType; + +beforeAll(async () => { + MONGOOSE_INSTANCE = await connect(); + [, , , testEventProject, testTask] = await createAndAssignTestTask(); +}); + +afterAll(async () => { + await disconnect(MONGOOSE_INSTANCE); +}); + +describe("resolvers -> EventProject -> Tasks", () => { + it(`returns the tasks objects for parent event project`, async () => { + const parent = testEventProject!.toObject(); + + const payload = await tasksResolver?.(parent, {}, {}); + + const taskObject = await Task.find({ + _id: testTask!._id, + }).lean(); + + expect(payload!.length).toEqual(1); + expect(payload).toMatchObject(taskObject); + }); +}); diff --git a/tests/resolvers/Mutation/addEventAttendee.spec.ts b/tests/resolvers/Mutation/addEventAttendee.spec.ts new file mode 100644 index 0000000000..8c6aeb256e --- /dev/null +++ b/tests/resolvers/Mutation/addEventAttendee.spec.ts @@ -0,0 +1,212 @@ +import "dotenv/config"; +import type mongoose from "mongoose"; +import { Types } from "mongoose"; +import { EventAttendee, User } from "../../../src/models"; +import type { MutationAddEventAttendeeArgs } from "../../../src/types/generatedGraphQLTypes"; +import { connect, disconnect } from "../../helpers/db"; +import { + EVENT_NOT_FOUND_ERROR, + USER_NOT_AUTHORIZED_ERROR, + USER_NOT_FOUND_ERROR, + USER_ALREADY_REGISTERED_FOR_EVENT, +} from "../../../src/constants"; +import { beforeAll, afterAll, describe, it, expect, vi } from "vitest"; +import { createTestUser, type TestUserType } from "../../helpers/userAndOrg"; +import { createTestEvent, type TestEventType } from "../../helpers/events"; + +let MONGOOSE_INSTANCE: typeof mongoose; +let testUser: TestUserType; +let randomTestUser: TestUserType; +let testEvent: TestEventType; + +beforeAll(async () => { + MONGOOSE_INSTANCE = await connect(); + randomTestUser = await createTestUser(); + [testUser, , testEvent] = await createTestEvent(); +}); + +afterAll(async () => { + await disconnect(MONGOOSE_INSTANCE); +}); + +describe("resolvers -> Mutation -> addEventAttendee", () => { + it(`throws NotFoundError if no user exists with _id === context.userId `, async () => { + const { requestContext } = await import("../../../src/libraries"); + + const spy = vi + .spyOn(requestContext, "translate") + .mockImplementationOnce((message) => `Translated ${message}`); + + try { + const args: MutationAddEventAttendeeArgs = { + data: { + userId: Types.ObjectId().toString(), + eventId: Types.ObjectId().toString(), + }, + }; + + const context = { userId: Types.ObjectId().toString() }; + + const { addEventAttendee: addEventAttendeeResolver } = await import( + "../../../src/resolvers/Mutation/addEventAttendee" + ); + + await addEventAttendeeResolver?.({}, args, context); + } catch (error: any) { + expect(error.message).toEqual( + `Translated ${USER_NOT_FOUND_ERROR.MESSAGE}` + ); + expect(spy).toHaveBeenLastCalledWith(USER_NOT_FOUND_ERROR.MESSAGE); + } + }); + + it(`throws NotFoundError if no event exists with _id === args.data.eventId`, async () => { + const { requestContext } = await import("../../../src/libraries"); + + const spy = vi + .spyOn(requestContext, "translate") + .mockImplementationOnce((message) => `Translated ${message}`); + + try { + const args: MutationAddEventAttendeeArgs = { + data: { + userId: Types.ObjectId().toString(), + eventId: Types.ObjectId().toString(), + }, + }; + + const context = { userId: randomTestUser!._id }; + + const { addEventAttendee: addEventAttendeeResolver } = await import( + "../../../src/resolvers/Mutation/addEventAttendee" + ); + + await addEventAttendeeResolver?.({}, args, context); + } catch (error: any) { + expect(error.message).toEqual( + `Translated ${EVENT_NOT_FOUND_ERROR.MESSAGE}` + ); + expect(spy).toHaveBeenLastCalledWith(EVENT_NOT_FOUND_ERROR.MESSAGE); + } + }); + + it(`throws Unauthorized error if the current user is not an admin of the event`, async () => { + const { requestContext } = await import("../../../src/libraries"); + + const spy = vi + .spyOn(requestContext, "translate") + .mockImplementationOnce((message) => `Translated ${message}`); + + try { + const args: MutationAddEventAttendeeArgs = { + data: { + userId: Types.ObjectId().toString(), + eventId: testEvent!._id, + }, + }; + + const context = { userId: randomTestUser!._id }; + + const { addEventAttendee: addEventAttendeeResolver } = await import( + "../../../src/resolvers/Mutation/addEventAttendee" + ); + + await addEventAttendeeResolver?.({}, args, context); + } catch (error: any) { + expect(error.message).toEqual( + `Translated ${USER_NOT_AUTHORIZED_ERROR.MESSAGE}` + ); + expect(spy).toHaveBeenLastCalledWith(USER_NOT_AUTHORIZED_ERROR.MESSAGE); + } + }); + + it(`throws NotFoundError if the request user with _id = args.data.userId does not exist`, async () => { + const { requestContext } = await import("../../../src/libraries"); + + const spy = vi + .spyOn(requestContext, "translate") + .mockImplementationOnce((message) => `Translated ${message}`); + + try { + const args: MutationAddEventAttendeeArgs = { + data: { + userId: Types.ObjectId().toString(), + eventId: testEvent!._id, + }, + }; + + const context = { userId: testUser!._id }; + + const { addEventAttendee: addEventAttendeeResolver } = await import( + "../../../src/resolvers/Mutation/addEventAttendee" + ); + + await addEventAttendeeResolver?.({}, args, context); + } catch (error: any) { + expect(error.message).toEqual( + `Translated ${USER_NOT_FOUND_ERROR.MESSAGE}` + ); + expect(spy).toHaveBeenLastCalledWith(USER_NOT_FOUND_ERROR.MESSAGE); + } + }); + + it(`registers the request user for the event successfully and returns the request user`, async () => { + const args: MutationAddEventAttendeeArgs = { + data: { + userId: testUser!._id, + eventId: testEvent!._id, + }, + }; + + const context = { userId: testUser!._id }; + + const { addEventAttendee: addEventAttendeeResolver } = await import( + "../../../src/resolvers/Mutation/addEventAttendee" + ); + + const payload = await addEventAttendeeResolver?.({}, args, context); + + const requestUser = await User.findOne({ + _id: testUser!._id, + }).lean(); + + const isUserRegistered = await EventAttendee.exists({ + ...args.data, + }); + + expect(payload).toEqual(requestUser); + expect(isUserRegistered).toBeTruthy(); + }); + + it(`throws error if the request user with _id = args.data.userId is already registered for the event`, async () => { + const { requestContext } = await import("../../../src/libraries"); + + const spy = vi + .spyOn(requestContext, "translate") + .mockImplementationOnce((message) => `Translated ${message}`); + + try { + const args: MutationAddEventAttendeeArgs = { + data: { + userId: testUser!._id, + eventId: testEvent!._id, + }, + }; + + const context = { userId: testUser!._id }; + + const { addEventAttendee: addEventAttendeeResolver } = await import( + "../../../src/resolvers/Mutation/addEventAttendee" + ); + + await addEventAttendeeResolver?.({}, args, context); + } catch (error: any) { + expect(error.message).toEqual( + `Translated ${USER_ALREADY_REGISTERED_FOR_EVENT.MESSAGE}` + ); + expect(spy).toHaveBeenLastCalledWith( + USER_ALREADY_REGISTERED_FOR_EVENT.MESSAGE + ); + } + }); +}); diff --git a/tests/resolvers/Mutation/checkIn.spec.ts b/tests/resolvers/Mutation/checkIn.spec.ts new file mode 100644 index 0000000000..373c16dd76 --- /dev/null +++ b/tests/resolvers/Mutation/checkIn.spec.ts @@ -0,0 +1,247 @@ +import "dotenv/config"; +import type mongoose from "mongoose"; +import { Types } from "mongoose"; +import { EventAttendee } from "../../../src/models"; +import type { MutationCheckInArgs } from "../../../src/types/generatedGraphQLTypes"; +import { connect, disconnect } from "../../helpers/db"; +import { + EVENT_NOT_FOUND_ERROR, + USER_NOT_AUTHORIZED_ERROR, + USER_NOT_FOUND_ERROR, + USER_NOT_REGISTERED_FOR_EVENT, + USER_ALREADY_CHECKED_IN, +} from "../../../src/constants"; +import { beforeAll, afterAll, describe, it, expect, vi } from "vitest"; +import { createTestUser, type TestUserType } from "../../helpers/userAndOrg"; +import { type TestEventType } from "../../helpers/events"; +import { createTestEventWithRegistrants } from "../../helpers/eventsWithRegistrants"; + +let MONGOOSE_INSTANCE: typeof mongoose; +let testUser: TestUserType; +let randomTestUser: TestUserType; +let testEvent: TestEventType; + +beforeAll(async () => { + MONGOOSE_INSTANCE = await connect(); + randomTestUser = await createTestUser(); + [testUser, , testEvent] = await createTestEventWithRegistrants(); +}); + +afterAll(async () => { + await disconnect(MONGOOSE_INSTANCE); +}); + +describe("resolvers -> Mutation -> checkIn", () => { + it(`throws NotFoundError if no user exists with _id === context.userId `, async () => { + const { requestContext } = await import("../../../src/libraries"); + + const spy = vi + .spyOn(requestContext, "translate") + .mockImplementationOnce((message) => `Translated ${message}`); + + try { + const args: MutationCheckInArgs = { + data: { + userId: Types.ObjectId().toString(), + eventId: Types.ObjectId().toString(), + }, + }; + + const context = { userId: Types.ObjectId().toString() }; + + const { checkIn: checkInResolver } = await import( + "../../../src/resolvers/Mutation/checkIn" + ); + + await checkInResolver?.({}, args, context); + } catch (error: any) { + expect(error.message).toEqual( + `Translated ${USER_NOT_FOUND_ERROR.MESSAGE}` + ); + expect(spy).toHaveBeenLastCalledWith(USER_NOT_FOUND_ERROR.MESSAGE); + } + }); + + it(`throws NotFoundError if no event exists with _id === args.data.eventId`, async () => { + const { requestContext } = await import("../../../src/libraries"); + + const spy = vi + .spyOn(requestContext, "translate") + .mockImplementationOnce((message) => `Translated ${message}`); + + try { + const args: MutationCheckInArgs = { + data: { + userId: Types.ObjectId().toString(), + eventId: Types.ObjectId().toString(), + }, + }; + + const context = { userId: randomTestUser!._id }; + + const { checkIn: checkInResolver } = await import( + "../../../src/resolvers/Mutation/checkIn" + ); + + await checkInResolver?.({}, args, context); + } catch (error: any) { + expect(error.message).toEqual( + `Translated ${EVENT_NOT_FOUND_ERROR.MESSAGE}` + ); + expect(spy).toHaveBeenLastCalledWith(EVENT_NOT_FOUND_ERROR.MESSAGE); + } + }); + + it(`throws Unauthorized Error if the current user is not an admin of the event`, async () => { + const { requestContext } = await import("../../../src/libraries"); + + const spy = vi + .spyOn(requestContext, "translate") + .mockImplementationOnce((message) => `Translated ${message}`); + + try { + const args: MutationCheckInArgs = { + data: { + userId: Types.ObjectId().toString(), + eventId: testEvent!._id, + }, + }; + + const context = { userId: randomTestUser!._id }; + + const { checkIn: checkInResolver } = await import( + "../../../src/resolvers/Mutation/checkIn" + ); + + await checkInResolver?.({}, args, context); + } catch (error: any) { + expect(error.message).toEqual( + `Translated ${USER_NOT_AUTHORIZED_ERROR.MESSAGE}` + ); + expect(spy).toHaveBeenLastCalledWith(USER_NOT_AUTHORIZED_ERROR.MESSAGE); + } + }); + + it(`throws NotFoundError if the request user with _id = args.data.userId does not exist`, async () => { + const { requestContext } = await import("../../../src/libraries"); + + const spy = vi + .spyOn(requestContext, "translate") + .mockImplementationOnce((message) => `Translated ${message}`); + + try { + const args: MutationCheckInArgs = { + data: { + userId: Types.ObjectId().toString(), + eventId: testEvent!._id, + }, + }; + + const context = { userId: testUser!._id }; + + const { checkIn: checkInResolver } = await import( + "../../../src/resolvers/Mutation/checkIn" + ); + + await checkInResolver?.({}, args, context); + } catch (error: any) { + expect(error.message).toEqual( + `Translated ${USER_NOT_FOUND_ERROR.MESSAGE}` + ); + expect(spy).toHaveBeenLastCalledWith(USER_NOT_FOUND_ERROR.MESSAGE); + } + }); + + it(`throws Conflict Error if the request user with _id = args.data.userId is not an attendee of the event with _id = args.data.eventId`, async () => { + const { requestContext } = await import("../../../src/libraries"); + + const spy = vi + .spyOn(requestContext, "translate") + .mockImplementationOnce((message) => `Translated ${message}`); + + try { + const args: MutationCheckInArgs = { + data: { + userId: randomTestUser!._id, + eventId: testEvent!._id, + }, + }; + + const context = { userId: testUser!._id }; + + const { checkIn: checkInResolver } = await import( + "../../../src/resolvers/Mutation/checkIn" + ); + + await checkInResolver?.({}, args, context); + } catch (error: any) { + expect(error.message).toEqual( + `Translated ${USER_NOT_REGISTERED_FOR_EVENT.MESSAGE}` + ); + expect(spy).toHaveBeenLastCalledWith( + USER_NOT_REGISTERED_FOR_EVENT.MESSAGE + ); + } + }); + + it(`Checks the user in successfully`, async () => { + const args: MutationCheckInArgs = { + data: { + userId: testUser!._id, + eventId: testEvent!._id, + allotedRoom: "test room", + allotedSeat: "test seat", + }, + }; + + const context = { userId: testUser!._id }; + + const { checkIn: checkInResolver } = await import( + "../../../src/resolvers/Mutation/checkIn" + ); + + const payload = await checkInResolver?.({}, args, context); + + const eventAttendee = await EventAttendee.findOne({ + eventId: testEvent!._id, + userId: testUser!._id, + }).lean(); + + expect(eventAttendee!.checkInId).not.toBeNull(); + expect(payload).toMatchObject({ + eventAttendeeId: eventAttendee!._id, + allotedSeat: "test seat", + allotedRoom: "test room", + }); + }); + + it(`throws error if the request user with _id = args.data.userId is already checkedIn for the event`, async () => { + const { requestContext } = await import("../../../src/libraries"); + + const spy = vi + .spyOn(requestContext, "translate") + .mockImplementationOnce((message) => `Translated ${message}`); + + try { + const args: MutationCheckInArgs = { + data: { + userId: testUser!._id, + eventId: testEvent!._id, + }, + }; + + const context = { userId: testUser!._id }; + + const { checkIn: checkInResolver } = await import( + "../../../src/resolvers/Mutation/checkIn" + ); + + await checkInResolver?.({}, args, context); + } catch (error: any) { + expect(error.message).toEqual( + `Translated ${USER_ALREADY_CHECKED_IN.MESSAGE}` + ); + expect(spy).toHaveBeenLastCalledWith(USER_ALREADY_CHECKED_IN.MESSAGE); + } + }); +}); diff --git a/tests/resolvers/Mutation/createEvent.spec.ts b/tests/resolvers/Mutation/createEvent.spec.ts index 1d38963a1e..f5e2b19628 100644 --- a/tests/resolvers/Mutation/createEvent.spec.ts +++ b/tests/resolvers/Mutation/createEvent.spec.ts @@ -1,7 +1,7 @@ import "dotenv/config"; import type mongoose from "mongoose"; import { Types } from "mongoose"; -import { User, Organization } from "../../../src/models"; +import { User, Organization, EventAttendee } from "../../../src/models"; import type { MutationCreateEventArgs } from "../../../src/types/generatedGraphQLTypes"; import { connect, disconnect } from "../../helpers/db"; @@ -202,17 +202,18 @@ describe("resolvers -> Mutation -> createEvent", () => { title: "newTitle", recurrance: "DAILY", creator: testUser?._id, - registrants: expect.arrayContaining([ - expect.objectContaining({ - userId: testUser?._id.toString(), - user: testUser?._id, - }), - ]), admins: expect.arrayContaining([testUser?._id]), organization: testOrganization?._id, }) ); + const attendeeExists = await EventAttendee.exists({ + userId: testUser!._id, + eventId: createEventPayload!._id, + }); + + expect(attendeeExists).toBeTruthy(); + const updatedTestUser = await User.findOne({ _id: testUser?._id, }) diff --git a/tests/resolvers/Mutation/createEventProject.spec.ts b/tests/resolvers/Mutation/createEventProject.spec.ts index 8226fe5914..545470e76d 100644 --- a/tests/resolvers/Mutation/createEventProject.spec.ts +++ b/tests/resolvers/Mutation/createEventProject.spec.ts @@ -22,6 +22,7 @@ import type { TestUserType, } from "../../helpers/userAndOrg"; import { createTestUser } from "../../helpers/userAndOrg"; +import { Types } from "mongoose"; let testUser: TestUserType; let testAdminUser: TestUserType; @@ -92,7 +93,9 @@ describe("resolvers -> Mutation -> createEventProject", () => { try { const args = { data: { - eventId: null, + title: "Test Event", + description: "Test Description", + eventId: Types.ObjectId().toString(), }, }; @@ -100,7 +103,7 @@ describe("resolvers -> Mutation -> createEventProject", () => { "../../../src/resolvers/Mutation/createEventProject" ); - await createEventProject(null, args, { user: null }); + await createEventProject!({}, args, { user: null }); } catch (err: any) { expect(spy).toBeCalledWith(USER_NOT_FOUND_ERROR.MESSAGE); expect(err.message).toEqual(`Translated ${USER_NOT_FOUND_ERROR.MESSAGE}`); @@ -110,7 +113,9 @@ describe("resolvers -> Mutation -> createEventProject", () => { it("Should throw an error if the event is not found", async () => { const args = { data: { - eventId: null, + title: "Test Event", + description: "Test Description", + eventId: Types.ObjectId().toString(), }, }; @@ -129,7 +134,7 @@ describe("resolvers -> Mutation -> createEventProject", () => { ); try { - await createEventProject(null, args, context); + await createEventProject!({}, args, context); } catch (error: any) { expect(spy).toBeCalledWith(EVENT_NOT_FOUND_ERROR.MESSAGE); expect(error.message).toEqual( @@ -141,10 +146,11 @@ describe("resolvers -> Mutation -> createEventProject", () => { it("Should throw an error if the user is not an admin of the event", async () => { const args = { data: { - eventId: testEvent?._id, + title: "Test Event", + description: "Test Description", + eventId: testEvent!._id, }, }; - const context = { userId: testUser?._id, }; @@ -160,7 +166,7 @@ describe("resolvers -> Mutation -> createEventProject", () => { ); try { - await createEventProject(null, args, context); + await createEventProject!({}, args, context); } catch (error: any) { expect(spy).toBeCalledWith(USER_NOT_AUTHORIZED_ERROR.MESSAGE); expect(error.message).toEqual( @@ -187,7 +193,7 @@ describe("resolvers -> Mutation -> createEventProject", () => { "../../../src/resolvers/Mutation/createEventProject" ); - const result = await createEventProject(null, args, context); + const result = await createEventProject!({}, args, context); expect(result).toHaveProperty("event", testEvent?._id); expect(result).toHaveProperty("title", args.data.title); diff --git a/tests/resolvers/Mutation/createTask.spec.ts b/tests/resolvers/Mutation/createTask.spec.ts index af1afd966a..b4a64ff918 100644 --- a/tests/resolvers/Mutation/createTask.spec.ts +++ b/tests/resolvers/Mutation/createTask.spec.ts @@ -1,22 +1,22 @@ import "dotenv/config"; import type mongoose from "mongoose"; import { Types } from "mongoose"; -import { Event } from "../../../src/models"; import type { MutationCreateTaskArgs } from "../../../src/types/generatedGraphQLTypes"; import { connect, disconnect } from "../../helpers/db"; - import { createTask as createTaskResolver } from "../../../src/resolvers/Mutation/createTask"; import { EVENT_NOT_FOUND_ERROR, USER_NOT_FOUND_ERROR, + USER_NOT_AUTHORIZED_ERROR, } from "../../../src/constants"; import { beforeAll, afterAll, describe, it, expect, vi } from "vitest"; -import type { TestUserType } from "../../helpers/userAndOrg"; -import { createTestEventWithRegistrants } from "../../helpers/eventsWithRegistrants"; -import type { TestEventType } from "../../helpers/events"; +import { createTestUser, type TestUserType } from "../../helpers/userAndOrg"; +import { createAndAssignTestTask } from "../../helpers/task"; +import type { TestEventProjectType } from "../../helpers/task"; +let randomUser: TestUserType; let testUser: TestUserType; -let testEvent: TestEventType; +let testEventProject: TestEventProjectType; let MONGOOSE_INSTANCE: typeof mongoose; beforeAll(async () => { @@ -25,9 +25,9 @@ beforeAll(async () => { vi.spyOn(requestContext, "translate").mockImplementation( (message) => message ); - const temp = await createTestEventWithRegistrants(); - testUser = temp[0]; - testEvent = temp[2]; + + randomUser = await createTestUser(); + [testUser, , , testEventProject] = await createAndAssignTestTask(); }); afterAll(async () => { @@ -38,7 +38,12 @@ describe("resolvers -> Mutation -> createTask", () => { it(`throws NotFoundError if no user exists with _id === context.userId`, async () => { try { const args: MutationCreateTaskArgs = { - eventId: "", + eventProjectId: testEventProject!._id, + data: { + title: `Test Task`, + description: `Test Description`, + deadline: new Date().toDateString(), + }, }; const context = { @@ -54,7 +59,12 @@ describe("resolvers -> Mutation -> createTask", () => { it(`throws NotFoundError if no event exists with _id === args.eventId`, async () => { try { const args: MutationCreateTaskArgs = { - eventId: Types.ObjectId().toString(), + eventProjectId: Types.ObjectId().toString(), + data: { + title: `Test Task`, + description: `Test Description`, + deadline: new Date().toDateString(), + }, }; const context = { @@ -67,9 +77,30 @@ describe("resolvers -> Mutation -> createTask", () => { } }); + it(`throws NotAuthorizedError if the user is not a superadmin on the creator of the event project`, async () => { + try { + const args: MutationCreateTaskArgs = { + eventProjectId: testEventProject!._id, + data: { + title: `Test Task`, + description: `Test Description`, + deadline: new Date().toDateString(), + }, + }; + + const context = { + userId: randomUser!.id, + }; + + await createTaskResolver?.({}, args, context); + } catch (error: any) { + expect(error.message).toEqual(USER_NOT_AUTHORIZED_ERROR.MESSAGE); + } + }); + it(`creates the task and returns it`, async () => { const args: MutationCreateTaskArgs = { - eventId: testEvent?.id, + eventProjectId: testEventProject!.id, data: { title: "title", deadline: new Date().toString(), @@ -90,13 +121,5 @@ describe("resolvers -> Mutation -> createTask", () => { }) ); expect(createTaskPayload?.deadline).toBeInstanceOf(Date); - - const testUpdatedEvent = await Event.findOne({ - _id: testEvent?._id, - }) - .select(["tasks"]) - .lean(); - - expect(testUpdatedEvent?.tasks).toEqual([createTaskPayload?._id]); }); }); diff --git a/tests/resolvers/Mutation/registerForEvent.spec.ts b/tests/resolvers/Mutation/registerForEvent.spec.ts index 42c81a94d0..41da21cc4d 100644 --- a/tests/resolvers/Mutation/registerForEvent.spec.ts +++ b/tests/resolvers/Mutation/registerForEvent.spec.ts @@ -1,7 +1,7 @@ import "dotenv/config"; import type mongoose from "mongoose"; import { Types } from "mongoose"; -import { User, Event } from "../../../src/models"; +import { User, Event, EventAttendee } from "../../../src/models"; import type { MutationRegisterForEventArgs } from "../../../src/types/generatedGraphQLTypes"; import { connect, disconnect } from "../../helpers/db"; @@ -30,38 +30,7 @@ let testEvent: TestEventType; beforeAll(async () => { MONGOOSE_INSTANCE = await connect(); - const temp = await createTestEventWithRegistrants(); - testUser = temp[0]; - const testOrganization = temp[1]; - - testEvent = await Event.create({ - creator: testUser?._id, - organization: testOrganization?._id, - isRegisterable: true, - isPublic: true, - title: "title", - description: "description", - allDay: true, - startDate: new Date().toString(), - registrants: [ - { - userId: testUser?.id, - user: testUser?._id, - status: "ACTIVE", - }, - ], - }); - - await User.updateOne( - { - _id: testUser?._id, - }, - { - $set: { - createdEvents: [testEvent._id], - }, - } - ); + [testUser, , testEvent] = await createTestEventWithRegistrants(); }); afterAll(async () => { @@ -104,6 +73,7 @@ describe("resolvers -> Mutation -> registerForEvent", () => { const spy = vi .spyOn(requestContext, "translate") .mockImplementationOnce((message) => message); + try { const args: MutationRegisterForEventArgs = { id: Types.ObjectId().toString(), @@ -124,21 +94,19 @@ describe("resolvers -> Mutation -> registerForEvent", () => { } }); - it(`throws NotFoundError if user with _id === context.userId is already a - registrant of event with _id === args.id and event.registrant.status === "ACTIVE" - for the registrant with registrant.userId === context.userId - `, async () => { + it(`throws error if user with _id === context.userId is already a registrant of event with _id === args.id`, async () => { const { requestContext } = await import("../../../src/libraries"); const spy = vi .spyOn(requestContext, "translate") .mockImplementationOnce((message) => message); + try { const args: MutationRegisterForEventArgs = { - id: testEvent?._id, + id: testEvent!._id, }; const context = { - userId: testUser?._id, + userId: testUser!._id, }; const { registerForEvent: registerForEventResolver } = await import( @@ -152,59 +120,11 @@ describe("resolvers -> Mutation -> registerForEvent", () => { } }); - it(`if user with _id === context.userId is already a registrant for event with _id === args.id - sets event.registrant.status field to "ACTIVE" for registrant with - _id === context.userId`, async () => { - await Event.updateOne( - { - _id: testEvent?._id, - }, - { - $set: { - registrants: [ - { - userId: testUser?.id, - user: testUser?._id, - status: "BLOCKED", - }, - ], - }, - } - ); - - const args: MutationRegisterForEventArgs = { - id: testEvent?._id, - }; - - const context = { - userId: testUser?._id, - }; - - const registerForEventPayload = await registerForEventResolver?.( - {}, - args, - context - ); - - const testRegisterForEventPayload = await Event.findOne({ - _id: testEvent?._id, - }).lean(); - - expect(registerForEventPayload).toEqual(testRegisterForEventPayload); - }); - - it(`registers user with _id === context.userId as a registrant for event with - _id === args.id`, async () => { - await Event.updateOne( - { - _id: testEvent?._id, - }, - { - $set: { - registrants: [], - }, - } - ); + it(`registers user with _id === context.userId as a registrant for event with _id === args.id`, async () => { + await EventAttendee.deleteOne({ + userId: testUser!._id, + eventId: testEvent!._id, + }); await User.updateOne( { @@ -218,11 +138,11 @@ describe("resolvers -> Mutation -> registerForEvent", () => { ); const args: MutationRegisterForEventArgs = { - id: testEvent?._id, + id: testEvent!._id, }; const context = { - userId: testUser?._id, + userId: testUser!._id, }; const registerForEventPayload = await registerForEventResolver?.( diff --git a/tests/resolvers/Mutation/removeEventAttendee.spec.ts b/tests/resolvers/Mutation/removeEventAttendee.spec.ts new file mode 100644 index 0000000000..1ed20abac6 --- /dev/null +++ b/tests/resolvers/Mutation/removeEventAttendee.spec.ts @@ -0,0 +1,214 @@ +import "dotenv/config"; +import type mongoose from "mongoose"; +import { Types } from "mongoose"; +import { EventAttendee, User } from "../../../src/models"; +import type { MutationRemoveEventAttendeeArgs } from "../../../src/types/generatedGraphQLTypes"; +import { connect, disconnect } from "../../helpers/db"; +import { + EVENT_NOT_FOUND_ERROR, + USER_NOT_AUTHORIZED_ERROR, + USER_NOT_FOUND_ERROR, + USER_NOT_REGISTERED_FOR_EVENT, +} from "../../../src/constants"; +import { beforeAll, afterAll, describe, it, expect, vi } from "vitest"; +import { createTestUser, type TestUserType } from "../../helpers/userAndOrg"; +import { createTestEvent, type TestEventType } from "../../helpers/events"; + +let MONGOOSE_INSTANCE: typeof mongoose; +let testUser: TestUserType; +let randomTestUser: TestUserType; +let testEvent: TestEventType; + +beforeAll(async () => { + MONGOOSE_INSTANCE = await connect(); + randomTestUser = await createTestUser(); + [testUser, , testEvent] = await createTestEvent(); +}); + +afterAll(async () => { + await disconnect(MONGOOSE_INSTANCE); +}); + +describe("resolvers -> Mutation -> removeEventAttendee", () => { + it(`throws NotFoundError if no user exists with _id === context.userId `, async () => { + const { requestContext } = await import("../../../src/libraries"); + + const spy = vi + .spyOn(requestContext, "translate") + .mockImplementationOnce((message) => `Translated ${message}`); + + try { + const args: MutationRemoveEventAttendeeArgs = { + data: { + userId: Types.ObjectId().toString(), + eventId: Types.ObjectId().toString(), + }, + }; + + const context = { userId: Types.ObjectId().toString() }; + + const { removeEventAttendee: removeEventAttendeeResolver } = await import( + "../../../src/resolvers/Mutation/removeEventAttendee" + ); + + await removeEventAttendeeResolver?.({}, args, context); + } catch (error: any) { + expect(error.message).toEqual( + `Translated ${USER_NOT_FOUND_ERROR.MESSAGE}` + ); + expect(spy).toHaveBeenLastCalledWith(USER_NOT_FOUND_ERROR.MESSAGE); + } + }); + + it(`throws NotFoundError if no event exists with _id === args.data.eventId`, async () => { + const { requestContext } = await import("../../../src/libraries"); + + const spy = vi + .spyOn(requestContext, "translate") + .mockImplementationOnce((message) => `Translated ${message}`); + + try { + const args: MutationRemoveEventAttendeeArgs = { + data: { + userId: Types.ObjectId().toString(), + eventId: Types.ObjectId().toString(), + }, + }; + + const context = { userId: randomTestUser!._id }; + + const { removeEventAttendee: removeEventAttendeeResolver } = await import( + "../../../src/resolvers/Mutation/removeEventAttendee" + ); + + await removeEventAttendeeResolver?.({}, args, context); + } catch (error: any) { + expect(error.message).toEqual( + `Translated ${EVENT_NOT_FOUND_ERROR.MESSAGE}` + ); + expect(spy).toHaveBeenLastCalledWith(EVENT_NOT_FOUND_ERROR.MESSAGE); + } + }); + + it(`throws Unauthorized error if the current user is not an admin of the event`, async () => { + const { requestContext } = await import("../../../src/libraries"); + + const spy = vi + .spyOn(requestContext, "translate") + .mockImplementationOnce((message) => `Translated ${message}`); + + try { + const args: MutationRemoveEventAttendeeArgs = { + data: { + userId: Types.ObjectId().toString(), + eventId: testEvent!._id, + }, + }; + + const context = { userId: randomTestUser!._id }; + + const { removeEventAttendee: removeEventAttendeeResolver } = await import( + "../../../src/resolvers/Mutation/removeEventAttendee" + ); + + await removeEventAttendeeResolver?.({}, args, context); + } catch (error: any) { + expect(error.message).toEqual( + `Translated ${USER_NOT_AUTHORIZED_ERROR.MESSAGE}` + ); + expect(spy).toHaveBeenLastCalledWith(USER_NOT_AUTHORIZED_ERROR.MESSAGE); + } + }); + + it(`throws NotFoundError if the request user with _id = args.data.userId does not exist`, async () => { + const { requestContext } = await import("../../../src/libraries"); + + const spy = vi + .spyOn(requestContext, "translate") + .mockImplementationOnce((message) => `Translated ${message}`); + + try { + const args: MutationRemoveEventAttendeeArgs = { + data: { + userId: Types.ObjectId().toString(), + eventId: testEvent!._id, + }, + }; + + const context = { userId: testUser!._id }; + + const { removeEventAttendee: removeEventAttendeeResolver } = await import( + "../../../src/resolvers/Mutation/removeEventAttendee" + ); + + await removeEventAttendeeResolver?.({}, args, context); + } catch (error: any) { + expect(error.message).toEqual( + `Translated ${USER_NOT_FOUND_ERROR.MESSAGE}` + ); + expect(spy).toHaveBeenLastCalledWith(USER_NOT_FOUND_ERROR.MESSAGE); + } + }); + + it(`throws error if the request user with _id = args.data.userId is not registered for the event`, async () => { + const { requestContext } = await import("../../../src/libraries"); + + const spy = vi + .spyOn(requestContext, "translate") + .mockImplementationOnce((message) => `Translated ${message}`); + + try { + const args: MutationRemoveEventAttendeeArgs = { + data: { + userId: testUser!._id, + eventId: testEvent!._id, + }, + }; + + const context = { userId: testUser!._id }; + + const { removeEventAttendee: removeEventAttendeeResolver } = await import( + "../../../src/resolvers/Mutation/removeEventAttendee" + ); + + await removeEventAttendeeResolver?.({}, args, context); + } catch (error: any) { + expect(error.message).toEqual( + `Translated ${USER_NOT_REGISTERED_FOR_EVENT.MESSAGE}` + ); + expect(spy).toHaveBeenLastCalledWith( + USER_NOT_REGISTERED_FOR_EVENT.MESSAGE + ); + } + }); + + it(`unregisters the request user for the event successfully and returns the request user`, async () => { + const args: MutationRemoveEventAttendeeArgs = { + data: { + userId: testUser!._id, + eventId: testEvent!._id, + }, + }; + + const context = { userId: testUser!._id }; + + await EventAttendee.create({ ...args.data }); + + const { removeEventAttendee: removeEventAttendeeResolver } = await import( + "../../../src/resolvers/Mutation/removeEventAttendee" + ); + + const payload = await removeEventAttendeeResolver?.({}, args, context); + + const requestUser = await User.findOne({ + _id: testUser!._id, + }).lean(); + + const isUserRegistered = await EventAttendee.exists({ + ...args.data, + }); + + expect(payload).toEqual(requestUser); + expect(isUserRegistered).toBeFalsy(); + }); +}); diff --git a/tests/resolvers/Mutation/removeEventProject.spec.ts b/tests/resolvers/Mutation/removeEventProject.spec.ts index d4f60e97cd..824e0d9aa7 100644 --- a/tests/resolvers/Mutation/removeEventProject.spec.ts +++ b/tests/resolvers/Mutation/removeEventProject.spec.ts @@ -23,6 +23,7 @@ import { import type { TestUserType } from "../../helpers/userAndOrg"; import type { TestEventType } from "../../helpers/events"; import { createTestEvent } from "../../helpers/events"; +import { Types } from "mongoose"; let MONGOOSE_INSTANCE: typeof mongoose; let testUser: TestUserType; @@ -70,9 +71,7 @@ afterEach(() => { describe("resolvers -> Mutation -> removeEventProject", () => { it("Should throw an error if the user is not found", async () => { const args = { - data: { - eventId: null, - }, + id: Types.ObjectId().toString(), }; const context = { @@ -90,7 +89,7 @@ describe("resolvers -> Mutation -> removeEventProject", () => { ); try { - await removeEventProject(null, args, context); + await removeEventProject!({}, args, context); } catch (error: any) { expect(spy).toBeCalledWith(USER_NOT_FOUND_ERROR.MESSAGE); expect(error.message).toBe(`Translated ${USER_NOT_FOUND_ERROR.MESSAGE}`); @@ -98,7 +97,7 @@ describe("resolvers -> Mutation -> removeEventProject", () => { }); it("Should throw an error if the eventProject is not found", async () => { const args = { - id: null, + id: Types.ObjectId().toString(), }; const context = { @@ -116,7 +115,7 @@ describe("resolvers -> Mutation -> removeEventProject", () => { ); try { - await removeEventProject(null, args, context); + await removeEventProject!({}, args, context); } catch (error: any) { expect(spy).toBeCalledWith(EVENT_PROJECT_NOT_FOUND_ERROR.MESSAGE); expect(error.message).toBe( @@ -144,7 +143,7 @@ describe("resolvers -> Mutation -> removeEventProject", () => { ); try { - await removeEventProject(null, args, context); + await removeEventProject!({}, args, context); } catch (error: any) { expect(spy).toBeCalledWith(USER_NOT_AUTHORIZED_ERROR.MESSAGE); expect(error.message).toBe( @@ -165,7 +164,7 @@ describe("resolvers -> Mutation -> removeEventProject", () => { "../../../src/resolvers/Mutation/removeEventProject" ); - await removeEventProject(null, args, context); + await removeEventProject!({}, args, context); const eventProject = await EventProject.findOne(testEventProject._id); diff --git a/tests/resolvers/Mutation/removeTask.spec.ts b/tests/resolvers/Mutation/removeTask.spec.ts index 2160db1776..b400241cb0 100644 --- a/tests/resolvers/Mutation/removeTask.spec.ts +++ b/tests/resolvers/Mutation/removeTask.spec.ts @@ -1,9 +1,7 @@ import "dotenv/config"; -import type { Document } from "mongoose"; import type mongoose from "mongoose"; import { Types } from "mongoose"; -import type { InterfaceTask } from "../../../src/models"; -import { User, Organization, Event, Task } from "../../../src/models"; +import { Task } from "../../../src/models"; import type { MutationRemoveTaskArgs } from "../../../src/types/generatedGraphQLTypes"; import { connect, disconnect } from "../../helpers/db"; @@ -14,89 +12,18 @@ import { TASK_NOT_FOUND_ERROR, } from "../../../src/constants"; import { beforeAll, afterAll, describe, it, expect, vi } from "vitest"; -import { createTestUserFunc } from "../../helpers/user"; -import type { TestUserType } from "../../helpers/userAndOrg"; +import { createTestUser, type TestUserType } from "../../helpers/userAndOrg"; +import { createAndAssignTestTask, type TestTaskType } from "../../helpers/task"; let MONGOOSE_INSTANCE: typeof mongoose; -let testUsers: TestUserType[]; -let testTask: InterfaceTask & Document; +let testUser: TestUserType; +let randomTestUser: TestUserType; +let testTask: TestTaskType; beforeAll(async () => { MONGOOSE_INSTANCE = await connect(); - - const tempUser1 = await createTestUserFunc(); - const tempUser2 = await createTestUserFunc(); - testUsers = [tempUser1, tempUser2]; - - const testOrganization = await Organization.create({ - name: "name", - description: "description", - isPublic: true, - creator: testUsers[0]?._id, - admins: [testUsers[0]?._id], - members: [testUsers[0]?._id], - }); - - await User.updateOne( - { - _id: testUsers[0]?._id, - }, - { - $set: { - createdOrganizations: [testOrganization._id], - adminFor: [testOrganization._id], - joinedOrganizations: [testOrganization._id], - }, - } - ); - - const testEvent = await Event.create({ - creator: testUsers[0]?._id, - registrants: [ - { - userId: testUsers[0]?._id, - user: testUsers[0]?._id, - }, - ], - admins: [testUsers[0]?._id], - organization: testOrganization._id, - isRegisterable: true, - isPublic: true, - title: "title", - description: "description", - allDay: true, - startDate: new Date().toString(), - }); - - await User.updateOne( - { - _id: testUsers[0]?._id, - }, - { - $set: { - createdEvents: [testEvent._id], - registeredEvents: [testEvent._id], - eventAdmin: [testEvent._id], - }, - } - ); - - testTask = await Task.create({ - title: "title", - event: testEvent._id, - creator: testUsers[0]?._id, - }); - - await Event.updateOne( - { - _id: testEvent._id, - }, - { - $push: { - tasks: testTask._id, - }, - } - ); + randomTestUser = await createTestUser(); + [testUser, , , , testTask] = await createAndAssignTestTask(); }); afterAll(async () => { @@ -140,7 +67,7 @@ describe("resolvers -> Mutation -> removeTask", () => { }; const context = { - userId: testUsers[0]?._id, + userId: testUser!._id, }; const { removeTask: removeTaskResolver } = await import( @@ -161,11 +88,11 @@ describe("resolvers -> Mutation -> removeTask", () => { .mockImplementationOnce((message) => message); try { const args: MutationRemoveTaskArgs = { - id: testTask._id, + id: testTask!._id, }; const context = { - userId: testUsers[1]?._id, + userId: randomTestUser!._id, }; const { removeTask: removeTaskResolver } = await import( @@ -181,31 +108,21 @@ describe("resolvers -> Mutation -> removeTask", () => { it(`removes the task with _id === args.id and returns it`, async () => { const args: MutationRemoveTaskArgs = { - id: testTask._id, + id: testTask!._id, }; const context = { - userId: testUsers[0]?._id, + userId: testUser!._id, }; const removeTaskPayload = await removeTaskResolver?.({}, args, context); - expect(removeTaskPayload).toEqual(testTask.toObject()); + expect(removeTaskPayload).toEqual(testTask!.toObject()); const testRemovedTask = await Task.findOne({ - _id: testTask._id, + _id: testTask!._id, }).lean(); expect(testRemovedTask).toEqual(null); - - const testUpdatedEvents = await Event.find({ - _id: testTask.event, - }).lean(); - - testUpdatedEvents.forEach((testUpdatedEvent) => { - testUpdatedEvent.tasks.forEach((task) => { - expect(task.toString()).not.toEqual(testTask._id.toString()); - }); - }); }); }); diff --git a/tests/resolvers/Mutation/setTaskVolunteers.spec.ts b/tests/resolvers/Mutation/setTaskVolunteers.spec.ts new file mode 100644 index 0000000000..222d6e2aa2 --- /dev/null +++ b/tests/resolvers/Mutation/setTaskVolunteers.spec.ts @@ -0,0 +1,219 @@ +import "dotenv/config"; +import type mongoose from "mongoose"; +import { Types } from "mongoose"; +import { TaskVolunteer } from "../../../src/models"; +import type { MutationSetTaskVolunteersArgs } from "../../../src/types/generatedGraphQLTypes"; +import { connect, disconnect } from "../../helpers/db"; +import { setTaskVolunteers as setTaskVolunteersResolver } from "../../../src/resolvers/Mutation/setTaskVolunteers"; +import { + TASK_NOT_FOUND_ERROR, + USER_NOT_AUTHORIZED_ERROR, + USER_NOT_FOUND_ERROR, + VOLUNTEER_NOT_FOUND_ERROR, + VOLUNTEER_NOT_MEMBER_ERROR, +} from "../../../src/constants"; +import { beforeAll, afterAll, describe, it, expect, vi } from "vitest"; +import { createTestUser, type TestUserType } from "../../helpers/userAndOrg"; +import { createAndAssignTestTask, type TestTaskType } from "../../helpers/task"; + +let MONGOOSE_INSTANCE: typeof mongoose; +let testUser: TestUserType; +let randomTestUser: TestUserType; +let testTask: TestTaskType; + +beforeAll(async () => { + MONGOOSE_INSTANCE = await connect(); + randomTestUser = await createTestUser(); + [testUser, , , , testTask] = await createAndAssignTestTask(); +}); + +afterAll(async () => { + await disconnect(MONGOOSE_INSTANCE); +}); + +describe("resolvers -> Mutation -> setTaskVolunteers", () => { + it(`throws NotFoundError if no user exists with _id === context.userId `, async () => { + const { requestContext } = await import("../../../src/libraries"); + + const spy = vi + .spyOn(requestContext, "translate") + .mockImplementationOnce((message) => `Translated ${message}`); + + try { + const args: MutationSetTaskVolunteersArgs = { + id: testTask!.id, + volunteers: [], + }; + + const context = { userId: Types.ObjectId().toString() }; + + const { setTaskVolunteers: setTaskVolunteersResolverNotFoundError } = + await import("../../../src/resolvers/Mutation/setTaskVolunteers"); + + await setTaskVolunteersResolverNotFoundError?.({}, args, context); + } catch (error: any) { + expect(error.message).toEqual( + `Translated ${USER_NOT_FOUND_ERROR.MESSAGE}` + ); + expect(spy).toHaveBeenLastCalledWith(USER_NOT_FOUND_ERROR.MESSAGE); + } + }); + + it(`throws NotFoundError if no task exists with _id === args.id `, async () => { + const { requestContext } = await import("../../../src/libraries"); + + const spy = vi + .spyOn(requestContext, "translate") + .mockImplementationOnce((message) => `Translated ${message}`); + + try { + const args: MutationSetTaskVolunteersArgs = { + id: Types.ObjectId().toString(), + volunteers: [], + }; + + const context = { + userId: testUser!.id, + }; + + const { setTaskVolunteers: setTaskVolunteersResolverNotFoundError } = + await import("../../../src/resolvers/Mutation/setTaskVolunteers"); + + await setTaskVolunteersResolverNotFoundError?.({}, args, context); + } catch (error: any) { + expect(spy).toHaveBeenLastCalledWith(TASK_NOT_FOUND_ERROR.MESSAGE); + expect(error.message).toEqual( + `Translated ${TASK_NOT_FOUND_ERROR.MESSAGE}` + ); + } + }); + + it(`throws NotAuthorizedError if task.creator !== context.userId task with _id === args.id`, async () => { + const { requestContext } = await import("../../../src/libraries"); + + const spy = vi + .spyOn(requestContext, "translate") + .mockImplementationOnce((message) => `Translated ${message}`); + + try { + const args: MutationSetTaskVolunteersArgs = { + id: testTask!._id, + volunteers: [], + }; + + const context = { + userId: randomTestUser!._id, + }; + + const { setTaskVolunteers: setTaskVolunteersResolverNotFoundError } = + await import("../../../src/resolvers/Mutation/setTaskVolunteers"); + + await setTaskVolunteersResolverNotFoundError?.({}, args, context); + } catch (error: any) { + expect(error.message).toEqual( + `Translated ${USER_NOT_AUTHORIZED_ERROR.MESSAGE}` + ); + expect(spy).toHaveBeenLastCalledWith( + `${USER_NOT_AUTHORIZED_ERROR.MESSAGE}` + ); + } + }); + + it(`throws VolunteerNotFound error if args.volunteers has a non existent user`, async () => { + const { requestContext } = await import("../../../src/libraries"); + + const spy = vi + .spyOn(requestContext, "translate") + .mockImplementationOnce((message) => `Translated ${message}`); + + try { + const args: MutationSetTaskVolunteersArgs = { + id: testTask!._id, + volunteers: [Types.ObjectId().toString()], + }; + + const context = { + userId: testUser!._id, + }; + + const { setTaskVolunteers: setTaskVolunteersResolverNotFoundError } = + await import("../../../src/resolvers/Mutation/setTaskVolunteers"); + + await setTaskVolunteersResolverNotFoundError?.({}, args, context); + } catch (error: any) { + expect(error.message).toEqual( + `Translated ${VOLUNTEER_NOT_FOUND_ERROR.MESSAGE}` + ); + expect(spy).toHaveBeenLastCalledWith( + `${VOLUNTEER_NOT_FOUND_ERROR.MESSAGE}` + ); + } + }); + + it(`throws VolunteerNotMember error if args.volunteers has an user which is not a member of the organization under which the task is created`, async () => { + const { requestContext } = await import("../../../src/libraries"); + + const spy = vi + .spyOn(requestContext, "translate") + .mockImplementationOnce((message) => `Translated ${message}`); + + try { + const args: MutationSetTaskVolunteersArgs = { + id: testTask!._id, + volunteers: [randomTestUser!._id], + }; + + const context = { + userId: testUser!._id, + }; + + const { setTaskVolunteers: setTaskVolunteersResolverNotFoundError } = + await import("../../../src/resolvers/Mutation/setTaskVolunteers"); + + await setTaskVolunteersResolverNotFoundError?.({}, args, context); + } catch (error: any) { + expect(error.message).toEqual( + `Translated ${VOLUNTEER_NOT_MEMBER_ERROR.MESSAGE}` + ); + expect(spy).toHaveBeenLastCalledWith( + `${VOLUNTEER_NOT_MEMBER_ERROR.MESSAGE}` + ); + } + }); + + it(`removes the task volunteers correctly and returns the task`, async () => { + const args: MutationSetTaskVolunteersArgs = { + id: testTask!._id, + volunteers: [], + }; + + const context = { userId: testUser?._id }; + + await setTaskVolunteersResolver?.({}, args, context); + + const assignedTask = await TaskVolunteer.exists({ + userId: testUser!._id, + taskId: testTask!._id, + }); + + expect(assignedTask).toBeFalsy(); + }); + + it(`adds the task volunteers correctly and returns the task`, async () => { + const args: MutationSetTaskVolunteersArgs = { + id: testTask!._id, + volunteers: [testUser!._id], + }; + + const context = { userId: testUser?._id }; + + await setTaskVolunteersResolver?.({}, args, context); + + const assignedTask = await TaskVolunteer.exists({ + userId: testUser!._id, + taskId: testTask!._id, + }); + + expect(assignedTask).toBeTruthy(); + }); +}); diff --git a/tests/resolvers/Mutation/unregisterForEventByUser.spec.ts b/tests/resolvers/Mutation/unregisterForEventByUser.spec.ts index e2e368a91c..9caf619d48 100644 --- a/tests/resolvers/Mutation/unregisterForEventByUser.spec.ts +++ b/tests/resolvers/Mutation/unregisterForEventByUser.spec.ts @@ -1,7 +1,7 @@ import "dotenv/config"; import type mongoose from "mongoose"; import { Types } from "mongoose"; -import { User, Event } from "../../../src/models"; +import { User, EventAttendee } from "../../../src/models"; import type { MutationUnregisterForEventByUserArgs } from "../../../src/types/generatedGraphQLTypes"; import { connect, disconnect } from "../../helpers/db"; @@ -29,9 +29,7 @@ let testEvent: TestEventType; beforeAll(async () => { MONGOOSE_INSTANCE = await connect(); - const temp = await createTestEvent(); - testUser = temp[0]; - testEvent = temp[2]; + [testUser, , testEvent] = await createTestEvent(); }); afterAll(async () => { @@ -102,8 +100,7 @@ describe("resolvers -> Mutation -> unregisterForEventByUser", () => { } }); - it(`throws NotFoundError if current user with _id === context.userId is - not a registrant of event with _id === args.id`, async () => { + it(`throws NotFoundError if current user with _id === context.userId is not a registrant of event with _id === args.id`, async () => { const { requestContext } = await import("../../../src/libraries"); const spy = vi .spyOn(requestContext, "translate") @@ -125,29 +122,19 @@ describe("resolvers -> Mutation -> unregisterForEventByUser", () => { await unregisterForEventByUserResolver?.({}, args, context); } catch (error: any) { - expect(spy).toHaveBeenCalledWith(USER_NOT_FOUND_ERROR.MESSAGE); + expect(spy).toHaveBeenCalledWith(USER_ALREADY_UNREGISTERED_ERROR.MESSAGE); expect(error.message).toEqual( - `Translated ${USER_NOT_FOUND_ERROR.MESSAGE}` + `Translated ${USER_ALREADY_UNREGISTERED_ERROR.MESSAGE}` ); } }); it(`unregisters current user with _id === context.userId from event with _id === args.id`, async () => { - await Event.updateOne( - { - _id: testEvent?._id, - }, - { - $push: { - registrants: { - userId: testUser?._id, - user: testUser?._id, - status: "ACTIVE", - }, - }, - } - ); + await EventAttendee.create({ + userId: testUser!._id, + eventId: testEvent!._id, + }); await User.updateOne( { @@ -171,45 +158,13 @@ describe("resolvers -> Mutation -> unregisterForEventByUser", () => { const { unregisterForEventByUser: unregisterForEventByUserResolver } = await import("../../../src/resolvers/Mutation/unregisterForEventByUser"); - const unregisterForEventByUserPayload = - await unregisterForEventByUserResolver?.({}, args, context); - - const testUnregisterForEventByUserPayload = await Event.findOne({ - _id: testEvent?._id, - }).lean(); - - expect(unregisterForEventByUserPayload).toEqual( - testUnregisterForEventByUserPayload - ); - }); + await unregisterForEventByUserResolver?.({}, args, context); - it(`throws NotFoundError if current user with _id === context.userId has - already unregistered from the event with _id === args.id`, async () => { - const { requestContext } = await import("../../../src/libraries"); - const spy = vi - .spyOn(requestContext, "translate") - .mockImplementation((message) => `Translated ${message}`); + const isUserRegistered = await EventAttendee.exists({ + userId: testUser!._id, + eventId: testEvent!._id, + }); - try { - const args: MutationUnregisterForEventByUserArgs = { - id: testEvent?._id, - }; - - const context = { - userId: testUser?._id, - }; - - const { unregisterForEventByUser: unregisterForEventByUserResolver } = - await import( - "../../../src/resolvers/Mutation/unregisterForEventByUser" - ); - - await unregisterForEventByUserResolver?.({}, args, context); - } catch (error: any) { - expect(spy).toHaveBeenCalledWith(USER_ALREADY_UNREGISTERED_ERROR.MESSAGE); - expect(error.message).toEqual( - `Translated ${USER_ALREADY_UNREGISTERED_ERROR.MESSAGE}` - ); - } + expect(isUserRegistered).toBeFalsy(); }); }); diff --git a/tests/resolvers/Mutation/updateEventProject.spec.ts b/tests/resolvers/Mutation/updateEventProject.spec.ts index 1fc192f512..99827dc4f1 100644 --- a/tests/resolvers/Mutation/updateEventProject.spec.ts +++ b/tests/resolvers/Mutation/updateEventProject.spec.ts @@ -69,8 +69,9 @@ describe("resolvers -> Mutation -> createEventProject", () => { try { const args = { + id: Types.ObjectId().toString(), data: { - eventId: null, + title: "New Title", }, }; @@ -101,6 +102,9 @@ describe("resolvers -> Mutation -> createEventProject", () => { try { const args = { id: Types.ObjectId().toString(), + data: { + title: "New title", + }, }; const context = { @@ -132,6 +136,9 @@ describe("resolvers -> Mutation -> createEventProject", () => { try { const args = { id: testEventProject.id.toString(), + data: { + title: "New Title", + }, }; const context = { diff --git a/tests/resolvers/Mutation/updateTask.spec.ts b/tests/resolvers/Mutation/updateTask.spec.ts index 5ae82cf555..0e211f91ff 100644 --- a/tests/resolvers/Mutation/updateTask.spec.ts +++ b/tests/resolvers/Mutation/updateTask.spec.ts @@ -1,12 +1,9 @@ import "dotenv/config"; -import type { Document } from "mongoose"; import type mongoose from "mongoose"; import { Types } from "mongoose"; -import type { InterfaceTask } from "../../../src/models"; -import { Event, Task } from "../../../src/models"; +import { Task } from "../../../src/models"; import type { MutationUpdateTaskArgs } from "../../../src/types/generatedGraphQLTypes"; import { connect, disconnect } from "../../helpers/db"; - import { updateTask as updateTaskResolver } from "../../../src/resolvers/Mutation/updateTask"; import { TASK_NOT_FOUND_ERROR, @@ -14,42 +11,18 @@ import { USER_NOT_FOUND_ERROR, } from "../../../src/constants"; import { beforeAll, afterAll, describe, it, expect, vi } from "vitest"; -import type { TestUserType } from "../../helpers/userAndOrg"; -import { createTestEventWithRegistrants } from "../../helpers/eventsWithRegistrants"; +import { createTestUser, type TestUserType } from "../../helpers/userAndOrg"; +import { createAndAssignTestTask, type TestTaskType } from "../../helpers/task"; let MONGOOSE_INSTANCE: typeof mongoose; let testUser: TestUserType; -let testTasks: (InterfaceTask & Document)[]; +let randomTestUser: TestUserType; +let testTask: TestTaskType; beforeAll(async () => { MONGOOSE_INSTANCE = await connect(); - const temp = await createTestEventWithRegistrants(); - testUser = temp[0]; - const testEvent = temp[2]; - - testTasks = await Task.insertMany([ - { - title: "title", - event: testEvent?._id, - creator: testUser?._id, - }, - { - title: "title", - event: testEvent?._id, - creator: Types.ObjectId().toString(), - }, - ]); - - await Event.updateOne( - { - _id: testEvent?._id, - }, - { - $push: { - tasks: [testTasks[0]._id, testTasks[1]._id], - }, - } - ); + randomTestUser = await createTestUser(); + [testUser, , , , testTask] = await createAndAssignTestTask(); }); afterAll(async () => { @@ -124,12 +97,12 @@ describe("resolvers -> Mutation -> updateTask", () => { try { const args: MutationUpdateTaskArgs = { - id: testTasks[1]._id, + id: testTask!._id, data: {}, }; const context = { - userId: testUser?._id, + userId: randomTestUser!._id, }; const { updateTask: updateTaskResolverNotFoundError } = await import( @@ -149,7 +122,7 @@ describe("resolvers -> Mutation -> updateTask", () => { it(`updates the task with _id === args.id and returns it`, async () => { const args: MutationUpdateTaskArgs = { - id: testTasks[0]._id, + id: testTask!._id, data: { title: "newTitle", deadline: Date.now().toString(), @@ -162,7 +135,7 @@ describe("resolvers -> Mutation -> updateTask", () => { const updateTaskPayload = await updateTaskResolver?.({}, args, context); const updatedTestTask = await Task.findOne({ - _id: testTasks[0]._id, + _id: testTask!._id, }).lean(); expect(updateTaskPayload).toEqual(updatedTestTask); diff --git a/tests/resolvers/Mutation/updateUserPassword.spec.ts b/tests/resolvers/Mutation/updateUserPassword.spec.ts index 58d0503f71..4cf36b17d3 100644 --- a/tests/resolvers/Mutation/updateUserPassword.spec.ts +++ b/tests/resolvers/Mutation/updateUserPassword.spec.ts @@ -174,7 +174,6 @@ describe("resolvers -> Mutation -> updateUserPassword", () => { await updateUserPasswordResolver?.({}, args, context); } catch (error: any) { - console.log(error.message); expect(error.message).toEqual(INVALID_CREDENTIALS_ERROR.MESSAGE); } }); diff --git a/tests/resolvers/Query/event.spec.ts b/tests/resolvers/Query/event.spec.ts index c1361cd814..da5f71b50f 100644 --- a/tests/resolvers/Query/event.spec.ts +++ b/tests/resolvers/Query/event.spec.ts @@ -30,8 +30,7 @@ afterAll(async () => { }); describe("resolvers -> Query -> event", () => { - it(`throws NotFoundError if no event exists with _id === args.id - and event.status === 'ACTIVE'`, async () => { + it(`throws NotFoundError if no event exists with _id === args.id and event.status === 'ACTIVE'`, async () => { try { const args: QueryEventArgs = { id: Types.ObjectId().toString(), @@ -54,7 +53,6 @@ describe("resolvers -> Query -> event", () => { _id: testEvent?._id, }) .populate("creator", "-password") - .populate("tasks") .populate("admins", "-password") .lean(); diff --git a/tests/resolvers/Query/eventsByOrganizationConnection.spec.ts b/tests/resolvers/Query/eventsByOrganizationConnection.spec.ts index 47fb4051c3..c52e757630 100644 --- a/tests/resolvers/Query/eventsByOrganizationConnection.spec.ts +++ b/tests/resolvers/Query/eventsByOrganizationConnection.spec.ts @@ -103,19 +103,15 @@ describe("resolvers -> Query -> organizationsMemberConnection", () => { for (let i = 0; i < event.admins.length; i++) { adminIds.push(event.admins[i]._id); } - const tasksIds = []; - for (let i = 0; i < event.tasks.length; i++) { - tasksIds.push(event.tasks[i]._id); - } return { ...event, creator: event?.creator._id, admins: adminIds, - tasks: tasksIds, }; }); expect(eventsByOrganizationConnectionPayload).toEqual(events); }); + it(`returns list of all existing events filtered by args.where === { id_not: testEvent[0]._id, title_not: testEvents[0].title, description_not:testEvents[0].description, location_not:testEvents[0].location } and sorted by descending order of event._id if args.orderBy === 'id_DESC'`, async () => { @@ -160,15 +156,10 @@ describe("resolvers -> Query -> organizationsMemberConnection", () => { for (let i = 0; i < event.admins.length; i++) { adminIds.push(event.admins[i]._id); } - const tasksIds = []; - for (let i = 0; i < event.tasks.length; i++) { - tasksIds.push(event.tasks[i]._id); - } return { ...event, creator: event?.creator._id, admins: adminIds, - tasks: tasksIds, }; }); expect(eventsByOrganizationConnectionPayload).toEqual(events); @@ -215,19 +206,16 @@ describe("resolvers -> Query -> organizationsMemberConnection", () => { for (let i = 0; i < event.admins.length; i++) { adminIds.push(event.admins[i]._id); } - const tasksIds = []; - for (let i = 0; i < event.tasks.length; i++) { - tasksIds.push(event.tasks[i]._id); - } return { ...event, creator: event?.creator._id, admins: adminIds, - tasks: tasksIds, }; }); + expect(eventsByOrganizationConnectionPayload).toEqual(events); }); + it(`returns list of all existing events filtered by args.where === { id_in: testEvent[0]._id, title_in: testEvents[0].title, description_in:testEvents[0].description, location_in: testEvents[0].location} and sorted by ascending order of event.title if args.orderBy === 'title_ASC'`, async () => { @@ -272,15 +260,10 @@ describe("resolvers -> Query -> organizationsMemberConnection", () => { for (let i = 0; i < event.admins.length; i++) { adminIds.push(event.admins[i]._id); } - const tasksIds = []; - for (let i = 0; i < event.tasks.length; i++) { - tasksIds.push(event.tasks[i]._id); - } return { ...event, creator: event?.creator._id, admins: adminIds, - tasks: tasksIds, }; }); expect(eventsByOrganizationConnectionPayload).toEqual(events); @@ -328,15 +311,10 @@ describe("resolvers -> Query -> organizationsMemberConnection", () => { for (let i = 0; i < event.admins.length; i++) { adminIds.push(event.admins[i]._id); } - const tasksIds = []; - for (let i = 0; i < event.tasks.length; i++) { - tasksIds.push(event.tasks[i]._id); - } return { ...event, creator: event?.creator._id, admins: adminIds, - tasks: tasksIds, }; }); expect(eventsByOrganizationConnectionPayload).toEqual(events); @@ -373,15 +351,10 @@ describe("resolvers -> Query -> organizationsMemberConnection", () => { for (let i = 0; i < event.admins.length; i++) { adminIds.push(event.admins[i]._id); } - const tasksIds = []; - for (let i = 0; i < event.tasks.length; i++) { - tasksIds.push(event.tasks[i]._id); - } return { ...event, creator: event?.creator._id, admins: adminIds, - tasks: tasksIds, }; }); expect(eventsByOrganizationConnectionPayload).toEqual(events); diff --git a/tests/resolvers/Query/isUserRegister.spec.ts b/tests/resolvers/Query/isUserRegister.spec.ts deleted file mode 100644 index 71a29b1ec3..0000000000 --- a/tests/resolvers/Query/isUserRegister.spec.ts +++ /dev/null @@ -1,172 +0,0 @@ -import "dotenv/config"; -import { isUserRegister as isUserRegisterResolver } from "../../../src/resolvers/Query/isUserRegister"; -import { connect, disconnect } from "../../helpers/db"; -import { Event } from "../../../src/models"; -import type mongoose from "mongoose"; -import { Types } from "mongoose"; -import type { QueryIsUserRegisterArgs } from "../../../src/types/generatedGraphQLTypes"; -import { EVENT_NOT_FOUND_ERROR } from "../../../src/constants"; -import { beforeAll, afterAll, describe, it, expect } from "vitest"; -import type { - TestUserType, - TestOrganizationType, -} from "../../helpers/userAndOrg"; -import { createTestUserAndOrganization } from "../../helpers/userAndOrg"; -import type { TestEventType } from "../../helpers/events"; -import { createEventWithRegistrant } from "../../helpers/events"; -import { createTestTask } from "../../helpers/task"; - -let MONGOOSE_INSTANCE: typeof mongoose; -let testEvent: TestEventType; -let testUser: TestUserType; -let testOrganization: TestOrganizationType; - -beforeAll(async () => { - MONGOOSE_INSTANCE = await connect(); - [testUser, testOrganization] = await createTestUserAndOrganization(); - testEvent = await createEventWithRegistrant( - testUser?._id, - testOrganization?._id, - true, - "ONCE" - ); - - await createTestTask(testEvent?._id, testUser?._id); -}); - -afterAll(async () => { - await disconnect(MONGOOSE_INSTANCE); -}); - -describe("resolvers -> Query -> isUserRegister", () => { - it(`throws NotFoundError if no event exists with _id === args.eventId - and event.status === 'ACTIVE'`, async () => { - try { - const args: QueryIsUserRegisterArgs = { - eventId: Types.ObjectId().toString(), - }; - - const context = {}; - - await isUserRegisterResolver?.({}, args, context); - } catch (error: any) { - expect(error.message).toEqual(EVENT_NOT_FOUND_ERROR.DESC); - } - }); - - it(`returns object with event object and isRegistered === false - if user with _id === context.userId is not a registrant to the event and - user's registrant status === 'ACTIVE'`, async () => { - const args: QueryIsUserRegisterArgs = { - eventId: testEvent?.id, - }; - - const context = { - userId: Types.ObjectId().toString(), - }; - - const event = await Event.findOne({ - _id: testEvent?._id, - status: "ACTIVE", - }) - .populate("creator", "-password") - .populate("tasks") - .populate("admins", "-password") - .lean(); - - const isUserRegisterPayload = await isUserRegisterResolver?.( - {}, - args, - context - ); - - expect(isUserRegisterPayload).toEqual({ - event, - isRegistered: false, - }); - }); - - it(`returns object with event object and isRegistered === false - if user with _id === context.userId is a registrant to the event and - user's registrant status !== 'ACTIVE'`, async () => { - const args: QueryIsUserRegisterArgs = { - eventId: testEvent?.id, - }; - - const context = { - userId: testUser?.id, - }; - - const event = await Event.findOneAndUpdate( - { - _id: testEvent?._id, - "registrants.userId": testUser?._id, - }, - { - $set: { - "registrants.$.status": "BLOCKED", - }, - }, - { - new: true, - } - ) - .populate("creator", "-password") - .populate("tasks") - .populate("admins", "-password") - .lean(); - - const isUserRegisterPayload = await isUserRegisterResolver?.( - {}, - args, - context - ); - - expect(isUserRegisterPayload).toEqual({ - event, - isRegistered: false, - }); - }); - - it(`returns object with event object and isRegistered === true - if user with _id === context.userId is a registrant to the event and - user's registrant status === 'ACTIVE'`, async () => { - const args: QueryIsUserRegisterArgs = { - eventId: testEvent?.id, - }; - - const context = { - userId: testUser?.id, - }; - - const event = await Event.findOneAndUpdate( - { - _id: testEvent?._id, - "registrants.userId": testUser?._id, - }, - { - $set: { - "registrants.$.status": "ACTIVE", - }, - }, - { - new: true, - } - ) - .populate("creator", "-password") - .populate("tasks") - .populate("admins", "-password") - .lean(); - - const isUserRegisterPayload = await isUserRegisterResolver?.( - {}, - args, - context - ); - - expect(isUserRegisterPayload).toEqual({ - event, - isRegistered: true, - }); - }); -}); diff --git a/tests/resolvers/Query/registrantsByEvent.spec.ts b/tests/resolvers/Query/registrantsByEvent.spec.ts deleted file mode 100644 index baec9e6ea1..0000000000 --- a/tests/resolvers/Query/registrantsByEvent.spec.ts +++ /dev/null @@ -1,73 +0,0 @@ -import "dotenv/config"; -import { registrantsByEvent as registrantsByEventResolver } from "../../../src/resolvers/Query/registrantsByEvent"; -import { connect, disconnect } from "../../helpers/db"; -import type mongoose from "mongoose"; -import { Types } from "mongoose"; -import { Event } from "../../../src/models"; - -import type { QueryRegistrantsByEventArgs } from "../../../src/types/generatedGraphQLTypes"; -import { EVENT_NOT_FOUND_ERROR } from "../../../src/constants"; -import { beforeAll, afterAll, describe, it, expect } from "vitest"; -import type { - TestUserType, - TestOrganizationType, -} from "../../helpers/userAndOrg"; -import { createTestUserAndOrganization } from "../../helpers/userAndOrg"; -import type { TestEventType } from "../../helpers/events"; -import { createEventWithRegistrant } from "../../helpers/events"; - -let MONGOOSE_INSTANCE: typeof mongoose; -let testEvent: TestEventType; -let testUser: TestUserType; -let testOrganization: TestOrganizationType; - -beforeAll(async () => { - MONGOOSE_INSTANCE = await connect(); - [testUser, testOrganization] = await createTestUserAndOrganization(); - testEvent = await createEventWithRegistrant( - testUser?._id, - testOrganization?._id, - true, - "ONCE" - ); -}); - -afterAll(async () => { - await disconnect(MONGOOSE_INSTANCE); -}); - -describe("resolvers -> Query -> registrantsByEvent", () => { - it("throws NotFoundError if no event exists with _id === args.id", async () => { - try { - const args: QueryRegistrantsByEventArgs = { - id: Types.ObjectId().toString(), - }; - - await registrantsByEventResolver?.({}, args, {}); - } catch (error: any) { - expect(error.message).toEqual(EVENT_NOT_FOUND_ERROR.DESC); - } - }); - - it("returns list of all registrants for event with _id === args.id", async () => { - const args: QueryRegistrantsByEventArgs = { id: testEvent?._id }; - - const registrantsByEventPayload = await registrantsByEventResolver?.( - {}, - args, - {} - ); - - const event = await Event.findOne({ - _id: testEvent?._id, - }) - .populate("registrants.user", "-password") - .lean(); - - const registrantsByEvent = event?.registrants.map((registrant) => { - return registrant.user; - }); - - expect(registrantsByEventPayload).toEqual(registrantsByEvent); - }); -}); diff --git a/tests/resolvers/Query/tasksByEvent.spec.ts b/tests/resolvers/Query/tasksByEvent.spec.ts deleted file mode 100644 index 83a2d1f083..0000000000 --- a/tests/resolvers/Query/tasksByEvent.spec.ts +++ /dev/null @@ -1,316 +0,0 @@ -import "dotenv/config"; -import { tasksByEvent as tasksByEventResolver } from "../../../src/resolvers/Query/tasksByEvent"; -import { connect, disconnect } from "../../helpers/db"; -import type mongoose from "mongoose"; -import { Task } from "../../../src/models"; -import type { QueryTasksByEventArgs } from "../../../src/types/generatedGraphQLTypes"; -import { beforeAll, afterAll, describe, it, expect } from "vitest"; -import { createTestUserAndOrganization } from "../../helpers/userAndOrg"; -import type { TestEventType } from "../../helpers/events"; -import { createEventWithRegistrant } from "../../helpers/events"; -import { createTestTask } from "../../helpers/task"; - -let MONGOOSE_INSTANCE: typeof mongoose; -let testEvent: TestEventType; - -beforeAll(async () => { - MONGOOSE_INSTANCE = await connect(); - const [testUser, testOrganization] = await createTestUserAndOrganization(); - testEvent = await createEventWithRegistrant( - testUser?._id, - testOrganization?._id, - true, - "ONCE" - ); - - await createTestTask(testEvent?._id, testUser?._id); - await createTestTask(testEvent?._id, testUser?._id); -}); - -afterAll(async () => { - await disconnect(MONGOOSE_INSTANCE); -}); - -describe("resolvers -> Query -> tasksByEvent", () => { - it("returns list of all tasks with task.creator === args.id", async () => { - const args: QueryTasksByEventArgs = { - id: testEvent?.id, - orderBy: null, - }; - - const tasksByEventPayload = await tasksByEventResolver?.({}, args, {}); - - const tasksByEvent = await Task.find({ - event: testEvent?._id, - }) - .populate("event") - .populate("creator", "-password") - .lean(); - - expect(tasksByEventPayload).toEqual(tasksByEvent); - }); - - it(`returns list of all tasks with task.creator === args.id and sorted by - ascending order of task._id if args.orderBy === 'id_ASC'`, async () => { - const sort = { - _id: 1, - }; - - const args: QueryTasksByEventArgs = { - id: testEvent?.id, - orderBy: "id_ASC", - }; - - const tasksByEventPayload = await tasksByEventResolver?.({}, args, {}); - - const tasksByEvent = await Task.find({ - event: testEvent?._id, - }) - .sort(sort) - .populate("event") - .populate("creator", "-password") - .lean(); - - expect(tasksByEventPayload).toEqual(tasksByEvent); - }); - - it(`returns list of all tasks with task.creator === args.id and sorted by - descending order of task._id if args.orderBy === 'id_DESC'`, async () => { - const sort = { - _id: -1, - }; - - const args: QueryTasksByEventArgs = { - id: testEvent?.id, - orderBy: "id_DESC", - }; - - const tasksByEventPayload = await tasksByEventResolver?.({}, args, {}); - - const tasksByEvent = await Task.find({ - event: testEvent?._id, - }) - .sort(sort) - .populate("event") - .populate("creator", "-password") - .lean(); - - expect(tasksByEventPayload).toEqual(tasksByEvent); - }); - - it(`returns list of all tasks with task.creator === args.id and sorted by - ascending order of task.title if args.orderBy === 'title_ASC'`, async () => { - const sort = { - title: 1, - }; - - const args: QueryTasksByEventArgs = { - id: testEvent?.id, - orderBy: "title_ASC", - }; - - const tasksByEventPayload = await tasksByEventResolver?.({}, args, {}); - - const tasksByEvent = await Task.find({ - event: testEvent?._id, - }) - .sort(sort) - .populate("event") - .populate("creator", "-password") - .lean(); - - expect(tasksByEventPayload).toEqual(tasksByEvent); - }); - - it(`returns list of all tasks with task.creator === args.id and sorted by - descending order of task.title if args.orderBy === 'title_DESC'`, async () => { - const sort = { - title: -1, - }; - - const args: QueryTasksByEventArgs = { - id: testEvent?.id, - orderBy: "title_DESC", - }; - - const tasksByEventPayload = await tasksByEventResolver?.({}, args, {}); - - const tasksByEvent = await Task.find({ - event: testEvent?._id, - }) - .sort(sort) - .populate("event") - .populate("creator", "-password") - .lean(); - - expect(tasksByEventPayload).toEqual(tasksByEvent); - }); - - it(`returns list of all tasks with task.creator === args.id and sorted by - ascending order of task.description if args.orderBy === 'description_ASC'`, async () => { - const sort = { - description: 1, - }; - - const args: QueryTasksByEventArgs = { - id: testEvent?.id, - orderBy: "description_ASC", - }; - - const tasksByEventPayload = await tasksByEventResolver?.({}, args, {}); - - const tasksByEvent = await Task.find({ - event: testEvent?._id, - }) - .sort(sort) - .populate("event") - .populate("creator", "-password") - .lean(); - - expect(tasksByEventPayload).toEqual(tasksByEvent); - }); - - it(`returns list of all tasks with task.creator === args.id and sorted by - descending order of task.description if args.orderBy === 'description_DESC'`, async () => { - const sort = { - description: -1, - }; - - const args: QueryTasksByEventArgs = { - id: testEvent?.id, - orderBy: "description_DESC", - }; - - const tasksByEventPayload = await tasksByEventResolver?.({}, args, {}); - - const tasksByEvent = await Task.find({ - event: testEvent?._id, - }) - .sort(sort) - .populate("event") - .populate("creator", "-password") - .lean(); - - expect(tasksByEventPayload).toEqual(tasksByEvent); - }); - - it(`returns list of all tasks with task.creator === args.id and sorted by - ascending order of task.createdAt if args.orderBy === 'createdAt_ASC'`, async () => { - const sort = { - createdAt: 1, - }; - - const args: QueryTasksByEventArgs = { - id: testEvent?.id, - orderBy: "createdAt_ASC", - }; - - const tasksByEventPayload = await tasksByEventResolver?.({}, args, {}); - - const tasksByEvent = await Task.find({ - event: testEvent?._id, - }) - .sort(sort) - .populate("event") - .populate("creator", "-password") - .lean(); - - expect(tasksByEventPayload).toEqual(tasksByEvent); - }); - - it(`returns list of all tasks with task.creator === args.id and sorted by - descending order of task.createdAt if args.orderBy === 'createdAt_DESC'`, async () => { - const sort = { - createdAt: -1, - }; - - const args: QueryTasksByEventArgs = { - id: testEvent?.id, - orderBy: "createdAt_DESC", - }; - - const tasksByEventPayload = await tasksByEventResolver?.({}, args, {}); - - const tasksByEvent = await Task.find({ - event: testEvent?._id, - }) - .sort(sort) - .populate("event") - .populate("creator", "-password") - .lean(); - - expect(tasksByEventPayload).toEqual(tasksByEvent); - }); - - it(`returns list of all tasks with task.creator === args.id and sorted by - ascending order of task.deadline if args.orderBy === 'deadline_ASC'`, async () => { - const sort = { - deadline: 1, - }; - - const args: QueryTasksByEventArgs = { - id: testEvent?.id, - orderBy: "deadline_ASC", - }; - - const tasksByEventPayload = await tasksByEventResolver?.({}, args, {}); - - const tasksByEvent = await Task.find({ - event: testEvent?._id, - }) - .sort(sort) - .populate("event") - .populate("creator", "-password") - .lean(); - - expect(tasksByEventPayload).toEqual(tasksByEvent); - }); - - it(`returns list of all tasks with task.creator === args.id and sorted by - descending order of task.deadline if args.orderBy === 'deadline_DESC'`, async () => { - const sort = { - deadline: -1, - }; - - const args: QueryTasksByEventArgs = { - id: testEvent?.id, - orderBy: "deadline_DESC", - }; - - const tasksByEventPayload = await tasksByEventResolver?.({}, args, {}); - - const tasksByEvent = await Task.find({ - event: testEvent?._id, - }) - .sort(sort) - .populate("event") - .populate("creator", "-password") - .lean(); - - expect(tasksByEventPayload).toEqual(tasksByEvent); - }); - - it(`returns list of all tasks with task.creator === args.id and sorted by - descending order of task.deadline if args.orderBy === undefined`, async () => { - const sort = { - deadline: -1, - }; - - const args: QueryTasksByEventArgs = { - id: testEvent?.id, - orderBy: undefined, - }; - - const tasksByEventPayload = await tasksByEventResolver?.({}, args, {}); - - const tasksByEvent = await Task.find({ - event: testEvent?._id, - }) - .sort(sort) - .populate("event") - .populate("creator", "-password") - .lean(); - - expect(tasksByEventPayload).toEqual(tasksByEvent); - }); -}); diff --git a/tests/resolvers/Query/tasksByUser.spec.ts b/tests/resolvers/Query/tasksByUser.spec.ts deleted file mode 100644 index a1949957cd..0000000000 --- a/tests/resolvers/Query/tasksByUser.spec.ts +++ /dev/null @@ -1,297 +0,0 @@ -import "dotenv/config"; -import { tasksByUser as tasksByUserResolver } from "../../../src/resolvers/Query/tasksByUser"; -import { connect, disconnect } from "../../helpers/db"; -import type mongoose from "mongoose"; -import { Task } from "../../../src/models"; -import type { QueryTasksByUserArgs } from "../../../src/types/generatedGraphQLTypes"; -import { beforeAll, afterAll, describe, it, expect } from "vitest"; -import type { - TestUserType, - TestOrganizationType, -} from "../../helpers/userAndOrg"; -import { createTestUserAndOrganization } from "../../helpers/userAndOrg"; -import { createEventWithRegistrant } from "../../helpers/events"; -import { createTestTask } from "../../helpers/task"; - -let MONGOOSE_INSTANCE: typeof mongoose; -let testUser: TestUserType; -let testOrganization: TestOrganizationType; - -beforeAll(async () => { - MONGOOSE_INSTANCE = await connect(); - [testUser, testOrganization] = await createTestUserAndOrganization(); - const testEvent = await createEventWithRegistrant( - testUser?._id, - testOrganization?._id, - true, - "ONCE" - ); - - await createTestTask(testEvent?._id, testUser?._id); - await createTestTask(testEvent?._id, testUser?._id); -}); - -afterAll(async () => { - await disconnect(MONGOOSE_INSTANCE); -}); - -describe("resolvers -> Query -> tasksByUser", () => { - it(`returns list of all tasks with task.creator === args.id without sorting - if args.orderBy === null`, async () => { - const args: QueryTasksByUserArgs = { - id: testUser?._id, - orderBy: null, - }; - - const tasksByUserPayload = await tasksByUserResolver?.({}, args, {}); - - const tasksByUser = await Task.find({ - creator: testUser?._id, - }) - .populate("event") - .populate("creator", "-password") - .lean(); - - expect(tasksByUserPayload).toEqual(tasksByUser); - }); - - it(`returns list of all tasks with task.creator === args.id and sorted by - ascending order of task._id if args.orderBy === 'id_ASC'`, async () => { - const sort = { - _id: 1, - }; - - const args: QueryTasksByUserArgs = { - id: testUser?._id, - orderBy: "id_ASC", - }; - - const tasksByUserPayload = await tasksByUserResolver?.({}, args, {}); - - const tasksByUser = await Task.find({ - creator: testUser?._id, - }) - .sort(sort) - .populate("event") - .populate("creator", "-password") - .lean(); - - expect(tasksByUserPayload).toEqual(tasksByUser); - }); - - it(`returns list of all tasks with task.creator === args.id and sorted by - descending order of task._id if args.orderBy === 'id_DESC'`, async () => { - const sort = { - _id: -1, - }; - - const args: QueryTasksByUserArgs = { - id: testUser?._id, - orderBy: "id_DESC", - }; - - const tasksByUserPayload = await tasksByUserResolver?.({}, args, {}); - - const tasksByUser = await Task.find({ - creator: testUser?._id, - }) - .sort(sort) - .populate("event") - .populate("creator", "-password") - .lean(); - - expect(tasksByUserPayload).toEqual(tasksByUser); - }); - - it(`returns list of all tasks with task.creator === args.id and sorted by - ascending order of task.title if args.orderBy === 'title_ASC'`, async () => { - const sort = { - title: 1, - }; - - const args: QueryTasksByUserArgs = { - id: testUser?._id, - orderBy: "title_ASC", - }; - - const tasksByUserPayload = await tasksByUserResolver?.({}, args, {}); - - const tasksByUser = await Task.find({ - creator: testUser?._id, - }) - .sort(sort) - .populate("event") - .populate("creator", "-password") - .lean(); - - expect(tasksByUserPayload).toEqual(tasksByUser); - }); - - it(`returns list of all tasks with task.creator === args.id and sorted by - descending order of task.title if args.orderBy === 'title_DESC'`, async () => { - const sort = { - title: -1, - }; - - const args: QueryTasksByUserArgs = { - id: testUser?._id, - orderBy: "title_DESC", - }; - - const tasksByUserPayload = await tasksByUserResolver?.({}, args, {}); - - const tasksByUser = await Task.find({ - creator: testUser?._id, - }) - .sort(sort) - .populate("event") - .populate("creator", "-password") - .lean(); - - expect(tasksByUserPayload).toEqual(tasksByUser); - }); - - it(`returns list of all tasks with task.creator === args.id and sorted by - ascending order of task.description if args.orderBy === 'description_ASC'`, async () => { - const sort = { - description: 1, - }; - - const args: QueryTasksByUserArgs = { - id: testUser?._id, - orderBy: "description_ASC", - }; - - const tasksByUserPayload = await tasksByUserResolver?.({}, args, {}); - - const tasksByUser = await Task.find({ - creator: testUser?._id, - }) - .sort(sort) - .populate("event") - .populate("creator", "-password") - .lean(); - - expect(tasksByUserPayload).toEqual(tasksByUser); - }); - - it(`returns list of all tasks with task.creator === args.id and sorted by - descending order of task.description if args.orderBy === 'description_DESC'`, async () => { - const sort = { - description: -1, - }; - - const args: QueryTasksByUserArgs = { - id: testUser?._id, - orderBy: "description_DESC", - }; - - const tasksByUserPayload = await tasksByUserResolver?.({}, args, {}); - - const tasksByUser = await Task.find({ - creator: testUser?._id, - }) - .sort(sort) - .populate("event") - .populate("creator", "-password") - .lean(); - - expect(tasksByUserPayload).toEqual(tasksByUser); - }); - - it(`returns list of all tasks with task.creator === args.id and sorted by - ascending order of task.createdAt if args.orderBy === 'createdAt_ASC'`, async () => { - const sort = { - createdAt: 1, - }; - - const args: QueryTasksByUserArgs = { - id: testUser?._id, - orderBy: "createdAt_ASC", - }; - - const tasksByUserPayload = await tasksByUserResolver?.({}, args, {}); - - const tasksByUser = await Task.find({ - creator: testUser?._id, - }) - .sort(sort) - .populate("event") - .populate("creator", "-password") - .lean(); - - expect(tasksByUserPayload).toEqual(tasksByUser); - }); - - it(`returns list of all tasks with task.creator === args.id and sorted by - descending order of task.createdAt if args.orderBy === 'createdAt_DESC'`, async () => { - const sort = { - createdAt: -1, - }; - - const args: QueryTasksByUserArgs = { - id: testUser?._id, - orderBy: "createdAt_DESC", - }; - - const tasksByUserPayload = await tasksByUserResolver?.({}, args, {}); - - const tasksByUser = await Task.find({ - creator: testUser?._id, - }) - .sort(sort) - .populate("event") - .populate("creator", "-password") - .lean(); - - expect(tasksByUserPayload).toEqual(tasksByUser); - }); - - it(`returns list of all tasks with task.creator === args.id and sorted by - ascending order of task.deadline if args.orderBy === 'deadline_ASC'`, async () => { - const sort = { - deadline: 1, - }; - - const args: QueryTasksByUserArgs = { - id: testUser?._id, - orderBy: "deadline_ASC", - }; - - const tasksByUserPayload = await tasksByUserResolver?.({}, args, {}); - - const tasksByUser = await Task.find({ - creator: testUser?._id, - }) - .sort(sort) - .populate("event") - .populate("creator", "-password") - .lean(); - - expect(tasksByUserPayload).toEqual(tasksByUser); - }); - - it(`returns list of all tasks with task.creator === args.id and sorted by - descending order of task.deadline if args.orderBy === 'deadline_DESC'`, async () => { - const sort = { - deadline: -1, - }; - - const args: QueryTasksByUserArgs = { - id: testUser?._id, - orderBy: "deadline_DESC", - }; - - const tasksByUserPayload = await tasksByUserResolver?.({}, args, {}); - - const tasksByUser = await Task.find({ - creator: testUser?._id, - }) - .sort(sort) - .populate("event") - .populate("creator", "-password") - .lean(); - - expect(tasksByUserPayload).toEqual(tasksByUser); - }); -}); diff --git a/tests/resolvers/Task/volunteers.spec.ts b/tests/resolvers/Task/volunteers.spec.ts new file mode 100644 index 0000000000..c587da285d --- /dev/null +++ b/tests/resolvers/Task/volunteers.spec.ts @@ -0,0 +1,35 @@ +import "dotenv/config"; +import { volunteers as volunteersResolver } from "../../../src/resolvers/Task/volunteers"; +import { connect, disconnect } from "../../helpers/db"; +import type mongoose from "mongoose"; +import { beforeAll, afterAll, describe, it, expect } from "vitest"; +import { createAndAssignTestTask, type TestTaskType } from "../../helpers/task"; +import type { TestUserType } from "../../helpers/userAndOrg"; +import { User } from "../../../src/models"; + +let MONGOOSE_INSTANCE: typeof mongoose; +let testUser: TestUserType; +let testTask: TestTaskType; + +beforeAll(async () => { + MONGOOSE_INSTANCE = await connect(); + [testUser, , , , testTask] = await createAndAssignTestTask(); +}); + +afterAll(async () => { + await disconnect(MONGOOSE_INSTANCE); +}); + +describe("resolvers -> Task -> Volunteers", () => { + it(`returns the volunteer user objects for parent task`, async () => { + const parent = testTask!.toObject(); + + const volunteersPayload = await volunteersResolver?.(parent, {}, {}); + + const volunteerObject = await User.findOne({ + _id: testUser!._id, + }).lean(); + + expect(volunteersPayload).toEqual([volunteerObject]); + }); +}); diff --git a/tests/resolvers/User/assignedTasks.spec.ts b/tests/resolvers/User/assignedTasks.spec.ts new file mode 100644 index 0000000000..b28a1d3e88 --- /dev/null +++ b/tests/resolvers/User/assignedTasks.spec.ts @@ -0,0 +1,35 @@ +import "dotenv/config"; +import { assignedTasks as assignedTasksResolver } from "../../../src/resolvers/User/assignedTasks"; +import { connect, disconnect } from "../../helpers/db"; +import type mongoose from "mongoose"; +import { Task } from "../../../src/models"; +import { beforeAll, afterAll, describe, it, expect } from "vitest"; +import type { TestUserType } from "../../helpers/userAndOrg"; +import { createAndAssignTestTask, type TestTaskType } from "../../helpers/task"; + +let testUser: TestUserType; +let testTask: TestTaskType; +let MONGOOSE_INSTANCE: typeof mongoose; + +beforeAll(async () => { + MONGOOSE_INSTANCE = await connect(); + [testUser, , , , testTask] = await createAndAssignTestTask(); +}); + +afterAll(async () => { + await disconnect(MONGOOSE_INSTANCE); +}); + +describe("resolvers -> User -> assignedTasks", () => { + it(`returns the assigned task objects for parent user`, async () => { + const parent = testUser!.toObject(); + + const assignedTasksPayload = await assignedTasksResolver?.(parent, {}, {}); + + const assignedTasks = await Task.find({ + _id: testTask!._id, + }).lean(); + + expect(assignedTasksPayload).toEqual(assignedTasks); + }); +});