From 9eb305de7288929ec077237e6eae1b17c9b364a6 Mon Sep 17 00:00:00 2001 From: Lakshya Satpal <81241551+LakshyaSatpal@users.noreply.github.com> Date: Thu, 15 Feb 2024 02:57:31 +0530 Subject: [PATCH] feat: Event Volunteers (#1774) * feat: add Event Volunteer collectionand CRUD mutations/queries * write tests for createEventVolunteer and updateEventVolunteer * write tests for eventVolunteersByEvent and removeEventVolunteers * fix formatting * better code coverage * fix formatting * better code coverage * fix formatting * better code coverage * fix linting test --- codegen.ts | 1 + schema.graphql | 257 +++++++++++++++--- src/constants.ts | 23 +- src/models/EventVolunteer.ts | 59 ++++ src/models/index.ts | 1 + src/resolvers/EventVolunteer/creator.ts | 8 + src/resolvers/EventVolunteer/event.ts | 8 + src/resolvers/EventVolunteer/index.ts | 10 + src/resolvers/EventVolunteer/user.ts | 8 + src/resolvers/Mutation/acceptAdmin.ts | 2 +- .../Mutation/createEventVolunteer.ts | 59 ++++ src/resolvers/Mutation/index.ts | 6 + .../Mutation/removeEventVolunteer.ts | 51 ++++ .../Mutation/updateEventVolunteer.ts | 87 ++++++ src/resolvers/Query/eventVolunteersByEvent.ts | 20 ++ src/resolvers/index.ts | 2 + src/typeDefs/enums.ts | 5 + src/typeDefs/inputs.ts | 12 + src/typeDefs/mutations.ts | 9 + src/typeDefs/queries.ts | 2 + src/typeDefs/types.ts | 12 + src/types/generatedGraphQLTypes.ts | 82 +++++- tests/helpers/events.ts | 32 ++- .../resolvers/EventVolunteer/creator.spec.ts | 46 ++++ tests/resolvers/EventVolunteer/event.spec.ts | 38 +++ tests/resolvers/EventVolunteer/user.spec.ts | 52 ++++ .../Mutation/createEventVolunteer.spec.ts | 161 +++++++++++ .../Mutation/removeEventVolunteer.spec.ts | 126 +++++++++ .../Mutation/updateEventVolunteer.spec.ts | 200 ++++++++++++++ .../Query/eventVolunteersByEvent.spec.ts | 40 +++ .../uploadEncodedImage.spec.ts | 18 +- 31 files changed, 1380 insertions(+), 57 deletions(-) create mode 100644 src/models/EventVolunteer.ts create mode 100644 src/resolvers/EventVolunteer/creator.ts create mode 100644 src/resolvers/EventVolunteer/event.ts create mode 100644 src/resolvers/EventVolunteer/index.ts create mode 100644 src/resolvers/EventVolunteer/user.ts create mode 100644 src/resolvers/Mutation/createEventVolunteer.ts create mode 100644 src/resolvers/Mutation/removeEventVolunteer.ts create mode 100644 src/resolvers/Mutation/updateEventVolunteer.ts create mode 100644 src/resolvers/Query/eventVolunteersByEvent.ts create mode 100644 tests/resolvers/EventVolunteer/creator.spec.ts create mode 100644 tests/resolvers/EventVolunteer/event.spec.ts create mode 100644 tests/resolvers/EventVolunteer/user.spec.ts create mode 100644 tests/resolvers/Mutation/createEventVolunteer.spec.ts create mode 100644 tests/resolvers/Mutation/removeEventVolunteer.spec.ts create mode 100644 tests/resolvers/Mutation/updateEventVolunteer.spec.ts create mode 100644 tests/resolvers/Query/eventVolunteersByEvent.spec.ts diff --git a/codegen.ts b/codegen.ts index a646f67b7f..92ed260ec9 100644 --- a/codegen.ts +++ b/codegen.ts @@ -50,6 +50,7 @@ const config: CodegenConfig = { EventAttendee: "../models/EventAttendee#InterfaceEventAttendee", UserFamily: "../models/userFamily#InterfaceUserFamily", + EventVolunteer: "../models/EventVolunteer#InterfaceEventVolunteer", Feedback: "../models/Feedback#InterfaceFeedback", diff --git a/schema.graphql b/schema.graphql index 992fb88dc8..827cf80bb9 100644 --- a/schema.graphql +++ b/schema.graphql @@ -334,6 +334,28 @@ enum EventOrderByInput { title_DESC } +type EventVolunteer { + _id: ID! + createdAt: DateTime! + creator: User + event: Event + isAssigned: Boolean + isInvited: Boolean + response: String + updatedAt: DateTime! + user: User! +} + +input EventVolunteerInput { + eventId: ID! + userId: ID! +} + +enum EventVolunteerResponse { + NO + YES +} + input EventWhereInput { description: String description_contains: String @@ -540,9 +562,17 @@ type Mutation { addEventAttendee(data: EventAttendeeInput!): User! addFeedback(data: FeedbackInput!): Feedback! addLanguageTranslation(data: LanguageInput!): Language! - addOrganizationCustomField(name: String!, organizationId: ID!, type: String!): OrganizationCustomField! + addOrganizationCustomField( + name: String! + organizationId: ID! + type: String! + ): OrganizationCustomField! addOrganizationImage(file: String!, organizationId: String!): Organization! - addUserCustomData(dataName: String!, dataValue: Any!, organizationId: ID!): UserCustomData! + addUserCustomData( + dataName: String! + dataValue: Any! + organizationId: ID! + ): UserCustomData! addUserImage(file: String!): User! addUserToGroupChat(chatId: ID!, userId: ID!): GroupChat! addUserToUserFamily(familyId: ID!, userId: ID!): UserFamily! @@ -553,20 +583,46 @@ type Mutation { blockUser(organizationId: ID!, userId: ID!): User! cancelMembershipRequest(membershipRequestId: ID!): MembershipRequest! checkIn(data: CheckInInput!): CheckIn! - createActionItem(actionItemCategoryId: ID!, data: CreateActionItemInput!): ActionItem! - createActionItemCategory(name: String!, organizationId: ID!): ActionItemCategory! + createActionItem( + actionItemCategoryId: ID! + data: CreateActionItemInput! + ): ActionItem! + createActionItemCategory( + name: String! + organizationId: ID! + ): ActionItemCategory! createAdmin(data: UserAndOrganizationInput!): User! - createAdvertisement(endDate: Date!, link: String!, name: String!, orgId: ID!, startDate: Date!, type: String!): Advertisement! + createAdvertisement( + endDate: Date! + link: String! + name: String! + orgId: ID! + startDate: Date! + type: String! + ): Advertisement! createAgendaCategory(input: CreateAgendaCategoryInput!): AgendaCategory! 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!, recurrenceRuleData: RecurrenceRuleInput): Event! + createDonation( + amount: Float! + nameOfOrg: String! + nameOfUser: String! + orgId: ID! + payPalId: ID! + userId: ID! + ): Donation! + createEvent(data: EventInput): Event! + createEventVolunteer(data: EventVolunteerInput!): EventVolunteer! createGroupChat(data: createGroupChatInput!): GroupChat! createMember(input: UserAndOrganizationInput!): Organization! createMessageChat(data: MessageChatInput!): MessageChat! createOrganization(data: OrganizationInput, file: String): Organization! - createPlugin(pluginCreatedBy: String!, pluginDesc: String!, pluginName: String!, uninstalledOrgs: [ID!]): Plugin! + createPlugin( + pluginCreatedBy: String! + pluginDesc: String! + pluginName: String! + uninstalledOrgs: [ID!] + ): Plugin! createPost(data: PostInput!, file: String): Post createSampleOrganization: Boolean! createUserFamily(data: createUserFamilyInput!): UserFamily! @@ -594,10 +650,14 @@ type Mutation { removeDirectChat(chatId: ID!, organizationId: ID!): DirectChat! removeEvent(id: ID!): Event! removeEventAttendee(data: EventAttendeeInput!): User! + removeEventVolunteer(id: ID!): EventVolunteer! removeGroupChat(chatId: ID!): GroupChat! removeMember(data: UserAndOrganizationInput!): Organization! removeOrganization(id: ID!): User! - removeOrganizationCustomField(customFieldId: ID!, organizationId: ID!): OrganizationCustomField! + removeOrganizationCustomField( + customFieldId: ID! + organizationId: ID! + ): OrganizationCustomField! removeOrganizationImage(organizationId: String!): Organization! removePost(id: ID!): Post removeSampleOrganization: Boolean! @@ -610,8 +670,14 @@ type Mutation { revokeRefreshTokenForUser: Boolean! saveFcmToken(token: String): Boolean! sendMembershipRequest(organizationId: ID!): MembershipRequest! - sendMessageToDirectChat(chatId: ID!, messageContent: String!): DirectChatMessage! - sendMessageToGroupChat(chatId: ID!, messageContent: String!): GroupChatMessage! + sendMessageToDirectChat( + chatId: ID! + messageContent: String! + ): DirectChatMessage! + sendMessageToGroupChat( + chatId: ID! + messageContent: String! + ): GroupChatMessage! signUp(data: UserInput!, file: String): AuthData! togglePostPin(id: ID!, title: String): Post! unassignUserTag(input: ToggleUserTagAssignInput!): User @@ -620,17 +686,37 @@ type Mutation { unlikePost(id: ID!): Post unregisterForEventByUser(id: ID!): Event! updateActionItem(data: UpdateActionItemInput!, id: ID!): ActionItem - updateActionItemCategory(data: UpdateActionItemCategoryInput!, id: ID!): ActionItemCategory - updateAdvertisement(input: UpdateAdvertisementInput!): UpdateAdvertisementPayload - updateAgendaCategory(id: ID!, input: UpdateAgendaCategoryInput!): AgendaCategory + updateActionItemCategory( + data: UpdateActionItemCategoryInput! + id: ID! + ): ActionItemCategory + updateAdvertisement( + input: UpdateAdvertisementInput! + ): UpdateAdvertisementPayload + updateAgendaCategory( + id: ID! + input: UpdateAgendaCategoryInput! + ): AgendaCategory updateEvent(data: UpdateEventInput, id: ID!): Event! + updateEventVolunteer( + data: UpdateEventVolunteerInput + id: ID! + ): EventVolunteer! updateLanguage(languageCode: String!): User! - updateOrganization(data: UpdateOrganizationInput, file: String, id: ID!): Organization! + updateOrganization( + data: UpdateOrganizationInput + file: String + id: ID! + ): Organization! updatePluginStatus(id: ID!, orgId: ID!): Plugin! updatePost(data: PostUpdateInput, id: ID!): Post! updateUserPassword(data: UpdateUserPasswordInput!): User! updateUserProfile(data: UpdateUserInput, file: String): User! - updateUserRoleInOrganization(organizationId: ID!, role: String!, userId: ID!): Organization! + updateUserRoleInOrganization( + organizationId: ID! + role: String! + userId: ID! + ): Organization! updateUserTag(input: UpdateUserTagInput!): UserTag updateUserType(data: UpdateUserTypeInput!): Boolean! } @@ -658,7 +744,12 @@ type Organization { pinnedPosts: [Post] updatedAt: DateTime! userRegistrationRequired: Boolean! - userTags(after: String, before: String, first: PositiveInt, last: PositiveInt): UserTagsConnection + userTags( + after: String + before: String + first: PositiveInt + last: PositiveInt + ): UserTagsConnection visibleInSearch: Boolean! } @@ -737,14 +828,20 @@ type OtpData { otpToken: String! } -"""Information about pagination in a connection.""" +""" +Information about pagination in a connection. +""" type PageInfo { currPageNo: Int - """When paginating forwards, are there more items?""" + """ + When paginating forwards, are there more items? + """ hasNextPage: Boolean! - """When paginating backwards, are there more items?""" + """ + When paginating backwards, are there more items? + """ hasPreviousPage: Boolean! nextPageNo: Int prevPageNo: Int @@ -805,14 +902,20 @@ type Post { videoUrl: URL } -"""A connection to a list of items.""" +""" +A connection to a list of items. +""" type PostConnection { aggregate: AggregatePost! - """A list of edges.""" + """ + A list of edges. + """ edges: [Post]! - """Information to aid in pagination.""" + """ + Information to aid in pagination. + """ pageInfo: PageInfo! } @@ -887,12 +990,23 @@ type Query { directChatsByUserID(id: ID!): [DirectChat] directChatsMessagesByChatID(id: ID!): [DirectChatMessage] event(id: ID!): Event + eventVolunteersByEvent(id: ID!): [EventVolunteer] eventsByOrganization(id: ID, orderBy: EventOrderByInput): [Event] - eventsByOrganizationConnection(first: Int, orderBy: EventOrderByInput, skip: Int, where: EventWhereInput): [Event!]! + eventsByOrganizationConnection( + first: Int + orderBy: EventOrderByInput + skip: Int + where: EventWhereInput + ): [Event!]! getAdvertisements: [Advertisement] getDonationById(id: ID!): Donation! getDonationByOrgId(orgId: ID!): [Donation] - getDonationByOrgIdConnection(first: Int, orgId: ID!, skip: Int, where: DonationWhereInput): [Donation!]! + getDonationByOrgIdConnection( + first: Int + orgId: ID! + skip: Int + where: DonationWhereInput + ): [Donation!]! getPlugins: [Plugin] getlanguage(lang_code: String!): [Translation] hasSubmittedFeedback(eventId: ID!, userId: ID!): Boolean @@ -901,18 +1015,47 @@ type Query { me: User! myLanguage: String organizations(id: ID, orderBy: OrganizationOrderByInput): [Organization] - organizationsConnection(first: Int, orderBy: OrganizationOrderByInput, skip: Int, where: OrganizationWhereInput): [Organization]! - organizationsMemberConnection(first: Int, orderBy: UserOrderByInput, orgId: ID!, skip: Int, where: UserWhereInput): UserConnection! + organizationsConnection( + first: Int + orderBy: OrganizationOrderByInput + skip: Int + where: OrganizationWhereInput + ): [Organization]! + organizationsMemberConnection( + first: Int + orderBy: UserOrderByInput + orgId: ID! + skip: Int + where: UserWhereInput + ): UserConnection! plugin(orgId: ID!): [Plugin] post(id: ID!): Post postsByOrganization(id: ID!, orderBy: PostOrderByInput): [Post] - postsByOrganizationConnection(first: Int, id: ID!, orderBy: PostOrderByInput, skip: Int, where: PostWhereInput): PostConnection + postsByOrganizationConnection( + first: Int + id: ID! + orderBy: PostOrderByInput + skip: Int + where: PostWhereInput + ): PostConnection registeredEventsByUser(id: ID, orderBy: EventOrderByInput): [Event] registrantsByEvent(id: ID!): [User] user(id: ID!): User! userLanguage(userId: ID!): String - users(adminApproved: Boolean, first: Int, orderBy: UserOrderByInput, skip: Int, userType: String, where: UserWhereInput): [User] - usersConnection(first: Int, orderBy: UserOrderByInput, skip: Int, where: UserWhereInput): [User]! + users( + adminApproved: Boolean + first: Int + orderBy: UserOrderByInput + skip: Int + userType: String + where: UserWhereInput + ): [User] + usersConnection( + first: Int + orderBy: UserOrderByInput + skip: Int + where: UserWhereInput + ): [User]! } input RecaptchaVerification { @@ -1024,6 +1167,13 @@ input UpdateEventInput { title: String } +input UpdateEventVolunteerInput { + eventId: ID + isAssigned: Boolean + isInvited: Boolean + response: EventVolunteerResponse +} + input UpdateOrganizationInput { address: AddressInput description: String @@ -1088,7 +1238,13 @@ type User { phone: UserPhone pluginCreationAllowed: Boolean! registeredEvents: [Event] - tagsAssignedWith(after: String, before: String, first: PositiveInt, last: PositiveInt, organizationId: ID): UserTagsConnection + tagsAssignedWith( + after: String + before: String + first: PositiveInt + last: PositiveInt + organizationId: ID + ): UserTagsConnection tokenVersion: Int! updatedAt: DateTime! userType: UserType! @@ -1189,6 +1345,43 @@ type UserTagsConnectionResult { errors: [ConnectionError!]! } +type UserToReturn { + _id: ID! + address: Address + adminApproved: Boolean + adminFor: [Organization] + appLanguageCode: String! + birthDate: Date + createdAt: DateTime! + createdEvents: [Event] + createdOrganizations: [Organization] + educationGrade: EducationGrade + email: EmailAddress! + employmentStatus: EmploymentStatus + eventAdmin: [Event] + firstName: String! + gender: Gender + image: String + joinedOrganizations: [Organization] + lastName: String! + maritalStatus: MaritalStatus + membershipRequests: [MembershipRequest] + organizationsBlockedBy: [Organization] + phone: UserPhone + pluginCreationAllowed: Boolean! + registeredEvents: [Event] + tagsAssignedWith( + after: String + before: String + first: PositiveInt + last: PositiveInt + organizationId: ID + ): UserTagsConnection + tokenVersion: Int! + updatedAt: DateTime! + userType: UserType! +} + enum UserType { ADMIN NON_USER @@ -1271,4 +1464,4 @@ input createGroupChatInput { input createUserFamilyInput { title: String! userIds: [ID!]! -} \ No newline at end of file +} diff --git a/src/constants.ts b/src/constants.ts index 0d316c35d7..7a457d7503 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -449,18 +449,18 @@ export const EMAIL_ALREADY_EXISTS_ERROR = { PARAM: "email", }; -export const VOLUNTEER_NOT_FOUND_ERROR = { +export const EVENT_VOLUNTEER_NOT_FOUND_ERROR = { DESC: "Volunteer not found", - CODE: "volunteer.notFound", - MESSAGE: "volunteer.notFound", - PARAM: "volunteers", + CODE: "eventVolunteer.notFound", + MESSAGE: "eventVolunteer.notFound", + PARAM: "eventVolunteers", }; -export const VOLUNTEER_NOT_MEMBER_ERROR = { - DESC: "Volunteer is not member of the organization.", - CODE: "volunteer.notMember", - MESSAGE: "volunteer.notMember", - PARAM: "volunteers", +export const EVENT_VOLUNTEER_INVITE_USER_MISTMATCH = { + DESC: "Current User is not the user of Event Volunteer", + CODE: "eventVolunteer.userMismatch", + MESSAGE: "eventVolunteer.userMismatch", + PARAM: "eventVolunteers", }; export const USER_ALREADY_CHECKED_IN = { @@ -568,3 +568,8 @@ export enum TransactionLogTypes { UPDATE = "UPDATE", DELETE = "DELETE", } + +export enum EventVolunteerResponse { + YES = "YES", + NO = "NO", +} diff --git a/src/models/EventVolunteer.ts b/src/models/EventVolunteer.ts new file mode 100644 index 0000000000..174c6b1858 --- /dev/null +++ b/src/models/EventVolunteer.ts @@ -0,0 +1,59 @@ +import type { PopulatedDoc, Document, Model, Types } from "mongoose"; +import { Schema, model, models } from "mongoose"; +import type { InterfaceUser } from "./User"; +import type { InterfaceEvent } from "./Event"; +import { createLoggingMiddleware } from "../libraries/dbLogger"; + +export interface InterfaceEventVolunteer { + _id: Types.ObjectId; + createdAt: Date; + creatorId: PopulatedDoc; + eventId: PopulatedDoc; + isAssigned: boolean; + isInvited: boolean; + response: string; + updatedAt: Date; + userId: PopulatedDoc; +} + +const eventVolunteerSchema = new Schema( + { + creatorId: { + type: Schema.Types.ObjectId, + ref: "User", + required: true, + }, + eventId: { + type: Schema.Types.ObjectId, + ref: "Event", + }, + response: { + type: String, + enum: ["YES", "NO", null], + }, + isAssigned: { + type: Boolean, + }, + isInvited: { + type: Boolean, + }, + userId: { + type: Schema.Types.ObjectId, + ref: "User", + required: true, + }, + }, + { + timestamps: true, + } +); + +// Enable logging on changes in EventVolunteer collection +createLoggingMiddleware(eventVolunteerSchema, "EventVolunteer"); + +const eventVolunteerModel = (): Model => + model("EventVolunteer", eventVolunteerSchema); + +// This syntax is needed to prevent Mongoose OverwriteModelError while running tests. +export const EventVolunteer = (models.EventVolunteer || + eventVolunteerModel()) as ReturnType; diff --git a/src/models/index.ts b/src/models/index.ts index cd70605ee5..30b902ddfe 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -9,6 +9,7 @@ export * from "./DirectChatMessage"; export * from "./Donation"; export * from "./Event"; export * from "./EventAttendee"; +export * from "./EventVolunteer"; export * from "./Feedback"; export * from "./File"; export * from "./Group"; diff --git a/src/resolvers/EventVolunteer/creator.ts b/src/resolvers/EventVolunteer/creator.ts new file mode 100644 index 0000000000..3167c28cfd --- /dev/null +++ b/src/resolvers/EventVolunteer/creator.ts @@ -0,0 +1,8 @@ +import { User } from "../../models"; +import type { EventVolunteerResolvers } from "../../types/generatedGraphQLTypes"; + +export const creator: EventVolunteerResolvers["creator"] = async (parent) => { + return await User.findOne({ + _id: parent.creatorId, + }).lean(); +}; diff --git a/src/resolvers/EventVolunteer/event.ts b/src/resolvers/EventVolunteer/event.ts new file mode 100644 index 0000000000..61f67ac48e --- /dev/null +++ b/src/resolvers/EventVolunteer/event.ts @@ -0,0 +1,8 @@ +import { Event } from "../../models"; +import type { EventVolunteerResolvers } from "../../types/generatedGraphQLTypes"; + +export const event: EventVolunteerResolvers["event"] = async (parent) => { + return await Event.findOne({ + _id: parent.eventId, + }).lean(); +}; diff --git a/src/resolvers/EventVolunteer/index.ts b/src/resolvers/EventVolunteer/index.ts new file mode 100644 index 0000000000..73e9e822e4 --- /dev/null +++ b/src/resolvers/EventVolunteer/index.ts @@ -0,0 +1,10 @@ +import type { EventVolunteerResolvers } from "../../types/generatedGraphQLTypes"; +import { event } from "./event"; +import { creator } from "./creator"; +import { user } from "./user"; + +export const EventVolunteer: EventVolunteerResolvers = { + creator, + event, + user, +}; diff --git a/src/resolvers/EventVolunteer/user.ts b/src/resolvers/EventVolunteer/user.ts new file mode 100644 index 0000000000..00f73c6e45 --- /dev/null +++ b/src/resolvers/EventVolunteer/user.ts @@ -0,0 +1,8 @@ +import { User } from "../../models"; +import type { EventVolunteerResolvers } from "../../types/generatedGraphQLTypes"; + +export const user: EventVolunteerResolvers["user"] = async (parent) => { + return await User.findOne({ + _id: parent.userId, + }).lean(); +}; diff --git a/src/resolvers/Mutation/acceptAdmin.ts b/src/resolvers/Mutation/acceptAdmin.ts index 109a1476f3..eca7524708 100644 --- a/src/resolvers/Mutation/acceptAdmin.ts +++ b/src/resolvers/Mutation/acceptAdmin.ts @@ -8,7 +8,7 @@ import { superAdminCheck } from "../../utilities/superAdminCheck"; * @param _parent - parent of current request * @param args - payload provided with the request * @param context - context of entire application - * @remarks THe following checks are done: + * @remarks The following checks are done: * 1. Whether the user exists * 2. Whether the user accepting the admin request is a superadmin or not. */ diff --git a/src/resolvers/Mutation/createEventVolunteer.ts b/src/resolvers/Mutation/createEventVolunteer.ts new file mode 100644 index 0000000000..4eab9f0955 --- /dev/null +++ b/src/resolvers/Mutation/createEventVolunteer.ts @@ -0,0 +1,59 @@ +import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; +import { EventVolunteer } from "../../models/EventVolunteer"; +import { Event, User } from "../../models"; +import { + EVENT_NOT_FOUND_ERROR, + USER_NOT_FOUND_ERROR, + EVENT_VOLUNTEER_NOT_FOUND_ERROR, +} from "../../constants"; +import { errors, requestContext } from "../../libraries"; + +/** + * This function enables to create an event volunteer. + * @param _parent - parent of current request + * @param args - payload provided with the request + * @param context - context of entire application + * @remarks The following checks are done: + * 1. If the current user exists + * 2. if the volunteer user exists + * 3. If the event exists, (if sent in args) + * @returns Created event volunteer + */ + +export const createEventVolunteer: MutationResolvers["createEventVolunteer"] = + async (_parent, args, context) => { + const currentUser = await User.findOne({ _id: context.userId }).lean(); + if (!currentUser) { + throw new errors.NotFoundError( + requestContext.translate(USER_NOT_FOUND_ERROR.MESSAGE), + USER_NOT_FOUND_ERROR.CODE, + USER_NOT_FOUND_ERROR.PARAM + ); + } + const volunteerUser = await User.findOne({ _id: args.data?.userId }).lean(); + if (!volunteerUser) { + throw new errors.NotFoundError( + requestContext.translate(EVENT_VOLUNTEER_NOT_FOUND_ERROR.MESSAGE), + EVENT_VOLUNTEER_NOT_FOUND_ERROR.CODE, + EVENT_VOLUNTEER_NOT_FOUND_ERROR.PARAM + ); + } + if (args.data?.eventId) { + const event = await Event.findById(args.data?.eventId); + if (!event) { + throw new errors.NotFoundError( + requestContext.translate(EVENT_NOT_FOUND_ERROR.MESSAGE), + EVENT_NOT_FOUND_ERROR.CODE, + EVENT_NOT_FOUND_ERROR.PARAM + ); + } + } + const createdVolunteer = await EventVolunteer.create({ + userId: args.data.userId, + eventId: args.data.eventId, + isAssigned: false, + isInvited: true, + creatorId: context.userId, + }); + return createdVolunteer.toObject(); + }; diff --git a/src/resolvers/Mutation/index.ts b/src/resolvers/Mutation/index.ts index b6dc14631d..f3113d23af 100644 --- a/src/resolvers/Mutation/index.ts +++ b/src/resolvers/Mutation/index.ts @@ -38,6 +38,7 @@ import { deleteDonationById } from "./deleteDonationById"; import { deleteAgendaCategory } from "./deleteAgendaCategory"; import { forgotPassword } from "./forgotPassword"; import { joinPublicOrganization } from "./joinPublicOrganization"; +import { createEventVolunteer } from "./createEventVolunteer"; import { leaveOrganization } from "./leaveOrganization"; import { likeComment } from "./likeComment"; import { likePost } from "./likePost"; @@ -59,6 +60,7 @@ import { addUserToUserFamily } from "./addUserToUserFamily"; import { removeUserFromUserFamily } from "./removeUserFromUserFamily"; import { removeUserFamily } from "./removeUserFamily"; import { createUserFamily } from "./createUserFamily"; +import { removeEventVolunteer } from "./removeEventVolunteer"; import { removeGroupChat } from "./removeGroupChat"; import { removeAdvertisement } from "./removeAdvertisement"; import { removeMember } from "./removeMember"; @@ -86,6 +88,7 @@ import { unregisterForEventByUser } from "./unregisterForEventByUser"; import { updateActionItem } from "./updateActionItem"; import { updateActionItemCategory } from "./updateActionItemCategory"; import { updateEvent } from "./updateEvent"; +import { updateEventVolunteer } from "./updateEventVolunteer"; import { updateLanguage } from "./updateLanguage"; import { updateOrganization } from "./updateOrganization"; import { updatePluginStatus } from "./updatePluginStatus"; @@ -143,6 +146,7 @@ export const Mutation: MutationResolvers = { deleteAgendaCategory, forgotPassword, joinPublicOrganization, + createEventVolunteer, leaveOrganization, likeComment, likePost, @@ -160,6 +164,7 @@ export const Mutation: MutationResolvers = { removeDirectChat, removeEvent, removeEventAttendee, + removeEventVolunteer, removeAdvertisement, removeGroupChat, removeMember, @@ -188,6 +193,7 @@ export const Mutation: MutationResolvers = { updateActionItemCategory, updateAgendaCategory, updateEvent, + updateEventVolunteer, updateLanguage, updateOrganization, updatePluginStatus, diff --git a/src/resolvers/Mutation/removeEventVolunteer.ts b/src/resolvers/Mutation/removeEventVolunteer.ts new file mode 100644 index 0000000000..7f188dde0c --- /dev/null +++ b/src/resolvers/Mutation/removeEventVolunteer.ts @@ -0,0 +1,51 @@ +import { + EVENT_VOLUNTEER_NOT_FOUND_ERROR, + USER_NOT_FOUND_ERROR, +} from "../../constants"; +import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; +import { errors, requestContext } from "../../libraries"; +import { User, EventVolunteer } from "../../models"; + +/** + * This function enables to remove an Event Volunteer. + * @param _parent - parent of current request + * @param args - payload provided with the request + * @param context - context of entire application + * @remarks The following checks are done: + * 1. If the current user exists + * 2. If the Event volunteer to be removed exists. + * @returns Event Volunteer. + */ + +export const removeEventVolunteer: MutationResolvers["removeEventVolunteer"] = + async (_parent, args, context) => { + const currentUser = await User.findOne({ + _id: context.userId, + }).lean(); + + if (!currentUser) { + throw new errors.NotFoundError( + requestContext.translate(USER_NOT_FOUND_ERROR.MESSAGE), + USER_NOT_FOUND_ERROR.CODE, + USER_NOT_FOUND_ERROR.PARAM + ); + } + + const volunteer = await EventVolunteer.findOne({ + _id: args.id, + }); + + if (!volunteer) { + throw new errors.NotFoundError( + requestContext.translate(EVENT_VOLUNTEER_NOT_FOUND_ERROR.MESSAGE), + EVENT_VOLUNTEER_NOT_FOUND_ERROR.CODE, + EVENT_VOLUNTEER_NOT_FOUND_ERROR.PARAM + ); + } + + await EventVolunteer.deleteOne({ + _id: args.id, + }); + + return volunteer; + }; diff --git a/src/resolvers/Mutation/updateEventVolunteer.ts b/src/resolvers/Mutation/updateEventVolunteer.ts new file mode 100644 index 0000000000..d6a9a08eb9 --- /dev/null +++ b/src/resolvers/Mutation/updateEventVolunteer.ts @@ -0,0 +1,87 @@ +import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; +import type { EventVolunteerResponse } from "../../constants"; +import { + EVENT_VOLUNTEER_INVITE_USER_MISTMATCH, + EVENT_VOLUNTEER_NOT_FOUND_ERROR, + USER_NOT_FOUND_ERROR, +} from "../../constants"; +import type { InterfaceEventVolunteer } from "../../models"; +import { User, EventVolunteer } from "../../models"; +import { errors, requestContext } from "../../libraries"; +/** + * This function accepts the Event Volunteer Invite sent to a user + * @param _parent - parent of current request + * @param args - payload provided with the request + * @param context - context of entire application + * @remarks The following checks are done: + * 1. Whether the user exists + * 2. Whether the EventVolunteer exists + * 3. Whether the current user is the user of EventVolunteer + * 4. Whether the EventVolunteer is invited + */ +export const updateEventVolunteer: MutationResolvers["updateEventVolunteer"] = + async (_parent, args, context) => { + const currentUser = await User.findOne({ + _id: context.userId, + }).lean(); + + if (!currentUser) { + throw new errors.NotFoundError( + requestContext.translate(USER_NOT_FOUND_ERROR.MESSAGE), + USER_NOT_FOUND_ERROR.CODE, + USER_NOT_FOUND_ERROR.PARAM + ); + } + + const eventVolunteer = await EventVolunteer.findOne({ + _id: args.id, + }).lean(); + + if (!eventVolunteer) { + throw new errors.NotFoundError( + requestContext.translate(EVENT_VOLUNTEER_NOT_FOUND_ERROR.MESSAGE), + EVENT_VOLUNTEER_NOT_FOUND_ERROR.CODE, + EVENT_VOLUNTEER_NOT_FOUND_ERROR.PARAM + ); + } + + if (eventVolunteer.userId.toString() !== context.userId.toString()) { + throw new errors.ConflictError( + requestContext.translate(EVENT_VOLUNTEER_INVITE_USER_MISTMATCH.MESSAGE), + EVENT_VOLUNTEER_INVITE_USER_MISTMATCH.CODE, + EVENT_VOLUNTEER_INVITE_USER_MISTMATCH.PARAM + ); + } + + const updatedVolunteer = await EventVolunteer.findOneAndUpdate( + { + _id: args.id, + }, + { + $set: { + eventId: + args.data?.eventId === undefined + ? eventVolunteer.eventId + : (args?.data.eventId as string), + isAssigned: + args.data?.isAssigned === undefined + ? eventVolunteer.isAssigned + : (args.data?.isAssigned as boolean), + isInvited: + args.data?.isInvited === undefined + ? eventVolunteer.isInvited + : (args.data?.isInvited as boolean), + response: + args.data?.response === undefined + ? eventVolunteer.response + : (args.data?.response as EventVolunteerResponse), + }, + }, + { + new: true, + runValidators: true, + } + ).lean(); + + return updatedVolunteer as InterfaceEventVolunteer; + }; diff --git a/src/resolvers/Query/eventVolunteersByEvent.ts b/src/resolvers/Query/eventVolunteersByEvent.ts new file mode 100644 index 0000000000..e982f58e18 --- /dev/null +++ b/src/resolvers/Query/eventVolunteersByEvent.ts @@ -0,0 +1,20 @@ +import type { QueryResolvers } from "../../types/generatedGraphQLTypes"; +import { EventVolunteer } from "../../models"; +/** + * This query will fetch all events volunteers for the given eventId from database. + * @param _parent- + * @param args - An object that contains `id` of the Event. + * @returns An object that holds all Event Volunteers for the given Event + */ +export const eventVolunteersByEvent: QueryResolvers["eventVolunteersByEvent"] = + async (_parent, args) => { + const eventId = args.id; + + const volunteers = EventVolunteer.find({ + eventId: eventId, + }) + .populate("userId", "-password") + .lean(); + + return volunteers; + }; diff --git a/src/resolvers/index.ts b/src/resolvers/index.ts index fbfe904d28..56cf8482a6 100644 --- a/src/resolvers/index.ts +++ b/src/resolvers/index.ts @@ -6,6 +6,7 @@ import { Comment } from "./Comment"; import { DirectChat } from "./DirectChat"; import { DirectChatMessage } from "./DirectChatMessage"; import { Event } from "./Event"; +import { EventVolunteer } from "./EventVolunteer"; import { Feedback } from "./Feedback"; import { GroupChat } from "./GroupChat"; import { GroupChatMessage } from "./GroupChatMessage"; @@ -41,6 +42,7 @@ const resolvers: Resolvers = { DirectChat, DirectChatMessage, Event, + EventVolunteer, Feedback, GroupChat, UserFamily, diff --git a/src/typeDefs/enums.ts b/src/typeDefs/enums.ts index 55b354eca5..b49f44cce5 100644 --- a/src/typeDefs/enums.ts +++ b/src/typeDefs/enums.ts @@ -149,6 +149,11 @@ export const enums = gql` OTHER } + enum EventVolunteerResponse { + YES + NO + } + enum MaritalStatus { SINGLE ENGAGED diff --git a/src/typeDefs/inputs.ts b/src/typeDefs/inputs.ts index c01321548d..1bdafc7a57 100644 --- a/src/typeDefs/inputs.ts +++ b/src/typeDefs/inputs.ts @@ -93,6 +93,18 @@ export const inputs = gql` organizationId: ID! } + input EventVolunteerInput { + userId: ID! + eventId: ID! + } + + input UpdateEventVolunteerInput { + eventId: ID + isAssigned: Boolean + isInvited: Boolean + response: EventVolunteerResponse + } + input EventWhereInput { id: ID id_not: ID diff --git a/src/typeDefs/mutations.ts b/src/typeDefs/mutations.ts index 2832212d62..915201ab3c 100644 --- a/src/typeDefs/mutations.ts +++ b/src/typeDefs/mutations.ts @@ -134,6 +134,8 @@ export const mutations = gql` joinPublicOrganization(organizationId: ID!): User! @auth + createEventVolunteer(data: EventVolunteerInput!): EventVolunteer! @auth + leaveOrganization(organizationId: ID!): User! @auth likeComment(id: ID!): Comment @auth @@ -175,6 +177,8 @@ export const mutations = gql` removeEventAttendee(data: EventAttendeeInput!): User! @auth + removeEventVolunteer(id: ID!): EventVolunteer! @auth + removeGroupChat(chatId: ID!): GroupChat! @auth removeMember(data: UserAndOrganizationInput!): Organization! @auth @@ -245,6 +249,11 @@ export const mutations = gql` updateEvent(id: ID!, data: UpdateEventInput): Event! @auth + updateEventVolunteer( + id: ID! + data: UpdateEventVolunteerInput + ): EventVolunteer! @auth + updatePost(id: ID!, data: PostUpdateInput): Post! @auth updateLanguage(languageCode: String!): User! @auth diff --git a/src/typeDefs/queries.ts b/src/typeDefs/queries.ts index 9106b0b66f..f7155b8b82 100644 --- a/src/typeDefs/queries.ts +++ b/src/typeDefs/queries.ts @@ -42,6 +42,8 @@ export const queries = gql` orderBy: EventOrderByInput ): [Event!]! + eventVolunteersByEvent(id: ID!): [EventVolunteer] + getDonationById(id: ID!): Donation! getDonationByOrgId(orgId: ID!): [Donation] diff --git a/src/typeDefs/types.ts b/src/typeDefs/types.ts index fe652fe4d9..02211bf973 100644 --- a/src/typeDefs/types.ts +++ b/src/typeDefs/types.ts @@ -189,6 +189,18 @@ export const types = gql` averageFeedbackScore: Float } + type EventVolunteer { + _id: ID! + createdAt: DateTime! + creator: User + event: Event + isAssigned: Boolean + isInvited: Boolean + response: String + user: User! + updatedAt: DateTime! + } + type Feedback { _id: ID! event: Event! diff --git a/src/types/generatedGraphQLTypes.ts b/src/types/generatedGraphQLTypes.ts index 9b93e6bc21..4491e72ed2 100644 --- a/src/types/generatedGraphQLTypes.ts +++ b/src/types/generatedGraphQLTypes.ts @@ -11,6 +11,7 @@ import type { InterfaceDonation as InterfaceDonationModel } from '../models/Dona import type { InterfaceEvent as InterfaceEventModel } from '../models/Event'; import type { InterfaceEventAttendee as InterfaceEventAttendeeModel } from '../models/EventAttendee'; import type { InterfaceUserFamily as InterfaceUserFamilyModel } from '../models/userFamily'; +import type { InterfaceEventVolunteer as InterfaceEventVolunteerModel } from '../models/EventVolunteer'; import type { InterfaceFeedback as InterfaceFeedbackModel } from '../models/Feedback'; import type { InterfaceGroup as InterfaceGroupModel } from '../models/Group'; import type { InterfaceGroupChat as InterfaceGroupChatModel } from '../models/GroupChat'; @@ -395,6 +396,28 @@ export type EventOrderByInput = | 'title_ASC' | 'title_DESC'; +export type EventVolunteer = { + __typename?: 'EventVolunteer'; + _id: Scalars['ID']['output']; + createdAt: Scalars['DateTime']['output']; + creator?: Maybe; + event?: Maybe; + isAssigned?: Maybe; + isInvited?: Maybe; + response?: Maybe; + updatedAt: Scalars['DateTime']['output']; + user: User; +}; + +export type EventVolunteerInput = { + eventId: Scalars['ID']['input']; + userId: Scalars['ID']['input']; +}; + +export type EventVolunteerResponse = + | 'NO' + | 'YES'; + export type EventWhereInput = { description?: InputMaybe; description_contains?: InputMaybe; @@ -630,6 +653,7 @@ export type Mutation = { createDirectChat: DirectChat; createDonation: Donation; createEvent: Event; + createEventVolunteer: EventVolunteer; createGroupChat: GroupChat; createMember: Organization; createMessageChat: MessageChat; @@ -662,6 +686,7 @@ export type Mutation = { removeDirectChat: DirectChat; removeEvent: Event; removeEventAttendee: User; + removeEventVolunteer: EventVolunteer; removeGroupChat: GroupChat; removeMember: Organization; removeOrganization: User; @@ -692,6 +717,7 @@ export type Mutation = { updateAdvertisement?: Maybe; updateAgendaCategory?: Maybe; updateEvent: Event; + updateEventVolunteer: EventVolunteer; updateLanguage: User; updateOrganization: Organization; updatePluginStatus: Plugin; @@ -862,6 +888,11 @@ export type MutationCreateEventArgs = { }; +export type MutationCreateEventVolunteerArgs = { + data: EventVolunteerInput; +}; + + export type MutationCreateGroupChatArgs = { data: CreateGroupChatInput; }; @@ -1018,6 +1049,11 @@ export type MutationRemoveEventAttendeeArgs = { }; +export type MutationRemoveEventVolunteerArgs = { + id: Scalars['ID']['input']; +}; + + export type MutationRemoveGroupChatArgs = { chatId: Scalars['ID']['input']; }; @@ -1165,6 +1201,12 @@ export type MutationUpdateEventArgs = { }; +export type MutationUpdateEventVolunteerArgs = { + data?: InputMaybe; + id: Scalars['ID']['input']; +}; + + export type MutationUpdateLanguageArgs = { languageCode: Scalars['String']['input']; }; @@ -1480,6 +1522,7 @@ export type Query = { directChatsByUserID?: Maybe>>; directChatsMessagesByChatID?: Maybe>>; event?: Maybe; + eventVolunteersByEvent?: Maybe>>; eventsByOrganization?: Maybe>>; eventsByOrganizationConnection: Array; getAdvertisements?: Maybe>>; @@ -1569,6 +1612,11 @@ export type QueryEventArgs = { }; +export type QueryEventVolunteersByEventArgs = { + id: Scalars['ID']['input']; +}; + + export type QueryEventsByOrganizationArgs = { id?: InputMaybe; orderBy?: InputMaybe; @@ -1815,6 +1863,13 @@ export type UpdateEventInput = { title?: InputMaybe; }; +export type UpdateEventVolunteerInput = { + eventId?: InputMaybe; + isAssigned?: InputMaybe; + isInvited?: InputMaybe; + response?: InputMaybe; +}; + export type UpdateOrganizationInput = { address?: InputMaybe; description?: InputMaybe; @@ -2205,6 +2260,9 @@ export type ResolversTypes = { EventAttendeeInput: EventAttendeeInput; EventInput: EventInput; EventOrderByInput: EventOrderByInput; + EventVolunteer: ResolverTypeWrapper; + EventVolunteerInput: EventVolunteerInput; + EventVolunteerResponse: EventVolunteerResponse; EventWhereInput: EventWhereInput; ExtendSession: ResolverTypeWrapper; Feedback: ResolverTypeWrapper; @@ -2279,6 +2337,7 @@ export type ResolversTypes = { UpdateAdvertisementPayload: ResolverTypeWrapper & { advertisement?: Maybe }>; UpdateAgendaCategoryInput: UpdateAgendaCategoryInput; UpdateEventInput: UpdateEventInput; + UpdateEventVolunteerInput: UpdateEventVolunteerInput; UpdateOrganizationInput: UpdateOrganizationInput; UpdateUserInput: UpdateUserInput; UpdateUserPasswordInput: UpdateUserPasswordInput; @@ -2348,6 +2407,8 @@ export type ResolversParentTypes = { Event: InterfaceEventModel; EventAttendeeInput: EventAttendeeInput; EventInput: EventInput; + EventVolunteer: InterfaceEventVolunteerModel; + EventVolunteerInput: EventVolunteerInput; EventWhereInput: EventWhereInput; ExtendSession: ExtendSession; Feedback: InterfaceFeedbackModel; @@ -2413,6 +2474,7 @@ export type ResolversParentTypes = { UpdateAdvertisementPayload: Omit & { advertisement?: Maybe }; UpdateAgendaCategoryInput: UpdateAgendaCategoryInput; UpdateEventInput: UpdateEventInput; + UpdateEventVolunteerInput: UpdateEventVolunteerInput; UpdateOrganizationInput: UpdateOrganizationInput; UpdateUserInput: UpdateUserInput; UpdateUserPasswordInput: UpdateUserPasswordInput; @@ -2675,6 +2737,19 @@ export type EventResolvers; }; +export type EventVolunteerResolvers = { + _id?: Resolver; + createdAt?: Resolver; + creator?: Resolver, ParentType, ContextType>; + event?: Resolver, ParentType, ContextType>; + isAssigned?: Resolver, ParentType, ContextType>; + isInvited?: Resolver, ParentType, ContextType>; + response?: Resolver, ParentType, ContextType>; + updatedAt?: Resolver; + user?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + export type ExtendSessionResolvers = { accessToken?: Resolver; refreshToken?: Resolver; @@ -2846,7 +2921,8 @@ export type MutationResolvers, ParentType, ContextType, RequireFields>; createDirectChat?: Resolver>; createDonation?: Resolver>; - createEvent?: Resolver>; + createEvent?: Resolver>; + createEventVolunteer?: Resolver>; createGroupChat?: Resolver>; createMember?: Resolver>; createMessageChat?: Resolver>; @@ -2879,6 +2955,7 @@ export type MutationResolvers>; removeEvent?: Resolver>; removeEventAttendee?: Resolver>; + removeEventVolunteer?: Resolver>; removeGroupChat?: Resolver>; removeMember?: Resolver>; removeOrganization?: Resolver>; @@ -2909,6 +2986,7 @@ export type MutationResolvers, ParentType, ContextType, RequireFields>; updateAgendaCategory?: Resolver, ParentType, ContextType, RequireFields>; updateEvent?: Resolver>; + updateEventVolunteer?: Resolver>; updateLanguage?: Resolver>; updateOrganization?: Resolver>; updatePluginStatus?: Resolver>; @@ -3043,6 +3121,7 @@ export type QueryResolvers>>, ParentType, ContextType, RequireFields>; directChatsMessagesByChatID?: Resolver>>, ParentType, ContextType, RequireFields>; event?: Resolver, ParentType, ContextType, RequireFields>; + eventVolunteersByEvent?: Resolver>>, ParentType, ContextType, RequireFields>; eventsByOrganization?: Resolver>>, ParentType, ContextType, Partial>; eventsByOrganizationConnection?: Resolver, ParentType, ContextType, Partial>; getAdvertisements?: Resolver>>, ParentType, ContextType>; @@ -3247,6 +3326,7 @@ export type Resolvers = { EmailAddress?: GraphQLScalarType; Error?: ErrorResolvers; Event?: EventResolvers; + EventVolunteer?: EventVolunteerResolvers; ExtendSession?: ExtendSessionResolvers; Feedback?: FeedbackResolvers; FieldError?: FieldErrorResolvers; diff --git a/tests/helpers/events.ts b/tests/helpers/events.ts index 53432afa53..d9abdb1376 100644 --- a/tests/helpers/events.ts +++ b/tests/helpers/events.ts @@ -1,12 +1,15 @@ import type { TestOrganizationType, TestUserType } from "./userAndOrg"; -import { createTestUserAndOrganization } from "./userAndOrg"; -import type { InterfaceEvent } from "../../src/models"; -import { Event, EventAttendee, User } from "../../src/models"; +import { createTestUser, createTestUserAndOrganization } from "./userAndOrg"; +import type { InterfaceEvent, InterfaceEventVolunteer } from "../../src/models"; +import { EventVolunteer, Event, EventAttendee, User } from "../../src/models"; import type { Document } from "mongoose"; import { nanoid } from "nanoid"; +import { EventVolunteerResponse } from "../../src/constants"; -export type TestEventType = - | (InterfaceEvent & Document) +export type TestEventType = (InterfaceEvent & Document) | null; + +export type TestEventVolunteerType = + | (InterfaceEventVolunteer & Document) | null; export const createTestEvent = async (): Promise< @@ -74,7 +77,7 @@ export const createEventWithRegistrant = async ( await EventAttendee.create({ userId, - eventId: testEvent!._id, + eventId: testEvent?._id, }); await User.updateOne( @@ -91,3 +94,20 @@ export const createEventWithRegistrant = async ( ); return testEvent; }; + +export const createTestEventAndVolunteer = async (): Promise< + [TestUserType, TestUserType, TestEventType, TestEventVolunteerType] +> => { + const [creatorUser, , testEvent] = await createTestEvent(); + const volunteerUser = await createTestUser(); + const testEventVolunteer = await EventVolunteer.create({ + userId: volunteerUser?._id, + eventId: testEvent?._id, + isInvited: true, + isAssigned: false, + creatorId: creatorUser?._id, + response: EventVolunteerResponse.NO, + }); + + return [volunteerUser, creatorUser, testEvent, testEventVolunteer]; +}; diff --git a/tests/resolvers/EventVolunteer/creator.spec.ts b/tests/resolvers/EventVolunteer/creator.spec.ts new file mode 100644 index 0000000000..0908644977 --- /dev/null +++ b/tests/resolvers/EventVolunteer/creator.spec.ts @@ -0,0 +1,46 @@ +import "dotenv/config"; +import { creator as creatorResolver } from "../../../src/resolvers/EventVolunteer/creator"; +import { connect, disconnect } from "../../helpers/db"; +import type mongoose from "mongoose"; +import { + beforeAll, + afterAll, + describe, + it, + expect, + beforeEach, + vi, +} from "vitest"; +import type { TestEventVolunteerType } from "../../helpers/events"; +import { createTestEventAndVolunteer } from "../../helpers/events"; +import type { TestUserType } from "../../helpers/userAndOrg"; +import type { InterfaceEventVolunteer } from "../../../src/models"; + +let MONGOOSE_INSTANCE: typeof mongoose; +let testEventVolunteer: TestEventVolunteerType; +let creatorUser: TestUserType; + +beforeAll(async () => { + MONGOOSE_INSTANCE = await connect(); + [, creatorUser, , testEventVolunteer] = await createTestEventAndVolunteer(); +}); + +afterAll(async () => { + await disconnect(MONGOOSE_INSTANCE); +}); + +describe("resolvers -> EventVolunteer -> user", () => { + beforeEach(() => { + vi.resetModules(); + }); + it(`returns the correct creator object for parent event volunteer`, async () => { + const parent = testEventVolunteer?.toObject(); + const creatorPayload = await creatorResolver?.( + parent as InterfaceEventVolunteer, + {}, + {} + ); + + expect(creatorPayload?._id).toEqual(creatorUser?._id); + }); +}); diff --git a/tests/resolvers/EventVolunteer/event.spec.ts b/tests/resolvers/EventVolunteer/event.spec.ts new file mode 100644 index 0000000000..3c709cf2d2 --- /dev/null +++ b/tests/resolvers/EventVolunteer/event.spec.ts @@ -0,0 +1,38 @@ +import "dotenv/config"; +import { event as eventResolver } from "../../../src/resolvers/EventVolunteer/event"; +import { connect, disconnect } from "../../helpers/db"; +import type mongoose from "mongoose"; +import { beforeAll, afterAll, describe, it, expect } from "vitest"; +import type { + TestEventType, + TestEventVolunteerType, +} from "../../helpers/events"; +import { createTestEventAndVolunteer } from "../../helpers/events"; +import type { InterfaceEventVolunteer } from "../../../src/models"; + +let MONGOOSE_INSTANCE: typeof mongoose; +let testEvent: TestEventType; +let testEventVolunteer: TestEventVolunteerType; + +beforeAll(async () => { + MONGOOSE_INSTANCE = await connect(); + [, , testEvent, testEventVolunteer] = await createTestEventAndVolunteer(); +}); + +afterAll(async () => { + await disconnect(MONGOOSE_INSTANCE); +}); + +describe("resolvers -> EventVolunteer -> event", () => { + it(`returns the correct event object for parent event volunteer`, async () => { + const parent = testEventVolunteer?.toObject(); + + const eventPayload = await eventResolver?.( + parent as InterfaceEventVolunteer, + {}, + {} + ); + + expect(eventPayload).toEqual(testEvent?.toObject()); + }); +}); diff --git a/tests/resolvers/EventVolunteer/user.spec.ts b/tests/resolvers/EventVolunteer/user.spec.ts new file mode 100644 index 0000000000..bec3088b9f --- /dev/null +++ b/tests/resolvers/EventVolunteer/user.spec.ts @@ -0,0 +1,52 @@ +import "dotenv/config"; +import { user as userResolver } from "../../../src/resolvers/EventVolunteer/user"; +import { connect, disconnect } from "../../helpers/db"; +import type mongoose from "mongoose"; +import { + beforeAll, + afterAll, + describe, + it, + expect, + beforeEach, + vi, +} from "vitest"; +import type { TestEventVolunteerType } from "../../helpers/events"; +import { createTestEventAndVolunteer } from "../../helpers/events"; +import type { TestUserType } from "../../helpers/userAndOrg"; +import type { InterfaceEventVolunteer } from "../../../src/models"; + +let MONGOOSE_INSTANCE: typeof mongoose; +let testUser: TestUserType; +let testEventVolunteer: TestEventVolunteerType; + +beforeAll(async () => { + MONGOOSE_INSTANCE = await connect(); + [testUser, , , testEventVolunteer] = await createTestEventAndVolunteer(); +}); + +afterAll(async () => { + await disconnect(MONGOOSE_INSTANCE); +}); + +describe("resolvers -> EventVolunteer -> user", () => { + beforeEach(() => { + vi.resetModules(); + }); + it(`returns the correct user object for parent event volunteer`, async () => { + const parent = testEventVolunteer?.toObject(); + console.log(testEventVolunteer?.userId); + console.log(testUser?._id); + + const userPayload = await userResolver?.( + parent as InterfaceEventVolunteer, + {}, + {} + ); + + expect(userPayload).toEqual({ + ...testUser?.toObject(), + updatedAt: expect.anything(), + }); + }); +}); diff --git a/tests/resolvers/Mutation/createEventVolunteer.spec.ts b/tests/resolvers/Mutation/createEventVolunteer.spec.ts new file mode 100644 index 0000000000..faaece149c --- /dev/null +++ b/tests/resolvers/Mutation/createEventVolunteer.spec.ts @@ -0,0 +1,161 @@ +import "dotenv/config"; +import type mongoose from "mongoose"; +import { Types } from "mongoose"; +import type { MutationCreateEventVolunteerArgs } from "../../../src/types/generatedGraphQLTypes"; +import { connect, disconnect } from "../../helpers/db"; + +import { + afterAll, + afterEach, + beforeAll, + describe, + expect, + it, + vi, +} from "vitest"; +import { + EVENT_NOT_FOUND_ERROR, + EVENT_VOLUNTEER_NOT_FOUND_ERROR, + USER_NOT_FOUND_ERROR, +} from "../../../src/constants"; +import type { TestUserType } from "../../helpers/userAndOrg"; +import { createTestEvent } from "../../helpers/events"; +import type { TestEventType } from "../../helpers/events"; +import { createTestUser } from "../../helpers/user"; + +let testUser1: TestUserType, testUser2: TestUserType; +let testEvent: TestEventType; +let MONGOOSE_INSTANCE: typeof mongoose; + +beforeAll(async () => { + MONGOOSE_INSTANCE = await connect(); + testUser2 = await createTestUser(); + testUser1 = await createTestUser(); + [, , testEvent] = await createTestEvent(); +}); + +afterAll(async () => { + await disconnect(MONGOOSE_INSTANCE); +}); + +describe("resolvers -> Mutation -> createEventVolunteer", () => { + afterEach(() => { + vi.doUnmock("../../../src/constants"); + vi.resetModules(); + }); + + 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) => message); + try { + const args: MutationCreateEventVolunteerArgs = { + data: { + userId: testUser2?._id, + eventId: testEvent?._id, + }, + }; + + const context = { + userId: Types.ObjectId().toString(), + }; + + const { createEventVolunteer: createEventVolunteerResolver } = + await import("../../../src/resolvers/Mutation/createEventVolunteer"); + + await createEventVolunteerResolver?.({}, args, context); + } catch (error: unknown) { + expect(spy).toBeCalledWith(USER_NOT_FOUND_ERROR.MESSAGE); + expect((error as Error).message).toEqual(USER_NOT_FOUND_ERROR.MESSAGE); + } + }); + it(`throws NotFoundError if no user with _id === args.userId exists`, async () => { + const { requestContext } = await import("../../../src/libraries"); + const spy = vi + .spyOn(requestContext, "translate") + .mockImplementationOnce((message) => message); + try { + const args: MutationCreateEventVolunteerArgs = { + data: { + userId: Types.ObjectId().toString(), + eventId: testEvent?._id, + }, + }; + + const context = { + userId: testUser1?.id, + }; + + const { createEventVolunteer: createEventVolunteerResolver } = + await import("../../../src/resolvers/Mutation/createEventVolunteer"); + + await createEventVolunteerResolver?.({}, args, context); + } catch (error: unknown) { + expect(spy).toBeCalledWith(EVENT_VOLUNTEER_NOT_FOUND_ERROR.MESSAGE); + expect((error as Error).message).toEqual( + EVENT_VOLUNTEER_NOT_FOUND_ERROR.MESSAGE + ); + } + }); + + it(`throws NotFoundError if no event exists with _id === args.eventId`, async () => { + const { requestContext } = await import("../../../src/libraries"); + const spy = vi + .spyOn(requestContext, "translate") + .mockImplementationOnce((message) => message); + try { + const args: MutationCreateEventVolunteerArgs = { + data: { + userId: testUser2?._id, + eventId: Types.ObjectId().toString(), + }, + }; + + const context = { + userId: testUser1?.id, + }; + + const { createEventVolunteer: createEventVolunteerResolver } = + await import("../../../src/resolvers/Mutation/createEventVolunteer"); + + await createEventVolunteerResolver?.({}, args, context); + } catch (error: unknown) { + expect(spy).toBeCalledWith(EVENT_NOT_FOUND_ERROR.MESSAGE); + expect((error as Error).message).toEqual(EVENT_NOT_FOUND_ERROR.MESSAGE); + } + }); + + it(`returns EventVolunteer object if valid userId and eventId in args`, async () => { + const args: MutationCreateEventVolunteerArgs = { + data: { + userId: testUser2?._id, + eventId: testEvent?._id, + }, + }; + + const context = { + userId: testUser1?.id, + }; + + const { createEventVolunteer: createEventVolunteerResolver } = await import( + "../../../src/resolvers/Mutation/createEventVolunteer" + ); + + const createdVolunteer = await createEventVolunteerResolver?.( + {}, + args, + context + ); + + expect(createdVolunteer).toEqual( + expect.objectContaining({ + eventId: Types.ObjectId(testEvent?.id), + userId: testUser2?._id, + creatorId: testUser1?._id, + isInvited: true, + isAssigned: false, + }) + ); + }); +}); diff --git a/tests/resolvers/Mutation/removeEventVolunteer.spec.ts b/tests/resolvers/Mutation/removeEventVolunteer.spec.ts new file mode 100644 index 0000000000..f71e48c1f4 --- /dev/null +++ b/tests/resolvers/Mutation/removeEventVolunteer.spec.ts @@ -0,0 +1,126 @@ +import type mongoose from "mongoose"; +import { Types } from "mongoose"; +import type { MutationUpdateEventVolunteerArgs } from "../../../src/types/generatedGraphQLTypes"; +import { connect, disconnect } from "../../helpers/db"; +import { + USER_NOT_FOUND_ERROR, + EVENT_VOLUNTEER_NOT_FOUND_ERROR, +} from "../../../src/constants"; +import { + beforeAll, + afterAll, + describe, + it, + expect, + vi, + afterEach, +} from "vitest"; +import type { TestUserType } from "../../helpers/userAndOrg"; +import type { TestEventVolunteerType } from "../../helpers/events"; +import { createTestEventAndVolunteer } from "../../helpers/events"; +import { User } from "../../../src/models"; + +let MONGOOSE_INSTANCE: typeof mongoose; +let testUser: TestUserType; +let testEventVolunteer: TestEventVolunteerType; + +beforeAll(async () => { + MONGOOSE_INSTANCE = await connect(); + const temp = await createTestEventAndVolunteer(); + testUser = temp[0]; + testEventVolunteer = temp[3]; +}); + +afterAll(async () => { + await disconnect(MONGOOSE_INSTANCE); +}); + +describe("resolvers -> Mutation -> removeEventVolunteer", () => { + afterEach(() => { + vi.doUnmock("../../../src/constants"); + vi.resetModules(); + }); + 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: MutationUpdateEventVolunteerArgs = { + id: testEventVolunteer?._id, + }; + + const context = { userId: Types.ObjectId().toString() }; + + const u = await User.findById(context.userId); + console.log(u); + + const { removeEventVolunteer: removeEventVolunteerResolver } = + await import("../../../src/resolvers/Mutation/removeEventVolunteer"); + + await removeEventVolunteerResolver?.({}, args, context); + } catch (error: unknown) { + expect(spy).toHaveBeenLastCalledWith(USER_NOT_FOUND_ERROR.MESSAGE); + expect((error as Error).message).toEqual( + `Translated ${USER_NOT_FOUND_ERROR.MESSAGE}` + ); + } + }); + + it(`throws NotFoundError if no event volunteer 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: MutationUpdateEventVolunteerArgs = { + id: Types.ObjectId().toString(), + }; + + const context = { userId: testUser?._id }; + + const { removeEventVolunteer: removeEventVolunteerResolver } = + await import("../../../src/resolvers/Mutation/removeEventVolunteer"); + + await removeEventVolunteerResolver?.({}, args, context); + } catch (error: unknown) { + expect(spy).toHaveBeenLastCalledWith( + EVENT_VOLUNTEER_NOT_FOUND_ERROR.MESSAGE + ); + expect((error as Error).message).toEqual( + `Translated ${EVENT_VOLUNTEER_NOT_FOUND_ERROR.MESSAGE}` + ); + } + }); + + it(`removes event volunteer with _id === args.id and returns it`, async () => { + const args: MutationUpdateEventVolunteerArgs = { + id: testEventVolunteer?._id, + }; + + const context = { userId: testUser?._id }; + const { removeEventVolunteer: removeEventVolunteerResolver } = await import( + "../../../src/resolvers/Mutation/removeEventVolunteer" + ); + + const deletedVolunteer = await removeEventVolunteerResolver?.( + {}, + args, + context + ); + + expect(deletedVolunteer).toEqual( + expect.objectContaining({ + _id: testEventVolunteer?._id, + userId: testEventVolunteer?.userId, + isInvited: testEventVolunteer?.isInvited, + isAssigned: testEventVolunteer?.isAssigned, + response: testEventVolunteer?.response, + }) + ); + }); +}); diff --git a/tests/resolvers/Mutation/updateEventVolunteer.spec.ts b/tests/resolvers/Mutation/updateEventVolunteer.spec.ts new file mode 100644 index 0000000000..151cc64693 --- /dev/null +++ b/tests/resolvers/Mutation/updateEventVolunteer.spec.ts @@ -0,0 +1,200 @@ +import type mongoose from "mongoose"; +import { Types } from "mongoose"; +import type { MutationUpdateEventVolunteerArgs } from "../../../src/types/generatedGraphQLTypes"; +import { connect, disconnect } from "../../helpers/db"; +import { + USER_NOT_FOUND_ERROR, + EventVolunteerResponse, + EVENT_VOLUNTEER_NOT_FOUND_ERROR, + EVENT_VOLUNTEER_INVITE_USER_MISTMATCH, +} from "../../../src/constants"; +import { + beforeAll, + afterAll, + describe, + it, + expect, + vi, + afterEach, +} from "vitest"; +import type { + TestEventType, + TestEventVolunteerType, +} from "../../helpers/events"; +import { createTestEventAndVolunteer } from "../../helpers/events"; +import { createTestUser } from "../../helpers/user"; + +let MONGOOSE_INSTANCE: typeof mongoose; +let testEvent: TestEventType; +let testEventVolunteer: TestEventVolunteerType; + +beforeAll(async () => { + MONGOOSE_INSTANCE = await connect(); + const temp = await createTestEventAndVolunteer(); + testEvent = temp[2]; + testEventVolunteer = temp[3]; +}); + +afterAll(async () => { + await disconnect(MONGOOSE_INSTANCE); +}); + +describe("resolvers -> Mutation -> updateEventVolunteer", () => { + afterEach(() => { + vi.doUnmock("../../../src/constants"); + vi.resetModules(); + }); + 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: MutationUpdateEventVolunteerArgs = { + id: testEventVolunteer?._id, + data: { + response: EventVolunteerResponse.YES, + }, + }; + + const context = { userId: Types.ObjectId().toString() }; + + const { updateEventVolunteer: updateEventVolunteerResolver } = + await import("../../../src/resolvers/Mutation/updateEventVolunteer"); + + await updateEventVolunteerResolver?.({}, args, context); + } catch (error: unknown) { + expect(spy).toHaveBeenLastCalledWith(USER_NOT_FOUND_ERROR.MESSAGE); + expect((error as Error).message).toEqual( + `Translated ${USER_NOT_FOUND_ERROR.MESSAGE}` + ); + } + }); + + it(`throws NotFoundError if no event volunteer 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: MutationUpdateEventVolunteerArgs = { + id: Types.ObjectId().toString(), + data: { + response: EventVolunteerResponse.YES, + }, + }; + + const context = { userId: testEventVolunteer?.userId }; + + const { updateEventVolunteer: updateEventVolunteerResolver } = + await import("../../../src/resolvers/Mutation/updateEventVolunteer"); + + await updateEventVolunteerResolver?.({}, args, context); + } catch (error: unknown) { + expect(spy).toHaveBeenLastCalledWith( + EVENT_VOLUNTEER_NOT_FOUND_ERROR.MESSAGE + ); + expect((error as Error).message).toEqual( + `Translated ${EVENT_VOLUNTEER_NOT_FOUND_ERROR.MESSAGE}` + ); + } + }); + + it(`throws ConflictError if userId of volunteer is not equal to context.userId `, async () => { + const { requestContext } = await import("../../../src/libraries"); + + const spy = vi + .spyOn(requestContext, "translate") + .mockImplementationOnce((message) => `Translated ${message}`); + + try { + const args: MutationUpdateEventVolunteerArgs = { + id: testEventVolunteer?._id, + data: { + response: EventVolunteerResponse.YES, + }, + }; + + const testUser2 = await createTestUser(); + const context = { userId: testUser2?._id }; + const { updateEventVolunteer: updateEventVolunteerResolver } = + await import("../../../src/resolvers/Mutation/updateEventVolunteer"); + + await updateEventVolunteerResolver?.({}, args, context); + } catch (error: unknown) { + expect(spy).toHaveBeenLastCalledWith( + EVENT_VOLUNTEER_INVITE_USER_MISTMATCH.MESSAGE + ); + expect((error as Error).message).toEqual( + `Translated ${EVENT_VOLUNTEER_INVITE_USER_MISTMATCH.MESSAGE}` + ); + } + }); + + it(`updates the Event Volunteer with _id === args.id and returns it`, async () => { + const args: MutationUpdateEventVolunteerArgs = { + id: testEventVolunteer?._id, + data: { + isAssigned: true, + response: EventVolunteerResponse.YES, + isInvited: true, + eventId: testEvent?._id, + }, + }; + + const context = { userId: testEventVolunteer?.userId }; + + const { updateEventVolunteer: updateEventVolunteerResolver } = await import( + "../../../src/resolvers/Mutation/updateEventVolunteer" + ); + + const updatedEventVolunteer = await updateEventVolunteerResolver?.( + {}, + args, + context + ); + + expect(updatedEventVolunteer).toEqual( + expect.objectContaining({ + isAssigned: true, + response: EventVolunteerResponse.YES, + eventId: testEvent?._id, + isInvited: true, + }) + ); + }); + + it(`updates the Event Volunteer with _id === args.id, even if args.data is empty object`, async () => { + const t = await createTestEventAndVolunteer(); + testEventVolunteer = t[3]; + const args: MutationUpdateEventVolunteerArgs = { + id: testEventVolunteer?._id, + data: {}, + }; + + const context = { userId: testEventVolunteer?.userId }; + + const { updateEventVolunteer: updateEventVolunteerResolver } = await import( + "../../../src/resolvers/Mutation/updateEventVolunteer" + ); + + const updatedEventVolunteer = await updateEventVolunteerResolver?.( + {}, + args, + context + ); + + expect(updatedEventVolunteer).toEqual( + expect.objectContaining({ + isAssigned: testEventVolunteer?.isAssigned, + response: testEventVolunteer?.response, + eventId: testEventVolunteer?.eventId, + isInvited: testEventVolunteer?.isInvited, + }) + ); + }); +}); diff --git a/tests/resolvers/Query/eventVolunteersByEvent.spec.ts b/tests/resolvers/Query/eventVolunteersByEvent.spec.ts new file mode 100644 index 0000000000..2b7a3593f1 --- /dev/null +++ b/tests/resolvers/Query/eventVolunteersByEvent.spec.ts @@ -0,0 +1,40 @@ +import type mongoose from "mongoose"; +import { connect, disconnect } from "../../helpers/db"; +import { eventVolunteersByEvent } from "../../../src/resolvers/Query/eventVolunteersByEvent"; +import { beforeAll, afterAll, describe, it, expect } from "vitest"; +import type { TestEventType } from "../../helpers/events"; +import { createTestEventAndVolunteer } from "../../helpers/events"; +import { EventVolunteer } from "../../../src/models"; + +let MONGOOSE_INSTANCE: typeof mongoose; +let testEvent: TestEventType; + +beforeAll(async () => { + MONGOOSE_INSTANCE = await connect(); + const temp = await createTestEventAndVolunteer(); + testEvent = temp[2]; +}); + +afterAll(async () => { + await disconnect(MONGOOSE_INSTANCE); +}); + +describe("resolvers -> Mutation -> eventVolunteersByEvent", () => { + it(`returns list of all existing event volunteers with eventId === args.id`, async () => { + const volunteersPayload = await eventVolunteersByEvent?.( + {}, + { + id: testEvent?._id, + }, + {} + ); + + const volunteers = await EventVolunteer.find({ + eventId: testEvent?._id, + }) + .populate("userId", "-password") + .lean(); + + expect(volunteersPayload).toEqual(volunteers); + }); +}); diff --git a/tests/utilities/encodedImageStorage/uploadEncodedImage.spec.ts b/tests/utilities/encodedImageStorage/uploadEncodedImage.spec.ts index 7d774760b6..479542a1d1 100644 --- a/tests/utilities/encodedImageStorage/uploadEncodedImage.spec.ts +++ b/tests/utilities/encodedImageStorage/uploadEncodedImage.spec.ts @@ -46,20 +46,22 @@ describe("src -> utilities -> encodedImageStorage -> uploadEncodedImage", () => "NAAAAKElEQVQ4jWNgYGD4Twzu6FhFFGYYNXDUwGFpIAk2E4dHDRw1cDgaCAASFOffhEIO" + "3gAAAABJRU5ErkJggg=="; await uploadEncodedImage(img, null); - } catch (error: any) { - expect(error.message).toEqual(`Translated ${INVALID_FILE_TYPE.MESSAGE}`); + } catch (error: unknown) { + expect((error as Error).message).toEqual( + `Translated ${INVALID_FILE_TYPE.MESSAGE}` + ); expect(spy).toBeCalledWith(INVALID_FILE_TYPE.MESSAGE); } }); it("should not create new image if it is bigger than the limit", async () => { - const size = Number(process.env.IMAGE_SIZE_LIMIT_KB); + const size = Number(process.env.IMAGE_SIZE_LIMIT_KB) || 3000; try { const img = "data:image/jpg;base64," + generateRandomString(size + 1000); await uploadEncodedImage(img, null); - } catch (error: any) { - expect(error.message).toEqual(IMAGE_SIZE_LIMIT_KB.MESSAGE); + } catch (error: unknown) { + expect((error as Error).message).toEqual(IMAGE_SIZE_LIMIT_KB.MESSAGE); } }); @@ -71,7 +73,7 @@ describe("src -> utilities -> encodedImageStorage -> uploadEncodedImage", () => "3gAAAABJRU5ErkJggg=="; const fileName = await uploadEncodedImage(img, null); expect(fileName).not.toBe(null); - } catch (error: any) { + } catch (error: unknown) { console.log(error); } }); @@ -85,7 +87,7 @@ describe("src -> utilities -> encodedImageStorage -> uploadEncodedImage", () => const fileName = await uploadEncodedImage(img, null); expect(fileName).not.toBe(null); testPreviousImagePath = fileName; - } catch (error: any) { + } catch (error: unknown) { console.log(error); } }); @@ -180,7 +182,7 @@ describe("src -> utilities -> encodedImageStorage -> uploadEncodedImage", () => fs.unlink(path.join(__dirname, "../../../".concat(fileName)), (err) => { if (err) throw err; }); - } catch (error: any) { + } catch (error: unknown) { console.log(error); } });