-
-
Notifications
You must be signed in to change notification settings - Fork 808
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add feedback model and typedefs * Add field level resolvers * Add mutation and queries * Add tests for field level resolvers * Complete testing for feedback system
- Loading branch information
Showing
27 changed files
with
909 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import type { Types, PopulatedDoc, Document, Model } from "mongoose"; | ||
import { Schema, model, models } from "mongoose"; | ||
import type { InterfaceEvent } from "./Event"; | ||
|
||
export interface InterfaceFeedback { | ||
_id: Types.ObjectId; | ||
eventId: PopulatedDoc<InterfaceEvent & Document>; | ||
rating: number; | ||
review: string | null; | ||
} | ||
|
||
const feedbackSchema = new Schema({ | ||
eventId: { | ||
type: Schema.Types.ObjectId, | ||
ref: "Event", | ||
required: true, | ||
}, | ||
rating: { | ||
type: Number, | ||
required: true, | ||
default: 0, | ||
max: 10, | ||
}, | ||
review: { | ||
type: String, | ||
required: false, | ||
}, | ||
}); | ||
|
||
// We will also create an index here for faster database querying | ||
feedbackSchema.index({ | ||
eventId: 1, | ||
}); | ||
|
||
const feedbackModel = (): Model<InterfaceFeedback> => | ||
model<InterfaceFeedback>("Feedback", feedbackSchema); | ||
|
||
// This syntax is needed to prevent Mongoose OverwriteModelError while running tests. | ||
export const Feedback = (models.Feedback || feedbackModel()) as ReturnType< | ||
typeof feedbackModel | ||
>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import type { EventResolvers } from "../../types/generatedGraphQLTypes"; | ||
import { Feedback } from "../../models"; | ||
|
||
export const averageFeedbackScore: EventResolvers["averageFeedbackScore"] = | ||
async (parent) => { | ||
const feedbacks = await Feedback.find({ | ||
eventId: parent._id, | ||
}) | ||
.select("rating") | ||
.lean(); | ||
|
||
// Return null if no feedback has been submitted | ||
if (feedbacks.length === 0) return null; | ||
|
||
// Return the average feedback score | ||
const sum = feedbacks.reduce( | ||
(accumulator, feedback) => accumulator + feedback.rating, | ||
0 | ||
); | ||
return sum / feedbacks.length; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import type { EventResolvers } from "../../types/generatedGraphQLTypes"; | ||
import { Feedback } from "../../models"; | ||
|
||
export const feedback: EventResolvers["feedback"] = async (parent) => { | ||
return Feedback.find({ | ||
eventId: parent._id, | ||
}).lean(); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,16 @@ | ||
import type { EventResolvers } from "../../types/generatedGraphQLTypes"; | ||
import { attendees } from "./attendees"; | ||
import { attendeesCheckInStatus } from "./attendeesCheckInStatus"; | ||
import { averageFeedbackScore } from "./averageFeedbackScore"; | ||
import { feedback } from "./feedback"; | ||
import { organization } from "./organization"; | ||
import { projects } from "./projects"; | ||
|
||
export const Event: EventResolvers = { | ||
attendees, | ||
attendeesCheckInStatus, | ||
averageFeedbackScore, | ||
feedback, | ||
organization, | ||
projects, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import type { FeedbackResolvers } from "../../types/generatedGraphQLTypes"; | ||
import { Event } from "../../models"; | ||
|
||
export const event: FeedbackResolvers["event"] = async (parent) => { | ||
return await Event.findOne({ | ||
_id: parent.eventId, | ||
}).lean(); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import type { FeedbackResolvers } from "../../types/generatedGraphQLTypes"; | ||
import { event } from "./event"; | ||
|
||
export const Feedback: FeedbackResolvers = { | ||
event, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import { | ||
EVENT_NOT_FOUND_ERROR, | ||
USER_NOT_FOUND_ERROR, | ||
USER_NOT_CHECKED_IN, | ||
USER_NOT_REGISTERED_FOR_EVENT, | ||
FEEDBACK_ALREADY_SUBMITTED, | ||
} from "../../constants"; | ||
import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; | ||
import { errors, requestContext } from "../../libraries"; | ||
import { User, Event, EventAttendee, CheckIn, Feedback } from "../../models"; | ||
|
||
export const addFeedback: MutationResolvers["addFeedback"] = async ( | ||
_parent, | ||
args, | ||
context | ||
) => { | ||
const currentUserExists = await User.exists({ | ||
_id: context.userId, | ||
}); | ||
|
||
if (!currentUserExists) { | ||
throw new errors.NotFoundError( | ||
requestContext.translate(USER_NOT_FOUND_ERROR.MESSAGE), | ||
USER_NOT_FOUND_ERROR.CODE, | ||
USER_NOT_FOUND_ERROR.PARAM | ||
); | ||
} | ||
|
||
const currentEventExists = await Event.exists({ | ||
_id: args.data.eventId, | ||
}); | ||
|
||
if (!currentEventExists) { | ||
throw new errors.NotFoundError( | ||
requestContext.translate(EVENT_NOT_FOUND_ERROR.MESSAGE), | ||
EVENT_NOT_FOUND_ERROR.CODE, | ||
EVENT_NOT_FOUND_ERROR.PARAM | ||
); | ||
} | ||
|
||
const eventAttendeeObject = await EventAttendee.findOne({ | ||
eventId: args.data.eventId, | ||
userId: context.userId, | ||
}) | ||
.populate("checkInId") | ||
.lean(); | ||
|
||
if (eventAttendeeObject === 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 (eventAttendeeObject.checkInId === null) { | ||
throw new errors.ConflictError( | ||
requestContext.translate(USER_NOT_CHECKED_IN.MESSAGE), | ||
USER_NOT_CHECKED_IN.CODE, | ||
USER_NOT_CHECKED_IN.PARAM | ||
); | ||
} | ||
|
||
if (eventAttendeeObject.checkInId.feedbackSubmitted) { | ||
throw new errors.ConflictError( | ||
requestContext.translate(FEEDBACK_ALREADY_SUBMITTED.MESSAGE), | ||
FEEDBACK_ALREADY_SUBMITTED.CODE, | ||
FEEDBACK_ALREADY_SUBMITTED.PARAM | ||
); | ||
} | ||
|
||
await CheckIn.findByIdAndUpdate(eventAttendeeObject.checkInId, { | ||
feedbackSubmitted: true, | ||
}); | ||
|
||
const feedback = await Feedback.create({ ...args.data }); | ||
|
||
return feedback; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import type { QueryResolvers } from "../../types/generatedGraphQLTypes"; | ||
import { User, Event, EventAttendee } from "../../models"; | ||
import { errors, requestContext } from "../../libraries"; | ||
import { | ||
EVENT_NOT_FOUND_ERROR, | ||
USER_NOT_FOUND_ERROR, | ||
USER_NOT_CHECKED_IN, | ||
USER_NOT_REGISTERED_FOR_EVENT, | ||
} from "../../constants"; | ||
|
||
export const hasSubmittedFeedback: QueryResolvers["hasSubmittedFeedback"] = | ||
async (_parent, args) => { | ||
const currentUserExists = await User.exists({ | ||
_id: args.userId, | ||
}); | ||
|
||
if (!currentUserExists) { | ||
throw new errors.NotFoundError( | ||
requestContext.translate(USER_NOT_FOUND_ERROR.MESSAGE), | ||
USER_NOT_FOUND_ERROR.CODE, | ||
USER_NOT_FOUND_ERROR.PARAM | ||
); | ||
} | ||
|
||
const currentEventExists = await Event.exists({ | ||
_id: args.eventId, | ||
}); | ||
|
||
if (!currentEventExists) { | ||
throw new errors.NotFoundError( | ||
requestContext.translate(EVENT_NOT_FOUND_ERROR.MESSAGE), | ||
EVENT_NOT_FOUND_ERROR.CODE, | ||
EVENT_NOT_FOUND_ERROR.PARAM | ||
); | ||
} | ||
|
||
const eventAttendeeObject = await EventAttendee.findOne({ | ||
...args, | ||
}) | ||
.populate("checkInId") | ||
.lean(); | ||
|
||
if (eventAttendeeObject === 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 (eventAttendeeObject.checkInId === null) { | ||
throw new errors.ConflictError( | ||
requestContext.translate(USER_NOT_CHECKED_IN.MESSAGE), | ||
USER_NOT_CHECKED_IN.CODE, | ||
USER_NOT_CHECKED_IN.PARAM | ||
); | ||
} | ||
|
||
return eventAttendeeObject.checkInId.feedbackSubmitted; | ||
}; |
Oops, something went wrong.