diff --git a/package-lock.json b/package-lock.json index 4c8b9083f9..172156bac0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@faker-js/faker": "^8.2.0", "@graphql-inspector/cli": "^5.0.6", "@graphql-tools/resolvers-composition": "^7.0.1", - "@graphql-tools/schema": "^10.0.0", + "@graphql-tools/schema": "^10.0.4", "@graphql-tools/utils": "^10.3.2", "@parcel/watcher": "^2.4.1", "@types/graphql-upload": "^16.0.5", @@ -59,14 +59,14 @@ "uuid": "^10.0.0", "validator": "^13.12.0", "winston": "^3.13.0", - "ws": "^8.17.0", + "ws": "^8.18.0", "yargs": "^17.7.2", "zod": "^3.23.8", "zod-error": "^1.5.0" }, "devDependencies": { "@graphql-codegen/cli": "^5.0.2", - "@graphql-codegen/typescript": "^4.0.7", + "@graphql-codegen/typescript": "^4.0.9", "@graphql-codegen/typescript-resolvers": "^4.2.0", "@graphql-eslint/eslint-plugin": "^3.20.1", "@parcel/watcher": "^2.4.1", @@ -86,7 +86,7 @@ "@types/node": "^20.14.9", "@types/nodemailer": "^6.4.15", "@types/uuid": "^9.0.7", - "@types/validator": "^13.11.10", + "@types/validator": "^13.12.0", "@typescript-eslint/eslint-plugin": "^7.14.1", "@typescript-eslint/parser": "^7.16.0", "@vitest/coverage-v8": "^1.6.0", @@ -98,7 +98,7 @@ "eslint-plugin-tsdoc": "^0.3.0", "get-graphql-schema": "^2.1.2", "graphql-markdown": "^7.0.0", - "husky": "^9.0.11", + "husky": "^9.1.1", "lint-staged": "^15.2.7", "prettier": "^3.2.5", "rimraf": "^6.0.1", @@ -2294,14 +2294,14 @@ } }, "node_modules/@graphql-codegen/typescript": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript/-/typescript-4.0.8.tgz", - "integrity": "sha512-kYS3SjGNnC9vgFS8N3vaxzRFkdXX2umMi1SOpHjMFCPjMe8NR0uNdW4nP9T0YEq+DvWgj+XojjpFy2oyz9q12w==", + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript/-/typescript-4.0.9.tgz", + "integrity": "sha512-0O35DMR4d/ctuHL1Zo6mRUUzp0BoszKfeWsa6sCm/g70+S98+hEfTwZNDkQHylLxapiyjssF9uw/F+sXqejqLw==", "dev": true, "dependencies": { "@graphql-codegen/plugin-helpers": "^5.0.4", "@graphql-codegen/schema-ast": "^4.0.2", - "@graphql-codegen/visitor-plugin-common": "5.3.0", + "@graphql-codegen/visitor-plugin-common": "5.3.1", "auto-bind": "~4.0.0", "tslib": "~2.6.0" }, @@ -2364,9 +2364,9 @@ } }, "node_modules/@graphql-codegen/typescript/node_modules/@graphql-codegen/visitor-plugin-common": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-5.3.0.tgz", - "integrity": "sha512-+kUk7gRD/72Wfkjd7D96Lonh9k4lFw9d3O1+I07Jyja4QN9H42kdFEO0hM/b4Q9lLkI1yJ66Oym7lWz2Ikj3aw==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-5.3.1.tgz", + "integrity": "sha512-MktoBdNZhSmugiDjmFl1z6rEUUaqyxtFJYWnDilE7onkPgyw//O0M+TuPBJPBWdyV6J2ond0Hdqtq+rkghgSIQ==", "dev": true, "dependencies": { "@graphql-codegen/plugin-helpers": "^5.0.4", @@ -3852,12 +3852,12 @@ } }, "node_modules/@graphql-tools/schema": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.3.tgz", - "integrity": "sha512-p28Oh9EcOna6i0yLaCFOnkcBDQECVf3SCexT6ktb86QNj9idnkhI+tCxnwZDh58Qvjd2nURdkbevvoZkvxzCog==", + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.4.tgz", + "integrity": "sha512-HuIwqbKxPaJujox25Ra4qwz0uQzlpsaBOzO6CVfzB/MemZdd+Gib8AIvfhQArK0YIN40aDran/yi+E5Xf0mQww==", "dependencies": { "@graphql-tools/merge": "^9.0.3", - "@graphql-tools/utils": "^10.0.13", + "@graphql-tools/utils": "^10.2.1", "tslib": "^2.4.0", "value-or-promise": "^1.0.12" }, @@ -5941,9 +5941,9 @@ "dev": true }, "node_modules/@types/validator": { - "version": "13.11.10", - "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.11.10.tgz", - "integrity": "sha512-e2PNXoXLr6Z+dbfx5zSh9TRlXJrELycxiaXznp4S5+D2M3b9bqJEitNHA5923jhnB2zzFiZHa2f0SI1HoIahpg==", + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.12.0.tgz", + "integrity": "sha512-nH45Lk7oPIJ1RVOF6JgFI6Dy0QpHEzq4QecZhvguxYPDwT8c93prCMqAtiIttm39voZ+DDR+qkNnMpJmMBRqag==", "dev": true }, "node_modules/@types/webidl-conversions": { @@ -10621,12 +10621,12 @@ } }, "node_modules/husky": { - "version": "9.0.11", - "resolved": "https://registry.npmjs.org/husky/-/husky-9.0.11.tgz", - "integrity": "sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==", + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.1.tgz", + "integrity": "sha512-fCqlqLXcBnXa/TJXmT93/A36tJsjdJkibQ1MuIiFyCCYUlpYpIaj2mv1w+3KR6Rzu1IC3slFTje5f6DUp2A2rg==", "dev": true, "bin": { - "husky": "bin.mjs" + "husky": "bin.js" }, "engines": { "node": ">=18" @@ -17215,9 +17215,9 @@ "dev": true }, "node_modules/ws": { - "version": "8.17.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz", - "integrity": "sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "engines": { "node": ">=10.0.0" }, diff --git a/package.json b/package.json index c43575a2b5..f634b418d5 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "@faker-js/faker": "^8.2.0", "@graphql-inspector/cli": "^5.0.6", "@graphql-tools/resolvers-composition": "^7.0.1", - "@graphql-tools/schema": "^10.0.0", + "@graphql-tools/schema": "^10.0.4", "@graphql-tools/utils": "^10.3.2", "@parcel/watcher": "^2.4.1", "@types/graphql-upload": "^16.0.5", @@ -93,14 +93,14 @@ "uuid": "^10.0.0", "validator": "^13.12.0", "winston": "^3.13.0", - "ws": "^8.17.0", + "ws": "^8.18.0", "yargs": "^17.7.2", "zod": "^3.23.8", "zod-error": "^1.5.0" }, "devDependencies": { "@graphql-codegen/cli": "^5.0.2", - "@graphql-codegen/typescript": "^4.0.7", + "@graphql-codegen/typescript": "^4.0.9", "@graphql-codegen/typescript-resolvers": "^4.2.0", "@graphql-eslint/eslint-plugin": "^3.20.1", "@parcel/watcher": "^2.4.1", @@ -120,7 +120,7 @@ "@types/node": "^20.14.9", "@types/nodemailer": "^6.4.15", "@types/uuid": "^9.0.7", - "@types/validator": "^13.11.10", + "@types/validator": "^13.12.0", "@typescript-eslint/eslint-plugin": "^7.14.1", "@typescript-eslint/parser": "^7.16.0", "@vitest/coverage-v8": "^1.6.0", @@ -132,7 +132,7 @@ "eslint-plugin-tsdoc": "^0.3.0", "get-graphql-schema": "^2.1.2", "graphql-markdown": "^7.0.0", - "husky": "^9.0.11", + "husky": "^9.1.1", "lint-staged": "^15.2.7", "prettier": "^3.2.5", "rimraf": "^6.0.1", diff --git a/schema.graphql b/schema.graphql index 561bf2ea9c..f605000c6a 100644 --- a/schema.graphql +++ b/schema.graphql @@ -539,7 +539,7 @@ type DirectChat { createdAt: DateTime! creator: User messages: [DirectChatMessage] - organization: Organization! + organization: Organization updatedAt: DateTime! users: [User!]! } @@ -915,6 +915,7 @@ type GroupChat { creator: User messages: [GroupChatMessage] organization: Organization! + title: String! updatedAt: DateTime! users: [User!]! } @@ -1467,6 +1468,7 @@ type Query { checkAuth: User! customDataByOrganization(organizationId: ID!): [UserCustomData!]! customFieldsByOrganization(id: ID!): [OrganizationCustomField] + directChatById(id: ID!): DirectChat directChatsByUserID(id: ID!): [DirectChat] directChatsMessagesByChatID(id: ID!): [DirectChatMessage] event(id: ID!): Event @@ -1492,6 +1494,8 @@ type Query { getPlugins: [Plugin] getVenueByOrgId(first: Int, orderBy: VenueOrderByInput, orgId: ID!, skip: Int, where: VenueWhereInput): [Venue] getlanguage(lang_code: String!): [Translation] + groupChatById(id: ID!): GroupChat + groupChatsByUserId(id: ID!): [GroupChat] hasSubmittedFeedback(eventId: ID!, userId: ID!): Boolean isSampleOrganization(id: ID!): Boolean! joinedOrganizations(id: ID): [Organization] @@ -1593,8 +1597,8 @@ enum Status { type Subscription { directMessageChat: MessageChat - messageSentToDirectChat: DirectChatMessage - messageSentToGroupChat: GroupChatMessage + messageSentToDirectChat(userId: ID!): DirectChatMessage + messageSentToGroupChat(userId: ID!): GroupChatMessage onPluginUpdate: Plugin } @@ -2009,7 +2013,7 @@ enum WeekDays { } input createChatInput { - organizationId: ID! + organizationId: ID userIds: [ID!]! } diff --git a/src/app.ts b/src/app.ts index 5479ad7fd0..a5571597a4 100644 --- a/src/app.ts +++ b/src/app.ts @@ -10,35 +10,41 @@ import path from "path"; import { appConfig } from "./config"; import { requestContext, requestTracing, stream } from "./libraries"; import graphqlUploadExpress from "graphql-upload/graphqlUploadExpress.mjs"; + const app = express(); +// Middleware for tracing requests app.use(requestTracing.middleware()); + +// Initialize i18n for internationalization app.use(i18n.init); +// Rate limiting middleware to prevent abuse const apiLimiter = rateLimit({ - windowMs: 60 * 60 * 1000, - max: 50000, + windowMs: 60 * 60 * 1000, // 1 hour window + max: 50000, // limit each IP to 50000 requests per windowMs message: "Too many requests from this IP, please try again after 15 minutes", }); +app.use(apiLimiter); // eslint-disable-next-line @typescript-eslint/no-unused-vars const corsOptions: cors.CorsOptions = { origin: (origin, next) => { if (process.env.NODE_ENV === "development") { - next(null, true); + next(null, true); // Allow all origins in development return; } else if (process.env.NODE_ENV === "production") { const talawaAdmin = process.env.TALAWA_ADMIN_URL; if (origin === talawaAdmin) { - next(null, true); + next(null, true); // Allow only specific origin in production return; } } - - next(new Error("Unauthorized")); + next(new Error("Unauthorized")); // Reject other origins }, }; +// Configure i18n settings i18n.configure({ directory: `${__dirname}/../locales`, staticCatalog: { @@ -55,45 +61,58 @@ i18n.configure({ updateFiles: process.env.NODE_ENV !== "production", syncFiles: process.env.NODE_ENV !== "production", }); - app.use(i18n.init); -app.use(apiLimiter); + +// Helmet middleware for security headers app.use( helmet({ contentSecurityPolicy: - process.env.NODE_ENV === "production" ? undefined : false, + process.env.NODE_ENV === "production" ? undefined : false, // Disable CSP in development }), ); + +// Sanitize data to prevent MongoDB operator injection app.use(mongoSanitize()); app.use(cors()); +// Serve static files with Cross-Origin-Resource-Policy header set app.use("/images", (req, res, next) => { res.setHeader("Cross-Origin-Resource-Policy", "cross-origin"); next(); }); +// Parse JSON requests with a size limit of 50mb app.use(express.json({ limit: "50mb" })); + +// Handle file uploads using graphql-upload app.use(graphqlUploadExpress()); + +// Parse URL-encoded requests with a size limit of 50mb app.use(express.urlencoded({ limit: "50mb", extended: true })); -// Fix added to stream +// Request logging middleware using Morgan app.use( requestLogger( ':remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] :response-time ms', { - stream: stream, + stream: stream, // Stream logs to a defined stream (e.g., file, console) }, ), ); +// Serve static files for images and videos app.use("/images", express.static(path.join(__dirname, "./../images"))); app.use("/videos", express.static(path.join(__dirname, "./../videos"))); +// Middleware for managing request context (e.g., user session) app.use(requestContext.middleware()); -if (process.env.NODE_ENV !== "production") +// Enable GraphQL Voyager visualization in development +if (process.env.NODE_ENV !== "production") { app.use("/voyager", voyagerMiddleware({ endpointUrl: "/graphql" })); +} +// Endpoint to check the health status of the application app.get("/", (req, res) => res.json({ "talawa-version": "v1", diff --git a/src/checks.ts b/src/checks.ts index 6ae11693ab..f5c602df22 100644 --- a/src/checks.ts +++ b/src/checks.ts @@ -5,36 +5,50 @@ import { getEnvIssues } from "./env"; import { logger } from "./libraries"; import { connect } from "./db"; -// Function to log warnings for super admin environment variable +/** + * Logs warnings regarding the LAST_RESORT_SUPERADMIN_EMAIL environment variable. + * This function checks if a super admin exists and if the LAST_RESORT_SUPERADMIN_EMAIL variable is present in the .env file. + * Depending on the conditions, appropriate warnings are logged. + */ const logWarningForSuperAdminEnvVariable = async (): Promise => { // Connect to the database await connect(); try { + // Check if there is an existing super admin profile const superAdminExist = await AppUserProfile.exists({ isSuperAdmin: true }); + // Check if the LAST_RESORT_SUPERADMIN_EMAIL variable is present in the .env file const isVariablePresentInEnvFile = !!LAST_RESORT_SUPERADMIN_EMAIL; if (superAdminExist && isVariablePresentInEnvFile) { + // Log a warning if both conditions are met logger.warn( "\x1b[1m\x1b[33m%s\x1b[0m", "The LAST_RESORT_SUPERADMIN_EMAIL variable configured in your .env file poses a security risk. We strongly recommend that you remove it if not required. Please refer to the documentation in the INSTALLATION.md file.You have created super admin, please remove the LAST_RESORT_SUPERADMIN_EMAIL variable from .env file if you don't require it", ); } else if (!superAdminExist && !isVariablePresentInEnvFile) { + // Log a warning if neither condition is met logger.warn( "\x1b[1m\x1b[33m%s\x1b[0m", "To create your first Super Admin, the LAST_RESORT_SUPERADMIN_EMAIL parameter needs to be set in the .env file. Please refer to the documentation in the INSTALLATION.md file.", ); } } catch (error) { + // Log an error if there's an exception while checking for super admin existence logger.error("Error checking for super admin existence:", error); } }; -// Function to log environment variable issues +/** + * Logs issues related to environment variables. + * This function logs any issues found with environment variables in the .env file. + * It also logs warnings regarding the LAST_RESORT_SUPERADMIN_EMAIL variable. + */ export const logIssues = async (): Promise => { try { - // Log all the environment variable issues + // Log all environment variable issues const issues = getEnvIssues(); if (issues) { + // Log errors if there are issues with environment variables logger.error( "Invalid environment variables found in your .env file, check the errors below!", ); @@ -44,12 +58,14 @@ export const logIssues = async (): Promise => { }), ); } else { + // Log info message if environment variables are valid logger.info("The environment variables are valid!"); } - // Log the SuperAdmin environment variable issue (if any) + // Log warnings regarding the LAST_RESORT_SUPERADMIN_EMAIL variable await logWarningForSuperAdminEnvVariable(); } catch (error) { + // Log an error if there's an exception while logging environment variable issues logger.error("Error logging environment variable issues:", error); } }; diff --git a/src/db.ts b/src/db.ts index 374dd85f03..f445206a56 100644 --- a/src/db.ts +++ b/src/db.ts @@ -6,16 +6,19 @@ import { checkReplicaSet } from "./utilities/checkReplicaSet"; let session!: mongoose.ClientSession; export const connect = async (dbName?: string): Promise => { - // firstly we check if a connection to the database already exists. + // Check if a connection to the database already exists. if (mongoose.connection.readyState !== 0) { - // if the connection state is not 0, it means a connection already exists so we return. + // If a connection already exists, return immediately. return; } - // if no connection exists, we try to establish a new connection. + + // If no connection exists, attempt to establish a new connection. try { await mongoose.connect(MONGO_DB_URL as string, { dbName, }); + + // Check if connected to a replica set and start a session if true. const replicaSet = await checkReplicaSet(); if (replicaSet) { logger.info("Session started --> Connected to a replica set!"); @@ -27,6 +30,7 @@ export const connect = async (dbName?: string): Promise => { if (error instanceof Error) { const errorMessage = error.toString(); if (errorMessage.includes("ECONNREFUSED")) { + // Handle connection errors due to common issues. logger.error("\n\n\n\x1b[1m\x1b[31m%s\x1b[0m", error); logger.error( "\n\n\x1b[1m\x1b[34m%s\x1b[0m", diff --git a/src/index.ts b/src/index.ts index 318ea6e19b..03617bf6e6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -101,6 +101,7 @@ const serverCleanup = useServer( { schema, context: (_ctx, _msg, _args) => ({ pubsub }) }, wsServer, ); +let serverHost = "localhost"; async function startServer(): Promise { await database.connect(); @@ -110,31 +111,39 @@ async function startServer(): Promise { app.use( "/graphql", expressMiddleware(server, { - context: async ({ req, res }) => ({ - ...isAuth(req), - req, - res, - pubsub, - apiRootUrl: `${req.protocol}://${req.get("host")}/`, - }), + context: async ({ req, res }) => { + serverHost = req.get("host") || "localhost"; + return { + ...isAuth(req), + req, + res, + pubsub, + apiRootUrl: `${req.protocol}://${serverHost}/`, + }; + }, }), ); // Modified server startup + const PORT = parseInt(SERVER_PORT || "4000", 10); + if (Number.isNaN(PORT) || PORT < 0 || PORT > 65535) { + throw new Error( + `Invalid SERVER_PORT: ${process.env.SERVER_PORT}. Please ensure it is a numeric value between 0 and 65535.`, + ); + } + await new Promise((resolve) => - httpServer.listen({ port: parseInt(SERVER_PORT as string) }, resolve), + httpServer.listen({ port: PORT }, resolve), ); // Log all the configuration related issues await logIssues(); logger.info( - `🚀 Server ready at ${ - process.env.NODE_ENV === "production" ? "https" : "http" - }://localhost:${SERVER_PORT}/graphql`, + `🚀 Server ready at ${process.env.NODE_ENV === "production" ? "https" : "http"}://${serverHost}:${PORT}/graphql`, ); logger.info( - `🚀 Subscription endpoint ready at ws://localhost:${SERVER_PORT}/graphql`, + `🚀 Subscription endpoint ready at ws://${serverHost}:${PORT}/graphql`, ); } diff --git a/src/libraries/dbLogger.ts b/src/libraries/dbLogger.ts index 7db246cd3c..d24aa02ae3 100644 --- a/src/libraries/dbLogger.ts +++ b/src/libraries/dbLogger.ts @@ -2,16 +2,26 @@ import { LOG, LOG_PATH, TransactionLogTypes } from "../constants"; import type { Query, Schema, Document } from "mongoose"; import winston from "winston"; +/** + * The structure of a transaction log entry. + */ export type TransactionLogInfo = { + /** The timestamp when the log entry was created */ timestamp: string; + /** The name of the model associated with the log entry */ model: string; + /** The type of transaction (e.g., create, update, delete) */ type: string; + /** The query executed (optional) */ query?: string; + /** The update performed (optional) */ update?: string; }; +// Initialize the database logger to null let dbLogger: winston.Logger | null = null; +// If logging is enabled and a log path is specified, create a Winston logger if (LOG && LOG_PATH) { dbLogger = winston.createLogger({ level: "info", @@ -29,22 +39,45 @@ if (LOG && LOG_PATH) { }); } +/** + * Interface for a document that includes logging information. + */ export interface InterfaceLoggableDocument extends Document { + /** Information about the transaction log */ logInfo: TransactionLogInfo; } +/** + * Interface for a query that can include logging information. + */ export interface InterfaceLoggableQuery extends Query { + /** Information about the transaction log (optional) */ logInfo?: TransactionLogInfo; } +/** + * Creates a logging middleware for a Mongoose schema. This middleware logs + * create, update, and delete operations on the specified schema. + * + * @param schema - The Mongoose schema to which the middleware will be added + * @param modelName - The name of the model associated with the schema + */ export function createLoggingMiddleware( schema: Schema, modelName: string, ): void { + // If no logger is configured, exit the function early if (!dbLogger) { return; } + /** + * Creates a log entry for a specific action. + * + * @param type - The type of transaction (create, update, delete) + * @param thisContext - The query context (optional) + * @returns A TransactionLogInfo object with details about the transaction + */ const logAction = ( type: TransactionLogTypes, thisContext?: InterfaceLoggableQuery, @@ -61,25 +94,30 @@ export function createLoggingMiddleware( }; }; + // Middleware to log "save" operations before they occur schema.pre("save", function (next) { this.logInfo = logAction(TransactionLogTypes.CREATE); next(); }); + // Middleware to log "save" operations after they occur schema.post("save", function () { if (dbLogger) { dbLogger.info("success", this.logInfo); } }); + // List of update operations to log const updateOperations: ("findOneAndUpdate" | "updateOne" | "updateMany")[] = ["findOneAndUpdate", "updateOne", "updateMany"]; updateOperations.forEach((operation) => { + // Middleware to log update operations before they occur schema.pre(operation, function (this: InterfaceLoggableQuery, next) { this.logInfo = logAction(TransactionLogTypes.UPDATE, this); next(); }); + // Middleware to log update operations after they occur schema.post(operation, function (this: InterfaceLoggableQuery) { if (dbLogger) { dbLogger?.info("success", this.logInfo); @@ -87,16 +125,19 @@ export function createLoggingMiddleware( }); }); + // List of delete operations to log const deleteOperations: ("deleteOne" | "deleteMany")[] = [ "deleteOne", "deleteMany", ]; deleteOperations.forEach((operation) => { + // Middleware to log delete operations before they occur schema.pre(operation, function (this: InterfaceLoggableQuery, next) { this.logInfo = logAction(TransactionLogTypes.DELETE, this); next(); }); + // Middleware to log delete operations after they occur schema.post(operation, function (this: InterfaceLoggableQuery) { if (dbLogger) { dbLogger.info("success", this.logInfo); @@ -105,4 +146,5 @@ export function createLoggingMiddleware( }); } +// Export the logger as the default export export default dbLogger; diff --git a/src/libraries/errors/ImageSizeLimitExceeded.ts b/src/libraries/errors/ImageSizeLimitExceeded.ts index 2d0748fa3b..9e6e7dfdc3 100644 --- a/src/libraries/errors/ImageSizeLimitExceeded.ts +++ b/src/libraries/errors/ImageSizeLimitExceeded.ts @@ -1,8 +1,18 @@ import { ApplicationError } from "./applicationError"; + /** - * This class detects invalid file type errors and sends those errors to the superclass ApplicationError. + * This class represents an error indicating that the image size limit has been exceeded. + * It extends the ApplicationError class to handle and format the error information. */ export class ImageSizeLimitExceeded extends ApplicationError { + /** + * Creates an instance of ImageSizeLimitExceeded. + * + * @param message - The error message (default is "Image Size Limit Exceeded"). + * @param code - Optional error code (default is null). + * @param param - Optional parameter associated with the error (default is null). + * @param metadata - Optional additional metadata associated with the error (default is an empty object). + */ constructor( message = "Image Size Limit Exceeded", code: string | null = null, @@ -10,6 +20,7 @@ export class ImageSizeLimitExceeded extends ApplicationError { // eslint-disable-next-line @typescript-eslint/no-explicit-any metadata: Record = {}, ) { + // Construct the error JSON in the required format for ApplicationError const errorJson = [ { message, @@ -18,6 +29,8 @@ export class ImageSizeLimitExceeded extends ApplicationError { metadata, }, ]; + + // Call the superclass ApplicationError constructor with the formatted error JSON super(errorJson, 403, message); } } diff --git a/src/libraries/errors/applicationError.ts b/src/libraries/errors/applicationError.ts index 6130ba1e2c..dcfabfb216 100644 --- a/src/libraries/errors/applicationError.ts +++ b/src/libraries/errors/applicationError.ts @@ -1,19 +1,37 @@ +/** + * Interface representing the structure of an error. + */ export interface InterfaceError { + /** The error message */ message: string; + /** The error code, can be null */ code: string | null; + /** The parameter associated with the error, can be null */ param: string | null; + /** Optional additional metadata associated with the error */ metadata?: Record; } + /** - * This class is responsible for finding the application errors. It adds those errors to superclass called Error. + * This class is responsible for handling application errors. + * It extends the built-in Error class to include additional properties and methods. */ export class ApplicationError extends Error { + /** An array of errors conforming to the InterfaceError interface */ public errors: InterfaceError[]; + /** The HTTP status code associated with the error */ public httpCode; + /** + * Creates an instance of ApplicationError. + * + * @param errors - An array of errors conforming to the InterfaceError interface. + * @param httpCode - The HTTP status code associated with the error (default is 422). + * @param message - The error message (default is "Error"). + */ constructor(errors: InterfaceError[], httpCode = 422, message = "Error") { - super(message); - this.errors = errors; - this.httpCode = httpCode; + super(message); // Call the constructor of the superclass Error + this.errors = errors; // Assign the errors to the instance + this.httpCode = httpCode; // Assign the HTTP status code to the instance } } diff --git a/src/libraries/errors/conflictError.ts b/src/libraries/errors/conflictError.ts index a80c6b106c..d47f074594 100644 --- a/src/libraries/errors/conflictError.ts +++ b/src/libraries/errors/conflictError.ts @@ -1,8 +1,17 @@ import { ApplicationError } from "./applicationError"; + /** - * This class detects conflict errors and sends those errors to the superclass ApplicationError. + * This class represents a conflict error. It extends the ApplicationError class + * and is used to handle situations where a conflicting entry is found. */ export class ConflictError extends ApplicationError { + /** + * Creates an instance of ConflictError. + * @param message - The error message. Defaults to "Conflicting entry found". + * @param code - The error code. Can be null. Defaults to null. + * @param param - The parameter related to the error. Can be null. Defaults to null. + * @param metadata - Additional metadata related to the error. Defaults to an empty object. + */ constructor( message = "Conflicting entry found", code: string | null = null, diff --git a/src/libraries/errors/inputValidationError.ts b/src/libraries/errors/inputValidationError.ts index 475ef31b91..d94dcee798 100644 --- a/src/libraries/errors/inputValidationError.ts +++ b/src/libraries/errors/inputValidationError.ts @@ -1,8 +1,17 @@ import { ApplicationError } from "./applicationError"; + /** - * This class detects input validation errors and sends those errors to the superclass ApplicationError. + * Represents an input validation error. It extends the ApplicationError class + * and is used to handle errors related to input validation failures. */ export class InputValidationError extends ApplicationError { + /** + * Creates an instance of InputValidationError. + * @param message - The error message. Defaults to "InputValidationError". + * @param code - The error code. Can be null. Defaults to null. + * @param param - The parameter related to the error. Can be null. Defaults to null. + * @param metadata - Additional metadata related to the error. Defaults to an empty object. + */ constructor( message = "InputValidationError", code: string | null = null, diff --git a/src/libraries/errors/internalServerError.ts b/src/libraries/errors/internalServerError.ts index 8d5bf4f87d..f99c0707a4 100644 --- a/src/libraries/errors/internalServerError.ts +++ b/src/libraries/errors/internalServerError.ts @@ -1,8 +1,18 @@ import { ApplicationError } from "./applicationError"; + /** - * This class detects internal server errors and sends those errors to the superclass ApplicationError. + * This class represents an error indicating an internal server error. + * It extends the ApplicationError class to handle and format the error information. */ export class InternalServerError extends ApplicationError { + /** + * Creates an instance of InternalServerError. + * + * @param message - The error message (default is "Internal Server Error!"). + * @param code - Optional error code (default is null). + * @param param - Optional parameter associated with the error (default is null). + * @param metadata - Optional additional metadata associated with the error (default is an empty object). + */ constructor( message = "Internal Server Error!", code: string | null = null, @@ -10,6 +20,7 @@ export class InternalServerError extends ApplicationError { // eslint-disable-next-line @typescript-eslint/no-explicit-any metadata: Record = {}, ) { + // Construct the error JSON in the required format for ApplicationError const errorJson = [ { message, @@ -18,6 +29,8 @@ export class InternalServerError extends ApplicationError { metadata, }, ]; + + // Call the superclass ApplicationError constructor with the formatted error JSON super(errorJson, 500, message); } } diff --git a/src/libraries/errors/invalidFileTypeError.ts b/src/libraries/errors/invalidFileTypeError.ts index a3663ca745..32442e4911 100644 --- a/src/libraries/errors/invalidFileTypeError.ts +++ b/src/libraries/errors/invalidFileTypeError.ts @@ -1,8 +1,18 @@ import { ApplicationError } from "./applicationError"; + /** - * This class detects invalid file type errors and sends those errors to the superclass ApplicationError. + * This class represents an error indicating an invalid file type. + * It extends the ApplicationError class to handle and format the error information. */ export class InvalidFileTypeError extends ApplicationError { + /** + * Creates an instance of InvalidFileTypeError. + * + * @param message - The error message (default is "Invalid File Type"). + * @param code - Optional error code (default is null). + * @param param - Optional parameter associated with the error (default is null). + * @param metadata - Optional additional metadata associated with the error (default is an empty object). + */ constructor( message = "Invalid File Type", code: string | null = null, @@ -10,6 +20,7 @@ export class InvalidFileTypeError extends ApplicationError { // eslint-disable-next-line @typescript-eslint/no-explicit-any metadata: Record = {}, ) { + // Construct the error JSON in the required format for ApplicationError const errorJson = [ { message, @@ -18,6 +29,8 @@ export class InvalidFileTypeError extends ApplicationError { metadata, }, ]; + + // Call the superclass ApplicationError constructor with the formatted error JSON super(errorJson, 403, message); } } diff --git a/src/libraries/errors/notFoundError.ts b/src/libraries/errors/notFoundError.ts index b4b6f45a89..68fbb0b4b0 100644 --- a/src/libraries/errors/notFoundError.ts +++ b/src/libraries/errors/notFoundError.ts @@ -1,8 +1,17 @@ import { ApplicationError } from "./applicationError"; + /** - * This class detects Not Found errors and sends those errors to the superclass ApplicationError. + * Represents a "Not Found" error. It extends the ApplicationError class + * and is used to handle situations where a requested resource is not found. */ export class NotFoundError extends ApplicationError { + /** + * Creates an instance of NotFoundError. + * @param message - The error message. Defaults to "Not Found". + * @param code - The error code. Can be null. Defaults to null. + * @param param - The parameter related to the error. Can be null. Defaults to null. + * @param metadata - Additional metadata related to the error. Defaults to an empty object. + */ constructor( message = "Not Found", code: string | null = null, diff --git a/src/libraries/errors/unauthenticatedError.ts b/src/libraries/errors/unauthenticatedError.ts index e090098c91..db55d6e2b0 100644 --- a/src/libraries/errors/unauthenticatedError.ts +++ b/src/libraries/errors/unauthenticatedError.ts @@ -1,8 +1,18 @@ import { ApplicationError } from "./applicationError"; + /** - * This class detects unauthenticated errors and sends those errors to the superclass ApplicationError. + * This class represents an error indicating an unauthenticated request. + * It extends the ApplicationError class to handle and format the error information. */ export class UnauthenticatedError extends ApplicationError { + /** + * Creates an instance of UnauthenticatedError. + * + * @param message - The error message (default is "UnauthenticatedError"). + * @param code - Optional error code (default is null). + * @param param - Optional parameter associated with the error (default is null). + * @param metadata - Optional additional metadata associated with the error (default is an empty object). + */ constructor( message = "UnauthenticatedError", code: string | null = null, @@ -10,6 +20,7 @@ export class UnauthenticatedError extends ApplicationError { // eslint-disable-next-line @typescript-eslint/no-explicit-any metadata: Record = {}, ) { + // Construct the error JSON in the required format for ApplicationError const errorJson = [ { message, @@ -18,6 +29,8 @@ export class UnauthenticatedError extends ApplicationError { metadata, }, ]; + + // Call the superclass ApplicationError constructor with the formatted error JSON super(errorJson, 401, message); } } diff --git a/src/libraries/errors/unauthorizedError.ts b/src/libraries/errors/unauthorizedError.ts index e09902edca..64784a466a 100644 --- a/src/libraries/errors/unauthorizedError.ts +++ b/src/libraries/errors/unauthorizedError.ts @@ -1,8 +1,17 @@ import { ApplicationError } from "./applicationError"; + /** - * This class detects unauthorized errors and sends those errors to the superclass ApplicationError. + * Represents an unauthorized error. It extends the ApplicationError class + * and is used to handle situations where access to a resource is unauthorized. */ export class UnauthorizedError extends ApplicationError { + /** + * Creates an instance of UnauthorizedError. + * @param message - The error message. Defaults to "UnauthorizedError". + * @param code - The error code. Can be null. Defaults to null. + * @param param - The parameter related to the error. Can be null. Defaults to null. + * @param metadata - Additional metadata related to the error. Defaults to an empty object. + */ constructor( message = "UnauthorizedError", code: string | null = null, diff --git a/src/libraries/errors/validationError.ts b/src/libraries/errors/validationError.ts index 3f197e2fa0..94ee11255e 100644 --- a/src/libraries/errors/validationError.ts +++ b/src/libraries/errors/validationError.ts @@ -1,10 +1,19 @@ import type { InterfaceError } from "./applicationError"; import { ApplicationError } from "./applicationError"; + /** - * This class detects validation errors and sends those errors to the superclass ApplicationError. + * This class represents an error indicating validation errors. + * It extends the ApplicationError class to handle and format the error information. */ export class ValidationError extends ApplicationError { + /** + * Creates an instance of ValidationError. + * + * @param errors - An array of errors conforming to the InterfaceError interface (default is an empty array). + * @param message - The error message (default is "Validation error"). + */ constructor(errors: InterfaceError[] = [], message = "Validation error") { + // Call the superclass ApplicationError constructor with the provided errors and HTTP status code super(errors, 422, message); } } diff --git a/src/libraries/index.ts b/src/libraries/index.ts index 4c633e4375..69152ddf47 100644 --- a/src/libraries/index.ts +++ b/src/libraries/index.ts @@ -1,4 +1,11 @@ +// Export all contents from the "errors" module as "errors" export * as errors from "./errors"; + +// Export all named exports from the "logger" module export * from "./logger"; + +// Export all contents from the "requestContext" module as "requestContext" export * as requestContext from "./requestContext"; + +// Export all contents from the "requestTracing" module as "requestTracing" export * as requestTracing from "./requestTracing"; diff --git a/src/libraries/logger.ts b/src/libraries/logger.ts index 194d627511..2901b17106 100644 --- a/src/libraries/logger.ts +++ b/src/libraries/logger.ts @@ -3,14 +3,16 @@ import { createLogger, transports, format } from "winston"; import { getTracingId } from "./requestTracing"; import { appConfig } from "../config"; +// Destructure the necessary format functions from winston.format const { combine, printf, splat, colorize, simple, timestamp } = format; +// Define log formats with and without colorization const formats = { colorized: combine( - colorize(), - splat(), - simple(), - timestamp({ format: "YYYY-MM-DD HH:mm:ss" }), + colorize(), // Add colors to log levels + splat(), // Allow string interpolation in log messages + simple(), // Simplify log message format + timestamp({ format: "YYYY-MM-DD HH:mm:ss" }), // Add timestamp in specified format printf( (info) => `${info.level || "-"} ${info.timestamp || "-"} ${ @@ -22,13 +24,13 @@ const formats = { : JSON.stringify( _.omit(info, ["level", "message", "stack", "timestamp"]), ) - } ${info.stack || ""}`, + } ${info.stack || ""}`, // Custom log message format ), ), non_colorized: combine( - splat(), - simple(), - timestamp({ format: "YYYY-MM-DD HH:mm:ss" }), + splat(), // Allow string interpolation in log messages + simple(), // Simplify log message format + timestamp({ format: "YYYY-MM-DD HH:mm:ss" }), // Add timestamp in specified format printf( (info) => `${info.level || "-"} ${info.timestamp || "-"} ${ @@ -40,28 +42,30 @@ const formats = { : JSON.stringify( _.omit(info, ["level", "message", "stack", "timestamp"]), ) - } ${info.stack || ""}`, + } ${info.stack || ""}`, // Custom log message format ), ), }; +// Create a Winston logger with a console transport const logger = createLogger({ transports: [ new transports.Console({ - level: appConfig.log_level, + level: appConfig.log_level, // Set log level from app configuration format: appConfig.colorize_logs === "true" - ? formats.colorized - : formats.non_colorized, + ? formats.colorized // Use colorized format if enabled in config + : formats.non_colorized, // Use non-colorized format otherwise }), ], }); -// The code block shifted before exporting logger +// Define a stream for use with other logging systems, such as morgan const stream = { write: (message: string | null): void => { - logger.info((message || "").trim()); + logger.info((message || "").trim()); // Log the message using the info level }, }; +// Export the logger and stream export { logger, stream }; diff --git a/src/libraries/requestContext.ts b/src/libraries/requestContext.ts index 3c238f8e41..3edb66081a 100644 --- a/src/libraries/requestContext.ts +++ b/src/libraries/requestContext.ts @@ -3,23 +3,42 @@ import cls from "cls-hooked"; import type { NextFunction, Request, Response } from "express"; import i18n from "i18n"; +// Create a namespace for managing request context export const requestContextNamespace = cls.createNamespace( "talawa-request-context", ); +/** + * Sets a value in the request context. + * @param key - The key under which the value is stored. + * @param value - The value to store. + * @returns The stored value. + */ export const setRequestContextValue = (key: string, value: T): T => { return requestContextNamespace.set(key, value); }; +/** + * Gets a value from the request context. + * @param key - The key under which the value is stored. + * @returns The retrieved value. + */ export const getRequestContextValue = (key: string): T => { return requestContextNamespace.get(key); }; +/** + * Sets the translation functions in the request context. + * @param obj - The object containing translation functions. + */ export const setRequestContext = (obj: any): void => { setRequestContextValue("translate", obj.__); setRequestContextValue("translatePlural", obj.__n); }; +/** + * Middleware to bind the request and response to the request context namespace. + */ export const middleware = () => { return (req: Request, res: Response, next: NextFunction): void => { requestContextNamespace.bindEmitter(req); @@ -31,10 +50,18 @@ export const middleware = () => { }; }; +/** + * Interface for initialization options. + */ interface InterfaceInitOptions extends Record { requestHandler?: () => T; } +/** + * Initializes the request context and i18n. + * @param options - The initialization options. + * @returns The result of the request handler or an empty object if not provided. + */ export const init = (options: InterfaceInitOptions = {}): T => { const obj: any = {}; @@ -65,6 +92,11 @@ export const init = (options: InterfaceInitOptions = {}): T => { }); }; +/** + * Translates a string using the current context's translation function. + * @param args - The arguments to pass to the translation function. + * @returns The translated string. + */ export const translate = (...args: any): any => { const _ = getRequestContextValue("translate"); if (typeof _ !== "function") { @@ -73,6 +105,11 @@ export const translate = (...args: any): any => { return args.map((arg: any) => _(arg)).join(","); }; +/** + * Translates a plural string using the current context's translation function. + * @param args - The arguments to pass to the translation function. + * @returns The translated string. + */ export const translatePlural = (...args: any): any => { const _n = getRequestContextValue("translatePlural"); if (typeof _n !== "function") { diff --git a/src/libraries/requestTracing.ts b/src/libraries/requestTracing.ts index 1fb7b15229..bcb63841e0 100644 --- a/src/libraries/requestTracing.ts +++ b/src/libraries/requestTracing.ts @@ -8,28 +8,52 @@ import type { NextFunction, Request, Response } from "express"; // Alphabets used in the custom nanoid function const alphabets = "0123456789abcdefghijklmnopqrstuvwxyz"; -/* -Custom nanoid function to generate a unique 10 characters request ID -using the characters in alphabets variable -*/ +/** + * Custom nanoid function to generate a unique 10 characters request ID + * using the characters in the alphabets variable. + */ const nanoid = customAlphabet(alphabets, 10); +/** + * Namespace for request tracing to maintain context across asynchronous operations. + */ export const requestTracingNamespace = cls.createNamespace("request-tracing"); +// Initialize cls-bluebird with the request tracing namespace clsBluebird(requestTracingNamespace); +/** + * Name of the header where the tracing ID will be stored. + */ export const tracingIdHeaderName = "X-Tracing-Id"; +/** + * Key name for storing the tracing ID in the namespace context. + */ const tracingIdContextKeyName = "tracingId"; +/** + * Sets the tracing ID in the namespace context. + * @param tracingId - The tracing ID to set. + * @returns The tracing ID that was set. + */ export const setTracingId = (tracingId: string): string => { return requestTracingNamespace.set(tracingIdContextKeyName, tracingId); }; +/** + * Gets the tracing ID from the namespace context. + * @returns The tracing ID. + */ export const getTracingId = (): string => { return requestTracingNamespace.get(tracingIdContextKeyName) as string; }; +/** + * Middleware to handle request tracing. It generates or retrieves a tracing ID, + * sets it in the headers of the request and response, and stores it in the namespace context. + * @returns A middleware function. + */ export const middleware = () => { return (req: Request, res: Response, next: NextFunction): void => { requestTracingNamespace.bindEmitter(req); @@ -47,6 +71,13 @@ export const middleware = () => { }; }; +/** + * Runs a method within the context of a tracing ID. If a tracing ID is provided, it uses that ID; + * otherwise, it generates a new one. + * @param tracingId - The tracing ID to use. + * @param method - The method to run within the context of the tracing ID. + * @returns A promise that resolves when the method completes. + */ export const trace = async ( tracingId: string, method: () => T, diff --git a/src/libraries/validators/compareDates.ts b/src/libraries/validators/compareDates.ts index e95cf9a8ee..53bafa5735 100644 --- a/src/libraries/validators/compareDates.ts +++ b/src/libraries/validators/compareDates.ts @@ -1,9 +1,23 @@ +/** + * Compares two dates and returns a message if the first date is later than the second date. + * + * @param date1 - The first date as a string. + * @param date2 - The second date as a string. + * @returns A message indicating that the start date must be earlier than the end date, or an empty string if the dates are in the correct order. + */ export function compareDates(date1: string, date2: string): string { + // Convert the date strings to Date objects const dateObj1 = new Date(date1); const dateObj2 = new Date(date2); + + // Calculate the difference in time between the two dates const result = dateObj1.getTime() - dateObj2.getTime(); + + // If the first date is later than the second date, return an error message if (result > 0) { return `start date must be earlier than end date`; } + + // Return an empty string if the dates are in the correct order return ""; } diff --git a/src/libraries/validators/compareTime.ts b/src/libraries/validators/compareTime.ts index b3442da6ed..11f401bddf 100644 --- a/src/libraries/validators/compareTime.ts +++ b/src/libraries/validators/compareTime.ts @@ -1,9 +1,23 @@ +/** + * Compares two times and returns a message if the first time is later than the second time. + * + * @param time1 - The first time as a string. + * @param time2 - The second time as a string. + * @returns A message indicating that the start time must be earlier than the end time, or an empty string if the times are in the correct order. + */ export function compareTime(time1: string, time2: string): string { + // Convert the time strings to Date objects const timeObj1 = new Date(time1); const timeObj2 = new Date(time2); + + // Calculate the difference in hours between the two times const result = timeObj1.getHours() - timeObj2.getHours(); + + // If the first time is later than the second time, return an error message if (result > 0) { return `start time must be earlier than end time`; } + + // Return an empty string if the times are in the correct order return ""; } diff --git a/src/libraries/validators/validateString.ts b/src/libraries/validators/validateString.ts index eff049c5c1..c5076a3762 100644 --- a/src/libraries/validators/validateString.ts +++ b/src/libraries/validators/validateString.ts @@ -1,3 +1,10 @@ +/** + * Checks if a given string is less than a specified maximum length. + * + * @param str - The string to check. + * @param maxLength - The maximum allowed length of the string. + * @returns An object containing a boolean indicating if the string is less than the maximum length. + */ export function isValidString( str: string, maxLength: number, diff --git a/src/middleware/index.ts b/src/middleware/index.ts index 7f43ff4d06..065f1a55bc 100644 --- a/src/middleware/index.ts +++ b/src/middleware/index.ts @@ -1 +1,2 @@ +// Export everything from this module, including isAuth function export * from "./isAuth"; diff --git a/src/middleware/isAuth.ts b/src/middleware/isAuth.ts index 570d9d9d28..7e96dcdf39 100644 --- a/src/middleware/isAuth.ts +++ b/src/middleware/isAuth.ts @@ -9,43 +9,36 @@ export interface InterfaceAuthData { expired: boolean | undefined; userId: string | undefined; } + /** * This function determines whether the user is authorised and whether the access token has expired. - * @param Request - User Request + * @param request - User Request object from Express. * @returns Returns `authData` object with `isAuth`, `expired` and `userId` properties. */ export const isAuth = (request: Request): InterfaceAuthData => { - /* - This object is the return value of this function. Mutate the fields of this - object conditionally as the authentication flow continues and return it from - the function whereever needed. - */ + // Initialize authData object with default values const authData: InterfaceAuthData = { isAuth: false, expired: undefined, userId: undefined, }; - // This checks to see if there is an authorization field within the incoming request + // Retrieve authorization header from request const authHeader = request.headers.authorization; - // If no authorization header was sent from the client + // If no authorization header is present, return default authData if (!authHeader) { return authData; } - // format of request sent will be Bearer tokenvalue - // this splits it into two values bearer and the token + // Extract token from authorization header const token = authHeader.split(" ")[1]; - // if the token is null or an empty string + // If token is missing or empty, return default authData if (!token || token === "") { return authData; } - // uses key created in the auth resolver - // to be changed in production - // only tokens created with this key will be valid tokens // eslint-disable-next-line @typescript-eslint/no-explicit-any let decodedToken: any; try { @@ -70,16 +63,16 @@ export const isAuth = (request: Request): InterfaceAuthData => { return authData; } - // if the decoded token is not set + // If decoded token is not set, log an info message and return default authData if (!decodedToken) { logger.info("decoded token is not present"); return authData; } - // shows the user is an authenticated user + // Set isAuth to true and extract userId from decoded token authData.isAuth = true; - // pulls user data(userId) out of the token and attaches it to userId field of authData object authData.userId = decodedToken.userId; + // Return the finalized authData object return authData; }; diff --git a/src/models/ActionItem.ts b/src/models/ActionItem.ts index 31f0324e2c..ac9d191012 100644 --- a/src/models/ActionItem.ts +++ b/src/models/ActionItem.ts @@ -6,9 +6,8 @@ import type { InterfaceActionItemCategory } from "./ActionItemCategory"; import { MILLISECONDS_IN_A_WEEK } from "../constants"; /** - * This is an interface that represents a database(MongoDB) document for ActionItem [test change]. + * Interface representing a database document for ActionItem in MongoDB. */ - export interface InterfaceActionItem { _id: Types.ObjectId; assigneeId: PopulatedDoc; @@ -27,22 +26,21 @@ export interface InterfaceActionItem { } /** - * This describes the schema for a `ActionItem` that corresponds to `InterfaceActionItem` document. - * @param assigneeId - User to whom the ActionItem is assigned, refer to `User` model. - * @param assignerId - User who assigned the ActionItem, refer to the `User` model. - * @param actionItemCategoryId - ActionItemCategory to which the ActionItem is related, refer to the `ActionItemCategory` model. - * @param preCompletionNotes - Notes prior to completion. - * @param postCompletionNotes - Notes on completion. - * @param assignmentDate - Date of assignment. - * @param dueDate - Due date. - * @param completionDate - Completion date. - * @param isCompleted - Whether the ActionItem has been completed. - * @param eventId - Event to which the ActionItem is related, refer to the `Event` model. - * @param creatorId - User who created the ActionItem, refer to the `User` model. + * Defines the schema for the ActionItem document. + * @param assigneeId - User to whom the ActionItem is assigned. + * @param assignerId - User who assigned the ActionItem. + * @param actionItemCategoryId - ActionItemCategory to which the ActionItem belongs. + * @param preCompletionNotes - Notes recorded before completion. + * @param postCompletionNotes - Notes recorded after completion. + * @param assignmentDate - Date when the ActionItem was assigned. + * @param dueDate - Due date for the ActionItem. + * @param completionDate - Date when the ActionItem was completed. + * @param isCompleted - Flag indicating if the ActionItem is completed. + * @param eventId - Optional: Event to which the ActionItem is related. + * @param creatorId - User who created the ActionItem. * @param createdAt - Timestamp when the ActionItem was created. * @param updatedAt - Timestamp when the ActionItem was last updated. */ - const actionItemSchema = new Schema( { assigneeId: { @@ -96,9 +94,13 @@ const actionItemSchema = new Schema( required: true, }, }, - { timestamps: true }, + { timestamps: true }, // Automatic timestamps for createdAt and updatedAt fields ); +/** + * Retrieves or creates the ActionItem model. + * Prevents Mongoose OverwriteModelError during testing. + */ const actionItemModel = (): Model => model("ActionItem", actionItemSchema); diff --git a/src/models/ActionItemCategory.ts b/src/models/ActionItemCategory.ts index 314d845511..e49506c2f7 100644 --- a/src/models/ActionItemCategory.ts +++ b/src/models/ActionItemCategory.ts @@ -4,9 +4,8 @@ import type { InterfaceUser } from "./User"; import type { InterfaceOrganization } from "./Organization"; /** - * This is an interface that represents a database(MongoDB) document for ActionItemCategory (~Test Check). + * Represents a database document for ActionItemCategory in MongoDB. */ - export interface InterfaceActionItemCategory { _id: Types.ObjectId; name: string; @@ -18,15 +17,14 @@ export interface InterfaceActionItemCategory { } /** - * This describes the schema for a `actionItemCategory` that corresponds to `InterfaceCategory` document. - * @param name - An actionItemCategory to be selected for ActionItems. - * @param organizationId - Organization the actionItemCategory belongs to, refer to the `Organization` model. - * @param isDisabled - Whether actionItemCategory is disabled or not. - * @param creatorId - Task creator, refer to `User` model. - * @param createdAt - Time stamp of data creation. - * @param updatedAt - Time stamp of data updation. + * Mongoose schema definition for ActionItemCategory document. + * @param name - The name of the action item category. + * @param organizationId - The ID of the organization the category belongs to, referencing the Organization model. + * @param isDisabled - Indicates if the action item category is disabled. + * @param creatorId - The ID of the user who created the action item category, referencing the User model. + * @param createdAt - Timestamp of when the data was created. + * @param updatedAt - Timestamp of when the data was last updated. */ - const actionItemCategorySchema = new Schema( { name: { @@ -52,11 +50,15 @@ const actionItemCategorySchema = new Schema( { timestamps: true }, ); +// Indexing for organizationId and name to ensure uniqueness actionItemCategorySchema.index( { organizationId: 1, name: 1 }, { unique: true }, ); +/** + * Returns the Mongoose Model for ActionItemCategory to prevent OverwriteModelError. + */ const actionItemCategoryModel = (): Model => model( "ActionItemCategory", diff --git a/src/models/Advertisement.ts b/src/models/Advertisement.ts index 06ef2a5d81..a7fb157bd9 100644 --- a/src/models/Advertisement.ts +++ b/src/models/Advertisement.ts @@ -3,8 +3,9 @@ import { Schema, model, models } from "mongoose"; import type { InterfaceUser } from "./User"; import { createLoggingMiddleware } from "../libraries/dbLogger"; import type { InterfaceOrganization } from "./Organization"; + /** - * This is an interface, that represents database - (MongoDB) document for Advertisement. + * Interface representing a database document for Advertisement in MongoDB. */ export interface InterfaceAdvertisement { _id: string; @@ -97,14 +98,20 @@ const advertisementSchema = new Schema( }, }, { - timestamps: true, + timestamps: true, // Automatic timestamps for createdAt and updatedAt fields }, ); +// Ensure uniqueness of combination of organizationId and name advertisementSchema.index({ organizationId: 1, name: 1 }, { unique: true }); +// Add logging middleware for Advertisement schema createLoggingMiddleware(advertisementSchema, "Advertisement"); +/** + * Retrieves or creates the Advertisement model. + * Prevents Mongoose OverwriteModelError during testing. + */ const advertisementModel = (): Model => model("Advertisement", advertisementSchema); diff --git a/src/models/AgendaCategory.ts b/src/models/AgendaCategory.ts index 021f5614e5..1eb4be5b9e 100644 --- a/src/models/AgendaCategory.ts +++ b/src/models/AgendaCategory.ts @@ -1,9 +1,10 @@ -import type { PopulatedDoc, Model, Types } from "mongoose"; +import type { PopulatedDoc, Model, Types, Document } from "mongoose"; import { Schema, model, models } from "mongoose"; import type { InterfaceOrganization } from "./Organization"; import type { InterfaceUser } from "./User"; + /** - * This is an interface representing a document for an agenda category in the database (MongoDB). + * Represents a document for an agenda category in the MongoDB database. */ export interface InterfaceAgendaCategory { _id: Types.ObjectId; // Unique identifier for the agenda category. @@ -12,13 +13,15 @@ export interface InterfaceAgendaCategory { organizationId: PopulatedDoc; // Reference to the organization associated with the agenda category. createdBy: PopulatedDoc; // Reference to the user who created the agenda category. updatedBy: PopulatedDoc; // Reference to the user who last updated the agenda category. + createdAt: Date; // Date when the agenda category was created. + updatedAt: Date; // Date when the agenda category was last updated. } /** - * This is the Mongoose schema for an agenda category (test-change). + * Mongoose schema definition for an agenda category document. * @param name - Name of the agenda category. * @param description - Optional description of the agenda category. - * @param organization - Reference to the organization associated with the agenda category. + * @param organizationId - Reference to the organization associated with the agenda category. * @param createdBy - Reference to the user who created the agenda category. * @param updatedBy - Reference to the user who last updated the agenda category. * @param createdAt - Date when the agenda category was created. @@ -58,6 +61,9 @@ export const AgendaCategorySchema = new Schema({ }, }); +/** + * Returns the Mongoose Model for AgendaCategory to prevent OverwriteModelError. + */ const agendaCategoryModel = (): Model => model("AgendaCategory", AgendaCategorySchema); diff --git a/src/models/AgendaItem.ts b/src/models/AgendaItem.ts index 447c16a974..fb3cc54d9b 100644 --- a/src/models/AgendaItem.ts +++ b/src/models/AgendaItem.ts @@ -7,7 +7,7 @@ import type { InterfaceEvent } from "./Event"; import type { InterfaceNote } from "./Note"; /** - * This is an interface representing a document for an agenda item in the database (MongoDB). + * Represents a document for an agenda item in the MongoDB database. */ export interface InterfaceAgendaItem { _id: Types.ObjectId; // Unique identifier for the agenda item. @@ -38,24 +38,23 @@ export enum ItemType { } /** - * This is the Mongoose schema for an agenda item. + * Mongoose schema definition for an agenda item document. * @param title - Title of the agenda item. * @param description - Optional description of the agenda item. - * @param relatedEvent - Reference to the event associated with the agenda item. + * @param relatedEventId - Reference to the event associated with the agenda item. * @param duration - Duration of the agenda item. * @param attachments - Optional array of attachment URLs. * @param createdBy - Reference to the user who created the agenda item. * @param updatedBy - Reference to the user who last updated the agenda item. - * @param urls - Optional users array indicating key note users for the agenda item. - * @param users - Optional user associated with the agenda item. + * @param urls - Optional array of URLs related to the agenda item. + * @param users - Optional array of users associated with the agenda item. * @param categories - Optional array of agenda categories associated with the agenda item. * @param sequence - Sequence number of the agenda item. * @param itemType - Type of the agenda item (Regular or Note). * @param createdAt - Date when the agenda item was created. * @param updatedAt - Date when the agenda item was last updated. - * @param isNote - Indicates whether the agenda item is a note. - * @param organization - Reference to the organization associated with the agenda item. - * @param notes - Reference to the notes associated with the agenda item. + * @param organizationId - Reference to the organization associated with the agenda item. + * @param notes - Array of notes associated with the agenda item. */ export const AgendaItemSchema = new Schema({ title: { @@ -126,6 +125,9 @@ export const AgendaItemSchema = new Schema({ ], }); +/** + * Returns the Mongoose Model for AgendaItem to prevent OverwriteModelError. + */ const agendaItemModel = (): Model => model("AgendaItem", AgendaItemSchema); diff --git a/src/models/AgendaSection.ts b/src/models/AgendaSection.ts index 551ba66c16..69f2ad2c16 100644 --- a/src/models/AgendaSection.ts +++ b/src/models/AgendaSection.ts @@ -3,8 +3,9 @@ import { Schema, model, models } from "mongoose"; import type { InterfaceUser } from "./User"; import { type InterfaceAgendaItem } from "./AgendaItem"; import type { InterfaceEvent } from "./Event"; + /** - * This is an interface representing a document for an agenda section in the database (MongoDB). + * Interface representing a document for an agenda section in MongoDB. */ export interface InterfaceAgendaSection { _id: Types.ObjectId; // Unique identifier for the agenda section. @@ -31,7 +32,7 @@ export interface InterfaceAgendaSection { export const AgendaSectionSchema = new Schema({ relatedEvent: { type: Schema.Types.ObjectId, - ref: "Event", + ref: "Event", // Refers to the Event model }, description: { type: String, @@ -40,7 +41,7 @@ export const AgendaSectionSchema = new Schema({ items: [ { type: Schema.Types.ObjectId, - ref: "AgendaItem", + ref: "AgendaItem", // Refers to the AgendaItem model }, ], sequence: { @@ -49,7 +50,7 @@ export const AgendaSectionSchema = new Schema({ }, createdBy: { type: Schema.Types.ObjectId, - ref: "User", + ref: "User", // Refers to the User model (creator) }, createdAt: { type: Date, @@ -61,9 +62,13 @@ export const AgendaSectionSchema = new Schema({ }, }); +/** + * Retrieves or creates the Mongoose model for AgendaSection. + * Prevents Mongoose OverwriteModelError during testing. + */ const agendaSectionModel = (): Model => model("AgendaSection", AgendaSectionSchema); -// Check if the AgendaItem model is already defined, if not, create a new one +// Export the AgendaSection model, preventing overwrite during tests export const AgendaSectionModel = (models.AgendaSection || agendaSectionModel()) as ReturnType; diff --git a/src/models/AppUserProfile.ts b/src/models/AppUserProfile.ts index cce4590ad3..ce0c07eb03 100644 --- a/src/models/AppUserProfile.ts +++ b/src/models/AppUserProfile.ts @@ -19,18 +19,18 @@ export interface InterfaceAppUserProfile { isSuperAdmin: boolean; } /** - * This describes the schema for a `AppUserProfile` that corresponds to `InterfaceAppUserProfile` document. - * @param user - User id of the AppUserProfile - * @param adminFor - Collection of organization where appuser is admin, each object refer to `Organization` model. - * @param appLanguageCode - AppUser's app language code. - * @param createdEvents - Collection of all events created by the user, each object refer to `Event` model. - * @param createdOrganizations - Collection of all organization created by the user, each object refer to `Organization` model. - * @param eventAdmin - Collection of the event admins, each object refer to `Event` model. - * @param pluginCreationAllowed - Wheather user is allowed to create plugins. - * @param tokenVersion - Token version. - * @param isSuperAdmin - Wheather user is super admin. - * @param token - Access token. - * */ + * Mongoose schema for an application user profile. + * @param userId - Reference to the user associated with the profile. + * @param adminFor - Array of organizations where the user is an admin. + * @param appLanguageCode - Language code preference of the app user. + * @param createdEvents - Array of events created by the user. + * @param createdOrganizations - Array of organizations created by the user. + * @param eventAdmin - Array of events where the user is an admin. + * @param pluginCreationAllowed - Flag indicating if user is allowed to create plugins. + * @param tokenVersion - Token version for authentication. + * @param isSuperAdmin - Flag indicating if the user is a super admin. + * @param token - Access token associated with the user profile. + */ const appUserSchema = new Schema( { diff --git a/src/models/CheckIn.ts b/src/models/CheckIn.ts index ba08070654..3b0ea7b646 100644 --- a/src/models/CheckIn.ts +++ b/src/models/CheckIn.ts @@ -10,6 +10,9 @@ import { import { type InterfaceEventAttendee } from "./EventAttendee"; import { createLoggingMiddleware } from "../libraries/dbLogger"; +/** + * Represents a document for a check-in entry in the MongoDB database. + */ export interface InterfaceCheckIn { _id: Types.ObjectId; eventAttendeeId: PopulatedDoc; @@ -19,6 +22,14 @@ export interface InterfaceCheckIn { updatedAt: Date; } +/** + * Mongoose schema definition for a check-in document. + * @param eventAttendeeId - Reference to the event attendee associated with the check-in. + * @param time - Date and time of the check-in. + * @param feedbackSubmitted - Indicates if feedback was submitted for the check-in. + * @param createdAt - Date when the check-in entry was created. + * @param updatedAt - Date when the check-in entry was last updated. + */ const checkInSchema = new Schema( { eventAttendeeId: { @@ -38,17 +49,21 @@ const checkInSchema = new Schema( }, }, { - timestamps: true, + timestamps: true, // Adds createdAt and updatedAt automatically }, ); -// We will also create an index here for faster database querying +// Create an index for faster querying by eventAttendeeId checkInSchema.index({ eventAttendeeId: 1, }); +// Apply logging middleware to the schema createLoggingMiddleware(checkInSchema, "CheckIn"); +/** + * Returns the Mongoose Model for CheckIn to prevent OverwriteModelError. + */ const checkInModel = (): Model => model("CheckIn", checkInSchema); diff --git a/src/models/CheckOut.ts b/src/models/CheckOut.ts index 3bd9e763c2..3ee11e684d 100644 --- a/src/models/CheckOut.ts +++ b/src/models/CheckOut.ts @@ -10,6 +10,9 @@ import { import { type InterfaceEventAttendee } from "./EventAttendee"; import { createLoggingMiddleware } from "../libraries/dbLogger"; +/** + * Interface representing a document for a check-out record in MongoDB. + */ export interface InterfaceCheckOut { _id: Types.ObjectId; eventAttendeeId: PopulatedDoc; @@ -18,31 +21,43 @@ export interface InterfaceCheckOut { updatedAt: Date; } +/** + * Mongoose schema for a check-out record. + * @param eventAttendeeId - Reference to the event attendee associated with the check-out. + * @param time - Time of the check-out. + * @param createdAt - Timestamp when the check-out record was created. + * @param updatedAt - Timestamp when the check-out record was last updated. + */ const checkOutSchema = new Schema( { eventAttendeeId: { type: Schema.Types.ObjectId, - ref: "EventAttendee", + ref: "EventAttendee", // Refers to the EventAttendee model required: true, }, time: { type: Date, required: true, - default: Date.now, + default: Date.now, // Default time is the current date/time }, }, { - timestamps: true, + timestamps: true, // Adds createdAt and updatedAt timestamps }, ); -// We will also create an index here for faster database querying +// Create an index for eventAttendeeId for optimized querying checkOutSchema.index({ eventAttendeeId: 1, }); +// Apply logging middleware for database operations on CheckOut collection createLoggingMiddleware(checkOutSchema, "CheckOut"); +/** + * Retrieves or creates the Mongoose model for CheckOut. + * Prevents Mongoose OverwriteModelError during testing. + */ const checkOutModel = (): Model => model("CheckOut", checkOutSchema); diff --git a/src/models/Comment.ts b/src/models/Comment.ts index 5d5150a359..0443b81210 100644 --- a/src/models/Comment.ts +++ b/src/models/Comment.ts @@ -3,8 +3,9 @@ import { Schema, model, models } from "mongoose"; import type { InterfaceUser } from "./User"; import type { InterfacePost } from "./Post"; import { createLoggingMiddleware } from "../libraries/dbLogger"; + /** - * This is an interface representing a document for a comment in the database - (MongoDB). + * Represents a document for a comment in the MongoDB database. */ export interface InterfaceComment { _id: Types.ObjectId; @@ -17,16 +18,17 @@ export interface InterfaceComment { likeCount: number; status: string; } + /** - * This is the Structure of the Comments - * @param text - Text - * @param createdAt - Date when the comment was created - * @param creatorId - Comment Creator, refer to `User` model - * @param postId - Id of the post on which this comment is created - * @param likedBy - Liked by whom - * @param likeCount - No of likes - * @param status - whether the comment is active, blocked or deleted. - * @param updatedAt - Date when the comment was updated + * Mongoose schema definition for a comment document. + * @param text - Text content of the comment. + * @param createdAt - Date when the comment was created. + * @param creatorId - Reference to the user who created the comment. + * @param updatedAt - Date when the comment was last updated. + * @param postId - Reference to the post on which this comment is created. + * @param likedBy - Array of users who liked the comment. + * @param likeCount - Number of likes for the comment. + * @param status - Status of the comment (ACTIVE, BLOCKED, DELETED). */ const commentSchema = new Schema( { @@ -62,12 +64,16 @@ const commentSchema = new Schema( }, }, { - timestamps: true, + timestamps: true, // Adds createdAt and updatedAt automatically }, ); +// Apply logging middleware to the schema createLoggingMiddleware(commentSchema, "Comment"); +/** + * Returns the Mongoose Model for Comment to prevent OverwriteModelError. + */ const commentModel = (): Model => model("Comment", commentSchema); diff --git a/src/models/Community.ts b/src/models/Community.ts index bc6ade46ce..d687748a0b 100644 --- a/src/models/Community.ts +++ b/src/models/Community.ts @@ -2,7 +2,7 @@ import { Schema, model, models } from "mongoose"; import type { Types, Model } from "mongoose"; /** - * This is an interface that represents a database(MongoDB) document for Community. + * Interface representing a document for a community in MongoDB. */ export interface InterfaceCommunity { _id: Types.ObjectId; @@ -18,25 +18,24 @@ export interface InterfaceCommunity { youTube: string; slack: string; reddit: string; - }; + }; // Object containing various social media URLs for the community. } /** - * This describes the schema for a `Community` that corresponds to `InterfaceCommunity` document. - * @param logoUrl - Community logo URL. - * @param socialMediaUrls - Social media URLs. + * Mongoose schema for a community. + * @param name - Name of the community. + * @param logoUrl - URL of the community's logo. + * @param websiteLink - URL of the community's website. + * @param socialMediaUrls - Object containing social media URLs for the community. * @param facebook - Facebook URL. - * @param instagram - Instagram URL + * @param instagram - Instagram URL. * @param twitter - Twitter URL. * @param linkedIn - LinkedIn URL. * @param gitHub - GitHub URL. * @param youTube - YouTube URL. * @param slack - Slack URL. * @param reddit - Reddit URL. - * @param websiteLink - Community website URL. - * @param name - Community name. */ - const communitySchema = new Schema({ name: { type: String, @@ -76,6 +75,10 @@ const communitySchema = new Schema({ }, }); +/** + * Retrieves or creates the Mongoose model for Community. + * Prevents Mongoose OverwriteModelError during testing. + */ const communityModel = (): Model => model("Community", communitySchema); diff --git a/src/models/DirectChat.ts b/src/models/DirectChat.ts index 2f08a1e7a9..3715e0f687 100644 --- a/src/models/DirectChat.ts +++ b/src/models/DirectChat.ts @@ -5,7 +5,7 @@ import type { InterfaceOrganization } from "./Organization"; import type { InterfaceUser } from "./User"; import { createLoggingMiddleware } from "../libraries/dbLogger"; /** - * This is an interface representing a document for direct chat in the database(MongoDB). + * Interface representing a document for direct chat in MongoDB. */ export interface InterfaceDirectChat { _id: Types.ObjectId; @@ -17,15 +17,16 @@ export interface InterfaceDirectChat { createdAt: Date; updatedAt: Date; } + /** - * This is the Structure of the direct chat. - * @param users - Users of the chat - * @param messages - Messages - * @param creatorId - Creator of the chat, ref to `User` model - * @param organization - Organization - * @param status - whether the chat is active, blocked or deleted. - * @param createdAt - Timestamp of chat creation - * @param updatedAt - Timestamp of chat updation + * Mongoose schema for a direct chat. + * @param users - Users participating in the chat. + * @param messages - Messages in the chat. + * @param creatorId - Creator of the chat, reference to `User` model. + * @param organization - Organization associated with the chat, reference to `Organization` model. + * @param status - Status of the chat (ACTIVE, BLOCKED, DELETED). + * @param createdAt - Timestamp of chat creation. + * @param updatedAt - Timestamp of chat update. */ const directChatSchema = new Schema( { @@ -47,11 +48,6 @@ const directChatSchema = new Schema( ref: "User", required: true, }, - organization: { - type: Schema.Types.ObjectId, - ref: "Organization", - required: true, - }, status: { type: String, required: true, @@ -64,8 +60,13 @@ const directChatSchema = new Schema( }, ); +// Add logging middleware for directChatSchema createLoggingMiddleware(directChatSchema, "DirectChat"); +/** + * Retrieves or creates the Mongoose model for DirectChat. + * Prevents Mongoose OverwriteModelError during testing. + */ const directChatModel = (): Model => model("DirectChat", directChatSchema); diff --git a/src/models/DirectChatMessage.ts b/src/models/DirectChatMessage.ts index 54ca87ee8d..a31dcf809b 100644 --- a/src/models/DirectChatMessage.ts +++ b/src/models/DirectChatMessage.ts @@ -3,8 +3,9 @@ import { Schema, model, models } from "mongoose"; import type { InterfaceDirectChat } from "./DirectChat"; import type { InterfaceUser } from "./User"; import { createLoggingMiddleware } from "../libraries/dbLogger"; + /** - * This is an interface representing a document for a direct chat message in the database(MongoDB). + * Represents a document for a direct chat message in the MongoDB database. */ export interface InterfaceDirectChatMessage { _id: Types.ObjectId; @@ -16,15 +17,16 @@ export interface InterfaceDirectChatMessage { createdAt: Date; updatedAt: Date; } + /** - * This is the Structure of the Direct chat Message - * @param directChatMessageBelongsTo - To whom the direct chat messages belong - * @param sender - Sender - * @param receiver - Receiver - * @param createdAt - Timestamp when the message was created - * @param updatedAt - Timestamp when the message was updated - * @param messageContent - Message content - * @param status - whether the message is active, blocked or deleted + * Mongoose schema definition for a direct chat message document. + * @param directChatMessageBelongsTo - Reference to the direct chat session to which the message belongs. + * @param sender - Reference to the user who sent the message. + * @param receiver - Reference to the user who received the message. + * @param messageContent - Content of the direct chat message. + * @param status - Status of the message (ACTIVE, BLOCKED, DELETED). + * @param createdAt - Date when the direct chat message was created. + * @param updatedAt - Date when the direct chat message was last updated. */ const directChatMessageSchema = new Schema( { @@ -55,12 +57,16 @@ const directChatMessageSchema = new Schema( }, }, { - timestamps: true, + timestamps: true, // Adds createdAt and updatedAt automatically }, ); +// Apply logging middleware to the schema createLoggingMiddleware(directChatMessageSchema, "DirectChatMessage"); +/** + * Returns the Mongoose Model for DirectChatMessage to prevent OverwriteModelError. + */ const directChatMessageModel = (): Model => model( "DirectChatMessage", diff --git a/src/models/Donation.ts b/src/models/Donation.ts index ce94d0cf86..f63bbb34f0 100644 --- a/src/models/Donation.ts +++ b/src/models/Donation.ts @@ -1,8 +1,9 @@ import type { Types, Model } from "mongoose"; import { Schema, model, models } from "mongoose"; import { createLoggingMiddleware } from "../libraries/dbLogger"; + /** - * This is an interface representing a document for a donation in the database(MongoDB). + * Interface representing a document for a donation in MongoDB. */ export interface InterfaceDonation { userId: Types.ObjectId | string; @@ -14,16 +15,17 @@ export interface InterfaceDonation { createdAt: Date; updatedAt: Date; } + /** - * This is the Structure of the Donation - * @param userId - User-id - * @param orgId - Organization-id - * @param nameOfOrg - Name of the organization - * @param payPalId - PayPalId - * @param nameOfUser - Name of the user - * @param amount - Amount of the donation - * @param createdAt - Timestamp of creation - * @param updatedAt - Timestamp of updation + * Mongoose schema for a donation. + * @param userId - ID of the user making the donation. + * @param orgId - ID of the organization receiving the donation. + * @param nameOfOrg - Name of the organization. + * @param payPalId - PayPal ID used for the donation. + * @param nameOfUser - Name of the user making the donation. + * @param amount - Amount of the donation. + * @param createdAt - Timestamp of donation creation. + * @param updatedAt - Timestamp of donation update. */ const donationSchema = new Schema( { @@ -57,8 +59,12 @@ const donationSchema = new Schema( }, ); +// Add logging middleware for donationSchema createLoggingMiddleware(donationSchema, "Donation"); +/** + * Retrieves or creates the Mongoose model for Donation. + */ const donationModel = (): Model => model("Donation", donationSchema); diff --git a/src/models/EncodedImage.ts b/src/models/EncodedImage.ts index 23dcbe7dfd..dbeeb3dd9b 100644 --- a/src/models/EncodedImage.ts +++ b/src/models/EncodedImage.ts @@ -1,8 +1,9 @@ import type { Types, Model } from "mongoose"; import { Schema, model, models } from "mongoose"; import { createLoggingMiddleware } from "../libraries/dbLogger"; + /** - * This is an interface that represents a database(MongoDB) document for Encoded Image. + * Represents a document for an encoded image in the MongoDB database. */ export interface InterfaceEncodedImage { _id: Types.ObjectId; @@ -10,11 +11,12 @@ export interface InterfaceEncodedImage { content: string; numberOfUses: number; } + /** - * This describes the schema for a `encodedImage` that corresponds to `InterfaceEncodedImage` document. - * @param fileName - File name. - * @param content - Content. - * @param numberOfUses - Number of Uses. + * Mongoose schema definition for an encoded image document. + * @param fileName - File name of the encoded image. + * @param content - Content of the encoded image. + * @param numberOfUses - Number of times the encoded image has been used. */ const encodedImageSchema = new Schema({ fileName: { @@ -32,6 +34,7 @@ const encodedImageSchema = new Schema({ }, }); +// Apply logging middleware to the schema createLoggingMiddleware(encodedImageSchema, "EncodedImage"); const encodedImageModel = (): Model => diff --git a/src/models/EncodedVideo.ts b/src/models/EncodedVideo.ts index 4cdf2f2ff7..eda2f21685 100644 --- a/src/models/EncodedVideo.ts +++ b/src/models/EncodedVideo.ts @@ -1,8 +1,9 @@ import type { Types, Model } from "mongoose"; import { Schema, model, models } from "mongoose"; import { createLoggingMiddleware } from "../libraries/dbLogger"; + /** - * This is an interface that represents a database(MongoDB) document for Encoded Video. + * Interface representing a document for an encoded video in MongoDB. */ export interface InterfaceEncodedVideo { _id: Types.ObjectId; @@ -10,11 +11,12 @@ export interface InterfaceEncodedVideo { content: string; numberOfUses: number; } + /** - * This describes the schema for a `encodedVideo` that corresponds to `InterfaceEncodedVideo` document. - * @param fileName - File name. - * @param content - Content. - * @param numberOfUses - Number of Uses. + * Mongoose schema for an encoded video. + * @param fileName - Name of the file for the encoded video. + * @param content - Content of the encoded video. + * @param numberOfUses - Number of times the encoded video has been used. */ const encodedVideoSchema = new Schema({ fileName: { @@ -28,12 +30,16 @@ const encodedVideoSchema = new Schema({ numberOfUses: { type: Number, required: true, - default: 1, + default: 1, // Default value set to 1 when a new document is created. }, }); +// Add logging middleware for encodedVideoSchema createLoggingMiddleware(encodedVideoSchema, "EncodedVideo"); +/** + * Retrieves or creates the Mongoose model for EncodedVideo. + */ const encodedVideoModel = (): Model => model("EncodedVideo", encodedVideoSchema); diff --git a/src/models/Event.ts b/src/models/Event.ts index 55ae7fe476..2a30077aad 100644 --- a/src/models/Event.ts +++ b/src/models/Event.ts @@ -8,7 +8,7 @@ import type { InterfaceRecurrenceRule } from "./RecurrenceRule"; import type { InterfaceAgendaItem } from "./AgendaItem"; /** - * This is an interface representing a document for an event in the database(MongoDB). + * Represents a document for an event in the MongoDB database. */ export interface InterfaceEvent { _id: Types.ObjectId; @@ -42,35 +42,33 @@ export interface InterfaceEvent { } /** - * This is the Structure of the Event - * @param admins - Admins - * @param allDay - Is the event occuring all day - * @param attendees - Attendees - * @param baseRecurringEventId - Id of the true recurring event used for generating this instance - * @param createdAt - Timestamp of event creation - * @param creatorId - Creator of the event - * @param description - Description of the event - * @param endDate - End date - * @param endTime - End Time - * @param images -Event Flyer - * @param isBaseRecurringEvent - Is the event a true recurring event that is used for generating new instances - * @param isPublic - Is the event public - * @param isRecurringEventException - Is the event an exception to the recurring pattern it was following - * @param isRegisterable - Is the event Registrable - * @param latitude - Latitude - * @param location - Location of the event - * @param longitude - Longitude - * @param organization - Organization - * @param recurrance - Periodicity of recurrance of the event - * @param recurrenceRuleId - Id of the recurrence rule document containing the recurrence pattern for the event - * @param recurring - Is the event recurring - * @param startDate - Start Date - * @param startTime - Start Time - * @param title - Title of the event - * @param updatedAt - Timestamp of event updation - * @param volunteerGroups - event volunteer groups for the event + * Mongoose schema definition for an event document. + * @param title - Title of the event. + * @param description - Description of the event. + * @param attendees - Optional attendees information. + * @param images - Array of image URLs associated with the event. + * @param location - Optional location of the event. + * @param latitude - Latitude coordinate of the event location. + * @param longitude - Longitude coordinate of the event location. + * @param recurring - Indicates if the event is recurring. + * @param isRecurringEventException - Indicates if the event is an exception to a recurring pattern. + * @param isBaseRecurringEvent - Indicates if the event is a base recurring event. + * @param recurrenceRuleId - Reference to the recurrence rule for recurring events. + * @param baseRecurringEventId - Reference to the base recurring event for generated instances. + * @param allDay - Indicates if the event occurs throughout the entire day. + * @param startDate - Start date of the event. + * @param endDate - Optional end date of the event. + * @param startTime - Optional start time of the event. + * @param endTime - Optional end time of the event. + * @param isPublic - Indicates if the event is public. + * @param isRegisterable - Indicates if the event is registerable. + * @param creatorId - Reference to the user who created the event. + * @param admins - Array of admins for the event. + * @param organization - Reference to the organization hosting the event. + * @param volunteerGroups - Array of volunteer groups associated with the event. + * @param createdAt - Timestamp of when the event was created. + * @param updatedAt - Timestamp of when the event was last updated. */ - const eventSchema = new Schema( { title: { @@ -194,6 +192,7 @@ const eventSchema = new Schema( }, ); +// Apply logging middleware to the schema createLoggingMiddleware(eventSchema, "Event"); const eventModel = (): Model => diff --git a/src/models/EventAttendee.ts b/src/models/EventAttendee.ts index 7d984a3b6d..6ff1cec5d9 100644 --- a/src/models/EventAttendee.ts +++ b/src/models/EventAttendee.ts @@ -6,6 +6,9 @@ import type { InterfaceCheckIn } from "./CheckIn"; import type { InterfaceCheckOut } from "./CheckOut"; import { createLoggingMiddleware } from "../libraries/dbLogger"; +/** + * Interface representing a document for an event attendee in MongoDB. + */ export interface InterfaceEventAttendee { _id: Schema.Types.ObjectId; userId: PopulatedDoc; @@ -18,6 +21,17 @@ export interface InterfaceEventAttendee { isCheckedOut: boolean; } +/** + * Mongoose schema for an event attendee. + * @param userId - Reference to the user attending the event. + * @param eventId - Reference to the event the attendee is associated with. + * @param checkInId - Reference to the check-in record if checked in, or null. + * @param checkOutId - Reference to the check-out record if checked out, or null. + * @param isInvited - Indicates if the attendee is invited to the event. + * @param isRegistered - Indicates if the attendee is registered for the event. + * @param isCheckedIn - Indicates if the attendee is checked in to the event. + * @param isCheckedOut - Indicates if the attendee is checked out from the event. + */ const eventAttendeeSchema = new Schema({ userId: { type: Schema.Types.ObjectId, @@ -67,8 +81,10 @@ const eventAttendeeSchema = new Schema({ }, }); +// Ensure uniqueness of combinations of userId and eventId eventAttendeeSchema.index({ userId: 1, eventId: 1 }, { unique: true }); +// Add logging middleware for eventAttendeeSchema createLoggingMiddleware(eventAttendeeSchema, "EventAttendee"); const eventAttendeeModel = (): Model => diff --git a/src/models/EventVolunteer.ts b/src/models/EventVolunteer.ts index f50c5d62db..0600f77b4c 100644 --- a/src/models/EventVolunteer.ts +++ b/src/models/EventVolunteer.ts @@ -5,6 +5,10 @@ import type { InterfaceEvent } from "./Event"; import { createLoggingMiddleware } from "../libraries/dbLogger"; import type { InterfaceEventVolunteerGroup } from "./EventVolunteerGroup"; +/** + * Represents a document for an event volunteer in the MongoDB database. + * This interface defines the structure and types of data that an event volunteer document will hold. + */ export interface InterfaceEventVolunteer { _id: Types.ObjectId; createdAt: Date; @@ -18,6 +22,20 @@ export interface InterfaceEventVolunteer { userId: PopulatedDoc; } +/** + * Mongoose schema definition for an event volunteer document. + * This schema defines how the data will be stored in the MongoDB database. + * + * @param creatorId - Reference to the user who created the event volunteer entry. + * @param eventId - Reference to the event for which the user volunteers. + * @param groupId - Reference to the volunteer group associated with the event. + * @param response - Response status of the volunteer ("YES", "NO", null). + * @param isAssigned - Indicates if the volunteer is assigned to a specific role. + * @param isInvited - Indicates if the volunteer has been invited to participate. + * @param userId - Reference to the user who is volunteering for the event. + * @param createdAt - Timestamp of when the event volunteer document was created. + * @param updatedAt - Timestamp of when the event volunteer document was last updated. + */ const eventVolunteerSchema = new Schema( { creatorId: { @@ -50,16 +68,25 @@ const eventVolunteerSchema = new Schema( }, }, { - timestamps: true, + timestamps: true, // Automatically manage `createdAt` and `updatedAt` fields }, ); -// Enable logging on changes in EventVolunteer collection +// Apply logging middleware to the schema createLoggingMiddleware(eventVolunteerSchema, "EventVolunteer"); +/** + * Creates a Mongoose model for the event volunteer schema. + * This function ensures that we don't create multiple models during testing, which can cause errors. + * + * @returns The EventVolunteer model. + */ const eventVolunteerModel = (): Model => model("EventVolunteer", eventVolunteerSchema); -// This syntax is needed to prevent Mongoose OverwriteModelError while running tests. +/** + * Export the EventVolunteer model. + * This syntax ensures we don't get an OverwriteModelError while running tests. + */ export const EventVolunteer = (models.EventVolunteer || eventVolunteerModel()) as ReturnType; diff --git a/src/models/EventVolunteerGroup.ts b/src/models/EventVolunteerGroup.ts index b93cd17929..8f8fc5072e 100644 --- a/src/models/EventVolunteerGroup.ts +++ b/src/models/EventVolunteerGroup.ts @@ -5,6 +5,10 @@ import type { InterfaceEvent } from "./Event"; import { createLoggingMiddleware } from "../libraries/dbLogger"; import type { InterfaceEventVolunteer } from "./EventVolunteer"; +/** + * Represents a document for an event volunteer group in the MongoDB database. + * This interface defines the structure and types of data that an event volunteer group document will hold. + */ export interface InterfaceEventVolunteerGroup { _id: Types.ObjectId; createdAt: Date; @@ -17,6 +21,19 @@ export interface InterfaceEventVolunteerGroup { volunteersRequired?: number; } +/** + * Mongoose schema definition for an event volunteer group document. + * This schema defines how the data will be stored in the MongoDB database. + * + * @param creatorId - Reference to the user who created the event volunteer group entry. + * @param eventId - Reference to the event for which the volunteer group is created. + * @param leaderId - Reference to the leader of the volunteer group. + * @param name - Name of the volunteer group. + * @param volunteers - List of volunteers in the group. + * @param volunteersRequired - Number of volunteers required for the group (optional). + * @param createdAt - Timestamp of when the event volunteer group document was created. + * @param updatedAt - Timestamp of when the event volunteer group document was last updated. + */ const eventVolunteerGroupSchema = new Schema( { creatorId: { @@ -50,19 +67,28 @@ const eventVolunteerGroupSchema = new Schema( }, }, { - timestamps: true, + timestamps: true, // Automatically manage `createdAt` and `updatedAt` fields }, ); -// Enable logging on changes in EventVolunteer collection +// Enable logging on changes in EventVolunteerGroup collection createLoggingMiddleware(eventVolunteerGroupSchema, "EventVolunteerGroup"); +/** + * Creates a Mongoose model for the event volunteer group schema. + * This function ensures that we don't create multiple models during testing, which can cause errors. + * + * @returns The EventVolunteerGroup model. + */ const eventVolunteerGroupModel = (): Model => model( "EventVolunteerGroup", eventVolunteerGroupSchema, ); -// This syntax is needed to prevent Mongoose OverwriteModelError while running tests. +/** + * Export the EventVolunteerGroup model. + * This syntax ensures we don't get an OverwriteModelError while running tests. + */ export const EventVolunteerGroup = (models.EventVolunteerGroup || eventVolunteerGroupModel()) as ReturnType; diff --git a/src/models/Feedback.ts b/src/models/Feedback.ts index 6a8d00faa7..c8c3ecbeb8 100644 --- a/src/models/Feedback.ts +++ b/src/models/Feedback.ts @@ -3,6 +3,10 @@ import { Schema, model, models } from "mongoose"; import type { InterfaceEvent } from "./Event"; import { createLoggingMiddleware } from "../libraries/dbLogger"; +/** + * Represents a document for feedback in the MongoDB database. + * This interface defines the structure and types of data that a feedback document will hold. + */ export interface InterfaceFeedback { _id: Types.ObjectId; eventId: PopulatedDoc; @@ -12,6 +16,16 @@ export interface InterfaceFeedback { updatedAt: Date; } +/** + * Mongoose schema definition for a feedback document. + * This schema defines how the data will be stored in the MongoDB database. + * + * @param eventId - Reference to the event for which the feedback is given. + * @param rating - The rating given for the event. + * @param review - The review text provided for the event (optional). + * @param createdAt - Timestamp of when the feedback document was created. + * @param updatedAt - Timestamp of when the feedback document was last updated. + */ const feedbackSchema = new Schema( { eventId: { @@ -31,21 +45,31 @@ const feedbackSchema = new Schema( }, }, { - timestamps: true, + timestamps: true, // Automatically manage `createdAt` and `updatedAt` fields }, ); -// We will also create an index here for faster database querying +// Create an index on the eventId field for faster database querying feedbackSchema.index({ eventId: 1, }); +// Enable logging on changes in the Feedback collection createLoggingMiddleware(feedbackSchema, "Feedback"); +/** + * Creates a Mongoose model for the feedback schema. + * This function ensures that we don't create multiple models during testing, which can cause errors. + * + * @returns The Feedback model. + */ const feedbackModel = (): Model => model("Feedback", feedbackSchema); -// This syntax is needed to prevent Mongoose OverwriteModelError while running tests. +/** + * Export the Feedback model. + * This syntax ensures we don't get an OverwriteModelError while running tests. + */ export const Feedback = (models.Feedback || feedbackModel()) as ReturnType< typeof feedbackModel >; diff --git a/src/models/File.ts b/src/models/File.ts index 19fee53c83..bd8550f2dc 100644 --- a/src/models/File.ts +++ b/src/models/File.ts @@ -16,23 +16,25 @@ export interface InterfaceFile { createdAt: Date; updatedAt: Date; } + /** - * This is the structure of a file - * @param name - Name - * @param url - URL - * @param size - Size - * @param secret - Secret - * @param createdAt - Timestamp of creation - * @param updatedAt - Timestamp of updation - * @param contentType - Content Type - * @param status - Status + * Mongoose schema for a file. + * Defines the structure of the file document stored in MongoDB. + * @param name - The name of the file. + * @param url - The URL where the file is stored. + * @param size - The size of the file in bytes. + * @param secret - A secret key associated with the file. + * @param contentType - The MIME type of the file. + * @param status - The status of the file (e.g., ACTIVE, BLOCKED, DELETED). + * @param createdAt - The date and time when the file was created. + * @param updatedAt - The date and time when the file was last updated. */ const fileSchema = new Schema( { name: { type: String, required: true, - default: uuidv4(), + default: uuidv4(), // Generates a unique identifier for the name by default }, url: { type: String, @@ -55,16 +57,26 @@ const fileSchema = new Schema( }, }, { - timestamps: true, + timestamps: true, // Automatically adds `createdAt` and `updatedAt` fields }, ); +// Add logging middleware for fileSchema createLoggingMiddleware(fileSchema, "File"); +/** + * Function to retrieve or create the Mongoose model for the File. + * This is necessary to avoid the OverwriteModelError during testing. + * @returns The Mongoose model for the File. + */ const fileModel = (): Model => model("File", fileSchema); -// This syntax is needed to prevent Mongoose OverwriteModelError while running tests. +/** + * The Mongoose model for the File. + * If the model already exists (e.g., during testing), it uses the existing model. + * Otherwise, it creates a new model. + */ export const File = (models.File || fileModel()) as ReturnType< typeof fileModel >; diff --git a/src/models/Fund.ts b/src/models/Fund.ts index d54bba5174..6c4cc93bc6 100644 --- a/src/models/Fund.ts +++ b/src/models/Fund.ts @@ -1,10 +1,11 @@ import type { Model, PopulatedDoc, Types } from "mongoose"; - import { Schema, model, models } from "mongoose"; import type { InterfaceFundraisingCampaign } from "./FundraisingCampaign"; import type { InterfaceUser } from "./User"; + /** - * This is an interface representing a document for fund in the database(MongoDB). + * This is an interface representing a document for a fund in the database (MongoDB). + * This interface defines the structure and types of data that a fund document will hold. */ export interface InterfaceFund { _id: Types.ObjectId; @@ -21,18 +22,19 @@ export interface InterfaceFund { } /** - * This is the structure of a file - * @param organizationId - Organization ID to which the fund belongs - * @param name - Name of the fund - * @param refrenceNumber - Reference number of the fund - * @param taxDeductible - Whether the fund is tax deductible - * @param isDefault - Whether the fund is default - * @param isArchived - Whether the fund is archived - * @param campaign - Campaigns associated with the fund - * @param createdAt - Timestamp of creation - * @param updatedAt - Timestamp of updation - * @param creatorId - User who created the fund, refer to `User` model. + * Mongoose schema definition for a fund document. + * This schema defines how the data will be stored in the MongoDB database. * + * @param organizationId - Organization ID to which the fund belongs. + * @param name - Name of the fund. + * @param refrenceNumber - Reference number of the fund. + * @param taxDeductible - Indicates whether the fund is tax deductible. + * @param isDefault - Indicates whether the fund is the default fund. + * @param isArchived - Indicates whether the fund is archived. + * @param creatorId - Reference to the user who created the fund (refers to the `User` model). + * @param campaigns - Campaigns associated with the fund. + * @param createdAt - Timestamp of when the fund document was created. + * @param updatedAt - Timestamp of when the fund document was last updated. */ const fundSchema = new Schema( { @@ -73,12 +75,23 @@ const fundSchema = new Schema( ], }, { - timestamps: true, + timestamps: true, // Automatically manage `createdAt` and `updatedAt` fields }, ); + +/** + * Creates a Mongoose model for the fund schema. + * This function ensures that we don't create multiple models during testing, which can cause errors. + * + * @returns The Fund model. + */ const fundModel = (): Model => model("Fund", fundSchema); -// This syntax is needed to prevent Mongoose OverwriteModelError while running tests. + +/** + * Export the Fund model. + * This syntax ensures we don't get an OverwriteModelError while running tests. + */ export const Fund = (models.Fund || fundModel()) as ReturnType< typeof fundModel >; diff --git a/src/models/FundraisingCampaign.ts b/src/models/FundraisingCampaign.ts index 9f9ed39676..33af442b4a 100644 --- a/src/models/FundraisingCampaign.ts +++ b/src/models/FundraisingCampaign.ts @@ -1,9 +1,13 @@ import type { Model, PopulatedDoc, Types } from "mongoose"; - import { Schema, model, models } from "mongoose"; import type { InterfaceFund } from "./Fund"; import type { InterfaceFundraisingCampaignPledges } from "./FundraisingCampaignPledge"; import { createLoggingMiddleware } from "../libraries/dbLogger"; + +/** + * Enum for currency types with their respective codes. + * This enum lists all the possible currency codes that can be used in the system. + */ export enum CurrencyType { AED = "AED", // United Arab Emirates Dirham AFN = "AFN", // Afghan Afghani @@ -170,18 +174,6 @@ export enum CurrencyType { ZWD = "ZWD", // Zimbabwean Dollar } -/** - * This is the structure of a file - * @param fund - parent fund to which campaign belongs - * @param name - Name of the campaign - * @param startDate - Start date of the campaign - * @param endDate - End date of the campaign - * @param fundingGoal - Goal of the campaign - * @param currency - Currency of the campaign - * @param createdAt - Timestamp of creation - * @param updatedAt - Timestamp of updation - * - */ export interface InterfaceFundraisingCampaign { _id: Types.ObjectId; fundId: PopulatedDoc; @@ -194,6 +186,19 @@ export interface InterfaceFundraisingCampaign { createdAt: Date; updatedAt: Date; } + +/** + * Mongoose schema definition for a fundraising campaign document. + * This schema defines how the data will be stored in the MongoDB database. + * + * @param fundId - Reference to the parent fund. + * @param name - Name of the fundraising campaign. + * @param startDate - Start date of the fundraising campaign. + * @param endDate - End date of the fundraising campaign. + * @param fundingGoal - Financial goal of the fundraising campaign. + * @param currency - Currency in which the funding goal is specified. + * @param pledges - List of pledges associated with the fundraising campaign. + */ const fundraisingCampaignSchema = new Schema( { fundId: { @@ -220,7 +225,7 @@ const fundraisingCampaignSchema = new Schema( currency: { type: String, required: true, - enum: Object.values(CurrencyType), + enum: Object.values(CurrencyType), // Must be one of the defined currency types }, pledges: [ { @@ -230,17 +235,31 @@ const fundraisingCampaignSchema = new Schema( ], }, { - timestamps: true, + timestamps: true, // Automatically manage `createdAt` and `updatedAt` fields }, ); +/** + * Adds logging middleware to the fundraising campaign schema. + * This middleware logs changes to fundraising campaign documents. + */ createLoggingMiddleware(fundraisingCampaignSchema, "FundRaisingCampaign"); +/** + * Creates a Mongoose model for the fundraising campaign schema. + * This function ensures that we don't create multiple models during testing, which can cause errors. + * + * @returns The FundraisingCampaign model. + */ const fundraisingCampaignModel = (): Model => model( "FundraisingCampaign", fundraisingCampaignSchema, ); -// This syntax is needed to prevent Mongoose OverwriteModelError while running tests. + +/** + * Export the FundraisingCampaign model. + * This syntax ensures we don't get an OverwriteModelError while running tests. + */ export const FundraisingCampaign = (models.FundraisingCampaign || fundraisingCampaignModel()) as ReturnType; diff --git a/src/models/FundraisingCampaignPledge.ts b/src/models/FundraisingCampaignPledge.ts index 2e4b066db8..ef443c575f 100644 --- a/src/models/FundraisingCampaignPledge.ts +++ b/src/models/FundraisingCampaignPledge.ts @@ -6,6 +6,9 @@ import { } from "./FundraisingCampaign"; import { type InterfaceUser } from "./User"; +/** + * Interface representing a document for a fundraising campaign pledge in the database (MongoDB). + */ export interface InterfaceFundraisingCampaignPledges { _id: Types.ObjectId; campaigns: PopulatedDoc[]; @@ -17,6 +20,19 @@ export interface InterfaceFundraisingCampaignPledges { createdAt: Date; updatedAt: Date; } + +/** + * Mongoose schema for a fundraising campaign pledge. + * Defines the structure of the pledge document stored in MongoDB. + * @param campaigns - The fundraising campaigns associated with the pledge. + * @param users - The users who made the pledge. + * @param startDate - The start date of the pledge. + * @param endDate - The end date of the pledge. + * @param amount - The amount pledged. + * @param currency - The currency type of the amount pledged. + * @param createdAt - The date and time when the pledge was created. + * @param updatedAt - The date and time when the pledge was last updated. + */ const fundraisingCampaignPledgeSchema = new Schema( { campaigns: [ @@ -50,9 +66,15 @@ const fundraisingCampaignPledgeSchema = new Schema( }, }, { - timestamps: true, + timestamps: true, // Automatically adds `createdAt` and `updatedAt` fields }, ); + +/** + * Function to retrieve or create the Mongoose model for the FundraisingCampaignPledge. + * This is necessary to avoid the OverwriteModelError during testing. + * @returns The Mongoose model for the FundraisingCampaignPledge. + */ const fundraisingCampaignPledgeModel = (): Model => model( @@ -60,7 +82,11 @@ const fundraisingCampaignPledgeModel = fundraisingCampaignPledgeSchema, ); -// This syntax is needed to prevent Mongoose OverwriteModelError while running tests. +/** + * The Mongoose model for the FundraisingCampaignPledge. + * If the model already exists (e.g., during testing), it uses the existing model. + * Otherwise, it creates a new model. + */ export const FundraisingCampaignPledge = (models.FundraisingCampaignPledge || fundraisingCampaignPledgeModel()) as ReturnType< typeof fundraisingCampaignPledgeModel diff --git a/src/models/Group.ts b/src/models/Group.ts index fd1acc6caf..e3d0256a7c 100644 --- a/src/models/Group.ts +++ b/src/models/Group.ts @@ -4,7 +4,7 @@ import type { InterfaceOrganization } from "./Organization"; import type { InterfaceUser } from "./User"; import { createLoggingMiddleware } from "../libraries/dbLogger"; /** - * This is an interface representing a document for a group in the database(MongoDB). + * Interface representing a document for a group in the database (MongoDB). */ export interface InterfaceGroup { _id: Types.ObjectId; @@ -17,13 +17,15 @@ export interface InterfaceGroup { updatedAt: Date; } /** - * This is the structure of a group - * @param title - Title - * @param description - Description - * @param createdAt - Timestamp of creation - * @param updatedAt - Timestamp of updation - * @param status - Status - * @param admins - Admins + * Mongoose schema for a group. + * Defines the structure of the group document stored in MongoDB. + * @param title - The title of the group. + * @param description - A description of the group. + * @param organization - The organization the group belongs to. + * @param status - The status of the group (e.g., ACTIVE, BLOCKED, DELETED). + * @param admins - The administrators of the group. + * @param createdAt - The date and time when the group was created. + * @param updatedAt - The date and time when the group was last updated. */ const groupSchema = new Schema( { @@ -54,16 +56,26 @@ const groupSchema = new Schema( ], }, { - timestamps: true, + timestamps: true, // Automatically adds `createdAt` and `updatedAt` fields }, ); +// Add logging middleware for groupSchema createLoggingMiddleware(groupSchema, "Group"); +/** + * Function to retrieve or create the Mongoose model for the Group. + * This is necessary to avoid the OverwriteModelError during testing. + * @returns The Mongoose model for the Group. + */ const groupModel = (): Model => model("Group", groupSchema); -// This syntax is needed to prevent Mongoose OverwriteModelError while running tests. +/** + * The Mongoose model for the Group. + * If the model already exists (e.g., during testing), it uses the existing model. + * Otherwise, it creates a new model. + */ export const Group = (models.Group || groupModel()) as ReturnType< typeof groupModel >; diff --git a/src/models/GroupChat.ts b/src/models/GroupChat.ts index 6e654ef2e2..3ecf20f70d 100644 --- a/src/models/GroupChat.ts +++ b/src/models/GroupChat.ts @@ -4,8 +4,9 @@ import type { InterfaceGroupChatMessage } from "./GroupChatMessage"; import type { InterfaceOrganization } from "./Organization"; import type { InterfaceUser } from "./User"; import { createLoggingMiddleware } from "../libraries/dbLogger"; + /** - * This is an interface representing a document for a group chat in the database(MongoDB). + * Interface representing a document for a group chat in the database (MongoDB). */ export interface InterfaceGroupChat { _id: Types.ObjectId; @@ -19,15 +20,17 @@ export interface InterfaceGroupChat { status: string; } /** - * This is the structure of a group chat - * @param title - Title - * @param users - Users of the chat - * @param messages - Message of the chat - * @param creatorId - Creator of the chat + * Mongoose schema definition for a group chat document. + * Defines how group chat data will be stored in MongoDB. + * + * @param title - Title of the group chat. + * @param users - Users participating in the group chat. + * @param messages - Messages sent in the group chat. + * @param creatorId - Creator of the group chat. * @param createdAt - Timestamp of creation * @param updatedAt - Timestamp of updation - * @param organization - Organization - * @param status - Status + * @param organization - Organization associated with the group chat. + * @param status - Status of the group chat. */ const groupChatSchema = new Schema( { @@ -39,7 +42,7 @@ const groupChatSchema = new Schema( { type: Schema.Types.ObjectId, ref: "User", - required: true, + required: true, // At least one user is required }, ], messages: [ @@ -61,21 +64,34 @@ const groupChatSchema = new Schema( status: { type: String, required: true, - enum: ["ACTIVE", "BLOCKED", "DELETED"], + enum: ["ACTIVE", "BLOCKED", "DELETED"], // Status must be one of these values default: "ACTIVE", }, }, { - timestamps: true, + timestamps: true, // Automatically manage `createdAt` and `updatedAt` fields }, ); +/** + * Adds logging middleware to the group chat schema. + * Middleware logs changes to group chat documents. + */ createLoggingMiddleware(groupChatSchema, "GroupChat"); +/** + * Creates a Mongoose model for the group chat schema. + * Ensures that we don't create multiple models during testing, which can cause errors. + * + * @returns The GroupChat model. + */ const groupChatModel = (): Model => model("GroupChat", groupChatSchema); -// This syntax is needed to prevent Mongoose OverwriteModelError while running tests. +/** + * Export the GroupChat model. + * This syntax ensures we don't get an OverwriteModelError while running tests. + */ export const GroupChat = (models.GroupChat || groupChatModel()) as ReturnType< typeof groupChatModel >; diff --git a/src/models/GroupChatMessage.ts b/src/models/GroupChatMessage.ts index 639196e153..f3b583bb2b 100644 --- a/src/models/GroupChatMessage.ts +++ b/src/models/GroupChatMessage.ts @@ -3,8 +3,9 @@ import { Schema, model, models } from "mongoose"; import type { InterfaceGroupChat } from "./GroupChat"; import type { InterfaceUser } from "./User"; import { createLoggingMiddleware } from "../libraries/dbLogger"; + /** - * This is an interface that represents a database(MongoDB) document for Group Chat Message. + * Interface representing a document for a group chat message in the database (MongoDB). */ export interface InterfaceGroupChatMessage { _id: Types.ObjectId; @@ -15,14 +16,16 @@ export interface InterfaceGroupChatMessage { messageContent: string; status: string; } + /** - * This represents the schema for a `GroupChatMessage`. - * @param groupChatMessageBelongsTo - This is the association referring to the `GroupChat` model. - * @param sender - Sender of the message. - * @param createdAt - Time stamp of data creation. - * @param updatedAt - Time stamp of data updation. - * @param messageContent - Content of the message. - * @param status - Status + * Mongoose schema for a group chat message. + * Defines the structure of the group chat message document stored in MongoDB. + * @param groupChatMessageBelongsTo - The association referring to the GroupChat model. + * @param sender - The sender of the message. + * @param messageContent - The content of the message. + * @param status - The status of the message (e.g., ACTIVE, BLOCKED, DELETED). + * @param createdAt - The date and time when the message was created. + * @param updatedAt - The date and time when the message was last updated. */ const groupChatMessageSchema = new Schema( { @@ -48,15 +51,25 @@ const groupChatMessageSchema = new Schema( }, }, { - timestamps: true, + timestamps: true, // Automatically adds `createdAt` and `updatedAt` fields }, ); +// Add logging middleware for groupChatMessageSchema createLoggingMiddleware(groupChatMessageSchema, "GroupChatMessage"); +/** + * Function to retrieve or create the Mongoose model for the GroupChatMessage. + * This is necessary to avoid the OverwriteModelError during testing. + * @returns The Mongoose model for the GroupChatMessage. + */ const groupChatMessageModel = (): Model => model("GroupChatMessage", groupChatMessageSchema); -// This syntax is needed to prevent Mongoose OverwriteModelError while running tests. +/** + * The Mongoose model for the GroupChatMessage. + * If the model already exists (e.g., during testing), it uses the existing model. + * Otherwise, it creates a new model. + */ export const GroupChatMessage = (models.GroupChatMessage || groupChatMessageModel()) as ReturnType; diff --git a/src/models/ImageHash.ts b/src/models/ImageHash.ts index 2dff1cb94e..54a22cbc0c 100644 --- a/src/models/ImageHash.ts +++ b/src/models/ImageHash.ts @@ -1,8 +1,9 @@ import type { Types, Model } from "mongoose"; import { Schema, model, models } from "mongoose"; import { createLoggingMiddleware } from "../libraries/dbLogger"; + /** - * This is an interface that represents a database(MongoDB) document for Image Hash. + * Interface representing a document for an image hash in the database (MongoDB). */ export interface InterfaceImageHash { _id: Types.ObjectId; @@ -11,12 +12,14 @@ export interface InterfaceImageHash { numberOfUses: number; status: string; } + /** - * This represents the schema for an `ImageHash`. - * @param hashValue - Hash value of an image. `type: String` - * @param fileName - Image file name. - * @param numberOfUses - Number of times used. - * @param status - Status. + * Mongoose schema for an image hash. + * Defines the structure of the image hash document stored in MongoDB. + * @param hashValue - The hash value of the image. + * @param fileName - The file name of the image. + * @param numberOfUses - The number of times the image hash has been used. + * @param status - The status of the image hash (e.g., ACTIVE, BLOCKED, DELETED). */ const imageHashSchema = new Schema({ hashValue: { @@ -40,12 +43,22 @@ const imageHashSchema = new Schema({ }, }); +// Add logging middleware for imageHashSchema createLoggingMiddleware(imageHashSchema, "ImageHash"); +/** + * Function to retrieve or create the Mongoose model for the ImageHash. + * This is necessary to avoid the OverwriteModelError during testing. + * @returns The Mongoose model for the ImageHash. + */ const imageHashModel = (): Model => model("ImageHash", imageHashSchema); -// This syntax is needed to prevent Mongoose OverwriteModelError while running tests. +/** + * The Mongoose model for the ImageHash. + * If the model already exists (e.g., during testing), it uses the existing model. + * Otherwise, it creates a new model. + */ export const ImageHash = (models.ImageHash || imageHashModel()) as ReturnType< typeof imageHashModel >; diff --git a/src/models/Language.ts b/src/models/Language.ts index a80a5e7f37..671aed737c 100644 --- a/src/models/Language.ts +++ b/src/models/Language.ts @@ -1,8 +1,9 @@ import type { Types, Document, PopulatedDoc, Model } from "mongoose"; import { Schema, model, models } from "mongoose"; import { createLoggingMiddleware } from "../libraries/dbLogger"; + /** - * This is an interface that represents a database document. + * Interface representing a Language Model document in the database (MongoDB). */ export interface InterfaceLanguageModel { lang_code: string; @@ -10,13 +11,14 @@ export interface InterfaceLanguageModel { verified: boolean; createdAt: Date; } + /** - * This schema defines the structure of a Language Model that corresponds to `InterfaceLanguageModel` document. - * which is utilised as an association in the 'languageSchema' schema. - * @param lang_code - Code of the language, for example: en for english. - * @param value - Value. - * @param verified - Language code is verified or not. - * @param createdAt - Time stamp of data creation. + * Mongoose schema for a Language Model. + * Defines the structure of the Language Model document stored in MongoDB. + * @param lang_code - The code of the language (e.g., "en" for English). + * @param value - The value associated with the language. + * @param verified - Indicates if the language code is verified. + * @param createdAt - The date and time when the language model was created. */ const languageModelSchema = new Schema({ lang_code: { @@ -41,8 +43,9 @@ const languageModelSchema = new Schema({ default: Date.now, }, }); + /** - * This is an interface that represents a database(MongoDB) document for Language. + * Interface representing a Language document in the database (MongoDB). */ export interface InterfaceLanguage { _id: Types.ObjectId; @@ -50,11 +53,13 @@ export interface InterfaceLanguage { translation: PopulatedDoc[]; createdAt: Date; } + /** - * This is the structure of a Language Schema that corresponds to `InterfaceLanguage` document. - * @param en - Code for english language. - * @param translation - Association that refers to `LangModel` Schema. - * @param createdAt - Time stamp of data creation. + * Mongoose schema for a Language. + * Defines the structure of the Language document stored in MongoDB. + * @param en - The code for the English language. + * @param translation - An array of Language Model documents associated with this language. + * @param createdAt - The date and time when the language document was created. */ const languageSchema = new Schema({ en: { @@ -71,12 +76,22 @@ const languageSchema = new Schema({ }, }); +// Add logging middleware for languageSchema createLoggingMiddleware(languageSchema, "Language"); +/** + * Function to retrieve or create the Mongoose model for the Language. + * This is necessary to avoid the OverwriteModelError during testing. + * @returns The Mongoose model for the Language. + */ const languageModel = (): Model => model("Language", languageSchema); -// This syntax is needed to prevent Mongoose OverwriteModelError while running tests. +/** + * The Mongoose model for the Language. + * If the model already exists (e.g., during testing), it uses the existing model. + * Otherwise, it creates a new model. + */ export const Language = (models.Language || languageModel()) as ReturnType< typeof languageModel >; diff --git a/src/models/MembershipRequest.ts b/src/models/MembershipRequest.ts index 97455b22ed..86b46c8d1f 100644 --- a/src/models/MembershipRequest.ts +++ b/src/models/MembershipRequest.ts @@ -4,7 +4,7 @@ import type { InterfaceOrganization } from "./Organization"; import type { InterfaceUser } from "./User"; import { createLoggingMiddleware } from "../libraries/dbLogger"; /** - * This is an interface that represents a database(MongoDB) document for Membership Request. + * Represents a database document for Membership Request. */ export interface InterfaceMembershipRequest { _id: Types.ObjectId; @@ -12,11 +12,12 @@ export interface InterfaceMembershipRequest { user: PopulatedDoc | undefined; status: string; } + /** - * This describes the schema for a `MembershipRequest` that corresponds to `InterfaceMembershipRequest` document. - * @param organization - Organization data for which membership request is added. - * @param user - User data who requested membership for an organization. - * @param status - Status. + * Mongoose schema definition for Membership Request documents. + * @param organization - The ID of the organization for which membership is requested. + * @param user - The ID of the user who requested membership. + * @param status - The current status of the membership request. */ const membershipRequestSchema = new Schema({ organization: { @@ -37,14 +38,18 @@ const membershipRequestSchema = new Schema({ }, }); +// Adding logging middleware for Membership Request schema. createLoggingMiddleware(membershipRequestSchema, "MembershipRequest"); +/** + * Mongoose model for Membership Request documents. + */ const membershipRequestModel = (): Model => model( "MembershipRequest", membershipRequestSchema, ); -// This syntax is needed to prevent Mongoose OverwriteModelError while running tests. +// Exporting the Membership Request model while preventing Mongoose OverwriteModelError during tests. export const MembershipRequest = (models.MembershipRequest || membershipRequestModel()) as ReturnType; diff --git a/src/models/Message.ts b/src/models/Message.ts index 53a2addaae..045a5a7c84 100644 --- a/src/models/Message.ts +++ b/src/models/Message.ts @@ -3,8 +3,9 @@ import { Schema, model, models } from "mongoose"; import type { InterfaceGroup } from "./Group"; import type { InterfaceUser } from "./User"; import { createLoggingMiddleware } from "../libraries/dbLogger"; + /** - * This is an interface that represents a database(MongoDB) document for Message. + * Interface representing a document for a message in the database (MongoDB). */ export interface InterfaceMessage { _id: Types.ObjectId; @@ -17,16 +18,18 @@ export interface InterfaceMessage { group: PopulatedDoc; status: string; } + /** - * This describes the schema for a `Message` that corresponds to `InterfaceMessage` document. - * @param text - Message content. - * @param imageUrl - Image URL attached in the message. - * @param videoUrl - Video URL attached in the message. - * @param createdAt - Timestamp of data creation. - * @param creatorId - Message Sender(User), referring to `User` model. - * @param updatedAt - Timestamp of data updation - * @param group - group data, referring to `Group` model. - * @param status - Status. + * Mongoose schema for a Message. + * Defines the structure of the Message document stored in MongoDB. + * @param text - The content of the message. + * @param imageUrl - Optional URL of an image attached to the message. + * @param videoUrl - Optional URL of a video attached to the message. + * @param creatorId - Reference to the User who created the message. + * @param group - Reference to the Group to which the message belongs. + * @param status - The status of the message (e.g., ACTIVE, BLOCKED, DELETED). + * @param createdAt - The date and time when the message was created. + * @param updatedAt - The date and time when the message was last updated. */ const messageSchema = new Schema( { @@ -60,16 +63,26 @@ const messageSchema = new Schema( }, }, { - timestamps: true, + timestamps: true, // Automatically adds `createdAt` and `updatedAt` fields }, ); +// Add logging middleware for messageSchema createLoggingMiddleware(messageSchema, "Message"); +/** + * Function to retrieve or create the Mongoose model for the Message. + * This is necessary to avoid the OverwriteModelError during testing. + * @returns The Mongoose model for the Message. + */ const messageModel = (): Model => model("Message", messageSchema); -// This syntax is needed to prevent Mongoose OverwriteModelError while running tests. +/** + * The Mongoose model for the Message. + * If the model already exists (e.g., during testing), it uses the existing model. + * Otherwise, it creates a new model. + */ export const Message = (models.Message || messageModel()) as ReturnType< typeof messageModel >; diff --git a/src/models/MessageChat.ts b/src/models/MessageChat.ts index 2e337321b9..7510ea8d1a 100644 --- a/src/models/MessageChat.ts +++ b/src/models/MessageChat.ts @@ -3,7 +3,7 @@ import { Schema, model, models } from "mongoose"; import type { InterfaceUser } from "./User"; import { createLoggingMiddleware } from "../libraries/dbLogger"; /** - * This is an interface representing a document for a chat in the database(MongoDB). + * Interface representing a document for a chat in the database (MongoDB). */ export interface InterfaceMessageChat { _id: Types.ObjectId; @@ -15,13 +15,14 @@ export interface InterfaceMessageChat { updatedAt: Date; } /** - * This the structure of a chat - * @param message - Chat message - * @param languageBarrier - Boolean Type - * @param sender - Sender - * @param receiver - Receiver - * @param createdAt - Date when the chat was created - * @param updatedAt - Date when the chat was updated + * Mongoose schema for a Message Chat. + * Defines the structure of the Message Chat document stored in MongoDB. + * @param message - The content of the chat message. + * @param languageBarrier - Indicates if there's a language barrier in the chat. + * @param sender - Reference to the User who sent the chat message. + * @param receiver - Reference to the User who received the chat message. + * @param createdAt - The date and time when the chat was created. + * @param updatedAt - The date and time when the chat was last updated. */ const messageChatSchema = new Schema( { @@ -46,15 +47,25 @@ const messageChatSchema = new Schema( }, }, { - timestamps: true, + timestamps: true, // Automatically adds `createdAt` and `updatedAt` fields }, ); +// Add logging middleware for messageChatSchema createLoggingMiddleware(messageChatSchema, "MessageChat"); +/** + * Function to retrieve or create the Mongoose model for the MessageChat. + * This is necessary to avoid the OverwriteModelError during testing. + * @returns The Mongoose model for the MessageChat. + */ const messageChatModel = (): Model => model("MessageChat", messageChatSchema); -// This syntax is needed to prevent Mongoose OverwriteModelError while running tests. +/** + * The Mongoose model for the MessageChat. + * If the model already exists (e.g., during testing), it uses the existing model. + * Otherwise, it creates a new model. + */ export const MessageChat = (models.MessageChat || messageChatModel()) as ReturnType; diff --git a/src/models/Note.ts b/src/models/Note.ts index 8dcef37fbb..e6d1deed2f 100644 --- a/src/models/Note.ts +++ b/src/models/Note.ts @@ -2,6 +2,9 @@ import type { Model, PopulatedDoc, Types } from "mongoose"; import { Schema, model, models } from "mongoose"; import type { InterfaceUser } from "./User"; +/** + * Represents a note document in the database. + */ export interface InterfaceNote { _id: Types.ObjectId; // Unique identifier for the note. content: string; // Content of the note. @@ -12,6 +15,15 @@ export interface InterfaceNote { agendaItemId: Types.ObjectId; // Reference to the agenda item associated with the note. } +/** + * Mongoose schema definition for Note documents. + * @param content - The content of the note. + * @param createdBy - The ID of the user who created the note. + * @param updatedBy - Optional: The ID of the user who last updated the note. + * @param createdAt - The date when the note was created. + * @param updatedAt - Optional: The date when the note was last updated. + * @param agendaItemId - The ID of the agenda item associated with the note. + */ export const NoteSchema = new Schema({ content: { type: String, @@ -42,9 +54,13 @@ export const NoteSchema = new Schema({ }, }); +/** + * Mongoose model for Note documents. + */ const noteModel = (): Model => model("Note", NoteSchema); +// Exporting the Note model while preventing Mongoose OverwriteModelError during tests. export const NoteModel = (models.Note || noteModel()) as ReturnType< typeof noteModel >; diff --git a/src/models/Organization.ts b/src/models/Organization.ts index 226fdb4beb..e307d615c6 100644 --- a/src/models/Organization.ts +++ b/src/models/Organization.ts @@ -7,11 +7,10 @@ import type { InterfaceMessage } from "./Message"; import type { InterfaceOrganizationCustomField } from "./OrganizationCustomField"; import type { InterfacePost } from "./Post"; import type { InterfaceUser } from "./User"; - import type { InterfaceAdvertisement } from "./Advertisement"; /** - * This is an interface that represents a database(MongoDB) document for Organization. + * Interface representing a document for an Organization in the database (MongoDB). */ export interface InterfaceOrganization { _id: Types.ObjectId; @@ -46,24 +45,31 @@ export interface InterfaceOrganization { funds: PopulatedDoc[]; visibleInSearch: boolean; } + /** - * This describes the schema for a `Organization` that corresponds to `InterfaceOrganization` document. - * @param apiUrl - API URL. - * @param image - Organization image URL. - * @param name - Organization name. - * @param description - Organization description. - * @param address - Organization address, stored as an object. - * @param creatorId - Organization creator, referring to `User` model. - * @param status - Status. - * @param members - Collection of members, each object refer to `User` model. - * @param admins - Collection of organization admins, each object refer to `User` model. - * @param groupChats - Collection of group chats, each object refer to `Message` model. - * @param posts - Collection of Posts in the Organization, each object refer to `Post` model. - * @param membershipRequests - Collection of membership requests in the Organization, each object refer to `MembershipRequest` model. - * @param blockedUsers - Collection of Blocked User in the Organization, each object refer to `User` model. - * @param tags - Collection of tags. - * @param createdAt - Time stamp of data creation. - * @param updatedAt - Time stamp of data updation. + * Mongoose schema for an Organization. + * Defines the structure of the Organization document stored in MongoDB. + * @param apiUrl - API URL associated with the organization. + * @param image - URL of the organization's image. + * @param name - Name of the organization. + * @param description - Description of the organization. + * @param address - Address details of the organization. + * @param creatorId - Creator of the organization, referencing the User model. + * @param status - Status of the organization. + * @param members - Collection of members in the organization, each object referencing the User model. + * @param admins - Collection of admins in the organization, each object referencing the User model. + * @param advertisements - Collection of advertisements associated with the organization, each object referencing the Advertisement model. + * @param groupChats - Collection of group chats associated with the organization, each object referencing the Message model. + * @param posts - Collection of posts associated with the organization, each object referencing the Post model. + * @param pinnedPosts - Collection of pinned posts associated with the organization, each object referencing the Post model. + * @param membershipRequests - Collection of membership requests associated with the organization, each object referencing the MembershipRequest model. + * @param blockedUsers - Collection of blocked users associated with the organization, each object referencing the User model. + * @param customFields - Collection of custom fields associated with the organization, each object referencing the OrganizationCustomField model. + * @param funds - Collection of funds associated with the organization, each object referencing the Fund model. + * @param createdAt - Timestamp of when the organization was created. + * @param updatedAt - Timestamp of when the organization was last updated. + * @param userRegistrationRequired - Indicates if user registration is required for the organization. + * @param visibleInSearch - Indicates if the organization should be visible in search results. */ const organizationSchema = new Schema( { @@ -193,15 +199,25 @@ const organizationSchema = new Schema( ], }, { - timestamps: true, + timestamps: true, // Automatically adds `createdAt` and `updatedAt` fields }, ); +// Add logging middleware for organizationSchema createLoggingMiddleware(organizationSchema, "Organization"); +/** + * Function to retrieve or create the Mongoose model for the Organization. + * This is necessary to avoid the OverwriteModelError during testing. + * @returns The Mongoose model for the Organization. + */ const organizationModel = (): Model => model("Organization", organizationSchema); -// This syntax is needed to prevent Mongoose OverwriteModelError while running tests. +/** + * The Mongoose model for the Organization. + * If the model already exists (e.g., during testing), it uses the existing model. + * Otherwise, it creates a new model. + */ export const Organization = (models.Organization || organizationModel()) as ReturnType; diff --git a/src/models/OrganizationCustomField.ts b/src/models/OrganizationCustomField.ts index daeadb632f..282427b477 100644 --- a/src/models/OrganizationCustomField.ts +++ b/src/models/OrganizationCustomField.ts @@ -2,6 +2,9 @@ import type { Model } from "mongoose"; import mongoose from "mongoose"; import { createLoggingMiddleware } from "../libraries/dbLogger"; +/** + * Represents the structure of an organization custom field in the database. + */ export interface InterfaceOrganizationCustomField { _id: string; organizationId: string; @@ -9,6 +12,12 @@ export interface InterfaceOrganizationCustomField { name: string; } +/** + * Mongoose schema definition for OrganizationCustomField documents. + * @param organizationId - The ID of the organization to which this custom field belongs. + * @param type - The type of the custom field. + * @param name - The name of the custom field. + */ const organizationCustomFieldSchema = new mongoose.Schema({ organizationId: { type: mongoose.Schema.Types.String, @@ -26,6 +35,9 @@ const organizationCustomFieldSchema = new mongoose.Schema({ }, }); +/** + * Middleware to log database operations on the OrganizationCustomField collection. + */ createLoggingMiddleware( organizationCustomFieldSchema, "OrganizationCustomField", diff --git a/src/models/OrganizationTagUser.ts b/src/models/OrganizationTagUser.ts index ad885513f9..467a500df5 100644 --- a/src/models/OrganizationTagUser.ts +++ b/src/models/OrganizationTagUser.ts @@ -3,6 +3,9 @@ import { Schema, model, models } from "mongoose"; import type { InterfaceOrganization } from "./Organization"; import { createLoggingMiddleware } from "../libraries/dbLogger"; +/** + * Interface representing a document for an Organization Tag User in the database (MongoDB). + */ export interface InterfaceOrganizationTagUser { _id: Types.ObjectId; organizationId: PopulatedDoc; @@ -11,9 +14,14 @@ export interface InterfaceOrganizationTagUser { tagColor: string; } -// A User Tag is used for the categorization and the grouping of related users -// Each tag belongs to a particular organization, and is private to the same. -// Each tag can be nested to hold other sub-tags so as to create a heriecheal structure. +/** + * Mongoose schema for an Organization Tag User. + * Defines the structure of the Organization Tag User document stored in MongoDB. + * @param name - Name of the organization tag user. + * @param organizationId - Reference to the organization to which the tag belongs. + * @param parentTagId - Reference to the parent tag (if any) for hierarchical organization. + * @param tagColor - Color associated with the tag. + */ const organizationTagUserSchema = new Schema({ name: { type: String, @@ -37,19 +45,30 @@ const organizationTagUserSchema = new Schema({ }, }); +// Index to ensure unique combination of organizationId, parentOrganizationTagUserId, and name organizationTagUserSchema.index( { organizationId: 1, parentOrganizationTagUserId: 1, name: 1 }, { unique: true }, ); +// Add logging middleware for organizationTagUserSchema createLoggingMiddleware(organizationTagUserSchema, "OrganizationTagUser"); +/** + * Function to retrieve or create the Mongoose model for the Organization Tag User. + * This is necessary to avoid the OverwriteModelError during testing. + * @returns The Mongoose model for the Organization Tag User. + */ const organizationTagUserModel = (): Model => model( "OrganizationTagUser", organizationTagUserSchema, ); -// This syntax is needed to prevent Mongoose OverwriteModelError while running tests. +/** + * The Mongoose model for the Organization Tag User. + * If the model already exists (e.g., during testing), it uses the existing model. + * Otherwise, it creates a new model. + */ export const OrganizationTagUser = (models.OrganizationTagUser || organizationTagUserModel()) as ReturnType; diff --git a/src/models/Plugin.ts b/src/models/Plugin.ts index 74a544ac66..05228b1f85 100644 --- a/src/models/Plugin.ts +++ b/src/models/Plugin.ts @@ -1,8 +1,9 @@ import type { Types, Model } from "mongoose"; import { Schema, model, models } from "mongoose"; import { createLoggingMiddleware } from "../libraries/dbLogger"; + /** - * This is an interface that represents a database(MongoDB) document for Plugin. + * Represents a MongoDB document for Plugin in the database. */ export interface InterfacePlugin { _id: Types.ObjectId; @@ -13,10 +14,11 @@ export interface InterfacePlugin { } /** - * @param pluginName- Name of the plugin preferred having underscores "_",(type: String) - * @param pluginCreatedBy- name of the plugin creator ex.John Doe,(type: String) - * @param pluginDesc- brief description of the plugin and it's features,(type: String) - * @param uninstalledOrgs - list of orgIDs which have disabled the feature on mobile app,(type: String[]) + * Mongoose schema definition for Plugin documents. + * @param pluginName - Name of the plugin preferred having underscores "_". + * @param pluginCreatedBy - Name of the plugin creator (e.g., "John Doe"). + * @param pluginDesc - Brief description of the plugin and its features. + * @param uninstalledOrgs - List of organization IDs which have disabled the feature on mobile app. */ const pluginSchema = new Schema({ @@ -41,6 +43,9 @@ const pluginSchema = new Schema({ ], }); +/** + * Middleware to log database operations on the Plugin collection. + */ createLoggingMiddleware(pluginSchema, "Plugin"); const pluginModel = (): Model => diff --git a/src/models/PluginField.ts b/src/models/PluginField.ts index 44e73f180e..88fcaa497d 100644 --- a/src/models/PluginField.ts +++ b/src/models/PluginField.ts @@ -1,8 +1,9 @@ import type { Types, Model } from "mongoose"; import { Schema, model, models } from "mongoose"; import { createLoggingMiddleware } from "../libraries/dbLogger"; + /** - * This is an interface that represents a database(MongoDB) document for Plugin Field. + * Interface representing a document for a Plugin Field in the database (MongoDB). */ export interface InterfacePluginField { _id: Types.ObjectId; @@ -11,12 +12,14 @@ export interface InterfacePluginField { status: string; createdAt: Date; } + /** - * This describes the schema for a `PluginField` that corresponds to `InterfacePluginField` document. + * Mongoose schema for a Plugin Field. + * Defines the structure of the Plugin Field document stored in MongoDB. * @param key - Plugin key. - * @param value - Value. - * @param status - Status. - * @param createdAt - Time stamp of data creation. + * @param value - Value associated with the plugin key. + * @param status - Status of the plugin field. + * @param createdAt - Timestamp of data creation. */ const pluginFieldSchema = new Schema({ key: { @@ -39,11 +42,21 @@ const pluginFieldSchema = new Schema({ }, }); +// Add logging middleware for pluginFieldSchema createLoggingMiddleware(pluginFieldSchema, "PluginField"); +/** + * Function to retrieve or create the Mongoose model for the Plugin Field. + * This is necessary to avoid the OverwriteModelError during testing. + * @returns The Mongoose model for the Plugin Field. + */ const pluginFieldModel = (): Model => model("PluginField", pluginFieldSchema); -// This syntax is needed to prevent Mongoose OverwriteModelError while running tests. +/** + * The Mongoose model for the Plugin Field. + * If the model already exists (e.g., during testing), it uses the existing model. + * Otherwise, it creates a new model. + */ export const PluginField = (models.PluginField || pluginFieldModel()) as ReturnType; diff --git a/src/models/Post.ts b/src/models/Post.ts index 8bccf6d58d..f3bd86a624 100644 --- a/src/models/Post.ts +++ b/src/models/Post.ts @@ -4,8 +4,9 @@ import mongoosePaginate from "mongoose-paginate-v2"; import type { InterfaceOrganization } from "./Organization"; import type { InterfaceUser } from "./User"; import { createLoggingMiddleware } from "../libraries/dbLogger"; + /** - * This is an interface that represents a database(MongoDB) document for Post. + * Represents a MongoDB document for Post in the database. */ export interface InterfacePost { _id: Types.ObjectId; @@ -23,21 +24,20 @@ export interface InterfacePost { updatedAt: Date; videoUrl: string | undefined | null; } + /** - * This describes the schema for a `Post` that corresponds to `InterfacePost` document. - * @param commentCount - Post comments count. - * @param createdAt - Time stamp of data creation. - * @param creatorId - Post creator, refer to `User` model. - * @param imageUrl - Post attached image URL(if attached). - * @param likeCount - Post likes count. - * @param likedBy - Collection of user liked the post, each object refer to `User` model. - * @param pinned - Post pinned status - * @param organization - Organization data where the post is uploaded, refer to `Organization` model. - * @param status - Status. - * @param text - Post description. - * @param title - Post title. - * @param updatedAt - Time stamp of post updation - * @param videoUrl - Post attached video URL(if attached). + * Mongoose schema definition for Post documents. + * @param text - Content or description of the post. + * @param title - Title of the post. + * @param status - Status of the post. + * @param imageUrl - URL of the attached image, if any. + * @param videoUrl - URL of the attached video, if any. + * @param creatorId - Reference to the user who created the post. + * @param organization - Reference to the organization where the post is uploaded. + * @param likedBy - Array of users who liked the post. + * @param likeCount - Number of likes on the post. + * @param commentCount - Number of comments on the post. + * @param pinned - Indicates if the post is pinned. */ const postSchema = new Schema( { @@ -96,12 +96,16 @@ const postSchema = new Schema( }, ); +// Apply pagination plugin to the schema postSchema.plugin(mongoosePaginate); +// Ensure indexing for organization field for efficient querying postSchema.index({ organization: 1 }, { unique: false }); +// Middleware to log database operations on the Post collection createLoggingMiddleware(postSchema, "Post"); +// Define and export the model directly const postModel = (): PaginateModel => model>("Post", postSchema); diff --git a/src/models/RecurrenceRule.ts b/src/models/RecurrenceRule.ts index b00795cd93..28bb65f15f 100644 --- a/src/models/RecurrenceRule.ts +++ b/src/models/RecurrenceRule.ts @@ -4,7 +4,7 @@ import type { InterfaceEvent } from "./Event"; import type { InterfaceOrganization } from "./Organization"; /** - * This is an interface representing a document for a recurrence rule in the database(MongoDB). + * Enumeration for recurrence frequencies. */ export enum Frequency { @@ -14,6 +14,9 @@ export enum Frequency { DAILY = "DAILY", } +/** + * Enumeration for weekdays. + */ export enum WeekDays { SUNDAY = "SUNDAY", MONDAY = "MONDAY", @@ -24,6 +27,9 @@ export enum WeekDays { SATURDAY = "SATURDAY", } +/** + * Interface representing a document for a recurrence rule in the database (MongoDB). + */ export interface InterfaceRecurrenceRule { _id: Types.ObjectId; organizationId: PopulatedDoc; @@ -110,6 +116,10 @@ const recurrenceRuleSchema = new Schema( const recurrenceRuleModel = (): Model => model("RecurrenceRule", recurrenceRuleSchema); -// This syntax is needed to prevent Mongoose OverwriteModelError while running tests. +/** + * The Mongoose model for the Recurrence Rule. + * If the model already exists (e.g., during testing), it uses the existing model. + * Otherwise, it creates a new model. + */ export const RecurrenceRule = (models.RecurrenceRule || recurrenceRuleModel()) as ReturnType; diff --git a/src/models/SampleData.ts b/src/models/SampleData.ts index ddf8cb9c49..8f09cc609d 100644 --- a/src/models/SampleData.ts +++ b/src/models/SampleData.ts @@ -2,6 +2,9 @@ import type { Model, Document } from "mongoose"; import { Schema, model, models } from "mongoose"; import { createLoggingMiddleware } from "../libraries/dbLogger"; +/** + * Interface representing a document for sample data in the database (MongoDB). + */ export interface InterfaceSampleData extends Document { documentId: string; collectionName: @@ -13,6 +16,10 @@ export interface InterfaceSampleData extends Document { | "AppUserProfile"; } +/** + * Mongoose schema for sample data. + * Defines the structure of the sample data document stored in MongoDB. + */ const sampleDataSchema = new Schema({ documentId: { type: String, @@ -25,10 +32,21 @@ const sampleDataSchema = new Schema({ }, }); +// Create logging middleware for sampleDataSchema createLoggingMiddleware(sampleDataSchema, "SampleData"); +/** + * Function to retrieve or create the Mongoose model for Sample Data. + * This prevents the OverwriteModelError during testing. + * @returns The Mongoose model for Sample Data. + */ const sampleDataModel = (): Model => model("SampleData", sampleDataSchema); +/** + * The Mongoose model for Sample Data. + * If the model already exists (e.g., during testing), it uses the existing model. + * Otherwise, it creates a new model. + */ export const SampleData = (models.SampleData || sampleDataModel()) as ReturnType; diff --git a/src/models/TagUser.ts b/src/models/TagUser.ts index e642594274..cd2ccb8d0c 100644 --- a/src/models/TagUser.ts +++ b/src/models/TagUser.ts @@ -4,14 +4,21 @@ import type { InterfaceOrganizationTagUser } from "./OrganizationTagUser"; import type { InterfaceUser } from "./User"; import { createLoggingMiddleware } from "../libraries/dbLogger"; +/** + * Represents a MongoDB document for TagUser in the database. + */ export interface InterfaceTagUser { _id: Types.ObjectId; userId: PopulatedDoc; tagId: PopulatedDoc; tagColor: PopulatedDoc; } - -// Relational schema used to keep track of assigned tags to users +/** + * Mongoose schema definition for TagUser documents. + * @param userId - Reference to the user associated with the tag. + * @param tagId - Reference to the tag associated with the user. + * @param tagColor - Color associated with the tag. + */ const tagUserSchema = new Schema({ userId: { type: Schema.Types.ObjectId, @@ -30,10 +37,13 @@ const tagUserSchema = new Schema({ }, }); +// Ensure uniqueness of tag assignments per user tagUserSchema.index({ userId: 1, tagId: 1 }, { unique: true }); +// Middleware to log database operations on the TagUser collection createLoggingMiddleware(tagUserSchema, "TagUser"); +// Define and export the model directly const tagUserModel = (): Model => model("TagUser", tagUserSchema); diff --git a/src/models/User.ts b/src/models/User.ts index 9d5f4341ba..a2c22cc7c4 100644 --- a/src/models/User.ts +++ b/src/models/User.ts @@ -9,7 +9,7 @@ import type { InterfaceMembershipRequest } from "./MembershipRequest"; import type { InterfaceOrganization } from "./Organization"; /** - * This is an interface that represents a database(MongoDB) document for User. + * Represents a MongoDB document for User in the database. */ export interface InterfaceUser { _id: Types.ObjectId; @@ -52,35 +52,29 @@ export interface InterfaceUser { updatedAt: Date; } -/** - * This describes the schema for a `User` that corresponds to `InterfaceUser` document. - * @param appUserProfileId - AppUserProfile id of the User - * @param address - User address - - * @param birthDate - User Date of birth - * @param createdAt - Time stamp of data creation. - - * @param educationGrade - User highest education degree - * @param email - User email id. - * @param employmentStatus - User employment status - - * @param firstName - User First Name. - * @param gender - User gender - * @param image - User Image URL. - * @param joinedOrganizations - Collection of the organization where user is the member, each object refer to `Organization` model. - * @param lastName - User Last Name. - * @param maritalStatus - User marital status - * @param membershipRequests - Collections of the membership request, each object refer to `MembershipRequest` model. - * @param organizationsBlockedBy - Collections of organizations where user is blocked, each object refer to `Organization` model. - * @param password - User hashed password. - * @param phone - User contact numbers, for mobile, home and work - - * @param registeredEvents - Collection of user registered Events, each object refer to `Event` model. - * @param status - Status - * - - * @param updatedAt - Timestamp of data updation +/** + * Mongoose schema definition for User documents. + * @param appUserProfileId - Reference to the user's app profile. + * @param address - User's address details. + * @param birthDate - User's date of birth. + * @param createdAt - Timestamp of when the user was created. + * @param educationGrade - User's highest education grade. + * @param email - User's email address (validated as an email). + * @param employmentStatus - User's employment status. + * @param firstName - User's first name. + * @param gender - User's gender. + * @param image - URL to the user's image. + * @param joinedOrganizations - Organizations the user has joined. + * @param lastName - User's last name. + * @param maritalStatus - User's marital status. + * @param membershipRequests - Membership requests made by the user. + * @param organizationsBlockedBy - Organizations that have blocked the user. + * @param password - User's hashed password. + * @param phone - User's contact numbers (home, mobile, work). + * @param registeredEvents - Events the user has registered for. + * @param status - User's status (ACTIVE, BLOCKED, DELETED). + * @param updatedAt - Timestamp of when the user was last updated. */ const userSchema = new Schema( { @@ -117,7 +111,6 @@ const userSchema = new Schema( birthDate: { type: Date, }, - educationGrade: { type: String, enum: [ @@ -232,6 +225,7 @@ const userSchema = new Schema( userSchema.plugin(mongoosePaginate); +// Create and export the User model const userModel = (): PaginateModel => model>("User", userSchema); diff --git a/src/models/UserCustomData.ts b/src/models/UserCustomData.ts index 1c90d57e69..f4198255fd 100644 --- a/src/models/UserCustomData.ts +++ b/src/models/UserCustomData.ts @@ -3,7 +3,7 @@ import mongoose, { model, models } from "mongoose"; import { createLoggingMiddleware } from "../libraries/dbLogger"; /** - * This is an interface representing a document for custom field in the database(MongoDB). + * Interface representing a document for custom field in the database (MongoDB). */ export interface InterfaceUserCustomData { _id: string; @@ -38,11 +38,21 @@ const userCustomDataSchema = new mongoose.Schema({ }, }); +// Create logging middleware for userCustomDataSchema createLoggingMiddleware(userCustomDataSchema, "UserCustomData"); +/** + * Function to retrieve or create the Mongoose model for User Custom Data. + * This prevents the OverwriteModelError during testing. + * @returns The Mongoose model for User Custom Data. + */ const userCustomData = (): Model => model("UserCustomData", userCustomDataSchema); -// This syntax is needed to prevent Mongoose OverwriteModelError while running tests. +/** + * The Mongoose model for User Custom Data. + * If the model already exists (e.g., during testing), it uses the existing model. + * Otherwise, it creates a new model. + */ export const UserCustomData = (models.UserCustomData || userCustomData()) as ReturnType; diff --git a/src/models/Venue.ts b/src/models/Venue.ts index 6e81a76417..1250811a7e 100644 --- a/src/models/Venue.ts +++ b/src/models/Venue.ts @@ -12,14 +12,13 @@ export interface InterfaceVenue { } /** - * This describes the schema for a Venue that corresponds to InterfaceVenue document. + * Mongoose schema definition for Venue documents. * @param name - Name of the venue. * @param description - Description of the venue. * @param capacity - Maximum capacity of the venue. - * @param imageUrl - Image URL(if attached) of the venue. - * @param organization - Organization in which the venue belongs. + * @param imageUrl - Image URL (if attached) of the venue. + * @param organization - Reference to the organization that owns the venue. */ - const venueSchema = new Schema({ name: { type: String, @@ -43,6 +42,7 @@ const venueSchema = new Schema({ }, }); +// Create and export the Venue model const venueModel = (): Model => model("Venue", venueSchema); diff --git a/src/models/userFamily.ts b/src/models/userFamily.ts index a9d8577ea4..b1a17598ed 100644 --- a/src/models/userFamily.ts +++ b/src/models/userFamily.ts @@ -1,10 +1,10 @@ import type { PopulatedDoc, Types, Document, Model } from "mongoose"; import { Schema, model, models } from "mongoose"; import type { InterfaceUser } from "./User"; + /** - * This is an interface that represents a database(MongoDB) document for Family. + * Interface representing a MongoDB document for User Family. */ - export interface InterfaceUserFamily { _id: Types.ObjectId; title: string; @@ -14,13 +14,11 @@ export interface InterfaceUserFamily { } /** - * @param title - Name of the user Family (type: String) - * Description: Name of the user Family. - */ - -/** - * @param users - Members associated with the user Family (type: String) - * Description: Members associated with the user Family. + * Mongoose schema definition for User Family documents. + * @param title - Name of the user Family. + * @param users - Members associated with the user Family. + * @param admins - Admins of the user Family. + * @param creator - Creator of the user Family. */ const userFamilySchema = new Schema({ title: { @@ -48,6 +46,7 @@ const userFamilySchema = new Schema({ }, }); +// Create and export the UserFamily model const userFamilyModel = (): Model => model("UserFamily", userFamilySchema); diff --git a/src/resolvers/ActionItem/actionItemCategory.ts b/src/resolvers/ActionItem/actionItemCategory.ts index 146d836017..47683a0203 100644 --- a/src/resolvers/ActionItem/actionItemCategory.ts +++ b/src/resolvers/ActionItem/actionItemCategory.ts @@ -1,6 +1,11 @@ import type { ActionItemResolvers } from "../../types/generatedGraphQLTypes"; import { ActionItemCategory } from "../../models"; +/** + * Resolver function to fetch the category of an action item. + * @param parent - The parent object containing the action item data. + * @returns The category of the action item found in the database. + */ export const actionItemCategory: ActionItemResolvers["actionItemCategory"] = async (parent) => { return ActionItemCategory.findOne({ diff --git a/src/resolvers/ActionItem/assignee.ts b/src/resolvers/ActionItem/assignee.ts index c3f3d13090..22c4c9a560 100644 --- a/src/resolvers/ActionItem/assignee.ts +++ b/src/resolvers/ActionItem/assignee.ts @@ -1,6 +1,20 @@ import type { ActionItemResolvers } from "../../types/generatedGraphQLTypes"; import { User } from "../../models"; +/** + * Resolver function for the `assignee` field of an `ActionItem`. + * + * This function fetches the user who is assigned to a specific action item. + * + * @param parent - The parent object representing the action item. It contains information about the action item, including the ID of the user assigned to it. + * @returns A promise that resolves to the user document found in the database. This document represents the user assigned to the action item. + * + * @example + * If the action item with an ID of `123` is assigned to a user with an ID of `456`, this resolver will find the user with the ID `456` in the database and return their information. + * + * @see User - The User model used to interact with the users collection in the database. + * @see ActionItemResolvers - The type definition for the resolvers of the ActionItem fields. + */ export const assignee: ActionItemResolvers["assignee"] = async (parent) => { return User.findOne({ _id: parent.assigneeId, diff --git a/src/resolvers/ActionItem/assigner.ts b/src/resolvers/ActionItem/assigner.ts index 1668d3b4a3..7b763a06d4 100644 --- a/src/resolvers/ActionItem/assigner.ts +++ b/src/resolvers/ActionItem/assigner.ts @@ -1,6 +1,17 @@ import type { ActionItemResolvers } from "../../types/generatedGraphQLTypes"; import { User } from "../../models"; +/** + * Resolver function for the `assigner` field of an `ActionItem`. + * + * This function fetches the user who is the assigner of a given action item. + * It uses the `assignerId` field from the parent `ActionItem` object to find the corresponding user in the database. + * The user details are then returned in a plain JavaScript object format. + * + * @param parent - The parent `ActionItem` object. This contains the `assignerId` field, which is used to find the user. + * @returns A promise that resolves to the user object found in the database, or `null` if no user is found. + * + */ export const assigner: ActionItemResolvers["assigner"] = async (parent) => { return User.findOne({ _id: parent.assignerId, diff --git a/src/resolvers/ActionItem/creator.ts b/src/resolvers/ActionItem/creator.ts index 70dbf78957..f286ba0d93 100644 --- a/src/resolvers/ActionItem/creator.ts +++ b/src/resolvers/ActionItem/creator.ts @@ -1,6 +1,26 @@ import type { ActionItemResolvers } from "../../types/generatedGraphQLTypes"; import { User } from "../../models"; +/** + * Resolver function for the `creator` field of an `ActionItem`. + * + * This function fetches the user who is the creator of a given action item. + * It uses the `creatorId` field from the parent `ActionItem` object to find the corresponding user in the database. + * The user details are then returned in a plain JavaScript object format. + * + * @param parent - The parent `ActionItem` object. This contains the `creatorId` field, which is used to find the user. + * @returns A promise that resolves to the user object found in the database, or `null` if no user is found. + * + * @example + * ```typescript + * const actionItem = { + * creatorId: "60d0fe4f5311236168a109cb" + * }; + * const user = await creator(actionItem); + * console.log(user); + * // Output might be: { _id: "60d0fe4f5311236168a109cb", name: "Jane Doe", email: "jane.doe@example.com" } + * ``` + */ export const creator: ActionItemResolvers["creator"] = async (parent) => { return User.findOne({ _id: parent.creatorId, diff --git a/src/resolvers/ActionItem/event.ts b/src/resolvers/ActionItem/event.ts index e79144e5a9..b4584d964f 100644 --- a/src/resolvers/ActionItem/event.ts +++ b/src/resolvers/ActionItem/event.ts @@ -1,6 +1,30 @@ import type { ActionItemResolvers } from "../../types/generatedGraphQLTypes"; import { Event } from "../../models"; +/** + * Resolver function for the `event` field of an `ActionItem`. + * + * This function retrieves the event associated with a specific action item. + * + * @param parent - The parent object representing the action item. It contains information about the action item, including the ID of the associated event. + * @returns A promise that resolves to the event document found in the database. This document represents the event associated with the action item. + * + * @example + * Here's how you might use this resolver in your GraphQL schema: + * + * ```graphql + * type ActionItem { + * event: Event + * # other fields... + * } + * ``` + * + * @example + * If the action item with an ID of `123` is associated with an event with an ID of `789`, this resolver will find the event with the ID `789` in the database and return its information. + * + * @see Event - The Event model used to interact with the events collection in the database. + * @see ActionItemResolvers - The type definition for the resolvers of the ActionItem fields. + */ export const event: ActionItemResolvers["event"] = async (parent) => { return Event.findOne({ _id: parent.eventId, diff --git a/src/resolvers/ActionItemCategory/creator.ts b/src/resolvers/ActionItemCategory/creator.ts index 8ccffc6a6a..e607824a11 100644 --- a/src/resolvers/ActionItemCategory/creator.ts +++ b/src/resolvers/ActionItemCategory/creator.ts @@ -1,6 +1,17 @@ import type { ActionItemCategoryResolvers } from "../../types/generatedGraphQLTypes"; import { User } from "../../models"; +/** + * Resolver function for the `creator` field of an `ActionItemCategory`. + * + * This function retrieves the user who created a specific action item category. + * + * @param parent - The parent object representing the action item category. It contains information about the action item category, including the ID of the user who created it. + * @returns A promise that resolves to the user document found in the database. This document represents the user who created the action item category. + * + * @see User - The User model used to interact with the users collection in the database. + * @see ActionItemCategoryResolvers - The type definition for the resolvers of the ActionItemCategory fields. + */ export const creator: ActionItemCategoryResolvers["creator"] = async ( parent, ) => { diff --git a/src/resolvers/ActionItemCategory/organization.ts b/src/resolvers/ActionItemCategory/organization.ts index 3981d6cb65..5c800a5ada 100644 --- a/src/resolvers/ActionItemCategory/organization.ts +++ b/src/resolvers/ActionItemCategory/organization.ts @@ -1,6 +1,26 @@ import type { ActionItemCategoryResolvers } from "../../types/generatedGraphQLTypes"; import { Organization } from "../../models"; +/** + * Resolver function for the `organization` field of an `ActionItemCategory`. + * + * This function fetches the organization associated with a given action item category. + * It uses the `organizationId` field from the parent `ActionItemCategory` object to find the corresponding organization in the database. + * The organization details are then returned in a plain JavaScript object format. + * + * @param parent - The parent `ActionItemCategory` object. This contains the `organizationId` field, which is used to find the organization. + * @returns A promise that resolves to the organization object found in the database, or `null` if no organization is found. + * + * @example + * ```typescript + * const actionItemCategory = { + * organizationId: "60d0fe4f5311236168a109cc" + * }; + * const organization = await organization(actionItemCategory); + * console.log(organization); + * // Output might be: { _id: "60d0fe4f5311236168a109cc", name: "Tech Corp", address: "123 Tech Lane" } + * ``` + */ export const organization: ActionItemCategoryResolvers["organization"] = async ( parent, ) => { diff --git a/src/resolvers/Advertisement/organization.ts b/src/resolvers/Advertisement/organization.ts index fe165a8f4e..51e49d0f68 100644 --- a/src/resolvers/Advertisement/organization.ts +++ b/src/resolvers/Advertisement/organization.ts @@ -1,6 +1,17 @@ import type { AdvertisementResolvers } from "../../types/generatedGraphQLTypes"; import { Organization } from "../../models"; +/** + * Resolver function for the `organization` field of an `Advertisement`. + * + * This function fetches the organization associated with a given advertisement. + * It uses the `organizationId` field from the parent `Advertisement` object to find the corresponding organization in the database. + * The organization details are then returned in a plain JavaScript object format. + * + * @param parent - The parent `Advertisement` object. This contains the `organizationId` field, which is used to find the organization. + * @returns A promise that resolves to the organization object found in the database, or `null` if no organization is found. + * + */ export const organization: AdvertisementResolvers["organization"] = async ( parent, ) => { diff --git a/src/resolvers/AgendaCategory/createdBy.ts b/src/resolvers/AgendaCategory/createdBy.ts index 98924a6ddd..15858d6cf9 100644 --- a/src/resolvers/AgendaCategory/createdBy.ts +++ b/src/resolvers/AgendaCategory/createdBy.ts @@ -1,5 +1,18 @@ import type { AgendaCategoryResolvers } from "../../types/generatedGraphQLTypes"; import { User } from "../../models"; + +/** + * Resolver function for the `createdBy` field of an `AgendaCategory`. + * + * This function retrieves the user who created a specific agenda category. + * + * @param parent - The parent object representing the agenda category. It contains information about the agenda category, including the ID of the user who created it. + * @returns A promise that resolves to the user document found in the database. This document represents the user who created the agenda category. + * + * @see User - The User model used to interact with the users collection in the database. + * @see AgendaCategoryResolvers - The type definition for the resolvers of the AgendaCategory fields. + * + */ //@ts-expect-error - type error export const createdBy: AgendaCategoryResolvers["createdBy"] = async ( parent, diff --git a/src/resolvers/AgendaCategory/organization.ts b/src/resolvers/AgendaCategory/organization.ts index de292433c3..8ec02fd33c 100644 --- a/src/resolvers/AgendaCategory/organization.ts +++ b/src/resolvers/AgendaCategory/organization.ts @@ -1,5 +1,17 @@ import type { AgendaCategoryResolvers } from "../../types/generatedGraphQLTypes"; import { Organization } from "../../models"; + +/** + * Resolver function for the `organization` field of an `AgendaCategory`. + * + * This function fetches the organization associated with a given agenda category. + * It uses the `organizationId` field from the parent `AgendaCategory` object to find the corresponding organization in the database. + * The organization details are then returned in a plain JavaScript object format. + * + * @param parent - The parent `AgendaCategory` object. This contains the `organizationId` field, which is used to find the organization. + * @returns A promise that resolves to the organization object found in the database, or `null` if no organization is found. + */ + //@ts-expect-error - type error export const organization: AgendaCategoryResolvers["organization"] = async ( parent, diff --git a/src/resolvers/AgendaCategory/updatedBy.ts b/src/resolvers/AgendaCategory/updatedBy.ts index 12bf122f8b..fd949c595f 100644 --- a/src/resolvers/AgendaCategory/updatedBy.ts +++ b/src/resolvers/AgendaCategory/updatedBy.ts @@ -1,6 +1,21 @@ import type { AgendaCategoryResolvers } from "../../types/generatedGraphQLTypes"; import { User } from "../../models"; +/** + * Resolver function for the `updatedBy` field of an `AgendaCategory`. + * + * This function retrieves the user who last updated a specific agenda category. + * + * @param parent - The parent object representing the agenda category. It contains information about the agenda category, including the ID of the user who last updated it. + * @returns A promise that resolves to the user document found in the database. This document represents the user who last updated the agenda category. + * + * @see User - The User model used to interact with the users collection in the database. + * @see AgendaCategoryResolvers - The type definition for the resolvers of the AgendaCategory fields. + * + * ```typescript + * return User.findOne({ _id: parent.updatedBy }).lean(); + * ``` + */ export const updatedBy: AgendaCategoryResolvers["updatedBy"] = async ( parent, ) => { diff --git a/src/resolvers/AgendaItem/Users.ts b/src/resolvers/AgendaItem/Users.ts index 93810885c1..df57368d8c 100644 --- a/src/resolvers/AgendaItem/Users.ts +++ b/src/resolvers/AgendaItem/Users.ts @@ -1,6 +1,18 @@ import type { AgendaItemResolvers } from "../../types/generatedGraphQLTypes"; import { User } from "../../models"; +/** + * Resolver function for the `users` field of an `AgendaItem`. + * + * This function retrieves the users associated with a specific agenda item. + * + * @param parent - The parent object representing the agenda item. It contains information about the agenda item, including the IDs of the users associated with it. + * @returns A promise that resolves to the user documents found in the database. These documents represent the users associated with the agenda item. + * + * @see User - The User model used to interact with the users collection in the database. + * @see AgendaItemResolvers - The type definition for the resolvers of the AgendaItem fields. + * + */ export const users: AgendaItemResolvers["users"] = async (parent) => { const userIds = parent.users; // Assuming parent.users is an array of user ids const users = await User.find({ _id: { $in: userIds } }); // Assuming User.find() returns a promise diff --git a/src/resolvers/AgendaItem/categories.ts b/src/resolvers/AgendaItem/categories.ts index 8e7f155c0a..464b78bda0 100644 --- a/src/resolvers/AgendaItem/categories.ts +++ b/src/resolvers/AgendaItem/categories.ts @@ -1,6 +1,18 @@ import type { AgendaItemResolvers } from "../../types/generatedGraphQLTypes"; import { AgendaCategoryModel } from "../../models"; -//@ts-expect-error - type error + +/** + * Resolver function for the `categories` field of an `AgendaItem`. + * + * This function retrieves the categories associated with a specific agenda item. + * + * @param parent - The parent object representing the agenda item. It contains a list of category IDs associated with it. + * @returns A promise that resolves to an array of category documents found in the database. These documents represent the categories associated with the agenda item. + * + * @see AgendaCategoryModel - The model used to interact with the categories collection in the database. + * @see AgendaItemResolvers - The type definition for the resolvers of the AgendaItem fields. + * + */ export const categories: AgendaItemResolvers["categories"] = async (parent) => { const relatedCategoryIds = parent.categories; diff --git a/src/resolvers/AgendaItem/createdBy.ts b/src/resolvers/AgendaItem/createdBy.ts index fd1f09e670..3323c72324 100644 --- a/src/resolvers/AgendaItem/createdBy.ts +++ b/src/resolvers/AgendaItem/createdBy.ts @@ -1,7 +1,18 @@ import type { AgendaItemResolvers } from "../../types/generatedGraphQLTypes"; import { User } from "../../models"; -//@ts-expect-error - type error +/** + * Resolver function for the `createdBy` field of an `AgendaItem`. + * + * This function retrieves the user who created a specific agenda item. + * It uses the `createdBy` field from the parent `AgendaItem` object to find the corresponding user in the database. + * The user details are then returned as a plain JavaScript object. + * + * @param parent - The parent `AgendaItem` object. This contains the `createdBy` field, which is used to query the user. + * @returns A promise that resolves to the user object found in the database, or `null` if no user is found. + * + */ +//@ts-expect-error - type error export const createdBy: AgendaItemResolvers["createdBy"] = async (parent) => { return User.findOne(parent.createdBy).lean(); }; diff --git a/src/resolvers/AgendaItem/organization.ts b/src/resolvers/AgendaItem/organization.ts index 0baa962844..3e0632d757 100644 --- a/src/resolvers/AgendaItem/organization.ts +++ b/src/resolvers/AgendaItem/organization.ts @@ -1,5 +1,18 @@ import type { AgendaItemResolvers } from "../../types/generatedGraphQLTypes"; import { Organization } from "../../models"; + +/** + * Resolver function for the `organization` field of an `AgendaItem`. + * + * This function retrieves the organization associated with a specific agenda item. + * + * @param parent - The parent object representing the agenda item. It contains information about the agenda item, including the ID of the organization it is associated with. + * @returns A promise that resolves to the organization document found in the database. This document represents the organization associated with the agenda item. + * + * @see Organization - The Organization model used to interact with the organizations collection in the database. + * @see AgendaItemResolvers - The type definition for the resolvers of the AgendaItem fields. + * + */ //@ts-expect-error - type error export const organization: AgendaItemResolvers["organization"] = async ( diff --git a/src/resolvers/AgendaItem/relatedEvent.ts b/src/resolvers/AgendaItem/relatedEvent.ts index 1a0a41794a..1ef36bc56a 100644 --- a/src/resolvers/AgendaItem/relatedEvent.ts +++ b/src/resolvers/AgendaItem/relatedEvent.ts @@ -1,6 +1,18 @@ import type { AgendaItemResolvers } from "../../types/generatedGraphQLTypes"; import { Event } from "../../models"; +/** + * Resolver function for the `relatedEvent` field of an `AgendaItem`. + * + * This function retrieves the event related to a specific agenda item. + * + * @param parent - The parent object representing the agenda item. It contains information about the agenda item, including the ID of the related event. + * @returns A promise that resolves to the event document found in the database. This document represents the event related to the agenda item. + * + * @see Event - The Event model used to interact with the events collection in the database. + * @see AgendaItemResolvers - The type definition for the resolvers of the AgendaItem fields. + * + */ export const relatedEvent: AgendaItemResolvers["relatedEvent"] = async ( parent, ) => { diff --git a/src/resolvers/AgendaItem/updatedBy.ts b/src/resolvers/AgendaItem/updatedBy.ts index 84a0e4ea78..c3706f4274 100644 --- a/src/resolvers/AgendaItem/updatedBy.ts +++ b/src/resolvers/AgendaItem/updatedBy.ts @@ -1,5 +1,18 @@ import type { AgendaItemResolvers } from "../../types/generatedGraphQLTypes"; import { User } from "../../models"; + +/** + * Resolver function for the `updatedBy` field of an `AgendaItem`. + * + * This function retrieves the user who last updated a specific agenda item. + * + * @param parent - The parent object representing the agenda item. It contains information about the agenda item, including the ID of the user who last updated it. + * @returns A promise that resolves to the user document found in the database. This document represents the user who last updated the agenda item. + * + * @see User - The User model used to interact with the users collection in the database. + * @see AgendaItemResolvers - The type definition for the resolvers of the AgendaItem fields. + * + */ //@ts-expect-error - type error export const updatedBy: AgendaItemResolvers["updatedBy"] = async (parent) => { diff --git a/src/resolvers/AgendaSection/createdBy.ts b/src/resolvers/AgendaSection/createdBy.ts index f5106c8820..40e7e3c4a0 100644 --- a/src/resolvers/AgendaSection/createdBy.ts +++ b/src/resolvers/AgendaSection/createdBy.ts @@ -1,6 +1,18 @@ import type { AgendaSectionResolvers } from "../../types/generatedGraphQLTypes"; import { User } from "../../models"; +/** + * Resolver function for the `createdBy` field of an `AgendaSection`. + * + * This function retrieves the user who created a specific agenda section. + * + * @param parent - The parent object representing the agenda section. It contains information about the agenda section, including the ID of the user who created it. + * @returns A promise that resolves to the user document found in the database. This document represents the user who created the agenda section. + * + * @see User - The User model used to interact with the users collection in the database. + * @see AgendaSectionResolvers - The type definition for the resolvers of the AgendaSection fields. + * + */ export const createdBy: AgendaSectionResolvers["createdBy"] = async ( parent, ) => { diff --git a/src/resolvers/AgendaSection/index.ts b/src/resolvers/AgendaSection/index.ts index 9fb981b9c0..3b242a9d94 100644 --- a/src/resolvers/AgendaSection/index.ts +++ b/src/resolvers/AgendaSection/index.ts @@ -3,6 +3,17 @@ import { createdBy } from "./createdBy"; import { items } from "./items"; import { relatedEvent } from "./relatedEvent"; +/** + * Resolver function for the `AgendaSection` type. + * + * This resolver is used to resolve the fields of an `AgendaSection` type. + * + * @see relatedEvent - The resolver function for the `relatedEvent` field of an `AgendaSection`. + * @see items - The resolver function for the `items` field of an `AgendaSection`. + * @see createdBy - The resolver function for the `createdBy` field of an `AgendaSection`. + * @see AgendaSectionResolvers - The type definition for the resolvers of the AgendaSection fields. + * + */ export const AgendaSection: AgendaSectionResolvers = { relatedEvent, items, diff --git a/src/resolvers/AgendaSection/items.ts b/src/resolvers/AgendaSection/items.ts index 73f58e10aa..4f8fe85dcf 100644 --- a/src/resolvers/AgendaSection/items.ts +++ b/src/resolvers/AgendaSection/items.ts @@ -1,6 +1,19 @@ import type { AgendaSectionResolvers } from "../../types/generatedGraphQLTypes"; import { AgendaItemModel } from "../../models"; +/** + * Resolver function for the `items` field of an `AgendaSection`. + * + * This function retrieves the agenda items associated with a specific agenda section. + * + * @param parent - The parent object representing the agenda section. It contains information about the agenda section, including the IDs of the agenda items associated with it. + * @returns A promise that resolves to the agenda item documents found in the database. These documents represent the agenda items associated with the agenda section. + * + * @see AgendaItemModel - The AgendaItem model used to interact with the agenda items collection in the database. + * @see AgendaSectionResolvers - The type definition for the resolvers of the AgendaSection fields. + * + */ + export const items: AgendaSectionResolvers["items"] = async (parent) => { const relatedAgendaItemIds = parent.items; diff --git a/src/resolvers/AgendaSection/relatedEvent.ts b/src/resolvers/AgendaSection/relatedEvent.ts index 7c60755d67..0fd7adc161 100644 --- a/src/resolvers/AgendaSection/relatedEvent.ts +++ b/src/resolvers/AgendaSection/relatedEvent.ts @@ -1,6 +1,19 @@ import type { AgendaSectionResolvers } from "../../types/generatedGraphQLTypes"; import { Event } from "../../models"; +/** + * Resolver function for the `relatedEvent` field of an `AgendaSection`. + * + * This function retrieves the event related to a specific agenda section. + * + * @param parent - The parent object representing the agenda section. It contains information about the agenda section, including the ID of the related event. + * @returns A promise that resolves to the event document found in the database. This document represents the event related to the agenda section. + * + * @see Event - The Event model used to interact with the events collection in the database. + * @see AgendaSectionResolvers - The type definition for the resolvers of the AgendaSection fields. + * + */ + export const relatedEvent: AgendaSectionResolvers["relatedEvent"] = async ( parent, ) => { diff --git a/src/resolvers/CheckIn/event.ts b/src/resolvers/CheckIn/event.ts index 39bb0ba264..b6f8af20db 100644 --- a/src/resolvers/CheckIn/event.ts +++ b/src/resolvers/CheckIn/event.ts @@ -1,6 +1,18 @@ import type { CheckInResolvers } from "../../types/generatedGraphQLTypes"; import { EventAttendee } from "../../models"; +/** + * Resolver function for the `event` field of a `CheckIn`. + * + * This function retrieves the event associated with a specific check-in. + * + * @param parent - The parent object representing the check-in. It contains information about the check-in, including the ID of the event attendee it is associated with. + * @returns A promise that resolves to the event document found in the database. This document represents the event associated with the check-in. + * + * @see EventAttendee - The EventAttendee model used to interact with the event attendees collection in the database. + * @see CheckInResolvers - The type definition for the resolvers of the CheckIn fields. + * + */ export const event: CheckInResolvers["event"] = async (parent) => { const attendeeObject = await EventAttendee.findOne({ _id: parent.eventAttendeeId, diff --git a/src/resolvers/CheckIn/user.ts b/src/resolvers/CheckIn/user.ts index cd2c778835..4db1f9f2b1 100644 --- a/src/resolvers/CheckIn/user.ts +++ b/src/resolvers/CheckIn/user.ts @@ -1,6 +1,18 @@ import type { CheckInResolvers } from "../../types/generatedGraphQLTypes"; import { EventAttendee } from "../../models"; +/** + * Resolver function for the `user` field of a `CheckIn`. + * + * This function retrieves the user who checked in to an event. + * + * @param parent - The parent object representing the check-in. It contains information about the check-in, including the ID of the event attendee who checked in. + * @returns A promise that resolves to the user document found in the database. This document represents the user who checked in to the event. + * + * @see EventAttendee - The EventAttendee model used to interact with the event attendees collection in the database. + * @see CheckInResolvers - The type definition for the resolvers of the CheckIn fields. + * + */ export const user: CheckInResolvers["user"] = async (parent) => { const attendeeObject = await EventAttendee.findOne({ _id: parent.eventAttendeeId, diff --git a/src/resolvers/Comment/creator.ts b/src/resolvers/Comment/creator.ts index 7c3973330c..56248ae22d 100644 --- a/src/resolvers/Comment/creator.ts +++ b/src/resolvers/Comment/creator.ts @@ -1,6 +1,18 @@ import type { CommentResolvers } from "../../types/generatedGraphQLTypes"; import { User } from "../../models"; +/** + * Resolver function for the `creator` field of a `Comment`. + * + * This function retrieves the user who created a specific comment. + * + * @param parent - The parent object representing the comment. It contains information about the comment, including the ID of the user who created it. + * @returns A promise that resolves to the user document found in the database. This document represents the user who created the comment. + * + * @see User - The User model used to interact with the users collection in the database. + * @see CommentResolvers - The type definition for the resolvers of the Comment fields. + * + */ export const creator: CommentResolvers["creator"] = async (parent) => { return await User.findOne({ _id: parent.creatorId, diff --git a/src/resolvers/DirectChat/index.ts b/src/resolvers/DirectChat/index.ts index a9147d84a2..8a0f6a4059 100644 --- a/src/resolvers/DirectChat/index.ts +++ b/src/resolvers/DirectChat/index.ts @@ -4,6 +4,18 @@ import { messages } from "./messages"; import { organization } from "./organization"; import { users } from "./users"; +/** + * Resolver function for the `DirectChat` type. + * + * This resolver is used to resolve the fields of a `DirectChat` type. + * + * @see users - The resolver function for the `users` field of a `DirectChat`. + * @see organization - The resolver function for the `organization` field of a `DirectChat`. + * @see messages - The resolver function for the `messages` field of a `DirectChat`. + * @see creator - The resolver function for the `creator` field of a `DirectChat`. + * @see DirectChatResolvers - The type definition for the resolvers of the DirectChat fields. + * + */ export const DirectChat: DirectChatResolvers = { creator, messages, diff --git a/src/resolvers/DirectChatMessage/directChatMessageBelongsTo.ts b/src/resolvers/DirectChatMessage/directChatMessageBelongsTo.ts index c42bcaa9c5..eb6a0d4d32 100644 --- a/src/resolvers/DirectChatMessage/directChatMessageBelongsTo.ts +++ b/src/resolvers/DirectChatMessage/directChatMessageBelongsTo.ts @@ -2,10 +2,18 @@ import type { DirectChatMessageResolvers } from "../../types/generatedGraphQLTyp import { DirectChat } from "../../models"; import { CHAT_NOT_FOUND_ERROR } from "../../constants"; import { errors, requestContext } from "../../libraries"; + /** - * This resolver method will retrieve and return from the database the Direct chat to which the specified message belongs. - * @param parent - An object that is the return value of the resolver for this field's parent. - * @returns An `object` that contains the Direct chat data. + * Resolver function for the `directChatMessageBelongsTo` field of a `DirectChatMessage`. + * + * This function retrieves the direct chat to which a specific direct chat message belongs. + * + * @param parent - The parent object representing the direct chat message. It contains information about the direct chat message, including the ID of the direct chat to which it belongs. + * @returns A promise that resolves to the direct chat document found in the database. This document represents the direct chat to which the direct chat message belongs. + * + * @see DirectChat - The DirectChat model used to interact with the direct chats collection in the database. + * @see DirectChatMessageResolvers - The type definition for the resolvers of the DirectChatMessage fields. + * */ export const directChatMessageBelongsTo: DirectChatMessageResolvers["directChatMessageBelongsTo"] = async (parent) => { diff --git a/src/resolvers/DirectChatMessage/receiver.ts b/src/resolvers/DirectChatMessage/receiver.ts index 93dd67b24f..1702f33b66 100644 --- a/src/resolvers/DirectChatMessage/receiver.ts +++ b/src/resolvers/DirectChatMessage/receiver.ts @@ -2,10 +2,18 @@ import type { DirectChatMessageResolvers } from "../../types/generatedGraphQLTyp import { User } from "../../models"; import { USER_NOT_FOUND_ERROR } from "../../constants"; import { errors, requestContext } from "../../libraries"; + /** - * This resolver function will fetch and return the receiver(user) of the Direct chat from the database. - * @param parent - An object that is the return value of the resolver for this field's parent. - * @returns An `object` that contains User's data. + * Resolver function for the `receiver` field of a `DirectChatMessage`. + * + * This function retrieves the user who received a specific direct chat message. + * + * @param parent - The parent object representing the direct chat message. It contains information about the direct chat message, including the ID of the user who received it. + * @returns A promise that resolves to the user document found in the database. This document represents the user who received the direct chat message. + * + * @see User - The User model used to interact with the users collection in the database. + * @see DirectChatMessageResolvers - The type definition for the resolvers of the DirectChatMessage fields. + * */ export const receiver: DirectChatMessageResolvers["receiver"] = async ( parent, diff --git a/src/resolvers/DirectChatMessage/sender.ts b/src/resolvers/DirectChatMessage/sender.ts index 655b677b58..9a20bb4490 100644 --- a/src/resolvers/DirectChatMessage/sender.ts +++ b/src/resolvers/DirectChatMessage/sender.ts @@ -2,10 +2,18 @@ import type { DirectChatMessageResolvers } from "../../types/generatedGraphQLTyp import { User } from "../../models"; import { USER_NOT_FOUND_ERROR } from "../../constants"; import { errors, requestContext } from "../../libraries"; + /** - * This resolver function will fetch and return the sender(user) of the Direct chat from the database. - * @param parent - An object that is the return value of the resolver for this field's parent. - * @returns An `object` that contains User's data. + * Resolver function for the `sender` field of a `DirectChatMessage`. + * + * This function retrieves the user who sent a specific direct chat message. + * + * @param parent - The parent object representing the direct chat message. It contains information about the direct chat message, including the ID of the user who sent it. + * @returns A promise that resolves to the user document found in the database. This document represents the user who sent the direct chat message. + * + * @see User - The User model used to interact with the users collection in the database. + * @see DirectChatMessageResolvers - The type definition for the resolvers of the DirectChatMessage fields. + * */ export const sender: DirectChatMessageResolvers["sender"] = async (parent) => { const result = await User.findOne({ diff --git a/src/resolvers/Event/actionItems.ts b/src/resolvers/Event/actionItems.ts index 0fcc7b29fb..b40c8a703c 100644 --- a/src/resolvers/Event/actionItems.ts +++ b/src/resolvers/Event/actionItems.ts @@ -1,9 +1,17 @@ import { ActionItem } from "../../models"; import type { EventResolvers } from "../../types/generatedGraphQLTypes"; + /** - * This resolver function will fetch and return the action items related to the event from database. - * @param parent - An object that is the return value of the resolver for this field's parent. - * @returns An object that contains the list of all action items related to the event. + * Resolver function for the `actionItems` field of an `Event`. + * + * This function retrieves the action items associated with a specific event. + * + * @param parent - The parent object representing the event. It contains information about the event, including the ID of the action items associated with it. + * @returns A promise that resolves to an array of action item documents found in the database. These documents represent the action items associated with the event. + * + * @see ActionItem - The ActionItem model used to interact with the action items collection in the database. + * @see EventResolvers - The type definition for the resolvers of the Event fields. + * */ export const actionItems: EventResolvers["actionItems"] = async (parent) => { return await ActionItem.find({ diff --git a/src/resolvers/Event/attendees.ts b/src/resolvers/Event/attendees.ts index 547bf38b61..c6b24c1f4d 100644 --- a/src/resolvers/Event/attendees.ts +++ b/src/resolvers/Event/attendees.ts @@ -1,6 +1,18 @@ import type { EventResolvers } from "../../types/generatedGraphQLTypes"; import { EventAttendee } from "../../models"; +/** + * Resolver function for the `attendees` field of an `Event`. + * + * This function retrieves the attendees of an event. + * + * @param parent - The parent object representing the event. It contains information about the event, including the ID of the event. + * @returns A promise that resolves to the user documents found in the database. These documents represent the attendees of the event. + * + * @see EventAttendee - The EventAttendee model used to interact with the event attendees collection in the database. + * @see EventResolvers - The type definition for the resolvers of the Event fields. + * + */ export const attendees: EventResolvers["attendees"] = async (parent) => { const eventAttendeeObjects = await EventAttendee.find({ eventId: parent._id, diff --git a/src/resolvers/Event/attendeesCheckInStatus.ts b/src/resolvers/Event/attendeesCheckInStatus.ts index 25ebb3f1d7..7935f4b98e 100644 --- a/src/resolvers/Event/attendeesCheckInStatus.ts +++ b/src/resolvers/Event/attendeesCheckInStatus.ts @@ -1,6 +1,18 @@ import type { EventResolvers } from "../../types/generatedGraphQLTypes"; import { EventAttendee } from "../../models"; +/** + * Resolver function for the `attendeesCheckInStatus` field of an `Event`. + * + * This function retrieves the attendees of an event and their check-in status. + * + * @param parent - The parent object representing the event. It contains information about the event, including the ID. + * @returns A promise that resolves to an array of objects. Each object contains information about an attendee of the event, including the user document and the check-in document. + * + * @see EventAttendee - The EventAttendee model used to interact with the event attendees collection in the database. + * @see EventResolvers - The type definition for the resolvers of the Event fields. + * + */ export const attendeesCheckInStatus: EventResolvers["attendeesCheckInStatus"] = async (parent) => { const eventAttendeeObjects = await EventAttendee.find({ diff --git a/src/resolvers/Event/averageFeedbackScore.ts b/src/resolvers/Event/averageFeedbackScore.ts index 087eca38e9..3f13f2ee0b 100644 --- a/src/resolvers/Event/averageFeedbackScore.ts +++ b/src/resolvers/Event/averageFeedbackScore.ts @@ -1,6 +1,18 @@ import type { EventResolvers } from "../../types/generatedGraphQLTypes"; import { Feedback } from "../../models"; +/** + * Resolver function for the `averageFeedbackScore` field of an `Event`. + * + * This function calculates the average feedback score for a specific event. + * + * @param parent - The parent object representing the event. It contains information about the event, including the ID of the feedback associated with it. + * @returns A promise that resolves to the average feedback score for the event. + * + * @see Feedback - The Feedback model used to interact with the feedback collection in the database. + * @see EventResolvers - The type definition for the resolvers of the Event fields. + * + */ export const averageFeedbackScore: EventResolvers["averageFeedbackScore"] = async (parent) => { const feedbacks = await Feedback.find({ diff --git a/src/resolvers/Event/baseRecurringEvent.ts b/src/resolvers/Event/baseRecurringEvent.ts index 35ef6f4688..d42ecb4bb2 100644 --- a/src/resolvers/Event/baseRecurringEvent.ts +++ b/src/resolvers/Event/baseRecurringEvent.ts @@ -1,6 +1,18 @@ import type { EventResolvers } from "../../types/generatedGraphQLTypes"; import { Event } from "../../models"; +/** + * Resolver function for the `baseRecurringEvent` field of an `Event`. + * + * This function retrieves the base recurring event of a specific event. + * + * @param parent - The parent object representing the event. It contains information about the event, including the ID of the base recurring event. + * @returns A promise that resolves to the event document found in the database. This document represents the base recurring event of the event. + * + * @see Event - The Event model used to interact with the events collection in the database. + * @see EventResolvers - The type definition for the resolvers of the Event fields. + * + */ export const baseRecurringEvent: EventResolvers["baseRecurringEvent"] = async ( parent, ) => { diff --git a/src/resolvers/Event/creator.ts b/src/resolvers/Event/creator.ts index 2b53c49fe5..233eeb94b9 100644 --- a/src/resolvers/Event/creator.ts +++ b/src/resolvers/Event/creator.ts @@ -1,6 +1,18 @@ import type { EventResolvers } from "../../types/generatedGraphQLTypes"; import { User } from "../../models"; +/** + * Resolver function for the `creator` field of an `Event`. + * + * This function retrieves the user who created a specific event. + * + * @param parent - The parent object representing the event. It contains information about the event, including the ID of the user who created it. + * @returns A promise that resolves to the user document found in the database. This document represents the user who created the event. + * + * @see User - The User model used to interact with the users collection in the database. + * @see EventResolvers - The type definition for the resolvers of the Event fields. + * + */ export const creator: EventResolvers["creator"] = async (parent) => { return User.findOne({ _id: parent.creatorId, diff --git a/src/resolvers/Event/feedback.ts b/src/resolvers/Event/feedback.ts index f362b6b2af..b712116c00 100644 --- a/src/resolvers/Event/feedback.ts +++ b/src/resolvers/Event/feedback.ts @@ -1,6 +1,18 @@ import type { EventResolvers } from "../../types/generatedGraphQLTypes"; import { Feedback } from "../../models"; +/** + * Resolver function for the `feedback` field of an `Event`. + * + * This function retrieves the feedback associated with a specific event. + * + * @param parent - The parent object representing the event. It contains information about the event, including the ID of the feedback associated with it. + * @returns A promise that resolves to an array of feedback documents found in the database. These documents represent the feedback associated with the event. + * + * @see Feedback - The Feedback model used to interact with the feedback collection in the database. + * @see EventResolvers - The type definition for the resolvers of the Event fields. + * + */ export const feedback: EventResolvers["feedback"] = async (parent) => { return Feedback.find({ eventId: parent._id, diff --git a/src/resolvers/Event/organization.ts b/src/resolvers/Event/organization.ts index 1bc86dfc8e..db115dcfce 100644 --- a/src/resolvers/Event/organization.ts +++ b/src/resolvers/Event/organization.ts @@ -1,6 +1,18 @@ import type { EventResolvers } from "../../types/generatedGraphQLTypes"; import { Organization } from "../../models"; +/** + * Resolver function for the `organization` field of an `Event`. + * + * This function retrieves the organization associated with a specific event. + * + * @param parent - The parent object representing the event. It contains information about the event, including the ID of the organization it is associated with. + * @returns A promise that resolves to the organization document found in the database. This document represents the organization associated with the event. + * + * @see Organization - The Organization model used to interact with the organizations collection in the database. + * @see EventResolvers - The type definition for the resolvers of the Event fields. + * + */ export const organization: EventResolvers["organization"] = async (parent) => { return Organization.findOne({ _id: parent.organization, diff --git a/src/resolvers/Event/recurrenceRule.ts b/src/resolvers/Event/recurrenceRule.ts index 5e26b84b3c..74fd20050c 100644 --- a/src/resolvers/Event/recurrenceRule.ts +++ b/src/resolvers/Event/recurrenceRule.ts @@ -2,9 +2,16 @@ import type { EventResolvers } from "../../types/generatedGraphQLTypes"; import { RecurrenceRule } from "../../models/RecurrenceRule"; /** - * This resolver function will fetch and return the recurrenceRule that the event is following. - * @param parent - An object that is the return value of the resolver for this field's parent. - * @returns An object that contains the RecurrenceRule of the event. + * Resolver function for the `recurrenceRule` field of an `Event`. + * + * This function retrieves the recurrence rule associated with a specific event. + * + * @param parent - The parent object representing the event. It contains information about the event, including the ID of the recurrence rule associated with it. + * @returns A promise that resolves to the recurrence rule document found in the database. This document represents the recurrence rule associated with the event. + * + * @see RecurrenceRule - The RecurrenceRule model used to interact with the recurrence rules collection in the database. + * @see EventResolvers - The type definition for the resolvers of the Event fields. + * */ export const recurrenceRule: EventResolvers["recurrenceRule"] = async ( diff --git a/src/resolvers/EventVolunteer/creator.ts b/src/resolvers/EventVolunteer/creator.ts index 3167c28cfd..59d13f2b57 100644 --- a/src/resolvers/EventVolunteer/creator.ts +++ b/src/resolvers/EventVolunteer/creator.ts @@ -1,6 +1,18 @@ import { User } from "../../models"; import type { EventVolunteerResolvers } from "../../types/generatedGraphQLTypes"; +/** + * Resolver function for the `creator` field of an `EventVolunteer`. + * + * This function retrieves the user who created a specific event volunteer. + * + * @param parent - The parent object representing the event volunteer. It contains information about the event volunteer, including the ID of the user who created it. + * @returns A promise that resolves to the user document found in the database. This document represents the user who created the event volunteer. + * + * @see User - The User model used to interact with the users collection in the database. + * @see EventVolunteerResolvers - The type definition for the resolvers of the EventVolunteer fields. + * + */ export const creator: EventVolunteerResolvers["creator"] = async (parent) => { return await User.findOne({ _id: parent.creatorId, diff --git a/src/resolvers/EventVolunteer/event.ts b/src/resolvers/EventVolunteer/event.ts index 61f67ac48e..438754aa91 100644 --- a/src/resolvers/EventVolunteer/event.ts +++ b/src/resolvers/EventVolunteer/event.ts @@ -1,6 +1,18 @@ import { Event } from "../../models"; import type { EventVolunteerResolvers } from "../../types/generatedGraphQLTypes"; +/** + * Resolver function for the `event` field of an `EventVolunteer`. + * + * This function retrieves the event associated with a specific event volunteer. + * + * @param parent - The parent object representing the event volunteer. It contains information about the event volunteer, including the ID of the event associated with it. + * @returns A promise that resolves to the event document found in the database. This document represents the event associated with the event volunteer. + * + * @see Event - The Event model used to interact with the events collection in the database. + * @see EventVolunteerResolvers - The type definition for the resolvers of the EventVolunteer fields. + * + */ export const event: EventVolunteerResolvers["event"] = async (parent) => { return await Event.findOne({ _id: parent.eventId, diff --git a/src/resolvers/EventVolunteer/group.ts b/src/resolvers/EventVolunteer/group.ts index cfa636c375..4fa94b9ee6 100644 --- a/src/resolvers/EventVolunteer/group.ts +++ b/src/resolvers/EventVolunteer/group.ts @@ -1,6 +1,18 @@ import { EventVolunteerGroup } from "../../models"; import type { EventVolunteerResolvers } from "../../types/generatedGraphQLTypes"; +/** + * Resolver function for the `group` field of an `EventVolunteer`. + * + * This function retrieves the group associated with a specific event volunteer. + * + * @param parent - The parent object representing the event volunteer. It contains information about the event volunteer, including the ID of the group associated with it. + * @returns A promise that resolves to the group document found in the database. This document represents the group associated with the event volunteer. + * + * @see EventVolunteerGroup - The EventVolunteerGroup model used to interact with the event volunteer groups collection in the database. + * @see EventVolunteerResolvers - The type definition for the resolvers of the EventVolunteer fields. + * + */ export const group: EventVolunteerResolvers["group"] = async (parent) => { return await EventVolunteerGroup.findOne({ _id: parent.groupId, diff --git a/src/resolvers/EventVolunteer/user.ts b/src/resolvers/EventVolunteer/user.ts index c70d68ccf7..5059bd1dcb 100644 --- a/src/resolvers/EventVolunteer/user.ts +++ b/src/resolvers/EventVolunteer/user.ts @@ -1,6 +1,18 @@ import { User } from "../../models"; import type { EventVolunteerResolvers } from "../../types/generatedGraphQLTypes"; +/** + * Resolver function for the `user` field of an `EventVolunteer`. + * + * This function retrieves the user who created a specific event volunteer. + * + * @param parent - The parent object representing the event volunteer. It contains information about the event volunteer, including the ID of the user who created it. + * @returns A promise that resolves to the user document found in the database. This document represents the user who created the event volunteer. + * + * @see User - The User model used to interact with the users collection in the database. + * @see EventVolunteerResolvers - The type definition for the resolvers of the EventVolunteer fields. + * + */ export const user: EventVolunteerResolvers["user"] = async (parent) => { const result = await User.findOne({ _id: parent.userId, diff --git a/src/resolvers/EventVolunteerGroup/creator.ts b/src/resolvers/EventVolunteerGroup/creator.ts index a65494c188..7924de2115 100644 --- a/src/resolvers/EventVolunteerGroup/creator.ts +++ b/src/resolvers/EventVolunteerGroup/creator.ts @@ -1,6 +1,18 @@ import { User } from "../../models"; import type { EventVolunteerGroupResolvers } from "../../types/generatedGraphQLTypes"; +/** + * Resolver function for the `creator` field of an `EventVolunteerGroup`. + * + * This function retrieves the user who created a specific event volunteer group. + * + * @param parent - The parent object representing the event volunteer group. It contains information about the event volunteer group, including the ID of the user who created it. + * @returns A promise that resolves to the user document found in the database. This document represents the user who created the event volunteer group. + * + * @see User - The User model used to interact with the users collection in the database. + * @see EventVolunteerGroupResolvers - The type definition for the resolvers of the EventVolunteerGroup fields. + * + */ export const creator: EventVolunteerGroupResolvers["creator"] = async ( parent, ) => { diff --git a/src/resolvers/EventVolunteerGroup/event.ts b/src/resolvers/EventVolunteerGroup/event.ts index 38e5eb8d13..b758191d0a 100644 --- a/src/resolvers/EventVolunteerGroup/event.ts +++ b/src/resolvers/EventVolunteerGroup/event.ts @@ -1,6 +1,18 @@ import { Event } from "../../models"; import type { EventVolunteerGroupResolvers } from "../../types/generatedGraphQLTypes"; +/** + * Resolver function for the `event` field of an `EventVolunteerGroup`. + * + * This function retrieves the event associated with a specific event volunteer group. + * + * @param parent - The parent object representing the event volunteer group. It contains information about the event volunteer group, including the ID of the event associated with it. + * @returns A promise that resolves to the event document found in the database. This document represents the event associated with the event volunteer group. + * + * @see Event - The Event model used to interact with the events collection in the database. + * @see EventVolunteerGroupResolvers - The type definition for the resolvers of the EventVolunteerGroup fields. + * + */ export const event: EventVolunteerGroupResolvers["event"] = async (parent) => { return await Event.findOne({ _id: parent.eventId, diff --git a/src/resolvers/EventVolunteerGroup/leader.ts b/src/resolvers/EventVolunteerGroup/leader.ts index 40ee89cdbd..93a47b3eab 100644 --- a/src/resolvers/EventVolunteerGroup/leader.ts +++ b/src/resolvers/EventVolunteerGroup/leader.ts @@ -2,6 +2,18 @@ import { User } from "../../models"; import type { InterfaceUser } from "../../models"; import type { EventVolunteerGroupResolvers } from "../../types/generatedGraphQLTypes"; +/** + * Resolver function for the `leader` field of an `EventVolunteerGroup`. + * + * This function retrieves the user who is the leader of a specific event volunteer group. + * + * @param parent - The parent object representing the event volunteer group. It contains information about the event volunteer group, including the ID of the user who is the leader. + * @returns A promise that resolves to the user document found in the database. This document represents the user who is the leader of the event volunteer group. + * + * @see User - The User model used to interact with the users collection in the database. + * @see EventVolunteerGroupResolvers - The type definition for the resolvers of the EventVolunteerGroup fields. + * + */ export const leader: EventVolunteerGroupResolvers["leader"] = async ( parent, ) => { diff --git a/src/resolvers/Feedback/event.ts b/src/resolvers/Feedback/event.ts index a31fe3c8f0..7a7dd05f44 100644 --- a/src/resolvers/Feedback/event.ts +++ b/src/resolvers/Feedback/event.ts @@ -1,6 +1,18 @@ import type { FeedbackResolvers } from "../../types/generatedGraphQLTypes"; import { Event } from "../../models"; +/** + * Resolver function for the `event` field of a `Feedback`. + * + * This function retrieves the event associated with a specific feedback. + * + * @param parent - The parent object representing the feedback. It contains information about the feedback, including the ID of the event associated with it. + * @returns A promise that resolves to the event document found in the database. This document represents the event associated with the feedback. + * + * @see Event - The Event model used to interact with the events collection in the database. + * @see FeedbackResolvers - The type definition for the resolvers of the Feedback fields. + * + */ export const event: FeedbackResolvers["event"] = async (parent) => { const result = await Event.findOne({ _id: parent.eventId, diff --git a/src/resolvers/Fund/creator.ts b/src/resolvers/Fund/creator.ts index dd402564b0..bb8c9968a7 100644 --- a/src/resolvers/Fund/creator.ts +++ b/src/resolvers/Fund/creator.ts @@ -2,6 +2,18 @@ import type { FundResolvers } from "../../types/generatedGraphQLTypes"; import { User } from "../../models"; import { Types } from "mongoose"; +/** + * Resolver function for the `creator` field of a `Fund`. + * + * This function retrieves the user who created a specific fund. + * + * @param parent - The parent object representing the fund. It contains information about the fund, including the ID of the user who created it. + * @returns A promise that resolves to the user document found in the database. This document represents the user who created the fund. + * + * @see User - The User model used to interact with the users collection in the database. + * @see FundResolvers - The type definition for the resolvers of the Fund fields. + * + */ export const creator: FundResolvers["creator"] = async (parent) => { return await User.findOne({ _id: new Types.ObjectId(parent.creatorId?.toString()), diff --git a/src/resolvers/Fund/index.ts b/src/resolvers/Fund/index.ts index 36ace6b101..f961853f05 100644 --- a/src/resolvers/Fund/index.ts +++ b/src/resolvers/Fund/index.ts @@ -1,5 +1,14 @@ import type { FundResolvers } from "../../types/generatedGraphQLTypes"; import { creator } from "./creator"; + +/** + * Resolver functions for the fields of a `Fund`. + * + * These functions define how to resolve the fields of a `Fund` type. + * + * @see FundResolvers - The type definition for the resolvers of the Fund fields. + * + */ export const Fund: FundResolvers = { creator, }; diff --git a/src/resolvers/FundraisingCampaignPledge/index.ts b/src/resolvers/FundraisingCampaignPledge/index.ts index b4ef6411f6..2eac0d9f4a 100644 --- a/src/resolvers/FundraisingCampaignPledge/index.ts +++ b/src/resolvers/FundraisingCampaignPledge/index.ts @@ -1,5 +1,6 @@ import type { FundraisingCampaignPledgeResolvers } from "../../types/generatedGraphQLTypes"; import { users } from "./users"; + export const FundraisingCampaignPledge: FundraisingCampaignPledgeResolvers = { users, }; diff --git a/src/resolvers/FundraisingCampaignPledge/users.ts b/src/resolvers/FundraisingCampaignPledge/users.ts index bc9c580bd3..e0126555a4 100644 --- a/src/resolvers/FundraisingCampaignPledge/users.ts +++ b/src/resolvers/FundraisingCampaignPledge/users.ts @@ -2,9 +2,16 @@ import { User } from "../../models"; import type { FundraisingCampaignPledgeResolvers } from "../../types/generatedGraphQLTypes"; /** - * This resolver function will fetch and return the list of users who have pledged to the Fundraising Campaign from database. - * @param parent - An object that is the return value of the resolver for this field's parent. - * @returns An object that contains the list of all users who have pledged to the Fundraising Campaign. + * Resolver function for the `users` field of a `FundraisingCampaignPledge`. + * + * This function retrieves the users who pledged to donate to a specific fundraising campaign. + * + * @param parent - The parent object representing the fundraising campaign pledge. It contains information about the fundraising campaign pledge, including the IDs of the users who pledged to donate. + * @returns A promise that resolves to an array of user documents found in the database. These documents represent the users who pledged to donate to the fundraising campaign. + * + * @see User - The User model used to interact with the users collection in the database. + * @see FundraisingCampaignPledgeResolvers - The type definition for the resolvers of the FundraisingCampaignPledge fields. + * */ export const users: FundraisingCampaignPledgeResolvers["users"] = async ( parent, diff --git a/src/resolvers/GroupChat/creator.ts b/src/resolvers/GroupChat/creator.ts index c64cc05699..ff7ea2cd51 100644 --- a/src/resolvers/GroupChat/creator.ts +++ b/src/resolvers/GroupChat/creator.ts @@ -1,9 +1,17 @@ import type { GroupChatResolvers } from "../../types/generatedGraphQLTypes"; import { User } from "../../models"; + /** - * This resolver function will fetch and return the Group Chat creator(User) from the database. - * @param parent - An object that is the return value of the resolver for this field's parent. - * @returns An object that contains the User data. + * Resolver function for the `creator` field of a `GroupChat`. + * + * This function retrieves the user who created a specific group chat. + * + * @param parent - The parent object representing the group chat. It contains information about the group chat, including the ID of the user who created it. + * @returns A promise that resolves to the user document found in the database. This document represents the user who created the group chat. + * + * @see User - The User model used to interact with the users collection in the database. + * @see GroupChatResolvers - The type definition for the resolvers of the GroupChat fields. + * */ export const creator: GroupChatResolvers["creator"] = async (parent) => { return await User.findOne({ diff --git a/src/resolvers/GroupChat/messages.ts b/src/resolvers/GroupChat/messages.ts index a83ed2e22e..cb03954b94 100644 --- a/src/resolvers/GroupChat/messages.ts +++ b/src/resolvers/GroupChat/messages.ts @@ -1,9 +1,17 @@ import type { GroupChatResolvers } from "../../types/generatedGraphQLTypes"; import { GroupChatMessage } from "../../models"; + /** - * This resolver function will fetch and return the list of group chat message from the database. - * @param parent - An object that is the return value of the resolver for this field's parent. - * @returns An object that contains the list of group chat messages. + * Resolver function for the `messages` field of a `GroupChat`. + * + * This function retrieves the messages associated with a specific group chat. + * + * @param parent - The parent object representing the group chat. It contains information about the group chat, including the IDs of the messages associated with it. + * @returns A promise that resolves to the message documents found in the database. These documents represent the messages associated with the group chat. + * + * @see GroupChatMessage - The GroupChatMessage model used to interact with the group chat messages collection in the database. + * @see GroupChatResolvers - The type definition for the resolvers of the GroupChat fields. + * */ export const messages: GroupChatResolvers["messages"] = async (parent) => { return await GroupChatMessage.find({ diff --git a/src/resolvers/GroupChat/organization.ts b/src/resolvers/GroupChat/organization.ts index d4d716a577..ab83f0f3b3 100644 --- a/src/resolvers/GroupChat/organization.ts +++ b/src/resolvers/GroupChat/organization.ts @@ -3,10 +3,18 @@ import { Organization } from "../../models"; import type { InterfaceOrganization } from "../../models"; import { cacheOrganizations } from "../../services/OrganizationCache/cacheOrganizations"; import { findOrganizationsInCache } from "../../services/OrganizationCache/findOrganizationsInCache"; + /** - * This resolver function will fetch and return the organization for group chat from database. - * @param parent - An object that is the return value of the resolver for this field's parent. - * @returns An `object` that contains the organization data. + * Resolver function for the `organization` field of a `GroupChat`. + * + * This function retrieves the organization associated with a specific group chat. + * + * @param parent - The parent object representing the group chat. It contains information about the group chat, including the ID of the organization it is associated with. + * @returns A promise that resolves to the organization document found in the database. This document represents the organization associated with the group chat. + * + * @see Organization - The Organization model used to interact with the organizations collection in the database. + * @see GroupChatResolvers - The type definition for the resolvers of the GroupChat fields. + * */ export const organization: GroupChatResolvers["organization"] = async ( parent, diff --git a/src/resolvers/GroupChat/users.ts b/src/resolvers/GroupChat/users.ts index 4b2c35b7c0..b4e0f363e1 100644 --- a/src/resolvers/GroupChat/users.ts +++ b/src/resolvers/GroupChat/users.ts @@ -1,9 +1,17 @@ import type { GroupChatResolvers } from "../../types/generatedGraphQLTypes"; import { User } from "../../models"; + /** - * This resolver function will fetch and return the list of all Users of the Group Chat from database. - * @param parent - An object that is the return value of the resolver for this field's parent. - * @returns An `object` that contains the User data. + * Resolver function for the `users` field of a `GroupChat`. + * + * This function retrieves the users who are members of a specific group chat. + * + * @param parent - The parent object representing the group chat. It contains information about the group chat, including the IDs of the users who are members of it. + * @returns A promise that resolves to the user documents found in the database. These documents represent the users who are members of the group chat. + * + * @see User - The User model used to interact with the users collection in the database. + * @see GroupChatResolvers - The type definition for the resolvers of the GroupChat fields. + * */ export const users: GroupChatResolvers["users"] = async (parent) => { return await User.find({ diff --git a/src/resolvers/GroupChatMessage/groupChatMessageBelongsTo.ts b/src/resolvers/GroupChatMessage/groupChatMessageBelongsTo.ts index 9f4881b192..c9e2ec6a08 100644 --- a/src/resolvers/GroupChatMessage/groupChatMessageBelongsTo.ts +++ b/src/resolvers/GroupChatMessage/groupChatMessageBelongsTo.ts @@ -2,10 +2,18 @@ import type { GroupChatMessageResolvers } from "../../types/generatedGraphQLType import { GroupChat } from "../../models"; import { errors, requestContext } from "../../libraries"; import { CHAT_NOT_FOUND_ERROR } from "../../constants"; + /** - * This resolver method will retrieve and return from the database the Group chat to which the specified message belongs. - * @param parent - An object that is the return value of the resolver for this field's parent. - * @returns An object that contains the Group chat data. + * Resolver function for the `groupChatMessageBelongsTo` field of a `GroupChatMessage`. + * + * This function retrieves the group chat to which a specific group chat message belongs. + * + * @param parent - The parent object representing the group chat message. It contains information about the group chat message, including the ID of the group chat to which it belongs. + * @returns A promise that resolves to the group chat document found in the database. This document represents the group chat to which the group chat message belongs. + * + * @see GroupChat - The GroupChat model used to interact with the group chats collection in the database. + * @see GroupChatMessageResolvers - The type definition for the resolvers of the GroupChatMessage fields. + * */ export const groupChatMessageBelongsTo: GroupChatMessageResolvers["groupChatMessageBelongsTo"] = async (parent) => { diff --git a/src/resolvers/GroupChatMessage/sender.ts b/src/resolvers/GroupChatMessage/sender.ts index 0c647b2575..9497a05f23 100644 --- a/src/resolvers/GroupChatMessage/sender.ts +++ b/src/resolvers/GroupChatMessage/sender.ts @@ -2,10 +2,18 @@ import type { GroupChatMessageResolvers } from "../../types/generatedGraphQLType import { User } from "../../models"; import { USER_NOT_FOUND_ERROR } from "../../constants"; import { errors, requestContext } from "../../libraries"; + /** - * This resolver function will fetch and return the send of the group chat message from database. - * @param parent - An object that is the return value of the resolver for this field's parent. - * @returns An object that will contain the User data. + * Resolver function for the `sender` field of a `GroupChatMessage`. + * + * This function retrieves the user who sent a specific group chat message. + * + * @param parent - The parent object representing the group chat message. It contains information about the group chat message, including the ID of the user who sent it. + * @returns A promise that resolves to the user document found in the database. This document represents the user who sent the group chat message. + * + * @see User - The User model used to interact with the users collection in the database. + * @see GroupChatMessageResolvers - The type definition for the resolvers of the GroupChatMessage fields. + * */ export const sender: GroupChatMessageResolvers["sender"] = async (parent) => { const result = await User.findOne({ diff --git a/src/resolvers/MembershipRequest/organization.ts b/src/resolvers/MembershipRequest/organization.ts index 2aa150c33f..10b9ef9503 100644 --- a/src/resolvers/MembershipRequest/organization.ts +++ b/src/resolvers/MembershipRequest/organization.ts @@ -2,10 +2,18 @@ import type { MembershipRequestResolvers } from "../../types/generatedGraphQLTyp import { Organization } from "../../models"; import { ORGANIZATION_NOT_FOUND_ERROR } from "../../constants"; import { errors, requestContext } from "../../libraries"; + /** - * This resolver function will get and return the organisation from the database for which a membership request was sent. - * @param parent - An object that is the return value of the resolver for this field's parent. - * @returns An object that contains the Organization data. + * Resolver function for the `organization` field of a `MembershipRequest`. + * + * This function retrieves the organization associated with a specific membership request. + * + * @param parent - The parent object representing the membership request. It contains information about the membership request, including the ID of the organization it is associated with. + * @returns A promise that resolves to the organization document found in the database. This document represents the organization associated with the membership request. + * + * @see Organization - The Organization model used to interact with the organizations collection in the database. + * @see MembershipRequestResolvers - The type definition for the resolvers of the MembershipRequest fields. + * */ export const organization: MembershipRequestResolvers["organization"] = async ( parent, diff --git a/src/resolvers/MembershipRequest/user.ts b/src/resolvers/MembershipRequest/user.ts index 43879c4126..ac33f80c17 100644 --- a/src/resolvers/MembershipRequest/user.ts +++ b/src/resolvers/MembershipRequest/user.ts @@ -2,10 +2,18 @@ import type { MembershipRequestResolvers } from "../../types/generatedGraphQLTyp import { User } from "../../models"; import { USER_NOT_FOUND_ERROR } from "../../constants"; import { errors, requestContext } from "../../libraries"; + /** - * This resolver function will retrieve and return the user who sent the membership request from the database. - * @param parent - An object that is the return value of the resolver for this field's parent. - * @returns An object that contains the User data. + * Resolver function for the `user` field of a `MembershipRequest`. + * + * This function retrieves the user who made a specific membership request. + * + * @param parent - The parent object representing the membership request. It contains information about the membership request, including the ID of the user who made it. + * @returns A promise that resolves to the user document found in the database. This document represents the user who made the membership request. + * + * @see User - The User model used to interact with the users collection in the database. + * @see MembershipRequestResolvers - The type definition for the resolvers of the MembershipRequest fields. + * */ export const user: MembershipRequestResolvers["user"] = async (parent) => { const result = await User.findOne({ diff --git a/src/resolvers/Mutation/addEventAttendee.ts b/src/resolvers/Mutation/addEventAttendee.ts index 816635f522..8c4242c484 100644 --- a/src/resolvers/Mutation/addEventAttendee.ts +++ b/src/resolvers/Mutation/addEventAttendee.ts @@ -21,6 +21,33 @@ import { cacheUsers } from "../../services/UserCache/cacheUser"; import { findUserInCache } from "../../services/UserCache/findUserInCache"; import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; +/** + * Mutation resolver function to add a user as an attendee to an event. + * + * This function performs the following actions: + * 1. Retrieves the current user from the cache or database. + * 2. Retrieves the current user's app profile from the cache or database. + * 3. Retrieves the event from the cache or database. + * 4. Checks if the user making the request is an admin of the event or a super admin. + * 5. Validates that the user to be added as an attendee exists and is not already registered for the event. + * 6. Checks if the user to be added is a member of the organization hosting the event. + * 7. Adds the user as an attendee to the event if all checks pass. + * + * @param _parent - The parent object for the mutation. Typically, this is not used in this resolver. + * @param args - The arguments for the mutation, including: + * - `data.eventId`: The ID of the event to which the user will be added as an attendee. + * - `data.userId`: The ID of the user to be added as an attendee. + * @param context - The context for the mutation, including: + * - `userId`: The ID of the current user making the request. + * + * @returns A promise that resolves to the user document representing the user added as an attendee. + * + * @see User - The User model used to interact with the users collection in the database. + * @see AppUserProfile - The AppUserProfile model used to interact with the app user profiles collection in the database. + * @see Event - The Event model used to interact with the events collection in the database. + * @see EventAttendee - The EventAttendee model used to manage event attendee registrations. + * @see MutationResolvers - The type definition for the mutation resolvers. + */ export const addEventAttendee: MutationResolvers["addEventAttendee"] = async ( _parent, args, diff --git a/src/resolvers/Mutation/addFeedback.ts b/src/resolvers/Mutation/addFeedback.ts index 8923e63dfb..0ed037ae01 100644 --- a/src/resolvers/Mutation/addFeedback.ts +++ b/src/resolvers/Mutation/addFeedback.ts @@ -8,6 +8,32 @@ import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; import { errors, requestContext } from "../../libraries"; import { Event, EventAttendee, CheckIn, Feedback } from "../../models"; +/** + * Mutation resolver function to add feedback for an event. + * + * This function pcerforms the following ations: + * 1. Checks if the specified event exists. + * 2. Retrieves the event attendee record for the current user and event. + * 3. Checks if the user is registered for the event and if they have checked in. + * 4. Ensures the user has not already submitted feedback for the event. + * 5. Updates the check-in record to mark feedback as submitted. + * 6. Creates and saves a new feedback entry. + * + * @param _parent - The parent object for the mutation. Typically, this is not used in this resolver. + * @param args - The arguments for the mutation, including: + * - `data.eventId`: The ID of the event for which feedback is being submitted. + * - `data.feedback`: The feedback content to be submitted. + * @param context - The context for the mutation, including: + * - `userId`: The ID of the current user making the request. + * + * @returns A promise that resolves to the newly created feedback document. + * + * @see Event - The Event model used to interact with the events collection in the database. + * @see EventAttendee - The EventAttendee model used to manage event attendee records. + * @see CheckIn - The CheckIn model used to manage check-in records. + * @see Feedback - The Feedback model used to create and manage feedback entries. + * @see MutationResolvers - The type definition for the mutation resolvers. + */ export const addFeedback: MutationResolvers["addFeedback"] = async ( _parent, args, diff --git a/src/resolvers/Mutation/addLanguageTranslation.ts b/src/resolvers/Mutation/addLanguageTranslation.ts index 96077de8b2..e00ae66b81 100644 --- a/src/resolvers/Mutation/addLanguageTranslation.ts +++ b/src/resolvers/Mutation/addLanguageTranslation.ts @@ -3,14 +3,32 @@ import { errors, requestContext } from "../../libraries"; import { Language } from "../../models"; import type { InterfaceLanguage } from "../../models"; import { TRANSLATION_ALREADY_PRESENT_ERROR } from "../../constants"; + /** - * This function adds language translation. - * @param _parent - parent of current request - * @param args - payload provided with the request - * @remarks The following checks are done: - * 1. If the language exists - * 2. If the translation already exists. - * @returns Updated langauge + * Mutation resolver function to add a translation for a language. + * + * This function performs the following actions: + * 1. Checks if the language with the provided English value exists in the database. + * 2. If the language exists, checks if the translation for the specified language code already exists. + * 3. If the translation already exists, throws a conflict error. + * 4. If the translation does not exist, updates the language with the new translation. + * 5. If the language does not exist, creates a new language entry with the provided translation. + * + * @param _parent - The parent object for the mutation. Typically, this is not used in this resolver. + * @param args - The arguments for the mutation, including: + * - `data.en_value`: The English value of the language to which the translation is being added. + * - `data.translation_lang_code`: The language code for the translation being added. + * - `data.translation_value`: The translation value to be added. + * + * @returns A promise that resolves to the updated or newly created language document. + * + * @see Language - The Language model used to interact with the languages collection in the database. + * @see MutationResolvers - The type definition for the mutation resolvers. + * + * @remarks + * The function uses the `findOne` method to locate an existing language entry by its English value. + * If the language exists, it checks the existing translations to prevent duplicate entries. + * If the language does not exist, a nsnew entry is created with the provided tralation. */ export const addLanguageTranslation: MutationResolvers["addLanguageTranslation"] = async (_parent, args) => { diff --git a/src/resolvers/Mutation/addOrganizationCustomField.ts b/src/resolvers/Mutation/addOrganizationCustomField.ts index 7a89de5f1e..88e3a62415 100644 --- a/src/resolvers/Mutation/addOrganizationCustomField.ts +++ b/src/resolvers/Mutation/addOrganizationCustomField.ts @@ -21,19 +21,28 @@ import { findUserInCache } from "../../services/UserCache/findUserInCache"; import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; /** - * This function enables an admin to add an organization colleciton field. - * @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 user exists - * 2. If the user has appProfile - * 3. If the organization exists. - * 4. If the user is an admin for the organization. - * 5. If the required name and value was provided for the new custom field - * @returns Newly Added Custom Field. + * Mutation resolver to add a custom field to an organization. + * + * This function allows an admin to add a new custom field to the collection of fields for a specified organization. It performs several checks: + * + * 1. Verifies the existence of the user. + * 2. Checks if the user has an application profile. + * 3. Confirms that the organization exists. + * 4. Ensures that the user is an admin for the organization or has super admin privileges. + * 5. Validates that the name and type of the custom field are provided. + * + * If any of these conditions are not met, appropriate errors are thrown. + * + * @param _parent - The parent object for the mutation (not used in this function). + * @param args - The arguments provided with the request, including: + * - `organizationId`: The ID of the organization to which the custom field will be added. + * - `name`: The name of the new custom field. + * - `type`: The type of the new custom field. + * @param context - The context of the entire application, containing user information and other context-specific data. + * + * @returns A promise that resolves to the newly added custom field object. + * */ - export const addOrganizationCustomField: MutationResolvers["addOrganizationCustomField"] = async (_parent, args, context) => { let currentUser: InterfaceUser | null; diff --git a/src/resolvers/Mutation/addOrganizationImage.ts b/src/resolvers/Mutation/addOrganizationImage.ts index 1b3c05ba4e..b167ab0036 100644 --- a/src/resolvers/Mutation/addOrganizationImage.ts +++ b/src/resolvers/Mutation/addOrganizationImage.ts @@ -10,15 +10,36 @@ import { adminCheck } from "../../utilities"; import { uploadEncodedImage } from "../../utilities/encodedImageStorage/uploadEncodedImage"; /** - * This function adds Organization Image. - * @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 organization exists - * 3. If the user trying to add the image is an admin of organization - * @returns Updated Organization + * Mutation resolver function to add or update an organization's image. + * + * This function performs the following actions: + * 1. Retrieves the organization from the cache or database based on the provided `organizationId`. + * 2. Checks if the organization exists. If not, throws a not found error. + * 3. Verifies if the current user is an admin of the organization. + * 4. Uploads the provided image file and updates the organization's image field with the new file name. + * 5. Updates the organization document in the database with the new image information. + * 6. Caches the updated organization data. + * + * @param _parent - The parent object for the mutation. Typically, this is not used in this resolver. + * @param args - The arguments for the mutation, including: + * - `organizationId`: The ID of the organization to which the image is being added or updated. + * - `file`: The encoded image file to be uploaded. + * @param context - The context for the mutation, including: + * - `userId`: The ID of the current user making the request. + * + * @returns A promise that resolves to the updated organization document with the new image. + * + * @see Organization - The Organization model used to interact with the organizations collection in the database. + * @see MutationResolvers - The type definition for the mutation resolvers. + * @see adminCheck - Utility function to verify if a user is an admin of an organization. + * @see uploadEncodedImage - Utility function to handle the upload of an encoded image file. + * @see cacheOrganizations - Service function to cache the updated organization data. + * @see findOrganizationsInCache - Service function to retrieve organizations from cache. + * + * @remarks + * The function uses the `findOrganizationsInCache` method to first attempt to retrieve the organization from the cache. + * If the organization is not found in the cache, it queries the database. + * It then verifies the user's admin status and performs the image upload before updating the organization's image field. */ export const addOrganizationImage: MutationResolvers["addOrganizationImage"] = async (_parent, args, context) => { diff --git a/src/resolvers/Mutation/addPledgeToFundraisingCampaign.ts b/src/resolvers/Mutation/addPledgeToFundraisingCampaign.ts index 0ea4a731c8..574c0d16e9 100644 --- a/src/resolvers/Mutation/addPledgeToFundraisingCampaign.ts +++ b/src/resolvers/Mutation/addPledgeToFundraisingCampaign.ts @@ -15,19 +15,29 @@ import { import { cacheUsers } from "../../services/UserCache/cacheUser"; import { findUserInCache } from "../../services/UserCache/findUserInCache"; import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; -/** - * This function adds campaign pledge to campaign. - * @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 pledge exists - * 3. If the campaign exists - * @returns Updated pledge +/** + * Mutation resolver to add a pledge to a fundraising campaign. + * + * This function adds a specified pledge to a fundraising campaign. It performs several checks: + * + * 1. Verifies that the current user exists. + * 2. Confirms that the pledge exists. + * 3. Checks that the campaign exists. + * 4. Ensures the user has made the pledge. + * 5. Verifies that the campaign is not already associated with the pledge. + * + * If any of these conditions are not met, appropriate errors are thrown. + * + * @param _parent - The parent object for the mutation (not used in this function). + * @param args - The arguments provided with the request, including: + * - `pledgeId`: The ID of the pledge to be added. + * - `campaignId`: The ID of the campaign to which the pledge will be added. + * @param context - The context of the entire application, containing user information and other context-specific data. + * + * @returns A promise that resolves to the updated pledge object. + * */ - export const addPledgeToFundraisingCampaign: MutationResolvers["addPledgeToFundraisingCampaign"] = async ( _parent, @@ -48,7 +58,8 @@ export const addPledgeToFundraisingCampaign: MutationResolvers["addPledgeToFundr await cacheUsers([currentUser]); } } - // Checks whether currentUser exists. + + // Checks whether the current user exists. if (!currentUser) { throw new errors.NotFoundError( requestContext.translate(USER_NOT_FOUND_ERROR.MESSAGE), @@ -56,11 +67,12 @@ export const addPledgeToFundraisingCampaign: MutationResolvers["addPledgeToFundr USER_NOT_FOUND_ERROR.PARAM, ); } + const pledge = await FundraisingCampaignPledge.findOne({ _id: args.pledgeId, }).lean(); - // Checks whether pledge exists. + // Checks whether the pledge exists. if (!pledge) { throw new errors.NotFoundError( requestContext.translate( @@ -70,11 +82,12 @@ export const addPledgeToFundraisingCampaign: MutationResolvers["addPledgeToFundr FUNDRAISING_CAMPAIGN_PLEDGE_NOT_FOUND_ERROR.PARAM, ); } + const campaign = await FundraisingCampaign.findOne({ _id: args.campaignId, }).lean(); - // Checks whether campaign exists. + // Checks whether the campaign exists. if (!campaign) { throw new errors.NotFoundError( requestContext.translate(FUNDRAISING_CAMPAIGN_NOT_FOUND_ERROR.MESSAGE), @@ -82,6 +95,7 @@ export const addPledgeToFundraisingCampaign: MutationResolvers["addPledgeToFundr FUNDRAISING_CAMPAIGN_NOT_FOUND_ERROR.PARAM, ); } + // Checks whether the user has made the pledge. const pledgeUserIds = pledge.users.map((id) => id?.toString()); if (!pledgeUserIds.includes(context.userId)) { @@ -91,6 +105,7 @@ export const addPledgeToFundraisingCampaign: MutationResolvers["addPledgeToFundr USER_NOT_MADE_PLEDGE_ERROR.PARAM, ); } + // Checks whether the campaign is already added to the pledge. const pledgeCampaignIds = pledge.campaigns.map((id) => id?.toString()); if (pledgeCampaignIds.includes(args.campaignId)) { @@ -100,7 +115,8 @@ export const addPledgeToFundraisingCampaign: MutationResolvers["addPledgeToFundr FUNDRAISING_CAMPAIGN_ALREADY_ADDED.PARAM, ); } - // Add the campaign to the pledge + + // Add the campaign to the pledge. const updatedPledge = await FundraisingCampaignPledge.findOneAndUpdate( { _id: args.pledgeId, @@ -111,7 +127,7 @@ export const addPledgeToFundraisingCampaign: MutationResolvers["addPledgeToFundr { new: true }, ); - // Add the pledge to the campaign + // Add the pledge to the campaign. await FundraisingCampaign.updateOne( { _id: args.campaignId, @@ -120,5 +136,6 @@ export const addPledgeToFundraisingCampaign: MutationResolvers["addPledgeToFundr $push: { pledges: args.pledgeId }, }, ); + return updatedPledge as InterfaceFundraisingCampaignPledges; }; diff --git a/src/resolvers/Mutation/addUserCustomData.ts b/src/resolvers/Mutation/addUserCustomData.ts index 87b92ad50e..45f16ca141 100644 --- a/src/resolvers/Mutation/addUserCustomData.ts +++ b/src/resolvers/Mutation/addUserCustomData.ts @@ -11,14 +11,25 @@ import { findUserInCache } from "../../services/UserCache/findUserInCache"; import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; /** - * This function enables a user to add data for a custom field for a joined organization. - * @param _parent - parent of the current request - * @param args - payload provided with the request - * @param context - context of the entire application - * @remarks The following checks are done: - * 1. If the user exists - * 2. If the organization exists. - * @returns Newly Added User Custom Field. + * Mutation resolver to add or update custom data for a user within a joined organization. + * + * This function allows a user to add or update a custom field with a name and value for an organization + * they are a part of. It performs several checks and operations: + * + * 1. Validates that the user exists. + * 2. Verifies that the organization exists. + * 3. Checks if user custom data for the given organization already exists. + * 4. If it exists, updates the custom field; if not, creates a new entry. + * + * @param _parent - The parent object for the mutation (not used in this function). + * @param args - The arguments provided with the request, including: + * - `organizationId`: The ID of the organization for which custom data is being added. + * - `dataName`: The name of the custom data field. + * - `dataValue`: The value of the custom data field. + * @param context - The context of the entire application, including user information and other context-specific data. + * + * @returns A promise that resolves to the newly added or updated user custom data object. + * */ export const addUserCustomData: MutationResolvers["addUserCustomData"] = async ( _parent, diff --git a/src/resolvers/Mutation/addUserImage.ts b/src/resolvers/Mutation/addUserImage.ts index 5c06d83a71..f034cb4303 100644 --- a/src/resolvers/Mutation/addUserImage.ts +++ b/src/resolvers/Mutation/addUserImage.ts @@ -6,14 +6,35 @@ import { cacheUsers } from "../../services/UserCache/cacheUser"; import { findUserInCache } from "../../services/UserCache/findUserInCache"; import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; import { uploadEncodedImage } from "../../utilities/encodedImageStorage/uploadEncodedImage"; + /** - * This function adds User Image. - * @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 - * @returns Updated User + * Mutation resolver function to add or update a user's profile image. + * + * This function performs the following actions: + * 1. Retrieves the current user from the cache or database based on the `userId` from the context. + * 2. Checks if the current user exists. If not, throws a not found error. + * 3. Uploads the provided encoded image file and updates the user's profile image with the new file path. + * 4. Updates the user document in the database with the new image information. + * 5. Caches the updated user data. + * + * @param _parent - The parent object for the mutation. Typically, this is not used in this resolver. + * @param args - The arguments for the mutation, including: + * - `file`: The encoded image file to be uploaded. + * @param context - The context for the mutation, including: + * - `userId`: The ID of the current user making the request. + * + * @returns A promise that resolves to the updated user document with the new image. + * + * @see User - The User model used to interact with the users collection in the database. + * @see MutationResolvers - The type definition for the mutation resolvers. + * @see uploadEncodedImage - Utility function to handle the upload of an encoded image file. + * @see cacheUsers - Service function to cache the updated user data. + * @see findUserInCache - Service function to retrieve users from cache. + * + * @remarks + * The function first attempts to retrieve the user from the cache using `findUserInCache`. + * If the user is not found in the cache, it queries the database. + * It then performs the image upload and updates the user's profile image before saving the changes to the database. */ export const addUserImage: MutationResolvers["addUserImage"] = async ( _parent, diff --git a/src/resolvers/Mutation/addUserToGroupChat.ts b/src/resolvers/Mutation/addUserToGroupChat.ts index ff0953cdb6..d71535f578 100644 --- a/src/resolvers/Mutation/addUserToGroupChat.ts +++ b/src/resolvers/Mutation/addUserToGroupChat.ts @@ -12,18 +12,34 @@ import { findOrganizationsInCache } from "../../services/OrganizationCache/findO import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; import { adminCheck } from "../../utilities"; import type { InterfaceGroupChat } from "../../models"; + /** - * This function adds user to group chat. - * @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 group chat exists - * 2. If the organization exists - * 3. If the user trying to add the user is an admin of organization - * 4. If the user exists - * 5. If the user is already a member of the chat - * @returns Updated Group chat + * Mutation resolver function to add a user to a group chat. + * + * This function performs the following actions: + * 1. Checks if the group chat specified by `args.chatId` exists. + * 2. Checks if the organization associated with the group chat exists. + * 3. Verifies that the current user (identified by `context.userId`) is an admin of the organization. + * 4. Confirms that the user to be added (specified by `args.userId`) exists. + * 5. Ensures that the user is not already a member of the group chat. + * 6. Adds the user to the list of users in the group chat and returns the updated group chat. + * + * @param _parent - The parent object for the mutation. This parameter is not used in this resolver. + * @param args - The arguments for the mutation, including: + * - `chatId`: The ID of the group chat to which the user will be added. + * - `userId`: The ID of the user to be added to the group chat. + * @param context - The context for the mutation, including: + * - `userId`: The ID of the current user making the request. + * + * @returns A promise that resolves to the updated group chat document with the new user added. + * + * @see GroupChat - The GroupChat model used to interact with the group chats collection in the database. + * @see Organization - The Organization model used to interact with the organizations collection in the database. + * @see User - The User model used to interact with the users collection in the database. + * @see MutationResolvers - The type definition for the mutation resolvers. + * @see adminCheck - Utility function to check if the current user is an admin of the organization. + * @see findOrganizationsInCache - Service function to retrieve organizations from cache. + * @see cacheOrganizations - Service function to cache updated organization data. */ export const addUserToGroupChat: MutationResolvers["addUserToGroupChat"] = async (_parent, args, context) => { diff --git a/src/resolvers/Mutation/addUserToUserFamily.ts b/src/resolvers/Mutation/addUserToUserFamily.ts index 39eab3dfae..1c52f462d7 100644 --- a/src/resolvers/Mutation/addUserToUserFamily.ts +++ b/src/resolvers/Mutation/addUserToUserFamily.ts @@ -13,17 +13,25 @@ import { } from "../../constants"; import { findUserInCache } from "../../services/UserCache/findUserInCache"; import { cacheUsers } from "../../services/UserCache/cacheUser"; + /** - * This function adds user to the family. - * @param _parent - parent of current request - * @param args - payload provided with the request - * @param context - context of the entire application - * @remarks The following checks are done: - * 1. If the family exists - * 2. If the user exists - * 3. If the user is already member of the family - * 4. If the user is admin of the user Family - * @returns Updated family + * Adds a user to a user family. + * + * This function allows an admin to add a user to a specific user family. It performs several checks: + * + * 1. Verifies if the user family exists. + * 2. Checks if the user exists. + * 3. Confirms that the user is not already a member of the family. + * 4. Ensures that the current user is an admin of the user family. + * + * @param _parent - The parent object for the mutation (not used in this function). + * @param args - The arguments provided with the request, including: + * - `familyId`: The ID of the user family to which the user will be added. + * - `userId`: The ID of the user to be added to the user family. + * @param context - The context of the entire application, including user information and other context-specific data. + * + * @returns A promise that resolves to the updated user family object. + * */ export const addUserToUserFamily: MutationResolvers["addUserToUserFamily"] = async (_parent, args, context) => { @@ -51,7 +59,7 @@ export const addUserToUserFamily: MutationResolvers["addUserToUserFamily"] = ); } - //check wheather family exists + // Check whether family exists. if (!userFamily) { throw new errors.NotFoundError( requestContext.translate(USER_FAMILY_NOT_FOUND_ERROR.MESSAGE), @@ -60,14 +68,14 @@ export const addUserToUserFamily: MutationResolvers["addUserToUserFamily"] = ); } - //check whether user is admin of the family + // Check whether user is an admin of the family. await adminCheck(currentUser?._id, userFamily); const isUserMemberOfUserFamily = userFamily.users.some((user) => user.equals(args.userId), ); - // Checks whether user with _id === args.userId is already a member of Family. + // Checks whether user with _id === args.userId is already a member of the family. if (isUserMemberOfUserFamily) { throw new errors.ConflictError( requestContext.translate(USER_ALREADY_MEMBER_ERROR.MESSAGE), @@ -76,7 +84,7 @@ export const addUserToUserFamily: MutationResolvers["addUserToUserFamily"] = ); } - // Adds args.userId to users lists on family group and return the updated family. + // Adds args.userId to the users list in the user family and returns the updated family. const updatedFamily = await UserFamily.findOneAndUpdate( { _id: args.familyId, diff --git a/src/resolvers/Mutation/adminRemoveGroup.ts b/src/resolvers/Mutation/adminRemoveGroup.ts index 67dcb37e51..972f6bca06 100644 --- a/src/resolvers/Mutation/adminRemoveGroup.ts +++ b/src/resolvers/Mutation/adminRemoveGroup.ts @@ -10,19 +10,34 @@ import { cacheOrganizations } from "../../services/OrganizationCache/cacheOrgani import { findOrganizationsInCache } from "../../services/OrganizationCache/findOrganizationsInCache"; import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; import { adminCheck } from "../../utilities"; + /** - * This function enables an admin to remove a group. - * @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 group chat exists - * 2. If the organization exists - * 3. If the user exists - * 4. If the user is an admin of organization - * @returns Deleted group chat + * Mutation resolver function to remove a group chat. + * + * This function performs the following actions: + * 1. Checks if the group chat specified by `args.groupId` exists. + * 2. Verifies that the organization associated with the group chat exists. + * 3. Ensures that the current user (identified by `context.userId`) exists. + * 4. Checks that the current user is authorized as an admin of the organization. + * 5. Deletes the group chat from the database. + * + * @param _parent - The parent object for the mutation. This parameter is not used in this resolver. + * @param args - The arguments for the mutation, including: + * - `groupId`: The ID of the group chat to be removed. + * @param context - The context for the mutation, including: + * - `userId`: The ID of the current user making the request. + * + * @returns A promise that resolves to the deleted group chat document. + * + * @see GroupChat - The GroupChat model used to interact with the group chats collection in the database. + * @see Organization - The Organization model used to interact with the organizations collection in the database. + * @see User - The User model used to interact with the users collection in the database. + * @see AppUserProfile - The AppUserProfile model used to retrieve the user's profile information. + * @see MutationResolvers - The type definition for the mutation resolvers. + * @see adminCheck - Utility function to check if the current user is an admin of the organization. + * @see findOrganizationsInCache - Service function to retrieve organizations from cache. + * @see cacheOrganizations - Service function to cache updated organization data. */ - export const adminRemoveGroup: MutationResolvers["adminRemoveGroup"] = async ( _parent, args, diff --git a/src/resolvers/Mutation/blockPluginCreationBySuperadmin.ts b/src/resolvers/Mutation/blockPluginCreationBySuperadmin.ts index 2aacf1a0b3..670bb918c2 100644 --- a/src/resolvers/Mutation/blockPluginCreationBySuperadmin.ts +++ b/src/resolvers/Mutation/blockPluginCreationBySuperadmin.ts @@ -11,16 +11,25 @@ import { cacheUsers } from "../../services/UserCache/cacheUser"; import { findUserInCache } from "../../services/UserCache/findUserInCache"; import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; import { superAdminCheck } from "../../utilities"; + /** - * This function enables an admin to create block plugin. - * @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 user exists - * 2.If the user has appUserProfile - * 2. If the user is the SUPERADMIN of organization - * @returns Deleted updated user + * Allows a superadmin to enable or disable plugin creation for a specific user. + * + * This function performs several checks: + * + * 1. Verifies if the current user exists. + * 2. Ensures that the current user has an associated app user profile. + * 3. Confirms that the current user is a superadmin. + * 4. Checks if the target user exists and updates their `pluginCreationAllowed` field based on the provided value. + * + * @param _parent - The parent object for the mutation (not used in this function). + * @param args - The arguments provided with the request, including: + * - `userId`: The ID of the user whose plugin creation permissions are being modified. + * - `blockUser`: A boolean indicating whether to block (`true`) or allow (`false`) plugin creation for the user. + * @param context - The context of the entire application, including user information and other context-specific data. + * + * @returns A promise that resolves to the updated user app profile object with the new `pluginCreationAllowed` value. + * */ export const blockPluginCreationBySuperadmin: MutationResolvers["blockPluginCreationBySuperadmin"] = async (_parent, args, context) => { @@ -68,6 +77,7 @@ export const blockPluginCreationBySuperadmin: MutationResolvers["blockPluginCrea // Checks whether currentUser is a SUPERADMIN. superAdminCheck(currentUserAppProfile as InterfaceAppUserProfile); + const userAppProfile = await AppUserProfile.findOne({ userId: args.userId, }).lean(); @@ -78,9 +88,10 @@ export const blockPluginCreationBySuperadmin: MutationResolvers["blockPluginCrea USER_NOT_FOUND_ERROR.PARAM, ); } + /* - Sets pluginCreationAllowed field on document of appUserProfile with _id === args.userId - to !args.blockUser and returns the updated user. + Sets the pluginCreationAllowed field on the document of the appUserProfile with _id === args.userId + to !args.blockUser and returns the updated user profile. */ return (await AppUserProfile.findOneAndUpdate( { diff --git a/src/resolvers/Mutation/blockUser.ts b/src/resolvers/Mutation/blockUser.ts index b23f6c14d3..cfe0104d5b 100644 --- a/src/resolvers/Mutation/blockUser.ts +++ b/src/resolvers/Mutation/blockUser.ts @@ -13,17 +13,34 @@ import { findOrganizationsInCache } from "../../services/OrganizationCache/findO import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; import { adminCheck } from "../../utilities"; import type { InterfaceUser } from "../../models"; + /** - * This function enables blocking 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. If the organization exists - * 2. If the user exists - * 3. If the user is an admin of organization - * 4. If the user to be blocked is already blocked by the organization - * @returns Deleted updated user + * Mutation resolver function to block a user from an organization. + * + * This function performs the following actions: + * 1. Verifies that the organization specified by `args.organizationId` exists. + * 2. Ensures that the user specified by `args.userId` exists. + * 3. Checks if the user attempting to block the user is an admin of the organization. + * 4. Verifies if the user to be blocked is currently a member of the organization. + * 5. Ensures that the user is not attempting to block themselves. + * 6. Blocks the user by adding them to the organization's `blockedUsers` list and removing them from the `members` list. + * 7. Updates the user's document to reflect that they have been blocked by the organization, and removes the organization from their `joinedOrganizations` list. + * + * @param _parent - The parent object for the mutation. This parameter is not used in this resolver. + * @param args - The arguments for the mutation, including: + * - `organizationId`: The ID of the organization from which the user is to be blocked. + * - `userId`: The ID of the user to be blocked. + * @param context - The context for the mutation, including: + * - `userId`: The ID of the current user making the request. + * + * @returns A promise that resolves to the updated user document after blocking. + * + * @see Organization - The Organization model used to interact with the organizations collection in the database. + * @see User - The User model used to interact with the users collection in the database. + * @see MutationResolvers - The type definition for the mutation resolvers. + * @see adminCheck - Utility function to check if the current user is an admin of the organization. + * @see findOrganizationsInCache - Service function to retrieve organizations from cache. + * @see cacheOrganizations - Service function to cache updated organization data. */ export const blockUser: MutationResolvers["blockUser"] = async ( _parent, @@ -110,7 +127,7 @@ export const blockUser: MutationResolvers["blockUser"] = async ( /* Adds args.userId to blockedUsers list on organization's document. - Removes args.userId from the organization's members list + Removes args.userId from the organization's members list. */ const updatedOrganization = await Organization.findOneAndUpdate( { @@ -134,9 +151,8 @@ export const blockUser: MutationResolvers["blockUser"] = async ( } /* - Adds organization._id to organizationsBlockedBy list on user's document - with _id === args.userId and returns the updated user. - Remove organization's id from joinedOrganizations list on args.userId. + Adds organization._id to organizationsBlockedBy list on user's document. + Removes organization._id from joinedOrganizations list on user's document. */ return (await User.findOneAndUpdate( { diff --git a/src/resolvers/Mutation/cancelMembershipRequest.ts b/src/resolvers/Mutation/cancelMembershipRequest.ts index d348d23fcf..4408d0b80f 100644 --- a/src/resolvers/Mutation/cancelMembershipRequest.ts +++ b/src/resolvers/Mutation/cancelMembershipRequest.ts @@ -12,17 +12,35 @@ import { findOrganizationsInCache } from "../../services/OrganizationCache/findO import { cacheUsers } from "../../services/UserCache/cacheUser"; import { findUserInCache } from "../../services/UserCache/findUserInCache"; import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; + /** - * This function enables to cancel membership request. - * @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 membership request exists - * 2. If the organization exists - * 3. If the user exists - * 4. If the user is the creator of the request - * @returns Deleted membership request + * Mutation resolver function to cancel a membership request. + * + * This function performs the following actions: + * 1. Verifies that the membership request specified by `args.membershipRequestId` exists. + * 2. Ensures that the organization associated with the membership request exists. + * 3. Confirms that the user specified by `context.userId` exists. + * 4. Checks if the current user is the creator of the membership request. + * 5. Deletes the membership request. + * 6. Updates the organization document to remove the membership request from its `membershipRequests` list. + * 7. Updates the user's document to remove the membership request from their `membershipRequests` list. + * + * @param _parent - The parent object for the mutation. This parameter is not used in this resolver. + * @param args - The arguments for the mutation, including: + * - `membershipRequestId`: The ID of the membership request to be canceled. + * @param context - The context for the mutation, including: + * - `userId`: The ID of the current user making the request. + * + * @returns A promise that resolves to the deleted membership request. + * + * @see MembershipRequest - The MembershipRequest model used to interact with the membership requests collection in the database. + * @see Organization - The Organization model used to interact with the organizations collection in the database. + * @see User - The User model used to interact with the users collection in the database. + * @see MutationResolvers - The type definition for the mutation resolvers. + * @see findOrganizationsInCache - Service function to retrieve organizations from cache. + * @see cacheOrganizations - Service function to cache updated organization data. + * @see findUserInCache - Service function to retrieve users from cache. + * @see cacheUsers - Service function to cache updated user data. */ export const cancelMembershipRequest: MutationResolvers["cancelMembershipRequest"] = async (_parent, args, context) => { diff --git a/src/resolvers/Mutation/createActionItem.ts b/src/resolvers/Mutation/createActionItem.ts index dc93a6941c..8491873e63 100644 --- a/src/resolvers/Mutation/createActionItem.ts +++ b/src/resolvers/Mutation/createActionItem.ts @@ -30,23 +30,31 @@ import { findUserInCache } from "../../services/UserCache/findUserInCache"; import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; /** - * This function enables to create an action item. - * @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 user exists - * 2.If the user has appUserProfile - * 3. If the asignee exists - * 4. If the actionItemCategory exists - * 5. If the actionItemCategory is disabled - * 6. If the asignee is a member of the organization - * 7. If the user is a member of the organization - * 8. If the event exists (if action item related to an event) - * 9. If the user is authorized. - * @returns Created action item + * Creates a new action item and assigns it to a user. + * + * This function performs several checks: + * + * 1. Verifies if the current user exists. + * 2. Ensures that the current user has an associated app user profile. + * 3. Checks if the assignee exists. + * 4. Validates if the action item category exists and is not disabled. + * 5. Confirms that the assignee is a member of the organization associated with the action item category. + * 6. If the action item is related to an event, checks if the event exists and whether the current user is an admin of that event. + * 7. Verifies if the current user is an admin of the organization or a superadmin. + * + * @param _parent - The parent object for the mutation (not used in this function). + * @param args - The arguments provided with the request, including: + * - `data`: An object containing: + * - `assigneeId`: The ID of the user to whom the action item is assigned. + * - `preCompletionNotes`: Notes to be added before the action item is completed. + * - `dueDate`: The due date for the action item. + * - `eventId` (optional): The ID of the event associated with the action item. + * - `actionItemCategoryId`: The ID of the action item category. + * @param context - The context of the entire application, including user information and other context-specific data. + * + * @returns A promise that resolves to the created action item object. + * */ - export const createActionItem: MutationResolvers["createActionItem"] = async ( _parent, args, @@ -63,6 +71,7 @@ export const createActionItem: MutationResolvers["createActionItem"] = async ( await cacheUsers([currentUser]); } } + // Checks whether currentUser with _id === context.userId exists. if (currentUser === null) { throw new errors.NotFoundError( @@ -71,6 +80,7 @@ export const createActionItem: MutationResolvers["createActionItem"] = async ( USER_NOT_FOUND_ERROR.PARAM, ); } + let currentUserAppProfile: InterfaceAppUserProfile | null; const appUserProfileFoundInCache = await findAppUserProfileCache([ currentUser.appUserProfileId?.toString(), @@ -84,6 +94,7 @@ export const createActionItem: MutationResolvers["createActionItem"] = async ( await cacheAppUserProfile([currentUserAppProfile]); } } + if (!currentUserAppProfile) { throw new errors.UnauthorizedError( requestContext.translate(USER_NOT_AUTHORIZED_ERROR.MESSAGE), @@ -96,7 +107,7 @@ export const createActionItem: MutationResolvers["createActionItem"] = async ( _id: args.data.assigneeId, }); - // Checks whether the asignee exists. + // Checks whether the assignee exists. if (assignee === null) { throw new errors.NotFoundError( requestContext.translate(USER_NOT_FOUND_ERROR.MESSAGE), @@ -109,7 +120,7 @@ export const createActionItem: MutationResolvers["createActionItem"] = async ( _id: args.actionItemCategoryId, }).lean(); - // Checks if the actionItemCategory exists + // Checks if the actionItemCategory exists. if (!actionItemCategory) { throw new errors.NotFoundError( requestContext.translate(ACTION_ITEM_CATEGORY_NOT_FOUND_ERROR.MESSAGE), @@ -118,7 +129,7 @@ export const createActionItem: MutationResolvers["createActionItem"] = async ( ); } - // Checks if the actionItemCategory is disabled + // Checks if the actionItemCategory is disabled. if (actionItemCategory.isDisabled) { throw new errors.ConflictError( requestContext.translate(ACTION_ITEM_CATEGORY_IS_DISABLED.MESSAGE), @@ -173,7 +184,7 @@ export const createActionItem: MutationResolvers["createActionItem"] = async ( ); } - // Checks if the currUser is an admin of the event + // Checks if the currUser is an admin of the event. currentUserIsEventAdmin = currEvent.admins.some( (admin) => admin === context.userID || @@ -181,7 +192,7 @@ export const createActionItem: MutationResolvers["createActionItem"] = async ( ); } - // Checks if the currUser is an admin of the organization + // Checks if the currentUser is an admin of the organization. const currentUserIsOrgAdmin = currentUserAppProfile.adminFor.some( (organizationId) => (organizationId && @@ -191,7 +202,7 @@ export const createActionItem: MutationResolvers["createActionItem"] = async ( ), ); - // Checks whether currentUser with _id === context.userId is authorized for the operation. + // Checks whether the currentUser is authorized for the operation. if ( currentUserIsEventAdmin === false && currentUserIsOrgAdmin === false && @@ -204,7 +215,7 @@ export const createActionItem: MutationResolvers["createActionItem"] = async ( ); } - // Creates new action item. + // Creates and returns the new action item. const createActionItem = await ActionItem.create({ assigneeId: args.data.assigneeId, assignerId: context.userId, @@ -215,6 +226,5 @@ export const createActionItem: MutationResolvers["createActionItem"] = async ( creatorId: context.userId, }); - // Returns created action item. return createActionItem.toObject(); }; diff --git a/src/resolvers/Mutation/createActionItemCategory.ts b/src/resolvers/Mutation/createActionItemCategory.ts index 462ff579d8..711f22449e 100644 --- a/src/resolvers/Mutation/createActionItemCategory.ts +++ b/src/resolvers/Mutation/createActionItemCategory.ts @@ -15,18 +15,34 @@ import { findUserInCache } from "../../services/UserCache/findUserInCache"; import { adminCheck } from "../../utilities"; /** - * This function enables to create an ActionItemCategory. - * @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 User exists - * 2. If the Organization exists - * 3. Is the User is Authorized - * 4. If the action item category already exists - * @returns Created ActionItemCategory + * Mutation resolver function to create a new ActionItemCategory. + * + * This function performs the following actions: + * 1. Verifies that the current user, identified by `context.userId`, exists. + * 2. Ensures that the organization specified by `args.organizationId` exists. + * 3. Checks if the current user is authorized to perform the operation (must be an admin). + * 4. Checks if an ActionItemCategory with the provided name already exists for the specified organization. + * 5. Creates a new ActionItemCategory if no conflicts are found. + * + * @param _parent - The parent object for the mutation. This parameter is not used in this resolver. + * @param args - The arguments for the mutation, including: + * - `name`: The name of the ActionItemCategory to be created. + * - `organizationId`: The ID of the organization where the ActionItemCategory will be created. + * @param context - The context for the mutation, including: + * - `userId`: The ID of the current user making the request. + * + * @returns A promise that resolves to the created ActionItemCategory. + * + * @see ActionItemCategory - The ActionItemCategory model used to interact with the ActionItemCategory collection in the database. + * @see Organization - The Organization model used to interact with the organizations collection in the database. + * @see User - The User model used to interact with the users collection in the database. + * @see MutationResolvers - The type definition for the mutation resolvers. + * @see findOrganizationsInCache - Service function to retrieve organizations from cache. + * @see cacheOrganizations - Service function to cache updated organization data. + * @see findUserInCache - Service function to retrieve users from cache. + * @see cacheUsers - Service function to cache updated user data. + * @see adminCheck - Utility function to check if a user is an admin of an organization. */ - export const createActionItemCategory: MutationResolvers["createActionItemCategory"] = async (_parent, args, context) => { let currentUser: InterfaceUser | null; diff --git a/src/resolvers/Mutation/createAdmin.ts b/src/resolvers/Mutation/createAdmin.ts index 4b0b3e1bf1..22f9df9fca 100644 --- a/src/resolvers/Mutation/createAdmin.ts +++ b/src/resolvers/Mutation/createAdmin.ts @@ -15,19 +15,34 @@ import { cacheOrganizations } from "../../services/OrganizationCache/cacheOrgani import { findOrganizationsInCache } from "../../services/OrganizationCache/findOrganizationsInCache"; import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; import { superAdminCheck } from "../../utilities"; + /** - * This function enables to create an admin for an organization. - * @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 organization exists - * 2. If the user has appUserProfile - * 3. If the current user is the creator of the organization - * 4. If the user exists - * 5. If the user is a member of the organization - * 6. If the user is already an admin of the organization - * @returns Updated appUserProfile + * Creates an admin for an organization by adding the specified user to the organization's admin list. + * + * This function performs several checks: + * + * 1. Verifies if the specified organization exists. + * 2. Ensures the current user is found and has an associated app user profile. + * 3. Checks if the current user is the creator of the organization. + * 4. Checks if the specified user exists and is a member of the organization. + * 5. Ensures the specified user is not already an admin of the organization. + * + * @param _parent - The parent object for the mutation (not used in this function). + * @param args - The arguments provided with the request, including: + * - `data`: An object containing: + * - `organizationId`: The ID of the organization to which the user will be added as an admin. + * - `userId`: The ID of the user to be made an admin. + * @param context - The context of the entire application, including user information and other context-specific data. + * + * @returns An object containing: + * - `user`: The updated app user profile of the user being added as an admin. + * - `userErrors`: An array of error objects if any errors occurred, otherwise an empty array. + * + * @remarks The function handles the following: + * - Caches and retrieves the organization data. + * - Verifies the existence and profile of the current user. + * - Ensures the user to be added is a member of the organization and is not already an admin. + * - Updates the organization's admin list and the app user profile of the newly added admin. */ export const createAdmin: MutationResolvers["createAdmin"] = async ( _parent, @@ -50,13 +65,8 @@ export const createAdmin: MutationResolvers["createAdmin"] = async ( await cacheOrganizations([organization as InterfaceOrganization]); } - // Checks whether organization exists. + // Checks whether the organization exists. if (!organization) { - // throw new errors.NotFoundError( - // requestContext.translate(ORGANIZATION_NOT_FOUND_ERROR.MESSAGE), - // ORGANIZATION_NOT_FOUND_ERROR.CODE, - // ORGANIZATION_NOT_FOUND_ERROR.PARAM, - // ); return { user: new AppUserProfile(), userErrors: [ @@ -69,16 +79,13 @@ export const createAdmin: MutationResolvers["createAdmin"] = async ( ], }; } - // Checks whether the current user is a superAdmin + + // Checks whether the current user exists and has an app user profile. const currentUser = await User.findById({ _id: context.userId, }); + if (!currentUser) { - // throw new errors.NotFoundError( - // requestContext.translate(USER_NOT_FOUND_ERROR.MESSAGE), - // USER_NOT_FOUND_ERROR.CODE, - // USER_NOT_FOUND_ERROR.PARAM, - // ); return { user: new AppUserProfile(), userErrors: [ @@ -89,16 +96,12 @@ export const createAdmin: MutationResolvers["createAdmin"] = async ( ], }; } + const currentUserAppProfile = await AppUserProfile.findOne({ userId: currentUser._id, }).lean(); if (!currentUserAppProfile) { - // throw new errors.UnauthorizedError( - // requestContext.translate(USER_NOT_AUTHORIZED_ERROR.MESSAGE), - // USER_NOT_AUTHORIZED_ERROR.CODE, - // USER_NOT_AUTHORIZED_ERROR.PARAM, - // ); return { user: new AppUserProfile(), userErrors: [ @@ -111,16 +114,12 @@ export const createAdmin: MutationResolvers["createAdmin"] = async ( } superAdminCheck(currentUserAppProfile as InterfaceAppUserProfile); + const userAppProfile = await AppUserProfile.findOne({ userId: args.data.userId, }).lean(); - 1; + if (!userAppProfile) { - // throw new errors.NotFoundError( - // requestContext.translate(USER_NOT_FOUND_ERROR.MESSAGE), - // USER_NOT_FOUND_ERROR.CODE, - // USER_NOT_FOUND_ERROR.PARAM, - // ); return { user: new AppUserProfile(), userErrors: [ @@ -131,34 +130,13 @@ export const createAdmin: MutationResolvers["createAdmin"] = async ( ], }; } - // const userExists = !!(await User.exists({ - // _id: args.data.userId, - // })); - - // // Checks whether user with _id === args.data.userId exists. - // if (userExists === false) { - // return { - // user: new AppUserProfile(), - // userErrors: [ - // { - // __typename: "UserNotFoundError", - // message: requestContext.translate("test"), - // }, - // ], - // }; - // } + // Checks if the user is a member of the organization. const userIsOrganizationMember = organization.members.some((member) => new mongoose.Types.ObjectId(member.toString()).equals(args.data.userId), ); - // Checks whether user with _id === args.data.userId is not a member of organization. if (userIsOrganizationMember === false) { - // throw new errors.NotFoundError( - // requestContext.translate(ORGANIZATION_MEMBER_NOT_FOUND_ERROR.MESSAGE), - // ORGANIZATION_MEMBER_NOT_FOUND_ERROR.CODE, - // ORGANIZATION_MEMBER_NOT_FOUND_ERROR.PARAM, - // ); return { user: new AppUserProfile(), userErrors: [ @@ -172,17 +150,12 @@ export const createAdmin: MutationResolvers["createAdmin"] = async ( }; } + // Checks if the user is already an admin of the organization. const userIsOrganizationAdmin = organization.admins.some((admin) => new mongoose.Types.ObjectId(admin.toString()).equals(args.data.userId), ); - // Checks whether user with _id === args.data.userId is already an admin of organization. if (userIsOrganizationAdmin === true) { - // throw new errors.UnauthorizedError( - // requestContext.translate(USER_NOT_AUTHORIZED_ERROR.MESSAGE), - // USER_NOT_AUTHORIZED_ERROR.CODE, - // USER_NOT_AUTHORIZED_ERROR.PARAM, - // ); return { user: new AppUserProfile(), userErrors: [ @@ -194,7 +167,7 @@ export const createAdmin: MutationResolvers["createAdmin"] = async ( }; } - // Adds args.data.userId to admins list of organization's document. + // Updates the organization document to add the user as an admin. const updatedOrganization = await Organization.findOneAndUpdate( { _id: organization._id, @@ -213,10 +186,7 @@ export const createAdmin: MutationResolvers["createAdmin"] = async ( await cacheOrganizations([updatedOrganization]); } - /* - Adds organization._id to adminFor list on appUserProfile's document with userId === args.data.userId - and returns the updated appUserProfile of the user. - */ + // Updates the app user profile to reflect the new admin role. return { user: await AppUserProfile.findOneAndUpdate( { diff --git a/src/resolvers/Mutation/createAdvertisement.ts b/src/resolvers/Mutation/createAdvertisement.ts index c623223149..1b5347851c 100644 --- a/src/resolvers/Mutation/createAdvertisement.ts +++ b/src/resolvers/Mutation/createAdvertisement.ts @@ -11,6 +11,29 @@ import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; import { uploadEncodedImage } from "../../utilities/encodedImageStorage/uploadEncodedImage"; import { uploadEncodedVideo } from "../../utilities/encodedVideoStorage/uploadEncodedVideo"; +/** + * Mutation resolver function to create a new advertisement. + * + * This function performs the following actions: + * 1. Verifies that the current user, identified by `context.userId`, exists. + * 2. Ensures that the organization specified by `args.input.organizationId` exists. + * 3. Uploads the media file if provided, determining its type (image or video). + * 4. Creates a new advertisement with the provided details. + * + * @param _parent - The parent object for the mutation. This parameter is not used in this resolver. + * @param args - The arguments for the mutation, including: + * - `input`: An object containing: + * - `organizationId`: The ID of the organization where the advertisement will be created. + * - `mediaFile`: The encoded media file (image or video) to be uploaded. + * - Other advertisement details. + * @param context - The context for the mutation, including: + * - `userId`: The ID of the current user making the request. + * - `apiRootUrl`: The root URL for the API to construct the media URL. + * + * @returns An object containing the created advertisement, including: + * - `advertisement`: The created advertisement details with the media URL. + * + */ export const createAdvertisement: MutationResolvers["createAdvertisement"] = async (_parent, args, context) => { // Get the current user diff --git a/src/resolvers/Mutation/createAgendaCategory.ts b/src/resolvers/Mutation/createAgendaCategory.ts index 0be0342a74..85c0176d1f 100644 --- a/src/resolvers/Mutation/createAgendaCategory.ts +++ b/src/resolvers/Mutation/createAgendaCategory.ts @@ -19,22 +19,36 @@ import { cacheUsers } from "../../services/UserCache/cacheUser"; import { findUserInCache } from "../../services/UserCache/findUserInCache"; import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; import { adminCheck } from "../../utilities"; + /** - * This is a resolver function for the GraphQL mutation 'createAgendaCategory'. + * Creates a new agenda category and associates it with a specified organization. + * + * This resolver function performs the following steps: + * + * 1. Retrieves the current user based on the userId from the context. + * 2. Fetches the associated app user profile for the current user. + * 3. Retrieves the organization specified in the input, either from the cache or from the database. + * 4. Validates the existence of the organization. + * 5. Checks if the current user is authorized to perform this operation. + * 6. Creates a new agenda category and associates it with the specified organization. + * 7. Updates the organization document with the new agenda category. + * + * @param _parent - The parent object for the mutation (not used in this function). + * @param args - The arguments provided with the request, including: + * - `input`: An object containing: + * - `organizationId`: The ID of the organization to which the new agenda category will be added. + * - `name`: The name of the new agenda category. + * - `description`: A description of the new agenda category. + * @param context - The context of the entire application, including user information (context.userId). * - * This resolver creates a new agenda category, associates it with an organization, - * and updates the organization with the new agenda category. + * @returns A promise that resolves to the created agenda category object. * - * @returns A promise that resolves to the created agenda category. - * @throws `NotFoundError` If the user or organization is not found. - * @throws `UnauthorizedError` If the user does not have the required permissions. - * @throws `InternalServerError` For other potential issues during agenda category creation. + * @remarks The function performs caching and retrieval operations to ensure the latest data is used, + * and it updates the organization document to include the new agenda category. */ - export const createAgendaCategory: MutationResolvers["createAgendaCategory"] = async (_parent, args, context) => { - // Find the current user based on the provided createdBy ID or use the context userId - + // Find the current user based on the provided userId from the context const userId = context.userId; let currentUser: InterfaceUser | null; @@ -91,7 +105,7 @@ export const createAgendaCategory: MutationResolvers["createAgendaCategory"] = await cacheOrganizations([organization]); } - // Checks whether the organization with _id === args.organizationId exists. + // Checks whether the organization with _id === args.input.organizationId exists. if (!organization) { throw new errors.NotFoundError( requestContext.translate(ORGANIZATION_NOT_FOUND_ERROR.MESSAGE), @@ -109,6 +123,8 @@ export const createAgendaCategory: MutationResolvers["createAgendaCategory"] = createdBy: currentUser?._id, createdAt: new Date(), }); + + // Update the organization's document to include the new agenda category await Organization.findByIdAndUpdate( organization._id, { diff --git a/src/resolvers/Mutation/createAgendaItem.ts b/src/resolvers/Mutation/createAgendaItem.ts index 342a4293ee..6d75d6a17b 100644 --- a/src/resolvers/Mutation/createAgendaItem.ts +++ b/src/resolvers/Mutation/createAgendaItem.ts @@ -27,13 +27,28 @@ import { findOrganizationsInCache } from "../../services/OrganizationCache/findO import { cacheUsers } from "../../services/UserCache/cacheUser"; import { findUserInCache } from "../../services/UserCache/findUserInCache"; import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; + /** - * Create an agenda item based on the provided input. + * Creates a new agenda item and associates it with an event if specified. + * + * This function performs the following actions: + * 1. Verifies that the current user exists and is authorized. + * 2. Checks the existence of the specified organization. + * 3. If a related event is specified, verifies its existence and checks if the user is an admin of the event. + * 4. Checks if the user is an admin of the organization or has super admin privileges. + * 5. Creates the new agenda item and associates it with the event if applicable. + * + * @param _parent - The parent object for the mutation. This parameter is not used in this resolver. + * @param args - The arguments for the mutation, including: + * - `input`: An object containing: + * - `organizationId`: The ID of the organization where the agenda item will be created. + * - `relatedEventId` (optional): The ID of the related event, if applicable. + * - Other agenda item details. + * @param context - The context for the mutation, including: + * - `userId`: The ID of the current user making the request. * - * @param _parent - parent of current request - * @param args - payload provided with the request - * @param context - context of entire application * @returns The created agenda item. + * */ export const createAgendaItem: MutationResolvers["createAgendaItem"] = async ( _parent, diff --git a/src/resolvers/Mutation/createAgendaSection.ts b/src/resolvers/Mutation/createAgendaSection.ts index a54d32d50d..71d90ac865 100644 --- a/src/resolvers/Mutation/createAgendaSection.ts +++ b/src/resolvers/Mutation/createAgendaSection.ts @@ -13,14 +13,26 @@ import { findUserInCache } from "../../services/UserCache/findUserInCache"; import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; /** - * Resolver function for the GraphQL mutation 'createAgendaSection'. + * Creates a new agenda section and performs authorization checks. * - * This resolver creates a new agenda section and performs necessary authorization checks. + * This resolver performs the following steps: * - * @param _parent - The parent object, not used in this resolver. - * @param args - The input arguments for the mutation. - * @param context - The context object containing user information. - * @returns A promise that resolves to the created agenda section. + * 1. Retrieves the current user based on the userId from the context. + * 2. Fetches the associated app user profile for the current user. + * 3. Validates the existence of the related event and checks user permissions. + * 4. Creates a new agenda section and sets the appropriate metadata. + * + * @param _parent - The parent object for the mutation (not used in this function). + * @param args - The arguments provided with the mutation, including: + * - `input`: An object containing: + * - `relatedEvent`: The ID of the event to which the new agenda section is related. + * - Additional fields for the agenda section. + * @param context - The context of the entire application, including user information (context.userId). + * + * @returns A promise that resolves to the created agenda section object. + * + * @remarks This function performs caching and retrieval operations to ensure the latest data is used. + * It also verifies that the user has the necessary permissions to create the agenda section in the context of the specified event. */ export const createAgendaSection: MutationResolvers["createAgendaSection"] = async (_parent, args, context) => { diff --git a/src/resolvers/Mutation/createComment.ts b/src/resolvers/Mutation/createComment.ts index 44521a12c8..8e1f83a7dd 100644 --- a/src/resolvers/Mutation/createComment.ts +++ b/src/resolvers/Mutation/createComment.ts @@ -6,13 +6,23 @@ import { cacheComments } from "../../services/CommentCache/cacheComments"; import { cachePosts } from "../../services/PostCache/cachePosts"; /** - * This function enables to create comment. - * @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 user exists - * @returns Created comment + * Creates a new comment and associates it with the specified post. + * + * This function performs the following actions: + * 1. Verifies that the post specified by `postId` exists. + * 2. Creates a new comment associated with the post. + * 3. Increments the `commentCount` for the post by 1. + * 4. Caches the newly created comment and updated post data. + * + * @param _parent - The parent object for the mutation. This parameter is not used in this resolver. + * @param args - The arguments for the mutation, including: + * - `postId`: The ID of the post to which the comment will be associated. + * - `data`: The comment data, including the content of the comment. + * @param context - The context for the mutation, including: + * - `userId`: The ID of the current user creating the comment. + * + * @returns The created comment. + * */ export const createComment: MutationResolvers["createComment"] = async ( _parent, diff --git a/src/resolvers/Mutation/createDirectChat.ts b/src/resolvers/Mutation/createDirectChat.ts index 6c8675790f..85d352638a 100644 --- a/src/resolvers/Mutation/createDirectChat.ts +++ b/src/resolvers/Mutation/createDirectChat.ts @@ -1,51 +1,35 @@ import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; -import { User, Organization, DirectChat } from "../../models"; +import { User, DirectChat } from "../../models"; import { errors, requestContext } from "../../libraries"; -import { - USER_NOT_FOUND_ERROR, - ORGANIZATION_NOT_FOUND_ERROR, -} from "../../constants"; -import { findOrganizationsInCache } from "../../services/OrganizationCache/findOrganizationsInCache"; -import { cacheOrganizations } from "../../services/OrganizationCache/cacheOrganizations"; + +import { USER_NOT_FOUND_ERROR } from "../../constants"; + /** - * This function enables to create direct chat. - * @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 organization exists - * 2. If the user exists - * @returns Created chat + * Creates a new direct chat and associates it with an organization. + * + * This resolver performs the following steps: + * + * 1. Retrieves the organization based on the provided `organizationId`. + * 2. Checks if the organization exists, either from cache or database. + * 3. Validates that all user IDs provided in `userIds` exist. + * 4. Creates a new direct chat with the specified users and organization. + * + * @param _parent - The parent object, not used in this resolver. + * @param args - The input arguments for the mutation, including: + * - `data`: An object containing: + * - `organizationId`: The ID of the organization to associate with the direct chat. + * - `userIds`: An array of user IDs to be included in the direct chat. + * @param context - The context object containing user information (context.userId). + * + * @returns A promise that resolves to the created direct chat object. + * + * @remarks This function includes caching operations to optimize data retrieval and ensures that all user IDs are valid before creating the direct chat. */ export const createDirectChat: MutationResolvers["createDirectChat"] = async ( _parent, args, context, ) => { - let organization; - - const organizationFoundInCache = await findOrganizationsInCache([ - args.data.organizationId, - ]); - - organization = organizationFoundInCache[0]; - - if (organizationFoundInCache.includes(null)) { - organization = await Organization.findOne({ - _id: args.data.organizationId, - }).lean(); - if (organization) await cacheOrganizations([organization]); - } - - // Checks whether organization with _id === args.data.organizationId exists. - if (!organization) { - throw new errors.NotFoundError( - requestContext.translate(ORGANIZATION_NOT_FOUND_ERROR.MESSAGE), - ORGANIZATION_NOT_FOUND_ERROR.CODE, - ORGANIZATION_NOT_FOUND_ERROR.PARAM, - ); - } - // Variable to store list of users to be members of directChat. const usersInDirectChat = []; @@ -71,7 +55,6 @@ export const createDirectChat: MutationResolvers["createDirectChat"] = async ( const createdDirectChat = await DirectChat.create({ creatorId: context.userId, users: usersInDirectChat, - organization: args.data.organizationId, }); // Returns createdDirectChat. diff --git a/src/resolvers/Mutation/createDonation.ts b/src/resolvers/Mutation/createDonation.ts index d082f85bb1..794a0a336f 100644 --- a/src/resolvers/Mutation/createDonation.ts +++ b/src/resolvers/Mutation/createDonation.ts @@ -2,11 +2,23 @@ import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; import { Donation } from "../../models"; /** - * This function enables to create a donation as transaction - * @param _parent - parent of current request - * @param args - payload provided with the request - * @param context - context of entire application - * @returns Created Donation + * Creates a new donation transaction. + * + * This function performs the following actions: + * 1. Creates a new donation record in the database with the specified details. + * + * @param _parent - The parent object for the mutation. This parameter is not used in this resolver. + * @param args - The arguments for the mutation, including: + * - `amount`: The amount of the donation. + * - `nameOfOrg`: The name of the organization receiving the donation. + * - `nameOfUser`: The name of the user making the donation. + * - `orgId`: The ID of the organization receiving the donation. + * - `payPalId`: The PayPal ID associated with the transaction. + * - `userId`: The ID of the user making the donation. + * @param context - The context for the mutation, which is not used in this resolver. + * + * @returns The created donation record. + * */ export const createDonation: MutationResolvers["createDonation"] = async ( _parent, diff --git a/src/resolvers/Mutation/createEvent.ts b/src/resolvers/Mutation/createEvent.ts index 8898c0d805..ac7acc931a 100644 --- a/src/resolvers/Mutation/createEvent.ts +++ b/src/resolvers/Mutation/createEvent.ts @@ -27,20 +27,32 @@ import { findUserInCache } from "../../services/UserCache/findUserInCache"; import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; /** - * This function enables to create an event. - * @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 user exists - * 2.If the user has appUserProfile - * 3. If the organization exists - * 4. If the user is a part of the organization. - * 5. If the event is recurring, create the recurring event instances. - * 6. If the event is non-recurring, create a single event. - * @returns Created event + * Creates a new event and associates it with an organization. + * + * This resolver handles both recurring and non-recurring events, performing the following steps: + * + * 1. Validates the existence of the user, their app user profile, and the associated organization. + * 2. Checks if the user is authorized to create an event in the organization. + * 3. Validates the provided event details, including title, description, location, and date range. + * 4. Creates the event using the appropriate method based on whether it's recurring or not. + * 5. Uses a database transaction to ensure data consistency. + * + * @param _parent - The parent object, not used in this resolver. + * @param args - The input arguments for the mutation, including: + * - `data`: An object containing: + * - `organizationId`: The ID of the organization to associate with the event. + * - `title`: The title of the event (max 256 characters). + * - `description`: A description of the event (max 500 characters). + * - `location`: The location of the event (max 50 characters). + * - `startDate`: The start date of the event. + * - `endDate`: The end date of the event. + * - `recurring`: A boolean indicating if the event is recurring. + * @param context - The context object containing user information (context.userId). + * + * @returns A promise that resolves to the created event object. + * + * @remarks This function uses a transaction to ensure that either all operations succeed or none do, maintaining data integrity. */ - export const createEvent: MutationResolvers["createEvent"] = async ( _parent, args, diff --git a/src/resolvers/Mutation/createEventVolunteer.ts b/src/resolvers/Mutation/createEventVolunteer.ts index f5782350a6..decaa931fc 100644 --- a/src/resolvers/Mutation/createEventVolunteer.ts +++ b/src/resolvers/Mutation/createEventVolunteer.ts @@ -14,19 +14,28 @@ import { findUserInCache } from "../../services/UserCache/findUserInCache"; import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; /** - * 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 - * 4. If the group exists - * 5. If the current user is leader of the group - * @returns Created event volunteer + * Creates a new event volunteer entry. + * + * This function performs the following actions: + * 1. Verifies the existence of the current user. + * 2. Verifies the existence of the volunteer user. + * 3. Verifies the existence of the event. + * 4. Verifies the existence of the volunteer group. + * 5. Ensures that the current user is the leader of the volunteer group. + * 6. Creates a new event volunteer record. + * 7. Adds the newly created volunteer to the group's list of volunteers. + * + * @param _parent - The parent object for the mutation. This parameter is not used in this resolver. + * @param args - The arguments for the mutation, including: + * - `data.userId`: The ID of the user to be assigned as a volunteer. + * - `data.eventId`: The ID of the event for which the volunteer is being created. + * - `data.groupId`: The ID of the volunteer group to which the user is being added. + * @param context - The context for the mutation, including: + * - `userId`: The ID of the current user performing the operation. + * + * @returns The created event volunteer record. + * */ - export const createEventVolunteer: MutationResolvers["createEventVolunteer"] = async (_parent, args, context) => { let currentUser: InterfaceUser | null; diff --git a/src/resolvers/Mutation/createEventVolunteerGroup.ts b/src/resolvers/Mutation/createEventVolunteerGroup.ts index 0c2060d810..060a2dde49 100644 --- a/src/resolvers/Mutation/createEventVolunteerGroup.ts +++ b/src/resolvers/Mutation/createEventVolunteerGroup.ts @@ -11,17 +11,28 @@ import { findUserInCache } from "../../services/UserCache/findUserInCache"; import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; /** - * This function enables to create an event volunteer group - * @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 eventId exists - * 3. If the current user is admin of event - * @returns Created event volunteer group + * Creates a new event volunteer group and associates it with an event. + * + * This resolver performs the following actions: + * + * 1. Validates the existence of the current user. + * 2. Checks if the specified event exists. + * 3. Verifies that the current user is an admin of the event. + * 4. Creates a new volunteer group for the event. + * 5. Updates the event to include the newly created volunteer group. + * + * @param _parent - The parent object, not used in this resolver. + * @param args - The input arguments for the mutation, including: + * - `data`: An object containing: + * - `eventId`: The ID of the event to associate the volunteer group with. + * - `name`: The name of the volunteer group. + * - `volunteersRequired`: The number of volunteers required for the group. + * @param context - The context object containing user information (context.userId). + * + * @returns A promise that resolves to the created event volunteer group object. + * + * @remarks This function first checks the cache for the current user and then queries the database if needed. It ensures that the user is authorized to create a volunteer group for the event before proceeding. */ - export const createEventVolunteerGroup: MutationResolvers["createEventVolunteerGroup"] = async (_parent, args, context) => { let currentUser: InterfaceUser | null; diff --git a/src/resolvers/Mutation/createFund.ts b/src/resolvers/Mutation/createFund.ts index a987fa8e76..bb595b2910 100644 --- a/src/resolvers/Mutation/createFund.ts +++ b/src/resolvers/Mutation/createFund.ts @@ -14,19 +14,32 @@ import { cacheUsers } from "../../services/UserCache/cacheUser"; import { findUserInCache } from "../../services/UserCache/findUserInCache"; import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; import { adminCheck } from "../../utilities"; + /** - * This function enables to create an organization specific fundraising funds. - * @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 user exists - * 2. If the organization exists - * 3. If the user is authorized. - * 4. If the fund already exists - * @returns Created fund + * Creates a new fundraising fund for a specified organization. + * + * This function performs the following actions: + * 1. Verifies the existence of the current user. + * 2. Retrieves and caches the user's profile if not already cached. + * 3. Verifies the existence of the specified organization. + * 4. Checks if the current user is an admin of the organization. + * 5. Verifies that the fund does not already exist for the given organization. + * 6. Creates a new fund with the provided details. + * 7. Updates the organization's list of funds to include the newly created fund. + * + * @param _parent - The parent object for the mutation. This parameter is not used in this resolver. + * @param args - The arguments for the mutation, including: + * - `data.organizationId`: The ID of the organization for which the fund is being created. + * - `data.name`: The name of the fund. + * - `data.refrenceNumber`: The reference number for the fund. + * - `data.taxDeductible`: Indicates if the fund is tax-deductible. + * - `data.isDefault`: Indicates if the fund is a default fund. + * - `data.isArchived`: Indicates if the fund is archived. + * @param context - The context for the mutation, including: + * - `userId`: The ID of the current user performing the operation. + * + * @returns The created fund record. */ - export const createFund: MutationResolvers["createFund"] = async ( _parent, args, @@ -86,22 +99,25 @@ export const createFund: MutationResolvers["createFund"] = async ( ORGANIZATION_NOT_FOUND_ERROR.PARAM, ); } - //checks whether the user is admin of organization or not + + // Checks whether the user is admin of the organization or not. await adminCheck(currentUser._id, organization); - const exisitingFund = await Fund.findOne({ + const existingFund = await Fund.findOne({ name: args.data.name, organizationId: args.data.organizationId, }); - //checks if the fund already exists - if (exisitingFund) { + + // Checks if the fund already exists. + if (existingFund) { throw new errors.ConflictError( requestContext.translate(FUND_ALREADY_EXISTS.MESSAGE), FUND_ALREADY_EXISTS.CODE, FUND_ALREADY_EXISTS.PARAM, ); } - //create Fund with the provided data + + // Creates Fund with the provided data. const createdFund = await Fund.create({ name: args.data.name, organizationId: args.data.organizationId, @@ -112,7 +128,7 @@ export const createFund: MutationResolvers["createFund"] = async ( creatorId: context.userId, }); - //push the created fund to the organization funds array + // Pushes the created fund to the organization's funds array. await Organization.updateOne( { _id: organization._id, diff --git a/src/resolvers/Mutation/createFundraisingCampaign.ts b/src/resolvers/Mutation/createFundraisingCampaign.ts index c729e71d57..b055d69b9e 100644 --- a/src/resolvers/Mutation/createFundraisingCampaign.ts +++ b/src/resolvers/Mutation/createFundraisingCampaign.ts @@ -14,20 +14,34 @@ import { cacheUsers } from "../../services/UserCache/cacheUser"; import { findUserInCache } from "../../services/UserCache/findUserInCache"; import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; import { validateDate } from "../../utilities/dateValidator"; + /** - * This function enables to create a fundraisingCampaigin . - * @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 startDate is valid - * 3. If the endDate is valid - * 4. if the parent fund exists - * 5. If the user is authorized. - * @returns Created fundraisingCampaign + * Creates a new fundraising campaign and associates it with a specified fund. + * + * This resolver performs the following actions: + * + * 1. Validates the existence of the current user. + * 2. Checks if the user has an associated profile and if they are authorized. + * 3. Ensures that a fundraising campaign with the same name does not already exist. + * 4. Validates the provided start and end dates for the campaign. + * 5. Verifies the existence of the specified fund and checks if the user is authorized to create a campaign for the fund. + * 6. Creates a new fundraising campaign and associates it with the fund. + * + * @param _parent - The parent object, not used in this resolver. + * @param args - The input arguments for the mutation, including: + * - `data`: An object containing: + * - `name`: The name of the fundraising campaign. + * - `fundId`: The ID of the fund to associate the campaign with. + * - `startDate`: The start date of the campaign. + * - `endDate`: The end date of the campaign. + * - `fundingGoal`: The funding goal for the campaign. + * - `currency`: The currency for the funding goal. + * @param context - The context object containing user information (context.userId). + * + * @returns A promise that resolves to the created fundraising campaign object. + * + * @remarks This function checks the cache for user and profile data, validates inputs, and ensures the user has the necessary permissions before creating the campaign. */ - export const createFundraisingCampaign: MutationResolvers["createFundraisingCampaign"] = async (_parent, args, context): Promise => { let currentUser: InterfaceUser | null; @@ -82,10 +96,11 @@ export const createFundraisingCampaign: MutationResolvers["createFundraisingCamp FUNDRAISING_CAMPAIGN_ALREADY_EXISTS.PARAM, ); } + const startDate = args.data.startDate; const endDate = args.data.endDate; - //validates StartDate and endDate + // Validates startDate and endDate validateDate(startDate, endDate); const fund = await Fund.findOne({ @@ -99,6 +114,7 @@ export const createFundraisingCampaign: MutationResolvers["createFundraisingCamp FUND_NOT_FOUND_ERROR.PARAM, ); } + const currentOrg = await Fund.findById(fund._id) .select("organizationId") .lean(); @@ -121,7 +137,7 @@ export const createFundraisingCampaign: MutationResolvers["createFundraisingCamp ); } console.log("here"); - // Creates a fundraisingCampaign. + // Creates a fundraising campaign. const campaign = await FundraisingCampaign.create({ name: args.data.name, fundId: args.data.fundId, @@ -131,7 +147,7 @@ export const createFundraisingCampaign: MutationResolvers["createFundraisingCamp currency: args.data.currency, }); - //add campaigin to the parent fund + // Adds campaign to the parent fund await Fund.findOneAndUpdate( { _id: args.data.fundId, diff --git a/src/resolvers/Mutation/createFundraisingCampaignPledge.ts b/src/resolvers/Mutation/createFundraisingCampaignPledge.ts index 72b5a158c4..4a71863a4b 100644 --- a/src/resolvers/Mutation/createFundraisingCampaignPledge.ts +++ b/src/resolvers/Mutation/createFundraisingCampaignPledge.ts @@ -13,17 +13,31 @@ import { cacheUsers } from "../../services/UserCache/cacheUser"; import { findUserInCache } from "../../services/UserCache/findUserInCache"; import { type MutationResolvers } from "../../types/generatedGraphQLTypes"; import { validateDate } from "../../utilities/dateValidator"; + /** - * This function enables to create a fundraisingCampaiginPledge . - * @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 startDate is valid - * 3. If the endDate is valid - * 4. if the fund campaign exists - * @returns Created fundraisingCampaignPledge + * Creates a new pledge for a fundraising campaign. + * + * This function performs the following actions: + * 1. Verifies the existence of the current user. + * 2. Retrieves and caches the user's details if not already cached. + * 3. Checks the validity of the provided or default campaign start and end dates. + * 4. Verifies the existence of the specified fundraising campaign. + * 5. Creates a new pledge for the specified campaign with the given details. + * 6. Updates the campaign to include the newly created pledge. + * + * @param _parent - The parent object for the mutation. This parameter is not used in this resolver. + * @param args - The arguments for the mutation, including: + * - `data.campaignId`: The ID of the fundraising campaign for which the pledge is being created. + * - `data.userIds`: An array of user IDs associated with the pledge. + * - `data.startDate`: The start date of the pledge (optional; defaults to the campaign's start date). + * - `data.endDate`: The end date of the pledge (optional; defaults to the campaign's end date). + * - `data.amount`: The amount pledged. + * - `data.currency`: The currency of the pledged amount. + * @param context - The context for the mutation, including: + * - `userId`: The ID of the current user performing the operation. + * + * @returns The created pledge record. + * */ export const createFundraisingCampaignPledge: MutationResolvers["createFundraisingCampaignPledge"] = async ( @@ -51,6 +65,7 @@ export const createFundraisingCampaignPledge: MutationResolvers["createFundraisi USER_NOT_FOUND_ERROR.PARAM, ); } + const campaign = await FundraisingCampaign.findOne({ _id: args.data.campaignId, }).lean(); @@ -64,14 +79,14 @@ export const createFundraisingCampaignPledge: MutationResolvers["createFundraisi ); } - //if startDate and endDate are not provided, then use the campaign's startDate and endDate + // If startDate and endDate are not provided, then use the campaign's startDate and endDate. const startDate = args.data?.startDate ?? campaign.startDate; const endDate = args.data?.endDate ?? campaign.endDate; - //validates startDate and endDate + // Validates startDate and endDate. validateDate(startDate, endDate); - // Create a new pledge + // Create a new pledge. const pledge = await FundraisingCampaignPledge.create({ campaigns: [args.data.campaignId], users: args.data.userIds, @@ -81,7 +96,7 @@ export const createFundraisingCampaignPledge: MutationResolvers["createFundraisi currency: args.data.currency, }); - // Update the campaign with the new pledge + // Update the campaign with the new pledge. await FundraisingCampaign.updateOne( { _id: args.data.campaignId, @@ -90,5 +105,6 @@ export const createFundraisingCampaignPledge: MutationResolvers["createFundraisi $push: { pledges: pledge._id }, }, ); + return pledge as InterfaceFundraisingCampaignPledges; }; diff --git a/src/resolvers/Mutation/createGroupChat.ts b/src/resolvers/Mutation/createGroupChat.ts index 2609cec40f..17dc1548b0 100644 --- a/src/resolvers/Mutation/createGroupChat.ts +++ b/src/resolvers/Mutation/createGroupChat.ts @@ -7,15 +7,28 @@ import { GroupChat, Organization, User } from "../../models"; import { cacheOrganizations } from "../../services/OrganizationCache/cacheOrganizations"; import { findOrganizationsInCache } from "../../services/OrganizationCache/findOrganizationsInCache"; import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; + /** - * This function enables to create a group chat. - * @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 user exists - * 2. If the organization exists - * @returns Created group chat + * Creates a new group chat and associates it with a specified organization. + * + * This resolver performs the following actions: + * + * 1. Checks if the specified organization exists in the cache, and if not, fetches it from the database and caches it. + * 2. Verifies that the organization with the given ID exists. + * 3. Checks if each user specified in the `userIds` list exists. + * 4. Creates a new group chat with the specified users, organization, and title. + * + * @param _parent - The parent object, not used in this resolver. + * @param args - The input arguments for the mutation, including: + * - `data`: An object containing: + * - `organizationId`: The ID of the organization to associate with the group chat. + * - `userIds`: A list of user IDs to be added to the group chat. + * - `title`: The title of the group chat. + * @param context - The context object containing user information (context.userId). + * + * @returns A promise that resolves to the created group chat object. + * + * @remarks This function ensures the existence of the organization and users, and caches the organization if it is not already cached. It returns the created group chat object. */ export const createGroupChat: MutationResolvers["createGroupChat"] = async ( _parent, @@ -37,7 +50,7 @@ export const createGroupChat: MutationResolvers["createGroupChat"] = async ( if (organization) await cacheOrganizations([organization]); } - // Checks whether organization with _id === args.data.organizationId exists. + // Checks whether the organization with _id === args.data.organizationId exists. if (!organization) { throw new errors.NotFoundError( requestContext.translate(ORGANIZATION_NOT_FOUND_ERROR.MESSAGE), @@ -46,7 +59,7 @@ export const createGroupChat: MutationResolvers["createGroupChat"] = async ( ); } - // Variable to store list of users to be members of groupChat. + // Variable to store list of users to be members of group chat. const usersInGroupChat = []; // Loops over each item in args.data.userIds list. @@ -67,7 +80,7 @@ export const createGroupChat: MutationResolvers["createGroupChat"] = async ( usersInGroupChat.push(userId); } - // Creates new groupChat. + // Creates new group chat. const createdGroupChat = await GroupChat.create({ creatorId: context.userId, users: usersInGroupChat, @@ -75,6 +88,6 @@ export const createGroupChat: MutationResolvers["createGroupChat"] = async ( title: args.data?.title, }); - // Returns createdGroupChat. + // Returns created group chat. return createdGroupChat.toObject(); }; diff --git a/src/resolvers/Mutation/createMember.ts b/src/resolvers/Mutation/createMember.ts index c492faf56b..7727844edf 100644 --- a/src/resolvers/Mutation/createMember.ts +++ b/src/resolvers/Mutation/createMember.ts @@ -20,19 +20,29 @@ import { findOrganizationsInCache } from "../../services/OrganizationCache/findO import { cacheUsers } from "../../services/UserCache/cacheUser"; import { findUserInCache } from "../../services/UserCache/findUserInCache"; import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; + /** - * This function enables to add a member. - * @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. Checks whether current user making the request is an superAdmin or an Admin. - * 2. If the organization exists - * 3. Checks whether curent user exists. - * 4. Checks whether current user has appProfile. - * 4. Checks whether user with _id === args.input.userId is already an member of organization.. + * Adds a user as a member to an organization. + * + * This resolver performs the following actions: + * + * 1. Verifies if the current user making the request exists and is either a superAdmin or an admin of the organization. + * 2. Checks if the specified organization exists in the cache; if not, fetches it from the database and caches it. + * 3. Checks if the specified user exists and is not already a member of the organization. + * 4. Adds the user to the organization's member list and updates the user's joinedOrganizations list. + * + * @param _parent - The parent object, not used in this resolver. + * @param args - The input arguments for the mutation, including: + * - `input`: An object containing: + * - `organizationId`: The ID of the organization to which the user will be added. + * - `userId`: The ID of the user to be added as a member. + * @param context - The context object containing user information (context.userId). * - * @returns Organization. + * @returns An object containing: + * - `organization`: The updated organization object. + * - `userErrors`: A list of errors encountered during the process. + * + * @remarks This function returns the updated organization and any errors encountered. It ensures that the user is not already a member before adding them and handles caching of the organization. */ export const createMember: MutationResolvers["createMember"] = async ( _parent, @@ -53,11 +63,7 @@ export const createMember: MutationResolvers["createMember"] = async ( } if (!currentUser) { - // throw new errors.NotFoundError( - // requestContext.translate(USER_NOT_FOUND_ERROR.MESSAGE), - // USER_NOT_FOUND_ERROR.CODE, - // USER_NOT_FOUND_ERROR.PARAM, - // ); + // Return an error if the user is not found return { organization: new Organization(), userErrors: [ @@ -68,6 +74,7 @@ export const createMember: MutationResolvers["createMember"] = async ( ], }; } + let currentUserAppProfile: InterfaceAppUserProfile | null; const appUserProfileFoundInCache = await findAppUserProfileCache([ currentUser.appUserProfileId?.toString(), @@ -83,11 +90,7 @@ export const createMember: MutationResolvers["createMember"] = async ( } if (!currentUserAppProfile) { - // throw new errors.UnauthorizedError( - // requestContext.translate(USER_NOT_AUTHORIZED_ERROR.MESSAGE), - // USER_NOT_AUTHORIZED_ERROR.CODE, - // USER_NOT_AUTHORIZED_ERROR.PARAM, - // ); + // Return an error if the user's app profile is not found return { organization: new Organization(), userErrors: [ @@ -117,11 +120,7 @@ export const createMember: MutationResolvers["createMember"] = async ( } if (!organization) { - // throw new errors.NotFoundError( - // requestContext.translate(ORGANIZATION_NOT_FOUND_ERROR.MESSAGE), - // ORGANIZATION_NOT_FOUND_ERROR.CODE, - // ORGANIZATION_NOT_FOUND_ERROR.PARAM, - // ); + // Return an error if the organization is not found return { organization: new Organization(), userErrors: [ @@ -134,12 +133,14 @@ export const createMember: MutationResolvers["createMember"] = async ( ], }; } + const userIsOrganizationAdmin = organization.admins.some( (admin) => admin === currentUser._id || new mongoose.Types.ObjectId(admin.toString()).equals(currentUser._id), ); if (!userIsOrganizationAdmin && !currentUserAppProfile.isSuperAdmin) { + // Return an error if the user is not authorized return { organization: new Organization(), userErrors: [ @@ -155,13 +156,9 @@ export const createMember: MutationResolvers["createMember"] = async ( _id: args.input.userId, }).lean(); - // Checks whether curent user exists + // Checks whether the user exists if (!user) { - // throw new errors.NotFoundError( - // requestContext.translate(USER_NOT_FOUND_ERROR.MESSAGE), - // USER_NOT_FOUND_ERROR.CODE, - // USER_NOT_FOUND_ERROR.PARAM, - // ); + // Return an error if the user is not found return { organization: new Organization(), userErrors: [ @@ -177,13 +174,9 @@ export const createMember: MutationResolvers["createMember"] = async ( member.equals(user._id), ); - // Checks whether user with _id === args.input.userId is already an member of organization. + // Checks whether the user is already a member of the organization if (userIsOrganizationMember) { - // throw new errors.NotFoundError( - // requestContext.translate(MEMBER_NOT_FOUND_ERROR.MESSAGE), - // MEMBER_NOT_FOUND_ERROR.CODE, - // MEMBER_NOT_FOUND_ERROR.PARAM, - // ); + // Return an error if the user is already a member return { organization: new Organization(), userErrors: [ @@ -195,7 +188,7 @@ export const createMember: MutationResolvers["createMember"] = async ( }; } - // add organization's id from joinedOrganizations list on user. + // Adds the organization ID to the user's joinedOrganizations list. await User.updateOne( { _id: args.input.userId, @@ -210,7 +203,7 @@ export const createMember: MutationResolvers["createMember"] = async ( }, ); - // add user's id to members list on organization and return it. + // Adds the user's ID to the organization's members list and returns it. const updatedOrganization = await Organization.findOneAndUpdate( { _id: organization?._id, diff --git a/src/resolvers/Mutation/createMessageChat.ts b/src/resolvers/Mutation/createMessageChat.ts index 60a9975499..e7d6fdab4c 100644 --- a/src/resolvers/Mutation/createMessageChat.ts +++ b/src/resolvers/Mutation/createMessageChat.ts @@ -10,16 +10,29 @@ import { findAppUserProfileCache } from "../../services/AppUserProfileCache/find import { cacheUsers } from "../../services/UserCache/cacheUser"; import { findUserInCache } from "../../services/UserCache/findUserInCache"; import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; + /** - * This function enables to create a chat. - * @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 receiver user exists - * 2. If the sender and receiver users have same language code. - * 3. If the sender and receiver users have appProfile. - * @returns Created message chat. + * Creates a new chat message between users. + * + * This function performs the following actions: + * 1. Verifies the existence of the current user. + * 2. Retrieves and caches the current user's details and application profile if not already cached. + * 3. Checks the existence of the receiver user and retrieves their application profile. + * 4. Ensures that both the current user and the receiver have valid application profiles. + * 5. Compares the language codes of the sender and receiver to determine if there is a language barrier. + * 6. Creates a new chat message with the specified content and language barrier status. + * 7. Publishes the created message chat to a pub/sub channel for real-time updates. + * + * @param _parent - The parent object for the mutation. This parameter is not used in this resolver. + * @param args - The arguments for the mutation, including: + * - `data.receiver`: The ID of the user receiving the message. + * - `data.message`: The content of the message being sent. + * @param context - The context for the mutation, including: + * - `userId`: The ID of the current user sending the message. + * - `pubsub`: The pub/sub instance for publishing real-time updates. + * + * @returns The created message chat record. + * */ export const createMessageChat: MutationResolvers["createMessageChat"] = async ( _parent, @@ -89,6 +102,7 @@ export const createMessageChat: MutationResolvers["createMessageChat"] = async ( USER_NOT_AUTHORIZED_ERROR.PARAM, ); } + // Boolean to identify whether both sender and receiver for messageChat have the same appLanguageCode. const isSenderReceiverLanguageSame = receiverUserAppProfile?.appLanguageCode === diff --git a/src/resolvers/Mutation/createNote.ts b/src/resolvers/Mutation/createNote.ts index f1a456fe52..f510d8b40a 100644 --- a/src/resolvers/Mutation/createNote.ts +++ b/src/resolvers/Mutation/createNote.ts @@ -14,13 +14,28 @@ import { findAppUserProfileCache } from "../../services/AppUserProfileCache/find import { cacheUsers } from "../../services/UserCache/cacheUser"; import { findUserInCache } from "../../services/UserCache/findUserInCache"; import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; + /** - * Create an note for an agenda item based on the provided input. + * Creates a note for a specified agenda item. + * + * This resolver performs the following actions: + * + * 1. Verifies the existence of the current user making the request. + * 2. Checks the user's app profile to ensure they are authenticated. + * 3. Checks if the specified agenda item exists. + * 4. Creates a new note associated with the agenda item. + * 5. Updates the agenda item to include the newly created note. + * + * @param _parent - The parent object, not used in this resolver. + * @param args - The input arguments for the mutation, including: + * - `data`: An object containing: + * - `agendaItemId`: The ID of the agenda item to which the note will be added. + * - `content`: The content of the note. + * @param context - The context object containing user information (context.userId). + * + * @returns The created note object. * - * @param _parent - parent of current request - * @param args - payload provided with the request - * @param context - context of entire application - * @returns The created note for an agenda item. + * @remarks This function creates a note, associates it with the specified agenda item, and updates the agenda item to include the new note. It also handles caching and error scenarios. */ export const createNote: MutationResolvers["createNote"] = async ( _parent, diff --git a/src/resolvers/Mutation/createOrganization.ts b/src/resolvers/Mutation/createOrganization.ts index b68fe9e89b..afb7bb0297 100644 --- a/src/resolvers/Mutation/createOrganization.ts +++ b/src/resolvers/Mutation/createOrganization.ts @@ -24,15 +24,33 @@ import { superAdminCheck } from "../../utilities"; import { uploadEncodedImage } from "../../utilities/encodedImageStorage/uploadEncodedImage"; import { findAppUserProfileCache } from "../../services/AppUserProfileCache/findAppUserProfileCache"; import { cacheAppUserProfile } from "../../services/AppUserProfileCache/cacheAppUserProfile"; + /** - * This function enables to create an organization. - * @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 user exists - * 2. If the user has appUserProfile - * @returns Created organization + * Creates a new organization. + * + * This resolver performs the following steps: + * + * 1. Verifies the existence of the current user making the request. + * 2. Checks the user's app profile to ensure they are authenticated and authorized as a super admin. + * 3. Validates the provided input data, including organization name, description, and address. + * 4. Uploads an optional image file associated with the organization. + * 5. Creates a new organization with the provided data and image. + * 6. Creates a default action item category for the new organization. + * 7. Updates the current user's document to include the new organization in their `joinedOrganizations`, `createdOrganizations`, and `adminFor` lists. + * 8. Caches the newly created organization. + * + * @param _parent - The parent object, not used in this resolver. + * @param args - The input arguments for the mutation, including: + * - `data`: An object containing: + * - `name`: The name of the organization. + * - `description`: A description of the organization. + * - `address`: An optional address object for the organization. + * - `file`: An optional encoded image file for the organization. + * @param context - The context object containing user information (context.userId). + * + * @returns The created organization object. + * + * @remarks This function creates an organization, uploads an optional image, validates the input data, creates a default action item category, updates user records, and manages caching. */ export const createOrganization: MutationResolvers["createOrganization"] = async (_parent, args, context) => { @@ -77,13 +95,13 @@ export const createOrganization: MutationResolvers["createOrganization"] = } superAdminCheck(currentUserAppProfile as InterfaceAppUserProfile); - //Upload file + // Upload file let uploadImageFileName = null; if (args.file) { uploadImageFileName = await uploadEncodedImage(args.file, null); } - // Checks if the recieved arguments are valid according to standard input norms + // Validate input arguments let validationResultName = { isLessThanMaxLength: false, }; @@ -123,7 +141,7 @@ export const createOrganization: MutationResolvers["createOrganization"] = throw new errors.InputValidationError("Not a Valid Address"); } - // Creates new organization. + // Create new organization const createdOrganization = await Organization.create({ ...args.data, address: args.data?.address, @@ -133,7 +151,7 @@ export const createOrganization: MutationResolvers["createOrganization"] = members: [context.userId], }); - // Creating a default actionItemCategory + // Create default action item category await ActionItemCategory.create({ name: "Default", organizationId: createdOrganization._id, @@ -142,11 +160,7 @@ export const createOrganization: MutationResolvers["createOrganization"] = await cacheOrganizations([createdOrganization.toObject()]); - /* - Adds createdOrganization._id to joinedOrganizations, createdOrganizations - and adminFor lists on currentUser's document with _id === context.userId - */ - + // Update currentUser's document await User.updateOne( { _id: context.userId, @@ -169,9 +183,10 @@ export const createOrganization: MutationResolvers["createOrganization"] = }, ); - // Returns createdOrganization. + // Return created organization return createdOrganization.toObject(); }; + /** * Validates an address object to ensure its fields meet specified criteria. * @param address - The address object to validate diff --git a/src/resolvers/Mutation/createPlugin.ts b/src/resolvers/Mutation/createPlugin.ts index 290d4db17c..fe45f45b40 100644 --- a/src/resolvers/Mutation/createPlugin.ts +++ b/src/resolvers/Mutation/createPlugin.ts @@ -2,13 +2,22 @@ import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; import { Plugin } from "../../models"; /** - * This function enables to create a plugin. - * @param _parent - parent of current request - * @param args - payload provided with the request - * @param _context - context of entire application - * @returns Created plugin + * Creates a new plugin and triggers a subscription event. + * + * This resolver performs the following steps: + * + * 1. Creates a new plugin using the provided arguments. + * 2. Publishes an update event to the `TALAWA_PLUGIN_UPDATED` subscription channel with the created plugin details. + * + * @param _parent - The parent object, not used in this resolver. + * @param args - The input arguments for the mutation, which include: + * - `data`: An object containing the plugin's details. + * @param _context - The context object, which includes the pubsub system for triggering subscriptions. + * + * @returns The created plugin object. + * + * @remarks This function creates a plugin record, updates the subscription channel with the new plugin details, and returns the created plugin. */ - export const createPlugin: MutationResolvers["createPlugin"] = async ( _parent, args, @@ -19,7 +28,8 @@ export const createPlugin: MutationResolvers["createPlugin"] = async ( ...args, uninstalledOrgs: [], }); - // calls subscription + + // Calls subscription context.pubsub.publish("TALAWA_PLUGIN_UPDATED", { onPluginUpdate: createdPlugin.toObject(), }); diff --git a/src/resolvers/Mutation/createPost.ts b/src/resolvers/Mutation/createPost.ts index 21e7ef611f..659c3497b2 100644 --- a/src/resolvers/Mutation/createPost.ts +++ b/src/resolvers/Mutation/createPost.ts @@ -27,16 +27,39 @@ import { findUserInCache } from "../../services/UserCache/findUserInCache"; import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; import { uploadEncodedImage } from "../../utilities/encodedImageStorage/uploadEncodedImage"; import { uploadEncodedVideo } from "../../utilities/encodedVideoStorage/uploadEncodedVideo"; + /** - * This function enables to create a post. - * @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 user exists - * 2. If the organization exists - * 3. If the user has appUserProfile - * @returns Created Post + * Creates a new post and associates it with an organization. + * + * This function performs the following actions: + * 1. Verifies the existence of the current user and retrieves their details and application profile. + * 2. Checks if the specified organization exists and retrieves its details. + * 3. Validates that the user is a member of the organization or is a super admin. + * 4. Handles file uploads for images and videos, if provided. + * 5. Validates the post title and ensures it meets the criteria for pinning. + * 6. Checks user permissions to pin the post if required. + * 7. Creates the post and updates the organization with the pinned post if applicable. + * 8. Caches the newly created post and organization. + * + * @param _parent - The parent object for the mutation. This parameter is not used in this resolver. + * @param args - The arguments for the mutation, including: + * - `data.organizationId`: The ID of the organization where the post will be created. + * - `data.title`: The title of the post (optional but required if the post is pinned). + * - `data.text`: The text content of the post. + * - `data.pinned`: A boolean indicating whether the post should be pinned. + * - `file`: An optional base64-encoded image or video file. + * @param context - The context for the mutation, including: + * - `userId`: The ID of the current user creating the post. + * - `apiRootUrl`: The root URL of the API for constructing file URLs. + * + * @returns The created post object, including URLs for uploaded image and video files if provided. + * + * @see User - The User model used to interact with user data in the database. + * @see AppUserProfile - The AppUserProfile model used to interact with user profile data in the database. + * @see Organization - The Organization model used to interact with organization data in the database. + * @see Post - The Post model used to interact with post data in the database. + * @see uploadEncodedImage - A utility function for uploading encoded image files. + * @see uploadEncodedVideo - A utility function for uploading encoded video files. */ export const createPost: MutationResolvers["createPost"] = async ( _parent, @@ -152,7 +175,7 @@ export const createPost: MutationResolvers["createPost"] = async ( ); } - // Checks if the recieved arguments are valid according to standard input norms + // Checks if the received arguments are valid according to standard input norms if (args.data?.title && args.data?.text) { const validationResultTitle = isValidString(args.data?.title, 256); const validationResultText = isValidString(args.data?.text, 500); diff --git a/src/resolvers/Mutation/createSampleOrganization.ts b/src/resolvers/Mutation/createSampleOrganization.ts index 255c054c01..895de80ae4 100644 --- a/src/resolvers/Mutation/createSampleOrganization.ts +++ b/src/resolvers/Mutation/createSampleOrganization.ts @@ -14,7 +14,21 @@ import { createSampleOrganization as createSampleOrgUtil } from "../../utilities /** * Generates sample data for testing or development purposes. - * @returns True if the sample data generation is successful, false otherwise. + * + * This resolver performs the following steps: + * + * 1. Verifies that the current user exists and is fetched from the cache or database. + * 2. Checks if the current user has a valid application profile and whether they are authorized. + * 3. Ensures that the current user is a super admin. + * 4. Utilizes a utility function to create a sample organization. + * + * @param _parent - The parent object, not used in this resolver. + * @param _args - The arguments for the mutation, not used in this resolver. + * @param _context - The context object, including the user ID and other necessary context for authorization. + * + * @returns True if the sample data generation is successful; false otherwise. + * + * @remarks This function is intended for creating sample data and should only be accessible by super admins. */ export const createSampleOrganization: MutationResolvers["createSampleOrganization"] = async (_parent, _args, _context) => { diff --git a/src/resolvers/Mutation/createUserFamily.ts b/src/resolvers/Mutation/createUserFamily.ts index 84ad699a13..5a0401a3f6 100644 --- a/src/resolvers/Mutation/createUserFamily.ts +++ b/src/resolvers/Mutation/createUserFamily.ts @@ -17,16 +17,30 @@ import { findAppUserProfileCache } from "../../services/AppUserProfileCache/find import { cacheUsers } from "../../services/UserCache/cacheUser"; import { findUserInCache } from "../../services/UserCache/findUserInCache"; import { superAdminCheck } from "../../utilities"; + /** - * This Function enables to create a user Family - * @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 user exists - * 2. If the user is super admin - * 3. If there are atleast two members in the family. - * @returns Created user Family + * Creates a new user family and associates users with it. + * + * This function performs the following actions: + * 1. Verifies the existence of the current user and retrieves their details and application profile. + * 2. Checks if the current user is a super admin. + * 3. Validates the user family name to ensure it does not exceed 256 characters. + * 4. Ensures that the user family has at least two members. + * 5. Creates the user family and associates it with the provided users. + * + * @param _parent - The parent object for the mutation. This parameter is not used in this resolver. + * @param args - The arguments for the mutation, including: + * - `data.title`: The title of the user family (must be a string with a maximum length of 256 characters). + * - `data.userIds`: An array of user IDs to be included in the user family (must contain at least 2 members). + * @param context - The context for the mutation, including: + * - `userId`: The ID of the current user creating the user family. + * + * @returns The created user family object. + * + * @see User - The User model used to interact with user data in the database. + * @see AppUserProfile - The AppUserProfile model used to interact with user profile data in the database. + * @see UserFamily - The UserFamily model used to interact with user family data in the database. + * @see superAdminCheck - A utility function to check if the user is a super admin. */ export const createUserFamily: MutationResolvers["createUserFamily"] = async ( _parent, @@ -45,7 +59,7 @@ export const createUserFamily: MutationResolvers["createUserFamily"] = async ( } } - // Checks whether user with _id === args.userId exists. + // Checks whether user with _id === context.userId exists. if (!currentUser) { throw new errors.NotFoundError( requestContext.translate(USER_NOT_FOUND_ERROR.MESSAGE), @@ -74,7 +88,8 @@ export const createUserFamily: MutationResolvers["createUserFamily"] = async ( USER_NOT_AUTHORIZED_ERROR.PARAM, ); } - // Check whether the user is super admin. + + // Check whether the user is a super admin. superAdminCheck(currentUserAppProfile as InterfaceAppUserProfile); let validationResultName = { diff --git a/src/resolvers/Mutation/createUserTag.ts b/src/resolvers/Mutation/createUserTag.ts index fdeb252058..1547662e47 100644 --- a/src/resolvers/Mutation/createUserTag.ts +++ b/src/resolvers/Mutation/createUserTag.ts @@ -22,6 +22,27 @@ import { cacheUsers } from "../../services/UserCache/cacheUser"; import { findUserInCache } from "../../services/UserCache/findUserInCache"; import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; +/** + * Creates a new tag for an organization if the user is authorized to do so. + * + * This resolver performs the following steps: + * + * 1. Verifies that the current user exists and is fetched from the cache or database. + * 2. Checks if the current user has an application profile. + * 3. Ensures the current user is authorized to create a tag by being either a super admin or an admin for the specified organization. + * 4. Checks if the provided organization exists. + * 5. Validates that the parent tag (if provided) belongs to the specified organization. + * 6. Ensures no other tag with the same name exists under the same parent tag. + * 7. Creates a new tag if all validation checks pass. + * + * @param _parent - The parent object, not used in this resolver. + * @param args - The input arguments for the mutation, including the tag details and organization ID. + * @param context - The context object, including the user ID and other necessary context for authorization. + * + * @returns The created tag object. + * + * @remarks This function is intended for creating new tags within an organization and includes validation to ensure the integrity of the tag creation process. + */ export const createUserTag: MutationResolvers["createUserTag"] = async ( _parent, args, @@ -61,7 +82,7 @@ export const createUserTag: MutationResolvers["createUserTag"] = async ( } } - //check whether current User has app profile or not + // Check whether current User has app profile or not if (!currentUserAppProfile) { throw new errors.UnauthorizedError( requestContext.translate(USER_NOT_AUTHORIZED_ERROR.MESSAGE), diff --git a/src/resolvers/Mutation/createVenue.ts b/src/resolvers/Mutation/createVenue.ts index 9d8bf0816c..f1f7398fdf 100644 --- a/src/resolvers/Mutation/createVenue.ts +++ b/src/resolvers/Mutation/createVenue.ts @@ -16,20 +16,27 @@ import { cacheUsers } from "../../services/UserCache/cacheUser"; import { findUserInCache } from "../../services/UserCache/findUserInCache"; import type { MutationResolvers } from "../../types/generatedGraphQLTypes"; import { uploadEncodedImage } from "../../utilities/encodedImageStorage/uploadEncodedImage"; + /** - * This function enables to create a venue in an organization. - * @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 user exists - * 2. If the organization exists - * 3. Whether the user is admin or superadmin or not - * 4. If the venue name is missing - * 5. If the same venue already exists in an organization - * @returns Created venue + * Creates a new venue within an organization, if the user has appropriate permissions and the venue does not already exist. + * + * This resolver performs the following checks: + * + * 1. Verifies the existence of the user and fetches their profile. + * 2. Checks if the specified organization exists. + * 3. Ensures the user is authorized to create a venue by verifying their admin or superadmin status within the organization. + * 4. Validates that a venue name is provided. + * 5. Ensures that no venue with the same name already exists within the organization. + * 6. Uploads an image if provided and associates it with the venue. + * + * @param _parent - The parent object, not used in this resolver. + * @param args - The input arguments for the mutation, including the venue details and organization ID. + * @param context - The context object, including the user ID, API root URL, and other necessary context for authorization and image upload. + * + * @returns The created venue object, including the associated organization. + * + * @remarks This function includes validation for user authorization, venue uniqueness, and handles image uploads if applicable. */ - export const createVenue: MutationResolvers["createVenue"] = async ( _parent, args, @@ -89,7 +96,7 @@ export const createVenue: MutationResolvers["createVenue"] = async ( ); } - // Checks Whether the user is admin or superadmin or not + // Checks whether the user is admin or superadmin. if ( !( organization.admins?.some((admin) => admin._id.equals(context.userId)) || @@ -103,7 +110,7 @@ export const createVenue: MutationResolvers["createVenue"] = async ( ); } - // Check if the venue name provided is empty string + // Check if the venue name provided is an empty string. if (!args.data?.name ?? "") { throw new errors.InputValidationError( requestContext.translate(VENUE_NAME_MISSING_ERROR.MESSAGE), @@ -112,7 +119,7 @@ export const createVenue: MutationResolvers["createVenue"] = async ( ); } - // Check if a venue with the same organizationId and name exists + // Check if a venue with the same organizationId and name exists. const existingVenue = await Venue.findOne({ name: args.data.name, organization: args.data.organizationId, diff --git a/src/resolvers/Organization/actionItemCategories.ts b/src/resolvers/Organization/actionItemCategories.ts index 37dbe69f26..e84de326a8 100644 --- a/src/resolvers/Organization/actionItemCategories.ts +++ b/src/resolvers/Organization/actionItemCategories.ts @@ -1,9 +1,17 @@ import { ActionItemCategory } from "../../models"; import type { OrganizationResolvers } from "../../types/generatedGraphQLTypes"; + /** - * This resolver function will fetch and return the categories of the Organization from database. - * @param parent - An object that is the return value of the resolver for this field's parent. - * @returns An object that contains the list of all categories of the organization. + * Resolver function for the `actionItemCategories` field of an `Organization`. + * + * This function retrieves the action item categories related to a specific organization. + * + * @param parent - The parent object representing the organization. It contains information about the organization, including the ID of the organization. + * @returns A promise that resolves to the action item category documents found in the database. These documents represent the action item categories related to the organization. + * + * @see ActionItemCategory - The ActionItemCategory model used to interact with the action item categories collection in the database. + * @see OrganizationResolvers - The type definition for the resolvers of the Organization fields. + * */ export const actionItemCategories: OrganizationResolvers["actionItemCategories"] = async (parent) => { diff --git a/src/resolvers/Organization/admins.ts b/src/resolvers/Organization/admins.ts index 87b525a090..ed113e5037 100644 --- a/src/resolvers/Organization/admins.ts +++ b/src/resolvers/Organization/admins.ts @@ -1,9 +1,17 @@ import { User } from "../../models"; import type { OrganizationResolvers } from "../../types/generatedGraphQLTypes"; + /** - * This resolver function will fetch and return the admins of the Organization from database. - * @param parent - An object that is the return value of the resolver for this field's parent. - * @returns An object that contains the list of all admins of the organization. + * Resolver function for the `admins` field of an `Organization`. + * + * This function retrieves the users who are admins of a specific organization. + * + * @param parent - The parent object representing the organization. It contains information about the organization, including the IDs of the users who are admins. + * @returns A promise that resolves to an array of user documents found in the database. These documents represent the users who are admins of the organization. + * + * @see User - The User model used to interact with the users collection in the database. + * @see OrganizationResolvers - The type definition for the resolvers of the Organization fields. + * */ export const admins: OrganizationResolvers["admins"] = async (parent) => { return await User.find({ diff --git a/src/resolvers/Organization/advertisements.ts b/src/resolvers/Organization/advertisements.ts index 74f7736400..77b3f336e0 100644 --- a/src/resolvers/Organization/advertisements.ts +++ b/src/resolvers/Organization/advertisements.ts @@ -16,10 +16,24 @@ import { GraphQLError } from "graphql"; import { MAXIMUM_FETCH_LIMIT } from "../../constants"; /** - * Resolver function to fetch and return advertisements created in an organization from the database. - * @param parent - An object that is the return value of the resolver for this field's parent. - * @param args - Arguments passed to the resolver. - * @returns An object containing an array of advertisements,totalCount of advertisements and pagination information. + * Resolver function for the `advertisements` field of an `Organization`. + * + * This resolver is used to resolve the `advertisements` field of an `Organization` type. + * + * @param parent - The parent object representing the organization. It contains information about the organization, including the ID of the organization. + * @param args - The arguments provided to the field. These arguments are used to filter, sort, and paginate the advertisements. + * @param context - The context object passed to the GraphQL resolvers. It contains the API root URL, which is used to construct the media URL for each advertisement. + * @returns A promise that resolves to a connection object containing the advertisements of the organization. + * + * @see Advertisement - The Advertisement model used to interact with the advertisements collection in the database. + * @see parseGraphQLConnectionArguments - The function used to parse the GraphQL connection arguments (filter, sort, pagination). + * @see transformToDefaultGraphQLConnection - The function used to transform the list of advertisements into a connection object. + * @see getCommonGraphQLConnectionFilter - The function used to get the common filter object for the GraphQL connection. + * @see getCommonGraphQLConnectionSort - The function used to get the common sort object for the GraphQL connection. + * @see MAXIMUM_FETCH_LIMIT - The maximum number of advertisements that can be fetched in a single request. + * @see GraphQLError - The error class used to throw GraphQL errors. + * @see OrganizationResolvers - The type definition for the resolvers of the Organization fields. + * */ export const advertisements: OrganizationResolvers["advertisements"] = async ( parent, @@ -96,10 +110,23 @@ This is typescript type of the parsed cursor for this connection resolver. */ type ParsedCursor = string; -/* -This function is used to validate and transform the cursor passed to this connnection -resolver. -*/ +/** + * Parses the cursor value for the `advertisements` connection resolver. + * + * This function is used to parse the cursor value provided in the arguments of the `advertisements` connection resolver. + * + * @param cursorValue - The cursor value provided in the arguments. + * @param cursorName - The name of the cursor argument. + * @param cursorPath - The path to the cursor argument in the arguments object. + * @param organizationId - The ID of the organization to which the advertisements belong. + * @returns An object containing the parsed cursor value, or an array of errors if the cursor value is invalid. + * + * @see Advertisement - The Advertisement model used to interact with the advertisements collection in the database. + * @see DefaultGraphQLArgumentError - The type definition for the default GraphQL argument error. + * @see ParseGraphQLConnectionCursorArguments - The type definition for the arguments of the parseCursor function. + * @see ParseGraphQLConnectionCursorResult - The type definition for the result of the parseCursor function. + * + */ export const parseCursor = async ({ cursorValue, cursorName, diff --git a/src/resolvers/Organization/agendaCategories.ts b/src/resolvers/Organization/agendaCategories.ts index 43a9acf48d..159f53bf10 100644 --- a/src/resolvers/Organization/agendaCategories.ts +++ b/src/resolvers/Organization/agendaCategories.ts @@ -1,9 +1,17 @@ import { AgendaCategoryModel } from "../../models"; import type { OrganizationResolvers } from "../../types/generatedGraphQLTypes"; + /** - * This resolver function will fetch and return the categories of the Organization from database. - * @param parent - An object that is the return value of the resolver for this field's parent. - * @returns An object that contains the list of all categories of the organization. + * Resolver function for the `agendaCategories` field of an `Organization`. + * + * This function retrieves the agenda categories of a specific organization. + * + * @param parent - The parent object representing the organization. It contains information about the organization, including the ID of the organization. + * @returns A promise that resolves to an array of agenda category documents found in the database. These documents represent the agenda categories of the organization. + * + * @see AgendaCategoryModel - The AgendaCategory model used to interact with the agendaCategories collection in the database. + * @see OrganizationResolvers - The type definition for the resolvers of the Organization fields. + * */ export const agendaCategories: OrganizationResolvers["agendaCategories"] = async (parent) => { diff --git a/src/resolvers/Organization/blockedUsers.ts b/src/resolvers/Organization/blockedUsers.ts index 70b5a5f9b5..cd6618f7e7 100644 --- a/src/resolvers/Organization/blockedUsers.ts +++ b/src/resolvers/Organization/blockedUsers.ts @@ -1,9 +1,17 @@ import { User } from "../../models"; import type { OrganizationResolvers } from "../../types/generatedGraphQLTypes"; + /** - * This resolver function will fetch and return the blocked users for the Organization from database. - * @param parent - An object that is the return value of the resolver for this field's parent. - * @returns An object that contains the list of blocked users for the organization. + * Resolver function for the `blockedUsers` field of an `Organization`. + * + * This function retrieves the users who are blocked by a specific organization. + * + * @param parent - The parent object representing the organization. It contains information about the organization, including the IDs of the users who are blocked. + * @returns A promise that resolves to an array of user documents found in the database. These documents represent the users who are blocked by the organization. + * + * @see User - The User model used to interact with the users collection in the database. + * @see OrganizationResolvers - The type definition for the resolvers of the Organization fields. + * */ export const blockedUsers: OrganizationResolvers["blockedUsers"] = async ( parent, diff --git a/src/resolvers/Organization/creator.ts b/src/resolvers/Organization/creator.ts index 822d0f6ad8..068faa97c8 100644 --- a/src/resolvers/Organization/creator.ts +++ b/src/resolvers/Organization/creator.ts @@ -2,10 +2,18 @@ import { User } from "../../models"; import { errors, requestContext } from "../../libraries"; import type { OrganizationResolvers } from "../../types/generatedGraphQLTypes"; import { USER_NOT_FOUND_ERROR } from "../../constants"; + /** - * This resolver function will fetch and return the creator of the Organization from database. - * @param parent - An object that is the return value of the resolver for this field's parent. - * @returns An object that contains the creator data. If the creator not exists then throws an `NotFoundError` error. + * Resolver function for the `creator` field of an `Organization`. + * + * This function retrieves the user who created a specific organization. + * + * @param parent - The parent object representing the organization. It contains information about the organization, including the ID of the user who created it. + * @returns A promise that resolves to the user document found in the database. This document represents the user who created the organization. + * + * @see User - The User model used to interact with the users collection in the database. + * @see OrganizationResolvers - The type definition for the resolvers of the Organization fields. + * */ export const creator: OrganizationResolvers["creator"] = async (parent) => { const user = await User.findOne({ diff --git a/src/resolvers/Organization/funds.ts b/src/resolvers/Organization/funds.ts index acdec929f9..18dc4eed9a 100644 --- a/src/resolvers/Organization/funds.ts +++ b/src/resolvers/Organization/funds.ts @@ -2,9 +2,16 @@ import { Fund } from "../../models"; import type { OrganizationResolvers } from "../../types/generatedGraphQLTypes"; /** - * This resolver function will fetch and return the funds of the Organization from database. - * @param parent - An object that is the return value of the resolver for this field's parent. - * @returns An array of objects that contains the funds data. If the funds not exists then returns an empty array. + * Resolver function for the `funds` field of an `Organization`. + * + * This function retrieves the funds related to a specific organization. + * + * @param parent - The parent object representing the organization. It contains information about the organization, including the ID of the organization. + * @returns A promise that resolves to the fund documents found in the database. These documents represent the funds related to the organization. + * + * @see Fund - The Fund model used to interact with the funds collection in the database. + * @see OrganizationResolvers - The type definition for the resolvers of the Organization fields. + * */ export const funds: OrganizationResolvers["funds"] = async (parent) => { return await Fund.find({ diff --git a/src/resolvers/Organization/image.ts b/src/resolvers/Organization/image.ts index c81fedcf60..b94afa9975 100644 --- a/src/resolvers/Organization/image.ts +++ b/src/resolvers/Organization/image.ts @@ -1,6 +1,17 @@ // Context object contains an apiRootUrl for mapping DNS request of server to its domain, for example: http:abcd.com/ import type { OrganizationResolvers } from "../../types/generatedGraphQLTypes"; +/** + * Resolver function for the `image` field of an `Organization`. + * + * This function retrieves the image URL of a specific organization. + * + * @param parent - The parent object representing the organization. It contains information about the organization, including the image URL. + * @returns The URL of the image of the organization. + * + * @see OrganizationResolvers - The type definition for the resolvers of the Organization fields. + * + */ export const image: OrganizationResolvers["image"] = ( parent, _args, diff --git a/src/resolvers/Organization/members.ts b/src/resolvers/Organization/members.ts index f13394077e..1cbff039fb 100644 --- a/src/resolvers/Organization/members.ts +++ b/src/resolvers/Organization/members.ts @@ -2,9 +2,16 @@ import { User } from "../../models"; import type { OrganizationResolvers } from "../../types/generatedGraphQLTypes"; /** - * This resolver function will fetch and return the list of members of the Organization from database. - * @param parent - An object that is the return value of the resolver for this field's parent. - * @returns An object that contains the list of all members of the organization. + * Resolver function for the `members` field of an `Organization`. + * + * This function retrieves the users who are members of a specific organization. + * + * @param parent - The parent object representing the organization. It contains information about the organization, including the IDs of the users who are members of it. + * @returns A promise that resolves to an array of user documents found in the database. These documents represent the users who are members of the organization. + * + * @see User - The User model used to interact with the users collection in the database. + * @see OrganizationResolvers - The type definition for the resolvers of the Organization fields. + * */ export const members: OrganizationResolvers["members"] = async (parent) => { return await User.find({ diff --git a/src/resolvers/Organization/membershipRequests.ts b/src/resolvers/Organization/membershipRequests.ts index ebb2433c6d..1af5c31174 100644 --- a/src/resolvers/Organization/membershipRequests.ts +++ b/src/resolvers/Organization/membershipRequests.ts @@ -1,10 +1,17 @@ import { MembershipRequest } from "../../models"; import type { OrganizationResolvers } from "../../types/generatedGraphQLTypes"; + /** - * This resolver function will fetch and return the list of Membership requests for the Organization from database. - * @param parent - An object that is the return value of the resolver for this field's parent. - * @param args - An object that contains relevant data to perform the query. - * @returns An object that contains the list of membership requests for the organization. + * Resolver function for the `membershipRequests` field of an `Organization`. + * + * This function retrieves the membership requests related to a specific organization. + * + * @param parent - The parent object representing the organization. It contains information about the organization, including the IDs of the membership requests. + * @returns A promise that resolves to an array of membership request documents found in the database. These documents represent the membership requests related to the organization. + * + * @see MembershipRequest - The MembershipRequest model used to interact with the membership requests collection in the database. + * @see OrganizationResolvers - The type definition for the resolvers of the Organization fields. + * */ export const membershipRequests: OrganizationResolvers["membershipRequests"] = async (parent, args) => { diff --git a/src/resolvers/Organization/pinnedPosts.ts b/src/resolvers/Organization/pinnedPosts.ts index e2347ae6ff..46bf4fcf28 100644 --- a/src/resolvers/Organization/pinnedPosts.ts +++ b/src/resolvers/Organization/pinnedPosts.ts @@ -3,6 +3,18 @@ import { cachePosts } from "../../services/PostCache/cachePosts"; import { findPostsInCache } from "../../services/PostCache/findPostsInCache"; import type { OrganizationResolvers } from "../../types/generatedGraphQLTypes"; +/** + * Resolver function for the `pinnedPosts` field of an `Organization`. + * + * This function retrieves the posts that are pinned by a specific organization. + * + * @param parent - The parent object representing the organization. It contains information about the organization, including the IDs of the posts that are pinned. + * @returns A promise that resolves to the post documents found in the database. These documents represent the posts that are pinned by the organization. + * + * @see Post - The Post model used to interact with the posts collection in the database. + * @see OrganizationResolvers - The type definition for the resolvers of the Organization fields. + * + */ export const pinnedPosts: OrganizationResolvers["pinnedPosts"] = async ( parent, ) => { diff --git a/src/resolvers/Organization/posts.ts b/src/resolvers/Organization/posts.ts index bc5c57b916..55f0ba9cae 100644 --- a/src/resolvers/Organization/posts.ts +++ b/src/resolvers/Organization/posts.ts @@ -17,15 +17,24 @@ import { GraphQLError } from "graphql"; import { MAXIMUM_FETCH_LIMIT } from "../../constants"; /** - * Resolver function to fetch and return posts created by a user from the database. - * This function implements cursor-based pagination using GraphQL connection arguments. + * Resolver function for the `posts` field of an `Organization`. * - * @param parent - An object that is the return value of the resolver for this field's parent. - * @param args - Arguments passed to the resolver. These should include pagination arguments such as `first`, `last`, `before`, and `after`. + * This resolver is used to resolve the `posts` field of an `Organization` type. * - * @returns A Promise that resolves to an object containing an array of posts, the total count of posts, and pagination information. The pagination information includes the `startCursor`, `endCursor`, `hasPreviousPage`, and `hasNextPage`. + * @param parent - The parent object representing the organization. It contains information about the organization, including the ID of the organization. + * @param args - The arguments provided to the field. These arguments are used to filter, sort, and paginate the posts. + * @param context - The context object passed to the GraphQL resolvers. It contains the API root URL, which is used to construct the media URL for each post. + * @returns A promise that resolves to a connection object containing the posts of the organization. + * + * @see Post - The Post model used to interact with the posts collection in the database. + * @see parseGraphQLConnectionArguments - The function used to parse the GraphQL connection arguments (filter, sort, pagination). + * @see transformToDefaultGraphQLConnection - The function used to transform the list of posts into a connection object. + * @see getCommonGraphQLConnectionFilter - The function used to get the common filter object for the GraphQL connection. + * @see getCommonGraphQLConnectionSort - The function used to get the common sort object for the GraphQL connection. + * @see MAXIMUM_FETCH_LIMIT - The maximum number of posts that can be fetched in a single request. + * @see GraphQLError - The error class used to throw GraphQL errors. + * @see OrganizationResolvers - The type definition for the resolvers of the Organization fields. * - * @throws GraphQLError Throws an error if the provided arguments are invalid. */ export const posts: OrganizationResolvers["posts"] = async ( parent, @@ -104,14 +113,21 @@ This is typescript type of the parsed cursor for this connection resolver. type ParsedCursor = string; /** - * This function is used to validate and transform the cursor passed to the `posts` connection resolver. + * Parses the cursor value for the `posts` connection resolver. + * + * This function is used to parse the cursor value for the `posts` connection resolver. * - * @param args - An object that includes the cursor value, cursor name, cursor path, and the ID of the creator. + * @param cursorValue - The cursor value to be parsed. + * @param cursorName - The name of the cursor argument. + * @param cursorPath - The path of the cursor argument in the GraphQL query. + * @param organization - The ID of the organization to which the posts belong. + * @returns An object containing the parsed cursor value or an array of errors if the cursor is invalid. * - * @returns A Promise that resolves to an object that includes a boolean indicating whether the operation was successful, and the parsed cursor value. If the operation was not successful, the object also includes an array of errors. + * @see Post - The Post model used to interact with the posts collection in the database. + * @see ParseGraphQLConnectionCursorArguments - The type definition for the arguments of the parseCursor function. + * @see ParseGraphQLConnectionCursorResult - The type definition for the result of the parseCursor function. * - * @throws Error Throws an error if the provided cursor is invalid. - * */ + */ export const parseCursor = async ({ cursorValue, cursorName, diff --git a/src/resolvers/Organization/userTags.ts b/src/resolvers/Organization/userTags.ts index e76b50220a..6ccdf71d5b 100644 --- a/src/resolvers/Organization/userTags.ts +++ b/src/resolvers/Organization/userTags.ts @@ -14,6 +14,25 @@ import { GraphQLError } from "graphql"; import { MAXIMUM_FETCH_LIMIT } from "../../constants"; import type { Types } from "mongoose"; +/** + * Resolver function for the `userTags` field of an `Organization`. + * + * This resolver is used to resolve the `userTags` field of an `Organization` type. + * + * @param parent - The parent object representing the organization. It contains information about the organization, including the ID of the organization. + * @param args - The arguments provided to the field. These arguments are used to filter, sort, and paginate the user tags. + * @returns A promise that resolves to a connection object containing the user tags of the organization. + * + * @see OrganizationTagUser - The OrganizationTagUser model used to interact with the user tags collection in the database. + * @see parseGraphQLConnectionArguments - The function used to parse the GraphQL connection arguments (filter, sort, pagination). + * @see transformToDefaultGraphQLConnection - The function used to transform the list of user tags into a connection object. + * @see getCommonGraphQLConnectionFilter - The function used to get the common filter object for the GraphQL connection. + * @see getCommonGraphQLConnectionSort - The function used to get the common sort object for the GraphQL connection. + * @see MAXIMUM_FETCH_LIMIT - The maximum number of user tags that can be fetched in a single request. + * @see GraphQLError - The error class used to throw GraphQL errors. + * @see OrganizationResolvers - The type definition for the resolvers of the Organization fields. + * + */ export const userTags: OrganizationResolvers["userTags"] = async ( parent, args, @@ -81,10 +100,23 @@ This is typescript type of the parsed cursor for this connection resolver. */ type ParsedCursor = string; -/* -This function is used to validate and transform the cursor passed to this connnection -resolver. -*/ +/** + * Parses the cursor value for the `userTags` connection resolver. + * + * This function is used to parse the cursor value for the `userTags` connection resolver. + * + * @param cursorValue - The cursor value to be parsed. + * @param cursorName - The name of the cursor argument. + * @param cursorPath - The path of the cursor argument in the query. + * @param organizationId - The ID of the organization to which the user tags belong. + * @returns An object containing the parsed cursor value or an array of errors if the cursor is invalid. + * + * @see OrganizationTagUser - The OrganizationTagUser model used to interact with the user tags collection in the database. + * @see DefaultGraphQLArgumentError - The type definition for the default GraphQL argument error. + * @see ParseGraphQLConnectionCursorArguments - The type definition for the arguments of the parseCursor function. + * @see ParseGraphQLConnectionCursorResult - The type definition for the result of the parseCursor function. + * + */ export const parseCursor = async ({ cursorValue, cursorName, diff --git a/src/resolvers/Organization/venues.ts b/src/resolvers/Organization/venues.ts index e283977c7a..e1d7af0b51 100644 --- a/src/resolvers/Organization/venues.ts +++ b/src/resolvers/Organization/venues.ts @@ -1,9 +1,17 @@ import type { OrganizationResolvers } from "./../../types/generatedGraphQLTypes"; import { Venue } from "../../models"; + /** - * This resolver will fetch the list of all venues within an Organization from database. - * @param parent- An object that contains `id` of the organization. - * @returns An array that contains the venues. + * Resolver function for the `venues` field of an `Organization`. + * + * This function retrieves the venues related to a specific organization. + * + * @param parent - The parent object representing the organization. It contains information about the organization, including the ID of the organization. + * @returns A promise that resolves to the venue documents found in the database. These documents represent the venues related to the organization. + * + * @see Venue - The Venue model used to interact with the venues collection in the database. + * @see OrganizationResolvers - The type definition for the resolvers of the Organization fields. + * */ export const venues: OrganizationResolvers["venues"] = async (parent) => { return await Venue.find({ organization: parent._id.toString() }).lean(); diff --git a/src/resolvers/Post/comments.ts b/src/resolvers/Post/comments.ts index 2fa0abd431..3672aad776 100644 --- a/src/resolvers/Post/comments.ts +++ b/src/resolvers/Post/comments.ts @@ -3,6 +3,18 @@ import { Comment } from "../../models"; import { cacheComments } from "../../services/CommentCache/cacheComments"; import { findCommentsByPostIdInCache } from "../../services/CommentCache/findCommentsByPostIdInCache"; +/** + * Resolver function for the `comments` field of a `Post`. + * + * This function retrieves the comments associated with a specific post. + * + * @param parent - The parent object representing the post. It contains information about the post, including the ID of the comments associated with it. + * @returns A promise that resolves to an array of comment documents found in the database. These documents represent the comments associated with the post. + * + * @see Comment - The Comment model used to interact with the comments collection in the database. + * @see PostResolvers - The type definition for the resolvers of the Post fields. + * + */ export const comments: PostResolvers["comments"] = async (parent) => { const commentsInCache = await findCommentsByPostIdInCache(parent._id); diff --git a/src/resolvers/Post/creator.ts b/src/resolvers/Post/creator.ts index a365a179d7..d9a7b6e507 100644 --- a/src/resolvers/Post/creator.ts +++ b/src/resolvers/Post/creator.ts @@ -1,6 +1,18 @@ import type { PostResolvers } from "../../types/generatedGraphQLTypes"; import { User } from "../../models"; +/** + * Resolver function for the `creator` field of a `Post`. + * + * This function retrieves the user who created a specific post. + * + * @param parent - The parent object representing the post. It contains information about the post, including the ID of the user who created it. + * @returns A promise that resolves to the user document found in the database. This document represents the user who created the post. + * + * @see User - The User model used to interact with the users collection in the database. + * @see PostResolvers - The type definition for the resolvers of the Post fields. + * + */ export const creator: PostResolvers["creator"] = async (parent) => { return await User.findOne({ _id: parent.creatorId, diff --git a/src/resolvers/Query/directChatById.ts b/src/resolvers/Query/directChatById.ts new file mode 100644 index 0000000000..00a4b9f852 --- /dev/null +++ b/src/resolvers/Query/directChatById.ts @@ -0,0 +1,31 @@ +import type { QueryResolvers } from "../../types/generatedGraphQLTypes"; +import { errors } from "../../libraries"; +import { DirectChat } from "../../models"; +import { CHAT_NOT_FOUND_ERROR } from "../../constants"; + +/** + * This query will fetch all messages for a certain direct chat for the user from database. + * @param _parent- + * @param args - An object that contains `id` of the direct chat. + * @returns A `directChatsMessages` object that holds all of the messages from the specified direct chat. + * If the `directChatsMessages` object is null then it throws `NotFoundError` error. + * @remarks You can learn about GraphQL `Resolvers` + * {@link https://www.apollographql.com/docs/apollo-server/data/resolvers/ | here}. + */ + +export const directChatById: QueryResolvers["directChatById"] = async ( + _parent, + args, +) => { + const directChat = await DirectChat.findById(args.id).lean(); + + if (!directChat) { + throw new errors.NotFoundError( + CHAT_NOT_FOUND_ERROR.DESC, + CHAT_NOT_FOUND_ERROR.CODE, + CHAT_NOT_FOUND_ERROR.PARAM, + ); + } + + return directChat; +}; diff --git a/src/resolvers/Query/groupChatById.ts b/src/resolvers/Query/groupChatById.ts new file mode 100644 index 0000000000..43a00ce65d --- /dev/null +++ b/src/resolvers/Query/groupChatById.ts @@ -0,0 +1,31 @@ +import type { QueryResolvers } from "../../types/generatedGraphQLTypes"; +import { errors } from "../../libraries"; +import { GroupChat } from "../../models"; +import { CHAT_NOT_FOUND_ERROR } from "../../constants"; + +/** + * This query will fetch all messages for a certain direct chat for the user from database. + * @param _parent- + * @param args - An object that contains `id` of the direct chat. + * @returns A `directChatsMessages` object that holds all of the messages from the specified direct chat. + * If the `directChatsMessages` object is null then it throws `NotFoundError` error. + * @remarks You can learn about GraphQL `Resolvers` + * {@link https://www.apollographql.com/docs/apollo-server/data/resolvers/ | here}. + */ + +export const groupChatById: QueryResolvers["groupChatById"] = async ( + _parent, + args, +) => { + const directChat = await GroupChat.findById(args.id).lean(); + + if (!directChat) { + throw new errors.NotFoundError( + CHAT_NOT_FOUND_ERROR.DESC, + CHAT_NOT_FOUND_ERROR.CODE, + CHAT_NOT_FOUND_ERROR.PARAM, + ); + } + + return directChat; +}; diff --git a/src/resolvers/Query/groupChatsByUserId.ts b/src/resolvers/Query/groupChatsByUserId.ts new file mode 100644 index 0000000000..a3b3310d3e --- /dev/null +++ b/src/resolvers/Query/groupChatsByUserId.ts @@ -0,0 +1,30 @@ +import type { QueryResolvers } from "../../types/generatedGraphQLTypes"; +import { errors } from "../../libraries"; +import { GroupChat } from "../../models"; +/** + * This query will fetch all the Direct chats for the current user from the database. + * @param _parent- + * @param args - An object that contains `id` of the user. + * @returns An object `GroupChat` that contains all direct chats of the current user. + * If the `directChats` object is null then it throws `NotFoundError` error. + * @remarks You can learn about GraphQL `Resolvers` + * {@link https://www.apollographql.com/docs/apollo-server/data/resolvers/ | here}. + */ +export const groupChatsByUserId: QueryResolvers["groupChatsByUserId"] = async ( + _parent, + args, +) => { + const groupChats = await GroupChat.find({ + users: args.id, + }).lean(); + + if (groupChats.length === 0) { + throw new errors.NotFoundError( + "Group Chats not found", + "groupChats.notFound", + "groupChats", + ); + } + + return groupChats; +}; diff --git a/src/resolvers/Query/index.ts b/src/resolvers/Query/index.ts index 9c7f96eac7..55c74a7bca 100644 --- a/src/resolvers/Query/index.ts +++ b/src/resolvers/Query/index.ts @@ -15,6 +15,9 @@ import { customDataByOrganization } from "./customDataByOrganization"; import { customFieldsByOrganization } from "./customFieldsByOrganization"; import { directChatsByUserID } from "./directChatsByUserID"; import { directChatsMessagesByChatID } from "./directChatsMessagesByChatID"; +import { directChatById } from "./directChatById"; +import { groupChatById } from "./groupChatById"; +import { groupChatsByUserId } from "./groupChatsByUserId"; import { event } from "./event"; import { eventsByOrganization } from "./eventsByOrganization"; import { eventsByOrganizationConnection } from "./eventsByOrganizationConnection"; @@ -63,6 +66,9 @@ export const Query: QueryResolvers = { customDataByOrganization, directChatsByUserID, directChatsMessagesByChatID, + directChatById, + groupChatById, + groupChatsByUserId, event, eventsByOrganization, eventsByOrganizationConnection, diff --git a/src/resolvers/RecurrenceRule/baseRecurringEvent.ts b/src/resolvers/RecurrenceRule/baseRecurringEvent.ts index 48b90afab1..35200640e8 100644 --- a/src/resolvers/RecurrenceRule/baseRecurringEvent.ts +++ b/src/resolvers/RecurrenceRule/baseRecurringEvent.ts @@ -1,6 +1,18 @@ import type { RecurrenceRuleResolvers } from "../../types/generatedGraphQLTypes"; import { Event } from "../../models"; +/** + * Resolver function for the `baseRecurringEvent` field of a `RecurrenceRule`. + * + * This function retrieves the base recurring event associated with a specific recurrence rule. + * + * @param parent - The parent object representing the recurrence rule. It contains information about the recurrence rule, including the ID of the base recurring event associated with it. + * @returns A promise that resolves to the event document found in the database. This document represents the base recurring event associated with the recurrence rule. + * + * @see Event - The Event model used to interact with the events collection in the database. + * @see RecurrenceRuleResolvers - The type definition for the resolvers of the RecurrenceRule fields. + * + */ export const baseRecurringEvent: RecurrenceRuleResolvers["baseRecurringEvent"] = async (parent) => { return await Event.findOne({ diff --git a/src/resolvers/RecurrenceRule/organization.ts b/src/resolvers/RecurrenceRule/organization.ts index b17690cb7e..6266884482 100644 --- a/src/resolvers/RecurrenceRule/organization.ts +++ b/src/resolvers/RecurrenceRule/organization.ts @@ -1,6 +1,19 @@ import type { RecurrenceRuleResolvers } from "../../types/generatedGraphQLTypes"; import { Organization } from "../../models"; +/** + * Resolver function for the `organization` field of a `RecurrenceRule`. + * + * This function retrieves the organization associated with a specific recurrence rule. + * + * @param parent - The parent object representing the recurrence rule. It contains information about the recurrence rule, including the ID of the organization associated with it. + * @returns A promise that resolves to the organization document found in the database. This document represents the organization associated with the recurrence rule. + * + * @see Organization - The Organization model used to interact with the organizations collection in the database. + * @see RecurrenceRuleResolvers - The type definition for the resolvers of the RecurrenceRule fields. + * + */ + export const organization: RecurrenceRuleResolvers["organization"] = async ( parent, ) => { diff --git a/src/resolvers/Subscription/messageSentToDirectChat.ts b/src/resolvers/Subscription/messageSentToDirectChat.ts index bd937b051f..55ce56e603 100644 --- a/src/resolvers/Subscription/messageSentToDirectChat.ts +++ b/src/resolvers/Subscription/messageSentToDirectChat.ts @@ -4,11 +4,16 @@ import type { SubscriptionResolvers } from "../../types/generatedGraphQLTypes"; const MESSAGE_SENT_TO_DIRECT_CHAT = "MESSAGE_SENT_TO_DIRECT_CHAT"; // eslint-disable-next-line @typescript-eslint/no-explicit-any -export const filterFunction = function (payload: any, context: any): boolean { - const { currentUserId } = context.context; +export const filterFunction = function (payload: any, variables: any): boolean { + const currentUserId = variables.userId.toString(); + console.log( + currentUserId, + payload.messageSentToDirectChat.receiver.toString(), + payload.messageSentToDirectChat.sender.toString(), + ); return ( - currentUserId === payload.messageSentToDirectChat.receiver || - currentUserId === payload.messageSentToDirectChat.sender + currentUserId === payload.messageSentToDirectChat.receiver.toString() || + currentUserId === payload.messageSentToDirectChat.sender.toString() ); }; /** @@ -26,6 +31,6 @@ export const messageSentToDirectChat: SubscriptionResolvers["messageSentToDirect (_parent, _args, context) => context.pubsub.asyncIterator([MESSAGE_SENT_TO_DIRECT_CHAT]), - (payload, _variables, context) => filterFunction(payload, context), + (payload, variables) => filterFunction(payload, variables), ), }; diff --git a/src/resolvers/Subscription/messageSentToGroupChat.ts b/src/resolvers/Subscription/messageSentToGroupChat.ts index 0d4c56fa59..ec45eb30c2 100644 --- a/src/resolvers/Subscription/messageSentToGroupChat.ts +++ b/src/resolvers/Subscription/messageSentToGroupChat.ts @@ -5,11 +5,18 @@ import { GroupChat } from "../../models"; const MESSAGE_SENT_TO_GROUP_CHAT = "MESSAGE_SENT_TO_GROUP_CHAT"; +/** + * This function is used to filter the subscription payload based on the current user's membership in the group chat. + * + * @param payload - The payload of the subscription message. + * @param context - The context object containing the current user's ID. + * @returns A promise that resolves to a boolean value indicating whether the current user is a member of the group chat. + */ export const filterFunction = async function ( payload: any, - context: any, + variables: any, ): Promise { - const { currentUserId } = context.context; + const currentUserId = variables.userId; const groupChatId = payload.messageSentToGroupChat.groupChatMessageBelongsTo; const groupChat = await GroupChat.findOne({ @@ -40,6 +47,6 @@ export const messageSentToGroupChat: SubscriptionResolvers["messageSentToGroupCh (_parent, _args, context) => context.pubsub.asyncIterator([MESSAGE_SENT_TO_GROUP_CHAT]), - (payload, _variables, context) => filterFunction(payload, context), + (payload, variables) => filterFunction(payload, variables), ), }; diff --git a/src/resolvers/Subscription/onPluginUpdate.ts b/src/resolvers/Subscription/onPluginUpdate.ts index f4744845dc..848bbaf648 100644 --- a/src/resolvers/Subscription/onPluginUpdate.ts +++ b/src/resolvers/Subscription/onPluginUpdate.ts @@ -25,9 +25,25 @@ export const filterFunction = async function ( ): Promise { return true; }; + +/** + * This function creates a response object for the `onPluginUpdate` subscription. + * + * @param payload - The payload received from the subscription. + * @returns The response object for the subscription. + */ export const createPluginUpdateResponse = (payload: any): any => { return payload.Plugin; }; + +/** + * This property included a `subscribe` method, which is used to + * subscribe the `current_user` to get updates for Group chats. + * + * @remarks To control updates on a per-client basis, the function uses the `withFilter` + * method imported from `apollo-server-express` module. + * You can learn about `subscription` {@link https://www.apollographql.com/docs/apollo-server/data/subscriptions/ | here }. + */ export const onPluginUpdate: SubscriptionResolvers["onPluginUpdate"] = { // @ts-expect-error-ts-ignore subscribe: withFilter( diff --git a/src/resolvers/UserFamily/admins.ts b/src/resolvers/UserFamily/admins.ts index 9571bd0c02..0eab116e98 100644 --- a/src/resolvers/UserFamily/admins.ts +++ b/src/resolvers/UserFamily/admins.ts @@ -1,9 +1,17 @@ import { User } from "../../models"; import type { UserFamilyResolvers } from "../../types/generatedGraphQLTypes"; + /** - * This resolver function will fetch and return the admins of the Organization from database. - * @param parent - An object that is the return value of the resolver for this field's parent. - * @returns An object that contains the list of all admins of the organization. + * Resolver function for the `admins` field of a `UserFamily`. + * + * This function retrieves the users who are admins of a specific user family. + * + * @param parent - The parent object representing the user family. It contains information about the user family, including the IDs of the users who are admins. + * @returns A promise that resolves to an array of user documents found in the database. These documents represent the users who are admins of the user family. + * + * @see User - The User model used to interact with the users collection in the database. + * @see UserFamilyResolvers - The type definition for the resolvers of the UserFamily fields. + * */ export const admins: UserFamilyResolvers["admins"] = async (parent) => { return await User.find({ diff --git a/src/resolvers/UserFamily/creator.ts b/src/resolvers/UserFamily/creator.ts index 69fb4b7e3b..1e0ec81727 100644 --- a/src/resolvers/UserFamily/creator.ts +++ b/src/resolvers/UserFamily/creator.ts @@ -2,10 +2,18 @@ import { User } from "../../models"; import { errors, requestContext } from "../../libraries"; import type { UserFamilyResolvers } from "../../types/generatedGraphQLTypes"; import { USER_NOT_FOUND_ERROR } from "../../constants"; + /** - * This resolver function will fetch and return the creator of the Organization from database. - * @param parent - An object that is the return value of the resolver for this field's parent. - * @returns An object that contains the creator data. If the creator not exists then throws an `NotFoundError` error. + * Resolver function for the `creator` field of a `UserFamily`. + * + * This function retrieves the user who created a specific user family. + * + * @param parent - The parent object representing the user family. It contains information about the user family, including the ID of the user who created it. + * @returns A promise that resolves to the user document found in the database. This document represents the user who created the user family. + * + * @see User - The User model used to interact with the users collection in the database. + * @see UserFamilyResolvers - The type definition for the resolvers of the UserFamily fields. + * */ export const creator: UserFamilyResolvers["creator"] = async (parent) => { const user = await User.findOne({ diff --git a/src/resolvers/UserFamily/users.ts b/src/resolvers/UserFamily/users.ts index c02ba909f4..2a139731b7 100644 --- a/src/resolvers/UserFamily/users.ts +++ b/src/resolvers/UserFamily/users.ts @@ -1,9 +1,17 @@ import type { UserFamilyResolvers } from "../../types/generatedGraphQLTypes"; import { User } from "../../models"; + /** - * This resolver function will fetch and return the list of all Members of the user family from database. - * @param parent - An object that is the return value of the resolver for this field's parent. - * @returns An `object` that contains the Member data. + * Resolver function for the `users` field of a `UserFamily`. + * + * This function retrieves the users who are part of a specific user family. + * + * @param parent - The parent object representing the user family. It contains information about the user family, including the IDs of the users who are part of it. + * @returns A promise that resolves to an array of user documents found in the database. These documents represent the users who are part of the user family. + * + * @see User - The User model used to interact with the users collection in the database. + * @see UserFamilyResolvers - The type definition for the resolvers of the UserFamily fields. + * */ export const users: UserFamilyResolvers["users"] = async (parent) => { return await User.find({ diff --git a/src/resolvers/UserTag/childTags.ts b/src/resolvers/UserTag/childTags.ts index 15704f6ab3..ae95fb2b16 100644 --- a/src/resolvers/UserTag/childTags.ts +++ b/src/resolvers/UserTag/childTags.ts @@ -14,6 +14,25 @@ import { GraphQLError } from "graphql"; import { MAXIMUM_FETCH_LIMIT } from "../../constants"; import type { Types } from "mongoose"; +/** + * Resolver function for the `childTags` field of a `UserTag`. + * + * This resolver is used to resolve the `childTags` field of a `UserTag` type. + * + * @param parent - The parent object representing the user tag. It contains information about the user tag, including the ID of the user tag. + * @param args - The arguments provided to the field. These arguments are used to filter, sort, and paginate the child tags. + * @returns A promise that resolves to a connection object containing the child tags of the user tag. + * + * @see OrganizationTagUser - The OrganizationTagUser model used to interact with the organization tag users collection in the database. + * @see parseGraphQLConnectionArguments - The function used to parse the GraphQL connection arguments (filter, sort, pagination). + * @see transformToDefaultGraphQLConnection - The function used to transform the list of child tags into a connection object. + * @see getCommonGraphQLConnectionFilter - The function used to get the common filter object for the GraphQL connection. + * @see getCommonGraphQLConnectionSort - The function used to get the common sort object for the GraphQL connection. + * @see MAXIMUM_FETCH_LIMIT - The maximum number of child tags that can be fetched in a single request. + * @see GraphQLError - The error class used to throw GraphQL errors. + * @see UserTagResolvers - The type definition for the resolvers of the UserTag fields. + * + */ export const childTags: UserTagResolvers["childTags"] = async ( parent, args, diff --git a/src/resolvers/UserTag/organization.ts b/src/resolvers/UserTag/organization.ts index b38ead0540..f14406a047 100644 --- a/src/resolvers/UserTag/organization.ts +++ b/src/resolvers/UserTag/organization.ts @@ -1,6 +1,18 @@ import type { UserTagResolvers } from "../../types/generatedGraphQLTypes"; import { Organization } from "../../models"; +/** + * Resolver function for the `organization` field of a `UserTag`. + * + * This function retrieves the organization associated with a specific user tag. + * + * @param parent - The parent object representing the user tag. It contains information about the user tag, including the ID of the organization associated with it. + * @returns A promise that resolves to the organization document found in the database. This document represents the organization associated with the user tag. + * + * @see Organization - The Organization model used to interact with the organizations collection in the database. + * @see UserTagResolvers - The type definition for the resolvers of the UserTag fields. + * + */ export const organization: UserTagResolvers["organization"] = async ( parent, ) => { diff --git a/src/resolvers/UserTag/parentTag.ts b/src/resolvers/UserTag/parentTag.ts index 29eb9f99ae..d4ecbd76b0 100644 --- a/src/resolvers/UserTag/parentTag.ts +++ b/src/resolvers/UserTag/parentTag.ts @@ -1,6 +1,18 @@ import type { UserTagResolvers } from "../../types/generatedGraphQLTypes"; import { OrganizationTagUser } from "../../models"; +/** + * Resolver function for the `parentTag` field of a `UserTag`. + * + * This function retrieves the parent tag associated with a specific user tag. + * + * @param parent - The parent object representing the user tag. It contains information about the user tag, including the ID of the parent tag associated with it. + * @returns A promise that resolves to the user tag document found in the database. This document represents the parent tag associated with the user tag. + * + * @see OrganizationTagUser - The OrganizationTagUser model used to interact with the user tags collection in the database. + * @see UserTagResolvers - The type definition for the resolvers of the UserTag fields. + * + */ export const parentTag: UserTagResolvers["parentTag"] = async (parent) => { // Check if the parentTag is null if (parent.parentTagId === null) return null; diff --git a/src/resolvers/UserTag/usersAssignedTo.ts b/src/resolvers/UserTag/usersAssignedTo.ts index ade6da58c0..ebc8dd20a6 100644 --- a/src/resolvers/UserTag/usersAssignedTo.ts +++ b/src/resolvers/UserTag/usersAssignedTo.ts @@ -14,6 +14,25 @@ import { GraphQLError } from "graphql"; import { MAXIMUM_FETCH_LIMIT } from "../../constants"; import type { Types } from "mongoose"; +/** + * Resolver function for the `usersAssignedTo` field of a `UserTag`. + * + * This resolver is used to resolve the `usersAssignedTo` field of a `UserTag` type. + * + * @param parent - The parent object representing the user tag. It contains information about the user tag, including the ID of the user tag. + * @param args - The arguments provided to the field. These arguments are used to filter, sort, and paginate the users assigned to the user tag. + * @returns A promise that resolves to a connection object containing the users assigned to the user tag. + * + * @see TagUser - The TagUser model used to interact with the tag users collection in the database. + * @see parseGraphQLConnectionArguments - The function used to parse the GraphQL connection arguments (filter, sort, pagination). + * @see transformToDefaultGraphQLConnection - The function used to transform the list of users assigned to the user tag into a connection object. + * @see getCommonGraphQLConnectionFilter - The function used to get the common filter object for the GraphQL connection. + * @see getCommonGraphQLConnectionSort - The function used to get the common sort object for the GraphQL connection. + * @see MAXIMUM_FETCH_LIMIT - The maximum number of users that can be fetched in a single request. + * @see GraphQLError - The error class used to throw GraphQL errors. + * @see UserTagResolvers - The type definition for the resolvers of the UserTag fields. + * + */ export const usersAssignedTo: UserTagResolvers["usersAssignedTo"] = async ( parent, args, @@ -86,10 +105,23 @@ This is typescript type of the parsed cursor for this connection resolver. */ type ParsedCursor = string; -/* -This function is used to validate and transform the cursor passed to this connnection -resolver. -*/ +/** + * Parses the cursor value for the `usersAssignedTo` connection resolver. + * + * This function is used to parse the cursor value provided to the `usersAssignedTo` connection resolver. + * + * @param cursorValue - The cursor value to be parsed. + * @param cursorName - The name of the cursor argument. + * @param cursorPath - The path of the cursor argument in the GraphQL query. + * @param tagId - The ID of the user tag to which the users are assigned. + * @returns An object containing the parsed cursor value or an array of errors if the cursor value is invalid. + * + * @see TagUser - The TagUser model used to interact with the tag users collection in the database. + * @see DefaultGraphQLArgumentError - The type definition for the default GraphQL argument error. + * @see ParseGraphQLConnectionCursorArguments - The type definition for the arguments provided to the parseCursor function. + * @see ParseGraphQLConnectionCursorResult - The type definition for the result of the parseCursor function. + * + */ export const parseCursor = async ({ cursorValue, cursorName, diff --git a/src/resolvers/middleware/currentUserExists.ts b/src/resolvers/middleware/currentUserExists.ts index 39fcb362aa..9aec9404fe 100644 --- a/src/resolvers/middleware/currentUserExists.ts +++ b/src/resolvers/middleware/currentUserExists.ts @@ -4,7 +4,19 @@ import { User } from "../../models"; import { USER_NOT_FOUND_ERROR } from "../../constants"; import { errors, requestContext } from "../../libraries"; -// This is a middleware that checks that the user specificied by the `userId` parameter in the context does indeed exist in the database +/** + * Middleware function to check if the current user exists in the database. + * + * This function is used to check if the user making a request to the server exists in the database. + * If the user does not exist, the function throws an error. + * + * @param next - The next function to call in the resolver chain. + * @returns The result of the next function in the resolver chain. + * + * @see User - The User model used to interact with the users collection in the database. + * @see USER_NOT_FOUND_ERROR - The error message to display when the user is not found. + * @see errors - The library used to create custom errors in the application. + */ export const currentUserExists = () => (next: (root: any, args: any, context: any, info: any) => any) => diff --git a/src/services/AppUserProfileCache/cacheAppUserProfile.ts b/src/services/AppUserProfileCache/cacheAppUserProfile.ts index 977079390e..afeb79f7b1 100644 --- a/src/services/AppUserProfileCache/cacheAppUserProfile.ts +++ b/src/services/AppUserProfileCache/cacheAppUserProfile.ts @@ -2,21 +2,34 @@ import { logger } from "../../libraries"; import type { InterfaceAppUserProfile } from "../../models"; import AppUserCache from "../redisCache"; +/** + * Stores app user profiles in Redis cache with a specified time-to-live (TTL). + * @param appUserProfiles - Array of app user profiles to be cached. + * @returns Promise + */ export async function cacheAppUserProfile( appUserProfiles: InterfaceAppUserProfile[], ): Promise { try { + // Create a pipeline for efficient Redis operations const pipeline = AppUserCache.pipeline(); + appUserProfiles.forEach((appUserProfile) => { if (appUserProfile !== null) { + // Generate key for each app user profile based on its ID const key = `appUserProfile:${appUserProfile._id}`; - // Set the appUserProfile in the cache + + // Store app user profile data as JSON string in Redis pipeline.set(key, JSON.stringify(appUserProfile)); - // SET the time to live for each of the appUserProfile in the cache to 300s. + + // Set TTL for each app user profile to 300 seconds (5 minutes) pipeline.expire(key, 300); } }); + + // Execute the pipeline for batch Redis operations } catch (error) { + // Log any errors that occur during caching logger.info(error); } } diff --git a/src/services/AppUserProfileCache/deleteAppUserFromCache.ts b/src/services/AppUserProfileCache/deleteAppUserFromCache.ts index d9ae97ee99..96983ce068 100644 --- a/src/services/AppUserProfileCache/deleteAppUserFromCache.ts +++ b/src/services/AppUserProfileCache/deleteAppUserFromCache.ts @@ -1,8 +1,17 @@ import AppUserCache from "../redisCache"; + +/** + * Deletes the specified app user profile from Redis cache. + * + * @param appUserProfileId - The string representing the app user profile ID to delete from cache. + * @returns A promise resolving to void. + */ export async function deleteAppUserFromCache( appUserProfileId: string, ): Promise { + // Generate the cache key for the app user profile based on its ID const key = `appUserProfile:${appUserProfileId}`; + // Delete the app user profile from Redis cache await AppUserCache.del(key); } diff --git a/src/services/CommentCache/cacheComments.ts b/src/services/CommentCache/cacheComments.ts index bfe9cd6d53..6e5fc1f6c4 100644 --- a/src/services/CommentCache/cacheComments.ts +++ b/src/services/CommentCache/cacheComments.ts @@ -2,30 +2,42 @@ import { logger } from "../../libraries"; import type { InterfaceComment } from "../../models"; import CommentCache from "../redisCache"; -// Function to store comments in the cache using pipelining +/** + * Stores comments in Redis cache with a specified time-to-live (TTL). + * @param comments - Array of comments to be cached. + * @returns Promise + */ export async function cacheComments( comments: InterfaceComment[], ): Promise { try { + // Create a pipeline for efficient Redis operations const pipeline = CommentCache.pipeline(); comments.forEach((comment) => { if (comment !== null) { + // Generate key for each comment based on its ID const key = `comment:${comment._id}`; + + // Generate key for indexing comments by postId const postID = `post_comments:${comment.postId}`; - // Set the comment in the cache + + // Store comment data as JSON string in Redis pipeline.set(key, JSON.stringify(comment)); - // Index comment on its postId + + // Index comment based on its postId pipeline.hset(postID, key, "null"); - // SET the time to live for each of the organization in the cache to 300s. + + // Set TTL for each comment and its postId index to 300 seconds (5 minutes) pipeline.expire(key, 300); pipeline.expire(postID, 300); } }); - // Execute the pipeline + // Execute the pipeline for batch Redis operations await pipeline.exec(); } catch (error) { + // Log any errors that occur during caching logger.info(error); } } diff --git a/src/services/CommentCache/deleteCommentFromCache.ts b/src/services/CommentCache/deleteCommentFromCache.ts index 30f8914e6d..70b826270d 100644 --- a/src/services/CommentCache/deleteCommentFromCache.ts +++ b/src/services/CommentCache/deleteCommentFromCache.ts @@ -1,10 +1,18 @@ import CommentCache from "../redisCache"; import type { InterfaceComment } from "../../models"; +/** + * Deletes the specified comment from Redis cache. + * + * @param comment - The InterfaceComment object representing the comment to delete. + * @returns A promise resolving to void. + */ export async function deleteCommentFromCache( comment: InterfaceComment, ): Promise { + // Generate the cache key for the comment based on its _id const key = `comment:${comment._id}`; + // Delete the comment from Redis cache await CommentCache.del(key); } diff --git a/src/services/EventCache/cacheEvents.ts b/src/services/EventCache/cacheEvents.ts index a73f300cb1..a07b9e27d3 100644 --- a/src/services/EventCache/cacheEvents.ts +++ b/src/services/EventCache/cacheEvents.ts @@ -2,23 +2,33 @@ import { logger } from "../../libraries"; import type { InterfaceEvent } from "../../models"; import EventCache from "../redisCache"; -// Function to store events in the cache using pipelining +/** + * Stores events in Redis cache with a specified time-to-live (TTL). + * @param events - Array of events to be cached. + * @returns Promise + */ export async function cacheEvents(events: InterfaceEvent[]): Promise { try { + // Create a pipeline for efficient Redis operations const pipeline = EventCache.pipeline(); events.forEach((event) => { if (event !== null) { + // Generate key for each event based on its ID const key = `event:${event._id}`; + + // Store event data as JSON string in Redis pipeline.set(key, JSON.stringify(event)); - // SET the time to live for each of the organization in the cache to 300s. + + // Set TTL for each event to 300 seconds (5 minutes) pipeline.expire(key, 300); } }); - // Execute the pipeline + // Execute the pipeline for batch Redis operations await pipeline.exec(); } catch (error) { + // Log any errors that occur during caching logger.info(error); } } diff --git a/src/services/EventCache/deleteEventFromCache.ts b/src/services/EventCache/deleteEventFromCache.ts index 0f36da3cce..f3e283a7a9 100644 --- a/src/services/EventCache/deleteEventFromCache.ts +++ b/src/services/EventCache/deleteEventFromCache.ts @@ -1,10 +1,18 @@ import EventCache from "../redisCache"; import type { Types } from "mongoose"; +/** + * Deletes the specified event from Redis cache. + * + * @param eventId - The ObjectId representing the event to delete from cache. + * @returns A promise resolving to void. + */ export async function deleteEventFromCache( eventId: Types.ObjectId, ): Promise { + // Generate the cache key for the event based on its eventId const key = `event:${eventId}`; + // Delete the event from Redis cache await EventCache.del(key); } diff --git a/src/services/OrganizationCache/cacheOrganizations.ts b/src/services/OrganizationCache/cacheOrganizations.ts index 6620ce04cf..6614e98f92 100644 --- a/src/services/OrganizationCache/cacheOrganizations.ts +++ b/src/services/OrganizationCache/cacheOrganizations.ts @@ -2,25 +2,35 @@ import { logger } from "../../libraries"; import type { InterfaceOrganization } from "../../models"; import OrganizationCache from "../redisCache"; -// Function to store organizations in the cache using pipelining +/** + * Stores organizations in Redis cache with a specified time-to-live (TTL). + * @param organizations - Array of organizations to be cached. + * @returns Promise + */ export async function cacheOrganizations( organizations: InterfaceOrganization[], ): Promise { try { + // Create a pipeline for efficient Redis operations const pipeline = OrganizationCache.pipeline(); organizations.forEach((org) => { if (org !== null) { + // Generate key for each organization based on its ID const key = `organization:${org._id}`; + + // Store organization data as JSON string in Redis pipeline.set(key, JSON.stringify(org)); - // SET the time to live for each of the organization in the cache to 300s. + + // Set TTL for each organization to 300 seconds (5 minutes) pipeline.expire(key, 300); } }); - // Execute the pipeline + // Execute the pipeline for batch Redis operations await pipeline.exec(); } catch (error) { + // Log any errors that occur during caching logger.info(error); } } diff --git a/src/services/OrganizationCache/deleteOrganizationFromCache.ts b/src/services/OrganizationCache/deleteOrganizationFromCache.ts index 030b13c64f..f760a778e0 100644 --- a/src/services/OrganizationCache/deleteOrganizationFromCache.ts +++ b/src/services/OrganizationCache/deleteOrganizationFromCache.ts @@ -1,10 +1,18 @@ import OrganizationCache from "../redisCache"; import type { InterfaceOrganization } from "../../models"; +/** + * Deletes the specified organization from Redis cache. + * + * @param organization - The InterfaceOrganization object representing the organization to delete. + * @returns A promise resolving to void. + */ export async function deleteOrganizationFromCache( organization: InterfaceOrganization, ): Promise { + // Generate the cache key for the organization based on its _id const key = `organization:${organization._id}`; + // Delete the organization from Redis cache await OrganizationCache.del(key); } diff --git a/src/services/PostCache/cachePosts.ts b/src/services/PostCache/cachePosts.ts index 91a5d123bd..0a703a1912 100644 --- a/src/services/PostCache/cachePosts.ts +++ b/src/services/PostCache/cachePosts.ts @@ -2,23 +2,35 @@ import { logger } from "../../libraries"; import type { InterfacePost } from "../../models"; import PostCache from "../redisCache"; -// Function to store posts in the cache using pipelining +/** + * Caches the provided array of InterfacePost objects in Redis. + * + * @param posts - An array of InterfacePost objects to be cached. + * @returns A promise resolving to void. + */ export async function cachePosts(posts: InterfacePost[]): Promise { try { + // Create a Redis pipeline for efficient multi-command execution const pipeline = PostCache.pipeline(); + // Iterate through each post in the array posts.forEach((post) => { if (post !== null) { + // Generate the cache key for each post const key = `post:${post._id}`; + + // Store the post object as a JSON string in Redis pipeline.set(key, JSON.stringify(post)); - // SET the time to live for each of the organization in the cache to 300s. - pipeline.expire(key, 300); + + // Set an expiration time for the cache key (in seconds) + pipeline.expire(key, 300); // 300 seconds (5 minutes) expiration } }); - // Execute the pipeline + // Execute the Redis pipeline await pipeline.exec(); } catch (error) { + // Log any errors that occur during caching logger.info(error); } } diff --git a/src/services/PostCache/deletePostFromCache.ts b/src/services/PostCache/deletePostFromCache.ts index 1a3ffa2dd6..aa0d80a9cf 100644 --- a/src/services/PostCache/deletePostFromCache.ts +++ b/src/services/PostCache/deletePostFromCache.ts @@ -1,7 +1,14 @@ import PostCache from "../redisCache"; +/** + * Deletes a post from Redis cache based on its postId. + * @param postId - The unique identifier of the post to delete. + * @returns Promise + */ export async function deletePostFromCache(postId: string): Promise { + // Construct the cache key for the specified postId const key = `post:${postId}`; + // Delete the post from Redis cache await PostCache.del(key); } diff --git a/src/services/UserCache/cacheUser.ts b/src/services/UserCache/cacheUser.ts index 0df2068e19..142420aa9e 100644 --- a/src/services/UserCache/cacheUser.ts +++ b/src/services/UserCache/cacheUser.ts @@ -2,18 +2,35 @@ import { logger } from "../../libraries"; import type { InterfaceUser } from "../../models"; import UserCache from "../redisCache"; +/** + * Caches the provided array of InterfaceUser objects in Redis. + * + * @param users - An array of InterfaceUser objects to be cached. + * @returns A promise resolving to void. + */ export async function cacheUsers(users: InterfaceUser[]): Promise { try { + // Create a Redis pipeline for efficient multi-command execution const pipeline = UserCache.pipeline(); + + // Iterate through each user in the array users.forEach((user) => { if (user !== null) { + // Generate the cache key for each user const key = `user:${user._id}`; + + // Store the user object as a JSON string in Redis pipeline.set(key, JSON.stringify(user)); - pipeline.expire(key, 300); + + // Set an expiration time for the cache key (in seconds) + pipeline.expire(key, 300); // 300 seconds (5 minutes) expiration } }); + + // Execute the Redis pipeline await pipeline.exec(); } catch (error) { + // Log any errors that occur during caching logger.info(error); } } diff --git a/src/services/UserCache/deleteUserFromCache.ts b/src/services/UserCache/deleteUserFromCache.ts index 70df4930c0..f451fd759d 100644 --- a/src/services/UserCache/deleteUserFromCache.ts +++ b/src/services/UserCache/deleteUserFromCache.ts @@ -1,6 +1,9 @@ import UserCache from "../redisCache"; + export async function deleteUserFromCache(userId: string): Promise { + // Construct the key for the user in the cache const key = `user:${userId}`; + // Delete the user entry from the Redis cache await UserCache.del(key); } diff --git a/src/services/UserCache/findUserInCache.ts b/src/services/UserCache/findUserInCache.ts index 19e8077c77..39a95438cb 100644 --- a/src/services/UserCache/findUserInCache.ts +++ b/src/services/UserCache/findUserInCache.ts @@ -3,22 +3,37 @@ import { logger } from "../../libraries"; import type { InterfaceUser } from "../../models"; import UserCache from "../redisCache"; +/** + * Retrieves user data from cache based on provided IDs. + * + * @param ids - An array of user IDs to retrieve from cache. + * @returns A promise resolving to an array of InterfaceUser objects or null if not found in cache. + */ export async function findUserInCache( ids: string[], ): Promise<(InterfaceUser | null)[]> { + // If no IDs are provided, return an array with null if (ids.length === 0) { return [null]; } + + // Generate cache keys for each ID const keys: string[] = ids.map((id) => { return `user:${id}`; }); + + // Retrieve user data from cache const userFoundInCache = await UserCache.mget(keys); + + // Parse cached JSON data into InterfaceUser objects const users = userFoundInCache.map((user: string | null) => { if (user === null) { return null; } try { const parsedUser = JSON.parse(user); + + // Convert specific fields to Types.ObjectId for Mongoose compatibility return { ...parsedUser, _id: new Types.ObjectId(parsedUser._id), @@ -49,8 +64,10 @@ export async function findUserInCache( : [], }; } catch (error) { + // Log error if parsing fails logger.info(`Error parsing user from cache: ${error}`); } }); + return users; } diff --git a/src/services/redisCache.ts b/src/services/redisCache.ts index b87469b859..8b8ad4a7f0 100644 --- a/src/services/redisCache.ts +++ b/src/services/redisCache.ts @@ -1,16 +1,19 @@ import { Redis } from "ioredis"; import { REDIS_HOST, REDIS_PASSWORD, REDIS_PORT } from "../constants"; +// Create a new Redis instance const RedisCache = new Redis({ host: REDIS_HOST, port: REDIS_PORT || 6379, password: REDIS_PASSWORD, }); -// Setting the limit of the max memory of the cache to 100MB +// Configure Redis settings if no password is set if (!REDIS_PASSWORD) { + // Set the maximum memory limit of the cache to 100MB RedisCache.config("SET", "maxmemory", 100 * 1024 * 1024); - // Setting the eviction policy to Least Frequently Used ,evicted first algorithm + + // Set the eviction policy to "Least Frequently Used, evicted first" algorithm RedisCache.config("SET", "maxmemory-policy", "allkeys-lfu"); } diff --git a/src/typeDefs/directives.ts b/src/typeDefs/directives.ts index d6f8c3cf15..e1f28c4a72 100644 --- a/src/typeDefs/directives.ts +++ b/src/typeDefs/directives.ts @@ -1,6 +1,11 @@ import { gql } from "graphql-tag"; // Place fields alphabetically to ensure easier lookup and navigation. + +/** + * GraphQL schema definition for directives. + */ + export const directives = gql` directive @auth on FIELD_DEFINITION diff --git a/src/typeDefs/errors/common.ts b/src/typeDefs/errors/common.ts index d6e81f986d..c76d90d392 100644 --- a/src/typeDefs/errors/common.ts +++ b/src/typeDefs/errors/common.ts @@ -1,5 +1,8 @@ import { gql } from "graphql-tag"; +/** + * GraphQL schema definition for common error types. + */ export const commonErrors = gql` interface Error { message: String! diff --git a/src/typeDefs/errors/connectionError.ts b/src/typeDefs/errors/connectionError.ts index ab496b189d..d865acdb3b 100644 --- a/src/typeDefs/errors/connectionError.ts +++ b/src/typeDefs/errors/connectionError.ts @@ -1,5 +1,8 @@ import { gql } from "graphql-tag"; +/** + * GraphQL schema definition for connection-related errors. + */ export const connectionError = gql` union ConnectionError = InvalidCursor | MaximumValueError diff --git a/src/typeDefs/errors/createAdminErrors.ts b/src/typeDefs/errors/createAdminErrors.ts index 7cac5a774d..d50bb898c3 100644 --- a/src/typeDefs/errors/createAdminErrors.ts +++ b/src/typeDefs/errors/createAdminErrors.ts @@ -1,5 +1,8 @@ import { gql } from "graphql-tag"; +/** + * GraphQL schema definition for errors related to creating an admin. + */ export const createAdminErrors = gql` type OrganizationNotFoundError implements Error { message: String! diff --git a/src/typeDefs/errors/createCommentErrors.ts b/src/typeDefs/errors/createCommentErrors.ts index 0a38ea4d32..383d1350c7 100644 --- a/src/typeDefs/errors/createCommentErrors.ts +++ b/src/typeDefs/errors/createCommentErrors.ts @@ -1,5 +1,9 @@ import { gql } from "graphql-tag"; +/** + * GraphQL schema definition for errors related to creating a comment. + */ + export const createCommentErrors = gql` type PostNotFoundError implements Error { message: String! diff --git a/src/typeDefs/errors/createDirectChatError.ts b/src/typeDefs/errors/createDirectChatError.ts index 72055321e2..f2a9b9eb43 100644 --- a/src/typeDefs/errors/createDirectChatError.ts +++ b/src/typeDefs/errors/createDirectChatError.ts @@ -1,4 +1,8 @@ import { gql } from "graphql-tag"; + +/** + * GraphQL schema definition for errors related to creating a direct chat. + */ export const createDirectChatErrors = gql` type OrganizationNotFoundError implements Error { message: String! diff --git a/src/typeDefs/errors/createMemberErrors.ts b/src/typeDefs/errors/createMemberErrors.ts index f291b0cbed..4786ca6839 100644 --- a/src/typeDefs/errors/createMemberErrors.ts +++ b/src/typeDefs/errors/createMemberErrors.ts @@ -1,5 +1,9 @@ import { gql } from "graphql-tag"; +/** + * GraphQL schema definition for errors related to creating a member. + */ + export const createMemberErrors = gql` type UserNotFoundError implements Error { message: String! diff --git a/src/typeDefs/errors/index.ts b/src/typeDefs/errors/index.ts index 9f51a5ec54..a71db2d6e3 100644 --- a/src/typeDefs/errors/index.ts +++ b/src/typeDefs/errors/index.ts @@ -4,6 +4,10 @@ import { createMemberErrors } from "./createMemberErrors"; import { createAdminErrors } from "./createAdminErrors"; import { createCommentErrors } from "./createCommentErrors"; import { createDirectChatErrors } from "./createDirectChatError"; + +/** + * Array of all error definitions. + */ export const errors = [ commonErrors, connectionError, diff --git a/src/typeDefs/inputs.ts b/src/typeDefs/inputs.ts index c15731a238..e209e9a09d 100644 --- a/src/typeDefs/inputs.ts +++ b/src/typeDefs/inputs.ts @@ -18,7 +18,7 @@ export const inputs = gql` input createChatInput { userIds: [ID!]! - organizationId: ID! + organizationId: ID } input createGroupChatInput { diff --git a/src/typeDefs/queries.ts b/src/typeDefs/queries.ts index 7a6da99cfb..80f0c4acaa 100644 --- a/src/typeDefs/queries.ts +++ b/src/typeDefs/queries.ts @@ -43,6 +43,12 @@ export const queries = gql` directChatsByUserID(id: ID!): [DirectChat] + directChatById(id: ID!): DirectChat + + groupChatById(id: ID!): GroupChat + + groupChatsByUserId(id: ID!): [GroupChat] + directChatsMessagesByChatID(id: ID!): [DirectChatMessage] event(id: ID!): Event diff --git a/src/typeDefs/subscriptions.ts b/src/typeDefs/subscriptions.ts index f7c1dff8c1..4c718b9761 100644 --- a/src/typeDefs/subscriptions.ts +++ b/src/typeDefs/subscriptions.ts @@ -4,8 +4,8 @@ import { gql } from "graphql-tag"; export const subscriptions = gql` type Subscription { directMessageChat: MessageChat - messageSentToDirectChat: DirectChatMessage - messageSentToGroupChat: GroupChatMessage + messageSentToDirectChat(userId: ID!): DirectChatMessage + messageSentToGroupChat(userId: ID!): GroupChatMessage onPluginUpdate: Plugin } `; diff --git a/src/typeDefs/types.ts b/src/typeDefs/types.ts index b275a935e6..a3a1b41ffa 100644 --- a/src/typeDefs/types.ts +++ b/src/typeDefs/types.ts @@ -183,7 +183,7 @@ export const types = gql` creator: User createdAt: DateTime! updatedAt: DateTime! - organization: Organization! + organization: Organization } type DirectChatMessage { @@ -372,6 +372,7 @@ export const types = gql` type GroupChat { _id: ID! + title: String! users: [User!]! messages: [GroupChatMessage] creator: User diff --git a/src/types/generatedGraphQLTypes.ts b/src/types/generatedGraphQLTypes.ts index f12b1b3643..4c05807c81 100644 --- a/src/types/generatedGraphQLTypes.ts +++ b/src/types/generatedGraphQLTypes.ts @@ -616,7 +616,7 @@ export type DirectChat = { createdAt: Scalars['DateTime']['output']; creator?: Maybe; messages?: Maybe>>; - organization: Organization; + organization?: Maybe; updatedAt: Scalars['DateTime']['output']; users: Array; }; @@ -1001,6 +1001,7 @@ export type GroupChat = { creator?: Maybe; messages?: Maybe>>; organization: Organization; + title: Scalars['String']['output']; updatedAt: Scalars['DateTime']['output']; users: Array; }; @@ -2259,6 +2260,7 @@ export type Query = { checkAuth: User; customDataByOrganization: Array; customFieldsByOrganization?: Maybe>>; + directChatById?: Maybe; directChatsByUserID?: Maybe>>; directChatsMessagesByChatID?: Maybe>>; event?: Maybe; @@ -2284,6 +2286,8 @@ export type Query = { getPlugins?: Maybe>>; getVenueByOrgId?: Maybe>>; getlanguage?: Maybe>>; + groupChatById?: Maybe; + groupChatsByUserId?: Maybe>>; hasSubmittedFeedback?: Maybe; isSampleOrganization: Scalars['Boolean']['output']; joinedOrganizations?: Maybe>>; @@ -2364,6 +2368,11 @@ export type QueryCustomFieldsByOrganizationArgs = { }; +export type QueryDirectChatByIdArgs = { + id: Scalars['ID']['input']; +}; + + export type QueryDirectChatsByUserIdArgs = { id: Scalars['ID']['input']; }; @@ -2491,6 +2500,16 @@ export type QueryGetlanguageArgs = { }; +export type QueryGroupChatByIdArgs = { + id: Scalars['ID']['input']; +}; + + +export type QueryGroupChatsByUserIdArgs = { + id: Scalars['ID']['input']; +}; + + export type QueryHasSubmittedFeedbackArgs = { eventId: Scalars['ID']['input']; userId: Scalars['ID']['input']; @@ -2670,6 +2689,16 @@ export type Subscription = { onPluginUpdate?: Maybe; }; + +export type SubscriptionMessageSentToDirectChatArgs = { + userId: Scalars['ID']['input']; +}; + + +export type SubscriptionMessageSentToGroupChatArgs = { + userId: Scalars['ID']['input']; +}; + export type ToggleUserTagAssignInput = { tagId: Scalars['ID']['input']; userId: Scalars['ID']['input']; @@ -3117,7 +3146,7 @@ export type WeekDays = | 'WEDNESDAY'; export type CreateChatInput = { - organizationId: Scalars['ID']['input']; + organizationId?: InputMaybe; userIds: Array; }; @@ -3911,7 +3940,7 @@ export type DirectChatResolvers; creator?: Resolver, ParentType, ContextType>; messages?: Resolver>>, ParentType, ContextType>; - organization?: Resolver; + organization?: Resolver, ParentType, ContextType>; updatedAt?: Resolver; users?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; @@ -4104,6 +4133,7 @@ export type GroupChatResolvers, ParentType, ContextType>; messages?: Resolver>>, ParentType, ContextType>; organization?: Resolver; + title?: Resolver; updatedAt?: Resolver; users?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; @@ -4502,6 +4532,7 @@ export type QueryResolvers; customDataByOrganization?: Resolver, ParentType, ContextType, RequireFields>; customFieldsByOrganization?: Resolver>>, ParentType, ContextType, RequireFields>; + directChatById?: Resolver, ParentType, ContextType, RequireFields>; directChatsByUserID?: Resolver>>, ParentType, ContextType, RequireFields>; directChatsMessagesByChatID?: Resolver>>, ParentType, ContextType, RequireFields>; event?: Resolver, ParentType, ContextType, RequireFields>; @@ -4527,6 +4558,8 @@ export type QueryResolvers>>, ParentType, ContextType>; getVenueByOrgId?: Resolver>>, ParentType, ContextType, RequireFields>; getlanguage?: Resolver>>, ParentType, ContextType, RequireFields>; + groupChatById?: Resolver, ParentType, ContextType, RequireFields>; + groupChatsByUserId?: Resolver>>, ParentType, ContextType, RequireFields>; hasSubmittedFeedback?: Resolver, ParentType, ContextType, RequireFields>; isSampleOrganization?: Resolver>; joinedOrganizations?: Resolver>>, ParentType, ContextType, Partial>; @@ -4575,8 +4608,8 @@ export type SocialMediaUrlsResolvers = { directMessageChat?: SubscriptionResolver, "directMessageChat", ParentType, ContextType>; - messageSentToDirectChat?: SubscriptionResolver, "messageSentToDirectChat", ParentType, ContextType>; - messageSentToGroupChat?: SubscriptionResolver, "messageSentToGroupChat", ParentType, ContextType>; + messageSentToDirectChat?: SubscriptionResolver, "messageSentToDirectChat", ParentType, ContextType, RequireFields>; + messageSentToGroupChat?: SubscriptionResolver, "messageSentToGroupChat", ParentType, ContextType, RequireFields>; onPluginUpdate?: SubscriptionResolver, "onPluginUpdate", ParentType, ContextType>; }; diff --git a/src/utilities/PII/decryption.ts b/src/utilities/PII/decryption.ts index d93b505ec8..4521fbae8a 100644 --- a/src/utilities/PII/decryption.ts +++ b/src/utilities/PII/decryption.ts @@ -1,17 +1,29 @@ import crypto from "crypto"; -// Decryption function +/** + * Decrypts the given encrypted text using AES-256-CBC decryption. + * + * @param encryptedText - The encrypted text to decrypt, encoded as a hexadecimal string. + * @param key - The encryption key used for decryption. + * @param iv - The initialization vector (IV), used to ensure different ciphertexts encrypt to different ciphertexts even if the plaintexts are identical. + * @returns The decrypted plaintext string. + */ export function decrypt( encryptedText: string, key: string, iv: string, ): string { + // Create a decipher object with AES-256-CBC algorithm, using the provided key and IV const decipher = crypto.createDecipheriv( "aes-256-cbc", - Buffer.from(key), - Buffer.from(iv, "hex"), + Buffer.from(key), // Convert key from string to buffer + Buffer.from(iv, "hex"), // Convert IV from hexadecimal string to buffer ); + + // Decrypt the encrypted text let decrypted = decipher.update(encryptedText, "hex", "utf8"); decrypted += decipher.final("utf8"); + + // Return the decrypted plaintext return decrypted; } diff --git a/src/utilities/PII/encryption.ts b/src/utilities/PII/encryption.ts index 05c85bd86a..26953657fa 100644 --- a/src/utilities/PII/encryption.ts +++ b/src/utilities/PII/encryption.ts @@ -1,13 +1,24 @@ import crypto from "crypto"; -// Encryption function +/** + * Encrypts plaintext using AES-256-CBC encryption. + * @param text - The plaintext to encrypt. + * @param key - The encryption key as a string. + * @param iv - The initialization vector (IV) as a string in hexadecimal format. + * @returns The encrypted ciphertext as a hexadecimal string. + */ export function encrypt(text: string, key: string, iv: string): string { + // Create a cipher object using AES-256-CBC algorithm with provided key and IV const cipher = crypto.createCipheriv( "aes-256-cbc", - Buffer.from(key), - Buffer.from(iv, "hex"), + Buffer.from(key), // Convert key string to buffer + Buffer.from(iv, "hex"), // Convert IV string from hexadecimal to buffer ); - let encrypted = cipher.update(text); - encrypted = Buffer.concat([encrypted, cipher.final()]); + + // Encrypt the plaintext + let encrypted = cipher.update(text); // Perform encryption + encrypted = Buffer.concat([encrypted, cipher.final()]); // Finalize encryption and concatenate + + // Return encrypted ciphertext as hexadecimal string return encrypted.toString("hex"); } diff --git a/src/utilities/PII/isAuthorised.ts b/src/utilities/PII/isAuthorised.ts index 65c4120de5..0fb2412fcd 100644 --- a/src/utilities/PII/isAuthorised.ts +++ b/src/utilities/PII/isAuthorised.ts @@ -1,11 +1,19 @@ import type { User } from "../../types/generatedGraphQLTypes"; +/** + * Checks if the requesting user is authorized to access or modify the requested user's data. + * @param requestingUser - The user making the request. + * @param requestedUser - The user whose data is being requested or modified. + * @returns `true` if the requesting user is authorized, `false` otherwise. + */ export function isAuthorised( requestingUser: User, requestedUser: User, ): boolean { + // Check if the requesting user is the same as the requested user if (requestedUser !== requestedUser) { - return false; + return false; // Not authorized if requesting user is not the same as requested user } - return true; + + return true; // Authorized if requesting user is the same as requested user } diff --git a/src/utilities/adminCheck.ts b/src/utilities/adminCheck.ts index 63df8d4b8f..3d49abcd83 100644 --- a/src/utilities/adminCheck.ts +++ b/src/utilities/adminCheck.ts @@ -4,27 +4,40 @@ import { USER_NOT_AUTHORIZED_ADMIN } from "../constants"; import { errors, requestContext } from "../libraries"; import type { InterfaceOrganization } from "../models"; import { AppUserProfile } from "../models"; + /** - * If the current user is an admin of the organisation, this function returns `true` otherwise it returns `false`. + * Checks if the current user is an admin of the organization. + * If the user is an admin, the function completes successfully. Otherwise, it throws an UnauthorizedError. * @remarks * This is a utility method. - * @param userId - Current user id. - * @param organization - Organization data of `InterfaceOrganization` type. + * @param userId - The ID of the current user. It can be a string or a Types.ObjectId. + * @param organization - The organization data of `InterfaceOrganization` type. * @returns `True` or `False`. */ export const adminCheck = async ( userId: string | Types.ObjectId, organization: InterfaceOrganization, ): Promise => { + /** + * Check if the user is listed as an admin in the organization. + * Compares the user ID with the admin IDs in the organization. + */ const userIsOrganizationAdmin = organization.admins.some( (admin) => admin === userId || new mongoose.Types.ObjectId(admin).toString() === userId.toString(), ); + /** + * Fetch the user's profile from the AppUserProfile collection. + */ const userAppProfile = await AppUserProfile.findOne({ userId, }).lean(); + + /** + * If the user's profile is not found, throw an UnauthorizedError. + */ if (!userAppProfile) { throw new errors.UnauthorizedError( requestContext.translate(USER_NOT_AUTHORIZED_ADMIN.MESSAGE), @@ -32,8 +45,15 @@ export const adminCheck = async ( USER_NOT_AUTHORIZED_ADMIN.PARAM, ); } + + /** + * Check if the user has super admin privileges. + */ const isUserSuperAdmin: boolean = userAppProfile.isSuperAdmin; + /** + * If the user is neither an organization admin nor a super admin, throw an UnauthorizedError. + */ if (!userIsOrganizationAdmin && !isUserSuperAdmin) { throw new errors.UnauthorizedError( requestContext.translate(`${USER_NOT_AUTHORIZED_ADMIN.MESSAGE}`), diff --git a/src/utilities/auth.ts b/src/utilities/auth.ts index 90335ddbaf..84389ac1f4 100644 --- a/src/utilities/auth.ts +++ b/src/utilities/auth.ts @@ -3,6 +3,9 @@ import { ACCESS_TOKEN_SECRET, REFRESH_TOKEN_SECRET } from "../constants"; import type { InterfaceAppUserProfile, InterfaceUser } from "../models"; import { User } from "../models"; +/** + * Interface representing the payload of a JWT token. + */ export interface InterfaceJwtTokenPayload { tokenVersion: number; userId: string; @@ -10,10 +13,13 @@ export interface InterfaceJwtTokenPayload { lastName: string; email: string; } + /** - * This function creates a json web token which expires in 15 minutes. - * It signs the given payload(user data) into a JSON Web Token string payload. + * Creates an access token (JWT) for a user that expires in 40 minutes. + * The token contains user data and is signed with the access token secret. + * * @param user - User data + * @param appUserProfile - Application user profile data * @returns JSON Web Token string payload */ export const createAccessToken = ( @@ -35,6 +41,14 @@ export const createAccessToken = ( ); }; +/** + * Creates a refresh token (JWT) for a user that expires in 30 days. + * The token contains user data and is signed with the refresh token secret. + * + * @param user - User data + * @param appUserProfile - Application user profile data + * @returns JSON Web Token string payload + */ export const createRefreshToken = ( user: InterfaceUser, appUserProfile: InterfaceAppUserProfile, @@ -54,6 +68,13 @@ export const createRefreshToken = ( ); }; +/** + * Revokes the refresh token for a user by removing the token from the user's profile. + * This function searches for the user by their ID and unsets the token field in the user's document. + * + * @param userId - The ID of the user whose refresh token is to be revoked + * @returns A promise that resolves when the token has been revoked + */ export const revokeRefreshToken = async (userId: string): Promise => { const user = await User.findOne({ _id: userId }).lean(); diff --git a/src/utilities/checkReplicaSet.ts b/src/utilities/checkReplicaSet.ts index 56f75deba8..61d76292b4 100644 --- a/src/utilities/checkReplicaSet.ts +++ b/src/utilities/checkReplicaSet.ts @@ -1,6 +1,13 @@ import mongoose from "mongoose"; import { logger } from "../libraries"; +/** + * Checks if the MongoDB connection is part of a replica set. + * This function sends a 'hello' command to the MongoDB admin database to retrieve server information, + * and determines if the connection is part of a replica set by checking for the presence of 'hosts' and 'setName' in the result. + * + * @returns A promise that resolves to a boolean indicating whether the connection is part of a replica set (true) or not (false). + */ export const checkReplicaSet = async (): Promise => { try { const adminDb = mongoose.connection.db.admin(); diff --git a/src/utilities/copyToClipboard.ts b/src/utilities/copyToClipboard.ts index d24dfafbc8..ba95a3ec67 100644 --- a/src/utilities/copyToClipboard.ts +++ b/src/utilities/copyToClipboard.ts @@ -1,13 +1,14 @@ import ncp from "copy-paste"; import { IN_PRODUCTION } from "../constants"; + /** - * This utility function copy the text into the clipboard (test change). + * Copies the given text to the clipboard. * @remarks - * This is a utility method. This works only in development or test mode. - * @param text - The content that need to be copied. + * This is a utility method and works only in development or test mode. + * @param text - The content that needs to be copied to the clipboard. */ export const copyToClipboard = (text: string): void => { - // Only copies in development or test mode + // Only copies text to the clipboard in development or test mode if (IN_PRODUCTION !== true) { ncp.copy(text, () => {}); } diff --git a/src/utilities/createSampleOrganizationUtil.ts b/src/utilities/createSampleOrganizationUtil.ts index f6523de23a..fcb51638ec 100644 --- a/src/utilities/createSampleOrganizationUtil.ts +++ b/src/utilities/createSampleOrganizationUtil.ts @@ -14,6 +14,13 @@ import { SampleData } from "../models/SampleData"; /* eslint-disable */ +/** + * Generates user data for a given organization and user type. + * + * @param organizationId - The ID of the organization the user belongs to + * @param userType - The type of the user ('ADMIN' or 'USER') + * @returns A promise that resolves to an object containing the created user and their application profile + */ export const generateUserData = async ( organizationId: string, userType: string, @@ -71,6 +78,13 @@ export const generateUserData = async ( }; }; +/** + * Generates event data for a given list of users and organization. + * + * @param users - The list of users associated with the event + * @param organizationId - The ID of the organization the event belongs to + * @returns A promise that resolves to the created event + */ export const generateEventData = async ( users: InterfaceUser[], organizationId: string, @@ -127,6 +141,13 @@ export const generateEventData = async ( return event; }; +/** + * Generates post data for a given list of users and organization. + * + * @param users - The list of users associated with the post + * @param organizationId - The ID of the organization the post belongs to + * @returns A promise that resolves to the created post + */ export const generatePostData = async ( users: InterfaceUser[], organizationId: string, @@ -157,6 +178,14 @@ export const generatePostData = async ( return post; }; +/** + * Creates multiple posts for a given list of users and organization. + * + * @param numPosts - The number of posts to create + * @param users - The list of users associated with the posts + * @param organizationId - The ID of the organization the posts belong to + * @returns A promise that resolves to an array of created posts + */ const createPosts = async ( numPosts: number, users: InterfaceUser[], @@ -170,6 +199,14 @@ const createPosts = async ( return posts; }; +/** + * Creates multiple events for a given list of users and organization. + * + * @param numEvents - The number of events to create + * @param users - The list of users associated with the events + * @param organizationId - The ID of the organization the events belong to + * @returns A promise that resolves to an array of created events + */ const createEvents = async ( numEvents: number, users: InterfaceUser[], @@ -184,6 +221,13 @@ const createEvents = async ( return events; }; +/** + * Generates random plugin data for a given number of plugins and list of users. + * + * @param numberOfPlugins - The number of plugins to create + * @param users - The list of users associated with the plugins + * @returns A promise that resolves to an array of promises for created plugins + */ export const generateRandomPlugins = async ( numberOfPlugins: number, users: string[], @@ -214,6 +258,11 @@ export const generateRandomPlugins = async ( return pluginPromises; }; +/** + * Creates a sample organization with associated users, events, posts, and plugins. + * + * @returns A promise that resolves when the sample organization and its related data have been created + */ export const createSampleOrganization = async (): Promise => { const _id = faker.database.mongodbObjectId(); const userData = await generateUserData(_id, "ADMIN"); diff --git a/src/utilities/dateValidator.ts b/src/utilities/dateValidator.ts index bb50fe740a..3dff36c2b4 100644 --- a/src/utilities/dateValidator.ts +++ b/src/utilities/dateValidator.ts @@ -3,15 +3,20 @@ import { START_DATE_VALIDATION_ERROR, } from "../constants"; import { errors, requestContext } from "../libraries"; + /** - * This function validates the date. - * @param startDate - starting Date - * @param endDate - Ending Date + * Validates the start and end dates. + * @param startDate - The starting date. + * @param endDate - The ending date. */ export const validateDate = ( startDate: Date | undefined, endDate: Date | undefined, ): void => { + /** + * Checks if the start date is provided and if it's in the past. + * Throws an InputValidationError if the start date is invalid. + */ if (startDate && new Date(startDate) < new Date(new Date().toDateString())) { throw new errors.InputValidationError( requestContext.translate(START_DATE_VALIDATION_ERROR.MESSAGE), @@ -20,7 +25,10 @@ export const validateDate = ( ); } - //Checks if the end date is valid + /** + * Checks if the end date is provided and if it's before the start date. + * Throws an InputValidationError if the end date is invalid. + */ if (endDate && startDate && new Date(endDate) < new Date(startDate)) { throw new errors.InputValidationError( requestContext.translate(END_DATE_VALIDATION_ERROR.MESSAGE), diff --git a/src/utilities/deleteDuplicatedImage.ts b/src/utilities/deleteDuplicatedImage.ts index a6ff192aa6..0e44555977 100644 --- a/src/utilities/deleteDuplicatedImage.ts +++ b/src/utilities/deleteDuplicatedImage.ts @@ -1,17 +1,21 @@ import type { PathLike } from "fs"; import { unlink } from "fs"; import { logger } from "../libraries"; + /** - * This function deletes a duplicated image using the function fs.unlink(). - * @param imagePath - Path of the image + * Deletes a duplicated image file using fs.unlink(). + * @param imagePath - The path to the image file to delete. + * @throws Throws an error if deletion fails. */ export const deleteDuplicatedImage = (imagePath: PathLike): void => { + // Attempt to delete the image file unlink(imagePath, function (error) { if (error) { + // Throw an error if deletion fails throw error; } - // if no error is thrown, file has been deleted successfully + // Log a success message if deletion succeeds logger.info("File was deleted as it already exists in the db!"); }); }; diff --git a/src/utilities/deleteImage.ts b/src/utilities/deleteImage.ts index 74c30cba1f..e578617929 100644 --- a/src/utilities/deleteImage.ts +++ b/src/utilities/deleteImage.ts @@ -2,12 +2,13 @@ import { unlink } from "fs"; import { logger } from "../libraries"; import { ImageHash } from "../models"; import { reuploadDuplicateCheck } from "./reuploadDuplicateCheck"; + /** - * This function deletes an image if it is only used once. - * It is also ensured that the image hash isn't used by multiple users/organization before deleting it - * After deleting the image, the number of uses of the hashed image are decremented by one. - * @param imageToBeDeleted - Path of image - * @param imageBelongingToItem - Does image belong to an item + * Deletes an image file if it meets deletion criteria based on usage and duplicate checks. + * + * @param imageToBeDeleted - The path of the image file to be deleted + * @param imageBelongingToItem - Optional. Indicates if the image belongs to a specific item for duplicate check + * @returns A promise that resolves once the image is successfully deleted */ export const deleteImage = async ( imageToBeDeleted: string, @@ -16,6 +17,7 @@ export const deleteImage = async ( let imageIsDuplicate = false; if (imageBelongingToItem) { + // Check if the image is a duplicate of another image belonging to the same item imageIsDuplicate = await reuploadDuplicateCheck( imageToBeDeleted, imageBelongingToItem, @@ -23,20 +25,21 @@ export const deleteImage = async ( } if (!imageIsDuplicate) { - /* - Only remove the old image if its different from the new one - Ensure image hash isn't used by multiple users/organization before deleting it - */ + // Proceed with deletion only if the image is not a duplicate + + // Retrieve the image hash information from the database const imageHash = await ImageHash.findOne({ fileName: imageToBeDeleted, }).lean(); if (imageHash && imageHash?.numberOfUses > 1) { - // Image can only be deleted if imageHash.numberOfUses === 1 + // If the image is used by multiple users/organizations, log that it cannot be deleted logger.info("Image cannot be deleted"); } else { + // If the image is only used once or not tracked by image hash, proceed with deletion logger.info("Image is only used once and therefore can be deleted"); + // Delete the image file from the filesystem unlink(imageToBeDeleted, (error) => { if (error) { throw error; @@ -47,6 +50,7 @@ export const deleteImage = async ( }); } + // Decrease the usage count of the image hash in the database await ImageHash.updateOne( { fileName: imageToBeDeleted, diff --git a/src/utilities/encodedImageStorage/deletePreviousImage.ts b/src/utilities/encodedImageStorage/deletePreviousImage.ts index 78a35b1712..62eaba2b7a 100644 --- a/src/utilities/encodedImageStorage/deletePreviousImage.ts +++ b/src/utilities/encodedImageStorage/deletePreviousImage.ts @@ -2,15 +2,24 @@ import { unlink } from "fs/promises"; import path from "path"; import { EncodedImage } from "../../models/EncodedImage"; +/** + * Deletes the previous image file if its `numberOfUses` is 1 and updates the `numberOfUses` in the database. + * @param imageToBeDeletedPath - Path of the image to be deleted. + */ export const deletePreviousImage = async ( imageToBeDeletedPath: string, ): Promise => { + // Find the EncodedImage document with the given fileName const imageToBeDeleted = await EncodedImage.findOne({ fileName: imageToBeDeletedPath ?? "", }); + // Check if the image exists and its numberOfUses is 1 if (imageToBeDeleted?.numberOfUses === 1) { + // Delete the image file from the file system await unlink(path.join(__dirname, "../../../" + imageToBeDeleted.fileName)); + + // Delete the EncodedImage document from the database await EncodedImage.deleteOne({ fileName: imageToBeDeletedPath, }); diff --git a/src/utilities/encodedImageStorage/encodedImageExtensionCheck.ts b/src/utilities/encodedImageStorage/encodedImageExtensionCheck.ts index bc6256012e..582f184ebb 100644 --- a/src/utilities/encodedImageStorage/encodedImageExtensionCheck.ts +++ b/src/utilities/encodedImageStorage/encodedImageExtensionCheck.ts @@ -1,4 +1,10 @@ +/** + * Checks if the extension of an encoded image URL is valid (png, jpg, jpeg). + * @param encodedUrl - Encoded URL of the image. + * @returns `true` if the extension is valid, otherwise `false`. + */ export const encodedImageExtentionCheck = (encodedUrl: string): boolean => { + // Extract the extension from the encodedUrl const extension = encodedUrl.substring( "data:".length, encodedUrl.indexOf(";base64"), diff --git a/src/utilities/encodedImageStorage/uploadEncodedImage.ts b/src/utilities/encodedImageStorage/uploadEncodedImage.ts index 919bc8e5ad..45fcaad12b 100644 --- a/src/utilities/encodedImageStorage/uploadEncodedImage.ts +++ b/src/utilities/encodedImageStorage/uploadEncodedImage.ts @@ -8,10 +8,22 @@ import { EncodedImage } from "../../models/EncodedImage"; import path from "path"; import { deletePreviousImage } from "./deletePreviousImage"; +/** + * Checks if the size of the base64 encoded image data is within the allowable limit. + * + * @param size - The size of the image data in kilobytes. + * @returns `true` if the size is within the limit, otherwise `false`. + */ const checkImageSizeLimit = (size: number): boolean => { return size > 0 && size <= 20000; }; +/** + * Calculates the size of the base64 encoded string in kilobytes. + * + * @param base64String - The base64 encoded string representing the image data. + * @returns The size of the image data in kilobytes. + */ const base64SizeInKb = (base64String: string): number => { // Count the number of Base64 characters const numBase64Chars = base64String.length; @@ -23,18 +35,32 @@ const base64SizeInKb = (base64String: string): number => { return sizeInKB; }; +/** + * Uploads an encoded image to the server. + * + * @param encodedImageURL - The URL or content of the encoded image to upload. + * @param previousImagePath - Optional. The path of the previous image to delete before uploading the new one. + * @returns The file name of the uploaded image. + */ export const uploadEncodedImage = async ( encodedImageURL: string, previousImagePath?: string | null, ): Promise => { + // Check if the uploaded image URL/content is a valid image file type const isURLValidImage = encodedImageExtentionCheck(encodedImageURL); + // Extract the base64 data from the image URL const data = encodedImageURL.replace(/^data:image\/\w+;base64,/, ""); + + // Calculate the size of the base64 encoded image data in kilobytes const sizeInKb = base64SizeInKb(data); + + // Retrieve the size limit from environment variables or set a default limit const limit = checkImageSizeLimit(Number(process.env.IMAGE_SIZE_LIMIT_KB)) ? Number(process.env.IMAGE_SIZE_LIMIT_KB) - : 3000; + : 3000; // Default limit in kilobytes + // Throw an error if the image size exceeds the allowable limit if (sizeInKb > limit) { throw new errors.ImageSizeLimitExceeded( IMAGE_SIZE_LIMIT_KB.MESSAGE, @@ -43,6 +69,7 @@ export const uploadEncodedImage = async ( ); } + // Throw an error if the uploaded image is not a valid file type if (!isURLValidImage) { throw new errors.InvalidFileTypeError( requestContext.translate(INVALID_FILE_TYPE.MESSAGE), @@ -51,10 +78,12 @@ export const uploadEncodedImage = async ( ); } + // Check if the encoded image already exists in the database const encodedImageAlreadyExist = await EncodedImage.findOne({ content: encodedImageURL, }); + // If the image already exists, increment its numberOfUses and handle previousImagePath if (encodedImageAlreadyExist) { if (encodedImageAlreadyExist?.fileName === previousImagePath) { return encodedImageAlreadyExist?.fileName; @@ -78,10 +107,12 @@ export const uploadEncodedImage = async ( return encodedImageAlreadyExist.fileName; } + // Handle deletion of previous image if previousImagePath is provided if (previousImagePath) { await deletePreviousImage(previousImagePath); } + // Generate a unique ID for the new image file using nanoid let id = nanoid(); id = "images/" + id + "image.png"; @@ -91,8 +122,10 @@ export const uploadEncodedImage = async ( content: encodedImageURL, }); + // Convert the base64 data into a buffer const buf = Buffer.from(data, "base64"); + // Create an 'images' directory if it doesn't exist if (!fs.existsSync(path.join(__dirname, "../../../images"))) { fs.mkdir(path.join(__dirname, "../../../images"), (err) => { if (err) { @@ -101,7 +134,9 @@ export const uploadEncodedImage = async ( }); } + // Write the image data to the file system await writeFile(path.join(__dirname, "../../../" + id), buf); + // Return the fileName of the uploaded image return uploadedEncodedImage.fileName; }; diff --git a/src/utilities/encodedVideoStorage/deletePreviousVideo.ts b/src/utilities/encodedVideoStorage/deletePreviousVideo.ts index 7cc8ca1035..40f248e60a 100644 --- a/src/utilities/encodedVideoStorage/deletePreviousVideo.ts +++ b/src/utilities/encodedVideoStorage/deletePreviousVideo.ts @@ -2,20 +2,32 @@ import { unlink } from "fs/promises"; import path from "path"; import { EncodedVideo } from "../../models/EncodedVideo"; +/** + * Deletes the previous video file and updates its database entry. + * + * @param videoToBeDeletedPath - The path of the video file to be deleted. + * @returns A promise that resolves once the video file and database entry are deleted or updated. + */ export const deletePreviousVideo = async ( videoToBeDeletedPath: string, ): Promise => { + // Find the EncodedVideo document corresponding to the video file const videoToBeDeleted = await EncodedVideo.findOne({ fileName: videoToBeDeletedPath, }); + // Check if the video file exists and has only one use left if (videoToBeDeleted?.numberOfUses === 1) { + // Delete the video file from the file system await unlink(path.join(__dirname, "../../../" + videoToBeDeleted.fileName)); + + // Delete the EncodedVideo document from the database await EncodedVideo.deleteOne({ fileName: videoToBeDeletedPath, }); } + // Decrease the numberOfUses in the database for the video file await EncodedVideo.findOneAndUpdate( { fileName: videoToBeDeletedPath, diff --git a/src/utilities/encodedVideoStorage/encodedVideoExtensionCheck.ts b/src/utilities/encodedVideoStorage/encodedVideoExtensionCheck.ts index f9cb626d04..290cb28042 100644 --- a/src/utilities/encodedVideoStorage/encodedVideoExtensionCheck.ts +++ b/src/utilities/encodedVideoStorage/encodedVideoExtensionCheck.ts @@ -1,11 +1,18 @@ +/** + * Checks if the provided base64 encoded URL represents a video with the "mp4" extension. + * @param encodedUrl - The base64 encoded URL of the video. + * @returns `true` if the encoded URL is a valid mp4 video, `false` otherwise. + */ export const encodedVideoExtentionCheck = (encodedUrl: string): boolean => { + // Extract the extension from the encoded URL const extension = encodedUrl.substring( - "data:".length, - encodedUrl.indexOf(";base64"), + "data:".length, // Start after "data:" + encodedUrl.indexOf(";base64"), // End before ";base64" ); - console.log(extension); + console.log(extension); // Log the extracted extension for debugging purposes + // Check if the extension matches "video/mp4" const isValidVideo = extension === "video/mp4"; if (isValidVideo) { return true; diff --git a/src/utilities/encodedVideoStorage/uploadEncodedVideo.ts b/src/utilities/encodedVideoStorage/uploadEncodedVideo.ts index fc01f9b264..c34274e961 100644 --- a/src/utilities/encodedVideoStorage/uploadEncodedVideo.ts +++ b/src/utilities/encodedVideoStorage/uploadEncodedVideo.ts @@ -8,11 +8,18 @@ import { EncodedVideo } from "../../models/EncodedVideo"; import path from "path"; import { deletePreviousVideo } from "./deletePreviousVideo"; +/** + * Uploads an encoded video to the server. + * + * @param encodedVideoURL - The URL or content of the encoded video to upload. + * @param previousVideoPath - Optional. The path of the previous video to delete before uploading the new one. + * @returns The file name of the uploaded video. + */ export const uploadEncodedVideo = async ( encodedVideoURL: string, - // eslint-disable-next-line @typescript-eslint/no-unused-vars previousVideoPath?: string | null, ): Promise => { + // Check if the uploaded video URL/content is a valid video file type const isURLValidVideo = encodedVideoExtentionCheck(encodedVideoURL); if (!isURLValidVideo) { @@ -23,15 +30,18 @@ export const uploadEncodedVideo = async ( ); } + // Check if the encoded video already exists in the database const encodedVideoAlreadyExist = await EncodedVideo.findOne({ content: encodedVideoURL, }); if (encodedVideoAlreadyExist) { + // If the encoded video already exists and its fileName matches previousVideoPath, return its fileName if (encodedVideoAlreadyExist?.fileName === previousVideoPath) { return encodedVideoAlreadyExist?.fileName; } + // Increment numberOfUses for the existing encoded video in the database await EncodedVideo.findOneAndUpdate( { content: encodedVideoURL, @@ -43,6 +53,7 @@ export const uploadEncodedVideo = async ( }, ); + // Delete the previous video if previousVideoPath is provided if (previousVideoPath) { await deletePreviousVideo(previousVideoPath); } @@ -50,23 +61,26 @@ export const uploadEncodedVideo = async ( return encodedVideoAlreadyExist.fileName; } + // Delete the previous video if previousVideoPath is provided if (previousVideoPath) { await deletePreviousVideo(previousVideoPath); } + // Generate a unique ID for the new video file using nanoid let id = nanoid(); - id = "videos/" + id + "video.mp4"; + // Create a new entry in EncodedVideo collection for the uploaded video const uploadedEncodedVideo = await EncodedVideo.create({ fileName: id, content: encodedVideoURL, }); + // Extract the video data from the URL (assuming it's base64 encoded) const data = encodedVideoURL.replace(/^data:video\/\w+;base64,/, ""); - const buf = Buffer.from(data, "base64"); + // Create a 'videos' directory if it doesn't exist if (!fs.existsSync(path.join(__dirname, "../../../videos"))) { fs.mkdir(path.join(__dirname, "../../../videos"), (error) => { if (error) { @@ -75,7 +89,9 @@ export const uploadEncodedVideo = async ( }); } + // Write the video data to the file system await writeFile(path.join(__dirname, "../../../" + id), buf); + // Return the fileName of the uploaded video return uploadedEncodedVideo.fileName; }; diff --git a/src/utilities/imageAlreadyInDbCheck.ts b/src/utilities/imageAlreadyInDbCheck.ts index 7364a08523..1d5b8a3506 100644 --- a/src/utilities/imageAlreadyInDbCheck.ts +++ b/src/utilities/imageAlreadyInDbCheck.ts @@ -6,12 +6,12 @@ import { errors, requestContext } from "../libraries"; import { INVALID_FILE_TYPE } from "../constants"; /** - * This function checks if an image already exists in the database using hash. - * If it does, then point to that image and remove the image just uploaded. - * Else, allow the file to get uploaded. - * @param oldImagePath - Path of image - * @param newImagePath - Does image belong to an item - * @returns file name. + * Checks if an image already exists in the database using its hash value. + * If the image exists, it points to the existing image and removes the newly uploaded image. + * If the image does not exist, it saves the image hash in the database. + * @param oldImagePath - Path of the old image that might be replaced. + * @param newImagePath - Path of the newly uploaded image. + * @returns The file name of the existing image if found; otherwise, undefined. */ export const imageAlreadyInDbCheck = async ( oldImagePath: string | null, @@ -20,6 +20,7 @@ export const imageAlreadyInDbCheck = async ( try { let fileName; + // Function to get the hash value of the new image const getImageHash = (): Promise => new Promise((resolve, reject) => { // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -32,26 +33,30 @@ export const imageAlreadyInDbCheck = async ( }); }); + // Get the hash value of the new image const hash = await getImageHash(); + // Check if there is an existing image with the same hash value in the database const existingImageHash = await ImageHash.findOne({ hashValue: hash, }).lean(); if (!existingImageHash) { + // If no existing image hash found, create a new entry in the ImageHash collection await ImageHash.create({ hashValue: hash, fileName: newImagePath, numberOfUses: 1, }); } else { + // If an image with the same hash exists, perform duplicate check const imageIsDuplicate = await reuploadDuplicateCheck( oldImagePath, newImagePath, ); if (imageIsDuplicate === false) { - // dont increment if the same user/org is using the same image multiple times for the same use case + // Increment the number of uses if it's not a duplicate await ImageHash.updateOne( { // Increase the number of places this image is used @@ -65,13 +70,16 @@ export const imageAlreadyInDbCheck = async ( ); } + // Delete the newly uploaded image as it's a duplicate deleteDuplicatedImage(newImagePath); - fileName = existingImageHash.fileName; // will include have file already in db if pic is already saved will be null otherwise + // Set the file name to the existing image's file name + fileName = existingImageHash.fileName; } return fileName as string; } catch (error) { + // Handle errors, such as invalid file types throw new errors.ValidationError( [ { diff --git a/src/utilities/imageExtensionCheck.ts b/src/utilities/imageExtensionCheck.ts index 337e3f806d..6fbea005d6 100644 --- a/src/utilities/imageExtensionCheck.ts +++ b/src/utilities/imageExtensionCheck.ts @@ -1,11 +1,12 @@ import { deleteImage } from "./deleteImage"; import { errors, requestContext } from "../libraries"; import { INVALID_FILE_TYPE } from "../constants"; + /** - * This function checks the extension of the file. - * If the extension isn't of type 'png', or 'jpg', or 'jpeg', - * then the file is deleted and a validation error is thrown. - * @param filename - Name of file + * Checks the file extension of the given filename. + * If the extension is not 'png', 'jpg', or 'jpeg', deletes the file and throws a validation error. + * + * @param filename - The name of the file to check */ export const imageExtensionCheck = async (filename: string): Promise => { const fileExtension = filename.split(".").pop(); @@ -15,8 +16,10 @@ export const imageExtensionCheck = async (filename: string): Promise => { fileExtension !== "jpg" && fileExtension !== "jpeg" ) { + // Delete the file because the extension is not allowed await deleteImage(filename); + // Throw a validation error indicating invalid file type throw new errors.ValidationError( [ { diff --git a/src/utilities/loadDefaultOrg.ts b/src/utilities/loadDefaultOrg.ts index c7714d98df..0bf21c59fb 100644 --- a/src/utilities/loadDefaultOrg.ts +++ b/src/utilities/loadDefaultOrg.ts @@ -3,21 +3,33 @@ import path from "path"; import { connect, disconnect } from "../db"; import { AppUserProfile, Organization, User } from "../models"; +/** + * Loads default organization data into the database. + * @param dbName - Optional name of the database to connect to. + * @returns Promise + */ export async function loadDefaultOrganiation(dbName?: string): Promise { try { + // Connect to the database await connect(dbName); + + // Read and insert default user data const userData = await fs.readFile( path.join(__dirname, `../../sample_data/defaultUser.json`), "utf8", ); const userDocs = JSON.parse(userData) as Record[]; await User.insertMany(userDocs); + + // Read and insert default app user profile data const appUserData = await fs.readFile( path.join(__dirname, `../../sample_data/defaultAppUserProfile.json`), "utf8", ); const appUserDocs = JSON.parse(appUserData) as Record[]; await AppUserProfile.insertMany(appUserDocs); + + // Read and insert default organization data const organizationData = await fs.readFile( path.join(__dirname, `../../sample_data/defaultOrganization.json`), "utf8", @@ -28,8 +40,10 @@ export async function loadDefaultOrganiation(dbName?: string): Promise { >[]; await Organization.insertMany(organizationDocs); + // Log success message console.log("Default organization loaded"); } catch (error) { + // Log any errors that occur during the process console.log(error); } finally { await disconnect(); // Close the database connection diff --git a/src/utilities/loadSampleData.ts b/src/utilities/loadSampleData.ts index 09c4760810..6309770018 100644 --- a/src/utilities/loadSampleData.ts +++ b/src/utilities/loadSampleData.ts @@ -19,6 +19,9 @@ interface InterfaceArgs { _: unknown; } +/** + * Lists sample data files and their document counts in the sample_data directory. + */ async function listSampleData(): Promise { try { const sampleDataPath = path.join(__dirname, "../../sample_data"); @@ -50,6 +53,9 @@ async function listSampleData(): Promise { } } +/** + * Clears all collections in the database. + */ async function formatDatabase(): Promise { // Clear all collections await Promise.all([ @@ -65,6 +71,10 @@ async function formatDatabase(): Promise { console.log("Cleared all collections\n"); } +/** + * Inserts data into specified collections. + * @param collections - Array of collection names to insert data into + */ async function insertCollections(collections: string[]): Promise { try { // Connect to MongoDB database @@ -141,6 +151,9 @@ async function insertCollections(collections: string[]): Promise { } } +/** + * Checks document counts in specified collections after data insertion. + */ async function checkCountAfterImport(): Promise { try { // Connect to MongoDB database diff --git a/src/utilities/mailer.ts b/src/utilities/mailer.ts index f7b5f93bd3..cfe5b46d44 100644 --- a/src/utilities/mailer.ts +++ b/src/utilities/mailer.ts @@ -8,18 +8,21 @@ import { SMTP_OPTIONS, } from "../constants"; +/** + * Interface for the fields required to send an email. + */ export interface InterfaceMailFields { - emailTo: string; - subject: string; - body: string; + emailTo: string; // Email address of the recipient + subject: string; // Subject of the email + body: string; // Body content of the email (HTML format) } + /** - * This function sends emails to the specified user using the node mailer module. + * Sends an email using Nodemailer. * @remarks - * This is a utility method. - * - * @param InterfaceMailFields - `Interface` type with emailTo(`string`), subject(`string`), and body(`string`) necessary attributes. - * @returns Promise along with resolve and reject methods. + * This is a utility method for sending emails. + * @param mailFields - An object containing emailTo, subject, and body fields. + * @returns A promise resolving to `SMTPTransport.SentMessageInfo` on success, or an error string on failure. */ export const mailer = ( mailFields: InterfaceMailFields, @@ -27,8 +30,7 @@ export const mailer = ( // Nodemailer configuration let transporter: Transporter; - // For using custom smtp server - /* c8 ignore next 12 */ + // Check if custom SMTP server is configured if (SMTP_OPTIONS.IS_SMTP) { transporter = nodemailer.createTransport({ host: String(SMTP_OPTIONS.SMTP_HOST), @@ -39,8 +41,8 @@ export const mailer = ( pass: SMTP_OPTIONS.SMTP_PASSWORD, }, } as SMTPTransport.Options); - // For using gmail transporter } else { + // Use Gmail transporter if custom SMTP is not configured transporter = nodemailer.createTransport({ service: "gmail", auth: { @@ -51,7 +53,6 @@ export const mailer = ( } const mailOptions = { - /* c8 ignore next 6 */ from: !SMTP_OPTIONS.IS_SMTP ? "Talawa<>noreply@gmail.com" : SMTP_OPTIONS.SMTP_USERNAME, @@ -59,13 +60,17 @@ export const mailer = ( subject: mailFields.subject, html: mailFields.body, }; + return new Promise((resolve, reject) => { + // Send email using transporter transporter.sendMail( mailOptions, function (error: Error | null, info: SMTPTransport.SentMessageInfo) { if (error) { + // Handle error if sending mail fails reject(ERROR_IN_SENDING_MAIL); } else { + // Resolve with sent message information if email is sent successfully resolve(info); } }, diff --git a/src/utilities/removeSampleOrganizationUtil.ts b/src/utilities/removeSampleOrganizationUtil.ts index d911407958..089e97368a 100644 --- a/src/utilities/removeSampleOrganizationUtil.ts +++ b/src/utilities/removeSampleOrganizationUtil.ts @@ -8,11 +8,20 @@ import { User, } from "../models"; +/** + * Removes sample organization data from respective collections based on entries in SampleData collection. + * Also deletes all documents in SampleData collection after removal. + * @returns Promise + */ export async function removeSampleOrganization(): Promise { + // Retrieve all documents from SampleData collection const sampleDataDocuments = await SampleData.find({}); + // Iterate through each document in SampleData for (const document of sampleDataDocuments) { const { collectionName, documentId } = document; + + // Define a mapping of collection names to their respective Mongoose models const collectionModels = { Organization, Post, @@ -22,11 +31,14 @@ export async function removeSampleOrganization(): Promise { AppUserProfile, }; + // Determine the model based on collectionName retrieved from SampleData const collectionModel = collectionModels[ collectionName ] as typeof Organization; + // Safely cast the model to its appropriate type and delete the document by ID await collectionModel.findByIdAndDelete(documentId); } + // Delete all documents from SampleData collection after cleanup await SampleData.deleteMany({}); } diff --git a/src/utilities/reuploadDuplicateCheck.ts b/src/utilities/reuploadDuplicateCheck.ts index dedff2eabf..6f92297afb 100644 --- a/src/utilities/reuploadDuplicateCheck.ts +++ b/src/utilities/reuploadDuplicateCheck.ts @@ -1,66 +1,70 @@ import { imageHash } from "image-hash"; import { requestContext, errors, logger } from "../libraries"; +// Interface for URL request object interface InterfaceUrlRequestObject { encoding?: string | null; url: string | null; } +// Interface for Buffer object interface InterfaceBufferObject { ext?: string; data: Buffer; name?: string; } +// Type definition for image path, can be string, InterfaceUrlRequestObject, or InterfaceBufferObject export type TypeImagePath = | string | InterfaceUrlRequestObject | InterfaceBufferObject; +/** + * Gets the hash value of an image using the image-hash library. + * @param oldSrc - Path of the image to hash, can be a string, URL request object, or buffer object. + * @returns Promise that resolves to the hash object. + */ const getImageHash = (oldSrc: TypeImagePath): object => { return new Promise((resolve, reject) => { // eslint-disable-next-line @typescript-eslint/no-explicit-any imageHash(oldSrc, 16, true, (error: Error, data: any) => { if (error) { - reject(error); + reject(error); // Reject promise if error occurs during hashing } - - resolve(data); + resolve(data); // Resolve promise with hash data }); }); }; + /** - * This function determines whether a user or an organisation is - * attempting to re-upload the same profile photo or organisation image. - * + * Checks if a user or organization is attempting to re-upload the same image. * @remarks * This is a utility method. - * - * @param oldImagePath - Path of a current Org/User image of `type: TypeImagePath`. - * @param newImagePath - Path of a new image of `type: TypeImagePath`. - * @returns If the identical image is trying to reuploaded, `true`; otherwise, `false`. + * @param oldImagePath - Path of the current image (could be a string, URL request object, or buffer object). + * @param newImagePath - Path of the new image being uploaded (could be a string, URL request object, or buffer object). + * @returns Promise that resolves to true if the images are identical, false otherwise. */ export const reuploadDuplicateCheck = async ( oldImagePath: TypeImagePath | null, newImagePath: TypeImagePath, ): Promise => { - /* - This function checks whether a user is trying to re-upload the same profile picture - or an organization is trying to re-upload the same organization image - */ try { if (oldImagePath) { + // Calculate hash of old and new images const oldImageHash = await getImageHash(oldImagePath); - const newImageHash = await getImageHash(newImagePath); + // Compare hashes to determine if images are identical return oldImageHash === newImageHash; } + // If oldImagePath is null, cannot be a duplicate upload return false; } catch (error) { - logger.error(error); + logger.error(error); // Log error for debugging purposes + // Throw a validation error with translated message throw new errors.ValidationError( [ { diff --git a/src/utilities/superAdminCheck.ts b/src/utilities/superAdminCheck.ts index 9074f12a87..7e49693a72 100644 --- a/src/utilities/superAdminCheck.ts +++ b/src/utilities/superAdminCheck.ts @@ -2,6 +2,12 @@ import { USER_NOT_AUTHORIZED_SUPERADMIN } from "../constants"; import { errors, requestContext } from "../libraries"; import type { InterfaceAppUserProfile } from "../models"; +/** + * Checks if the provided application user profile is a super admin. + * Throws an UnauthorizedError if the user is not a super admin. + * + * @param appUserProfile - The user profile of the application. + */ export const superAdminCheck = ( appUserProfile: InterfaceAppUserProfile, ): void => { diff --git a/src/utilities/uploadImage.ts b/src/utilities/uploadImage.ts index 962734c437..c99a44a8a5 100644 --- a/src/utilities/uploadImage.ts +++ b/src/utilities/uploadImage.ts @@ -5,15 +5,15 @@ import { logger } from "../libraries"; import { imageAlreadyInDbCheck } from "./imageAlreadyInDbCheck"; import { deleteImage } from "./deleteImage"; import { imageExtensionCheck } from "./imageExtensionCheck"; + /** - * This function uploads the new image and deletes the previously uploaded image if exists. + * Uploads a new image, deletes the previously uploaded image if it exists, and checks for duplicates in the database. * @remarks * This is a utility method. - * @param newImageFile - File of a new Image with `TypeNewImageFile` type. - * @param oldImagePath - File of a current Image. It can be `null`. - * @returns Path of an uploaded image. + * @param newImageFile - File object of the new image with `TypeNewImageFile` type. + * @param oldImagePath - Path of the current image to be replaced. Can be `null` if no image exists. + * @returns An object containing paths of the newly uploaded image and any duplicate image found in the database. */ - type TypeNewImageFile = { createReadStream: () => NodeJS.ReadStream; filename: string; @@ -23,11 +23,13 @@ export const uploadImage = async ( newImageFile: TypeNewImageFile, oldImagePath: string | null, ): Promise<{ newImagePath: string; imageAlreadyInDbPath: string }> => { + // Generate a unique ID for the new image file const id = nanoid(); + // Extract filename from new image file const { createReadStream, filename } = await newImageFile; - // throw an error if file is not png or jpg + // Validate image file extension (must be PNG or JPG) await imageExtensionCheck(filename); // upload new image @@ -49,6 +51,7 @@ export const uploadImage = async ( const newImagePath = `images/${id}-${filename}`; + // If there is an old image path, delete it and perform duplicate check if (oldImagePath !== null) { console.log("oldImagePath is not null"); @@ -58,11 +61,13 @@ export const uploadImage = async ( await deleteImage(oldImagePath, newImagePath); } + // Check if the newly uploaded image already exists in the database const imageAlreadyInDbPath = await imageAlreadyInDbCheck( oldImagePath, newImagePath, ); + // Return paths of the newly uploaded image and any duplicate found in the database return { newImagePath, imageAlreadyInDbPath, diff --git a/src/utilities/userFamilyAdminCheck.ts b/src/utilities/userFamilyAdminCheck.ts index 95bc2a4e1a..ea17c934c4 100644 --- a/src/utilities/userFamilyAdminCheck.ts +++ b/src/utilities/userFamilyAdminCheck.ts @@ -4,32 +4,36 @@ import { USER_NOT_AUTHORIZED_ADMIN } from "../constants"; import { errors, requestContext } from "../libraries"; import { AppUserProfile } from "../models"; import type { InterfaceUserFamily } from "../models/userFamily"; + /** - * If the current user is an admin of the organisation, this function returns `true` otherwise it returns `false`. + * Checks if the current user is an admin of the organization or a super admin. + * Throws an UnauthorizedError if the user is neither an admin nor a super admin. + * * @remarks - * This is a utility method. - * @param userId - Current user id. - * @param userFamily - userFamily data of `InterfaceuserFamily` type. - * @returns `True` or `False`. + * This function queries the `userFamily` to check if the `userId` is listed as an admin. + * Additionally, it queries the `AppUserProfile` to check if the `userId` is a super admin. + * + * @param userId - The ID of the current user. + * @param userFamily - The user family data of type `InterfaceUserFamily`. */ export const adminCheck = async ( userId: string | Types.ObjectId, userFamily: InterfaceUserFamily, ): Promise => { + // Check if the user is listed as an admin in userFamily const userIsUserFamilyAdmin = userFamily.admins.some( (admin) => admin === userId || new mongoose.Types.ObjectId(admin.toString()).equals(userId), ); - // const user = await User.findOne({ - // _id: userId, - // }); + // Query AppUserProfile to check if the user is a super admin const appUserProfile = await AppUserProfile.findOne({ userId: userId, }); const isUserSuperAdmin: boolean = appUserProfile?.isSuperAdmin || false; + // If the user is neither an admin nor a super admin, throw UnauthorizedError if (!userIsUserFamilyAdmin && !isUserSuperAdmin) { throw new errors.UnauthorizedError( requestContext.translate(`${USER_NOT_AUTHORIZED_ADMIN.MESSAGE}`), diff --git a/tests/helpers/directChat.ts b/tests/helpers/directChat.ts index 96d8888e78..0b4d523b23 100644 --- a/tests/helpers/directChat.ts +++ b/tests/helpers/directChat.ts @@ -9,7 +9,7 @@ import { createTestUserAndOrganization } from "./userAndOrg"; import type { Document } from "mongoose"; export type TestDirectChatType = - | (InterfaceDirectChat & Document) + | (InterfaceDirectChat & Document) | null; export type TestDirectChatMessageType = diff --git a/tests/helpers/groupChat.ts b/tests/helpers/groupChat.ts index a7a53b96b9..a41591c962 100644 --- a/tests/helpers/groupChat.ts +++ b/tests/helpers/groupChat.ts @@ -9,7 +9,7 @@ import { createTestUserAndOrganization } from "./userAndOrg"; import type { Document } from "mongoose"; export type TestGroupChatType = - | (InterfaceGroupChat & Document) + | (InterfaceGroupChat & Document) | null; export type TestGroupChatMessageType = diff --git a/tests/resolvers/Mutation/createDirectChat.spec.ts b/tests/resolvers/Mutation/createDirectChat.spec.ts index ee51c5e9d2..7c46e94092 100644 --- a/tests/resolvers/Mutation/createDirectChat.spec.ts +++ b/tests/resolvers/Mutation/createDirectChat.spec.ts @@ -123,7 +123,6 @@ describe("resolvers -> Mutation -> createDirectChat", () => { expect.objectContaining({ creatorId: testUser?._id, users: [testUser?._id], - organization: testOrganization?._id, }), ); }); diff --git a/tests/resolvers/Query/directChatById.spec.ts b/tests/resolvers/Query/directChatById.spec.ts new file mode 100644 index 0000000000..0b6e993df1 --- /dev/null +++ b/tests/resolvers/Query/directChatById.spec.ts @@ -0,0 +1,58 @@ +import "dotenv/config"; +import type mongoose from "mongoose"; +import { Types } from "mongoose"; +import { connect, disconnect } from "../../helpers/db"; + +import { directChatById as directChatByIdResolver } from "../../../src/resolvers/Query/directChatById"; +import { DirectChat } from "../../../src/models"; +import type { QueryDirectChatsByUserIdArgs } from "../../../src/types/generatedGraphQLTypes"; +import { beforeAll, afterAll, describe, it, expect } from "vitest"; +import { createTestDirectChat } from "../../helpers/directChat"; +import type { TestDirectChatType } from "../../helpers/directChat"; + +let testDirectChat: TestDirectChatType; +let MONGOOSE_INSTANCE: typeof mongoose; + +beforeAll(async () => { + MONGOOSE_INSTANCE = await connect(); + const resultArray = await createTestDirectChat(); + testDirectChat = resultArray[2]; +}); + +afterAll(async () => { + await disconnect(MONGOOSE_INSTANCE); +}); + +describe("resolvers -> Query -> directChatsById", () => { + it(`throws NotFoundError if no directChats exists with directChats._id === args.id`, async () => { + try { + const args: QueryDirectChatsByUserIdArgs = { + id: new Types.ObjectId().toString(), + }; + + await directChatByIdResolver?.({}, args, {}); + } catch (error: unknown) { + expect((error as Error).message).toEqual("Chat not found"); + } + }); + + it(`returns list of all directChats with directChat.users containing the user + with _id === args.id`, async () => { + const args: QueryDirectChatsByUserIdArgs = { + id: testDirectChat?._id, + }; + + const directChatsByUserIdPayload = await directChatByIdResolver?.( + {}, + args, + {}, + ); + + const directChatsByUserId = await DirectChat.findById( + testDirectChat?._id, + ).lean(); + console.log(directChatsByUserIdPayload); + console.log(directChatsByUserId); + expect(directChatsByUserIdPayload).toEqual(directChatsByUserId); + }); +}); diff --git a/tests/resolvers/Query/groupChatById.spec.ts b/tests/resolvers/Query/groupChatById.spec.ts new file mode 100644 index 0000000000..770557a301 --- /dev/null +++ b/tests/resolvers/Query/groupChatById.spec.ts @@ -0,0 +1,61 @@ +import "dotenv/config"; +import type mongoose from "mongoose"; +import { Types } from "mongoose"; +import { connect, disconnect } from "../../helpers/db"; + +import { groupChatById as groupChatByIdResolver } from "../../../src/resolvers/Query/groupChatById"; +import { GroupChat } from "../../../src/models"; +import type { + QueryGroupChatByIdArgs, + QueryGroupChatsByUserIdArgs, +} from "../../../src/types/generatedGraphQLTypes"; +import { beforeAll, afterAll, describe, it, expect } from "vitest"; +import { createTestGroupChat } from "../../helpers/groupChat"; +import type { TestGroupChatType } from "../../helpers/groupChat"; + +let testGroupChat: TestGroupChatType; +let MONGOOSE_INSTANCE: typeof mongoose; + +beforeAll(async () => { + MONGOOSE_INSTANCE = await connect(); + const resultArray = await createTestGroupChat(); + testGroupChat = resultArray[2]; +}); + +afterAll(async () => { + await disconnect(MONGOOSE_INSTANCE); +}); + +describe("resolvers -> Query -> directChatsById", () => { + it(`throws NotFoundError if no directChats exists with directChats._id === args.id`, async () => { + try { + const args: QueryGroupChatByIdArgs = { + id: new Types.ObjectId().toString(), + }; + + await groupChatByIdResolver?.({}, args, {}); + } catch (error: unknown) { + expect((error as Error).message).toEqual("Chat not found"); + } + }); + + it(`returns list of all directChats with directChat.users containing the user + with _id === args.id`, async () => { + const args: QueryGroupChatsByUserIdArgs = { + id: testGroupChat?._id, + }; + + const directChatsByUserIdPayload = await groupChatByIdResolver?.( + {}, + args, + {}, + ); + + const directChatsByUserId = await GroupChat.findById( + testGroupChat?._id, + ).lean(); + console.log(directChatsByUserIdPayload); + console.log(directChatsByUserId); + expect(directChatsByUserIdPayload).toEqual(directChatsByUserId); + }); +}); diff --git a/tests/resolvers/Query/groupChatsByUserId.spec.ts b/tests/resolvers/Query/groupChatsByUserId.spec.ts new file mode 100644 index 0000000000..7026580b17 --- /dev/null +++ b/tests/resolvers/Query/groupChatsByUserId.spec.ts @@ -0,0 +1,58 @@ +import "dotenv/config"; +import type mongoose from "mongoose"; +import { Types } from "mongoose"; +import { connect, disconnect } from "../../helpers/db"; + +import { groupChatsByUserId as groupChatsByUserIdResolver } from "../../../src/resolvers/Query/groupChatsByUserId"; +import { GroupChat } from "../../../src/models"; +import type { QueryGroupChatsByUserIdArgs } from "../../../src/types/generatedGraphQLTypes"; +import { beforeAll, afterAll, describe, it, expect } from "vitest"; +import { createTestGroupChat } from "../../helpers/groupChat"; +import type { TestUserType } from "../../helpers/userAndOrg"; + +let testUser: TestUserType; +let MONGOOSE_INSTANCE: typeof mongoose; + +beforeAll(async () => { + MONGOOSE_INSTANCE = await connect(); + const resultArray = await createTestGroupChat(); + testUser = resultArray[0]; +}); + +afterAll(async () => { + await disconnect(MONGOOSE_INSTANCE); +}); + +describe("resolvers -> Query -> groupChatsByUserId", () => { + it(`throws NotFoundError if no groupChats exists with groupChats.users + containing user with _id === args.id`, async () => { + try { + const args: QueryGroupChatsByUserIdArgs = { + id: new Types.ObjectId().toString(), + }; + + await groupChatsByUserIdResolver?.({}, args, {}); + } catch (error: unknown) { + expect((error as Error).message).toEqual("Group Chats not found"); + } + }); + + it(`returns list of all groupChats with groupChat.users containing the user + with _id === args.id`, async () => { + const args: QueryGroupChatsByUserIdArgs = { + id: testUser?._id, + }; + + const groupChatsByUserIdPayload = await groupChatsByUserIdResolver?.( + {}, + args, + {}, + ); + + const groupChatsByUserId = await GroupChat.find({ + users: testUser?._id, + }).lean(); + + expect(groupChatsByUserIdPayload).toEqual(groupChatsByUserId); + }); +}); diff --git a/tests/resolvers/Subscription/messageSentToDirectChat.spec.ts b/tests/resolvers/Subscription/messageSentToDirectChat.spec.ts index 4eab859433..8a6fdff245 100644 --- a/tests/resolvers/Subscription/messageSentToDirectChat.spec.ts +++ b/tests/resolvers/Subscription/messageSentToDirectChat.spec.ts @@ -38,6 +38,9 @@ describe("src -> resolvers -> Subscription -> messageSentToDirectChat", () => { }, context: { currentUserId: testCurrentUser?._id }, }; + const variables = { + userId: testCurrentUser?._id, + }; const payload = { messageSentToDirectChat: { receiver: testDirectChatMessage?.receiver, @@ -53,11 +56,11 @@ describe("src -> resolvers -> Subscription -> messageSentToDirectChat", () => { context, ); expect(x).not.toBe(null); - expect(await filterFunction(payload, context)).toBe(true); + expect(await filterFunction(payload, variables)).toBe(true); // If current User is sender payload.messageSentToDirectChat.receiver = "receiver"; - expect(await filterFunction(payload, context)).toBe(true); + expect(await filterFunction(payload, variables)).toBe(true); }); it("user is not notified if it is not a part of DirectChat", async () => { @@ -76,6 +79,9 @@ describe("src -> resolvers -> Subscription -> messageSentToDirectChat", () => { }, context: { currentUserId: testCurrentUser?._id }, }; + const variables = { + userId: testCurrentUser?._id, + }; const payload = { messageSentToDirectChat: { @@ -92,6 +98,6 @@ describe("src -> resolvers -> Subscription -> messageSentToDirectChat", () => { context, ); expect(x).not.toBe(null); - expect(await filterFunction(payload, context)).toBe(false); + expect(await filterFunction(payload, variables)).toBe(false); }); }); diff --git a/tests/resolvers/Subscription/messageSentToGroupChat.spec.ts b/tests/resolvers/Subscription/messageSentToGroupChat.spec.ts index 5474a0ea8b..c1aa13072a 100644 --- a/tests/resolvers/Subscription/messageSentToGroupChat.spec.ts +++ b/tests/resolvers/Subscription/messageSentToGroupChat.spec.ts @@ -34,6 +34,9 @@ describe("src -> resolvers -> Subscription -> messageSentToGroupChat", () => { }, context: { currentUserId: testGroupChat?.users[0] }, }; + const variables = { + userId: testGroupChat?.users[0], + }; const payload = { messageSentToGroupChat: { groupChatMessageBelongsTo: testGroupChat?._id, @@ -44,7 +47,7 @@ describe("src -> resolvers -> Subscription -> messageSentToGroupChat", () => { // @ts-expect-error-ignore const x = messageSentToGroupChatPayload?.subscribe(_parent, _args, context); expect(x).not.toBe(null); - expect(await filterFunction(payload, context)).toBe(true); + expect(await filterFunction(payload, variables)).toBe(true); }); it("subscription filter function returns false when group chat not found with the id", async () => { const { messageSentToGroupChat: messageSentToGroupChatPayload } = @@ -67,11 +70,14 @@ describe("src -> resolvers -> Subscription -> messageSentToGroupChat", () => { groupChatMessageBelongsTo: new mongoose.Types.ObjectId(), }, }; + const variables = { + userId: testGroupChat?.users[0], + }; // @ts-expect-error-ignore messageSentToGroupChatPayload.payload = payload; // @ts-expect-error-ignore const x = messageSentToGroupChatPayload?.subscribe(_parent, _args, context); expect(x).not.toBe(null); - expect(await filterFunction(payload, context)).toBe(false); + expect(await filterFunction(payload, variables)).toBe(false); }); });