Skip to content

Commit

Permalink
feat: Add support for Categories
Browse files Browse the repository at this point in the history
  • Loading branch information
meetulr committed Jan 3, 2024
1 parent a15c1c1 commit 8e7f186
Show file tree
Hide file tree
Showing 21 changed files with 593 additions and 0 deletions.
4 changes: 4 additions & 0 deletions codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ const config: CodegenConfig = {
// functionality is useful because what we retrieve from the database and what we choose to return from a graphql server
// could be completely different fields. Address to models here is relative to the location of generated types.
mappers: {
ActionItem: "../models/ActionItem#InterfaceActionItem",

Category: "../models/Category#InterfaceCategory",

CheckIn: "../models/CheckIn#InterfaceCheckIn",

MessageChat: "../models/MessageChat#InterfaceMessageChat",
Expand Down
7 changes: 7 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ if (!issues) {
ENV = envSchema.parse(process.env);
}

export const CATEGORY_NOT_FOUND_ERROR = {
DESC: "Category not found",
CODE: "category.notFound",
MESSAGE: "category.notFound",
PARAM: "category",
};

export const CHAT_NOT_FOUND_ERROR = {
DESC: "Chat not found",
CODE: "chat.notFound",
Expand Down
109 changes: 109 additions & 0 deletions src/models/ActionItem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import type { PopulatedDoc, Types, Document, Model } from "mongoose";
import { Schema, model, models } from "mongoose";
import type { InterfaceUser } from "./User";
import type { InterfaceEvent } from "./Event";
import type { InterfaceCategory } from "./Category";

/**
* This is an interface that represents a database(MongoDB) document for ActionItem.
*/

export interface InterfaceActionItem {
_id: Types.ObjectId;
assignedTo: PopulatedDoc<InterfaceUser & Document>;
assignedBy: PopulatedDoc<InterfaceUser & Document>;
category: PopulatedDoc<InterfaceCategory & Document>;
preCompletionNotes: string;
postCompletionNotes: string;
assignmentDate: Date;
dueDate: Date;
completionDate: Date;
completed: boolean;
eventId: PopulatedDoc<InterfaceEvent & Document>;
createdBy: PopulatedDoc<InterfaceUser & Document>;
updatedBy: PopulatedDoc<InterfaceUser & Document>;
createdAt: Date;
updatedAt: Date;
}

/**
* This describes the schema for a `ActionItem` that corresponds to `InterfaceActionItem` document.
* @param assignedTo - User to whom the ActionItem is assigned, refer to `User` model.
* @param assignedBy - User who assigned the ActionItem, refer to the `User` model.
* @param category - Category to which the ActionItem is related, refer to the `Category` 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 completed - Whether the ActionItem has been completed.
* @param eventId - Event to which the ActionItem is related, refer to the `Event` model.
* @param createdBy - User who created the ActionItem, refer to the `User` model.
* @param updatedBy - User who last updated the ActionItem, refer to the `User` model.
* @param createdAt - Timestamp when the ActionItem was created.
* @param updatedAt - Timestamp when the ActionItem was last updated.
*/

const actionItemSchema = new Schema(
{
assignedTo: {
type: Schema.Types.ObjectId,
ref: "User",
required: true,
},
assignedBy: {
type: Schema.Types.ObjectId,
ref: "User",
required: true,
},
category: {
type: Schema.Types.ObjectId,
ref: "Category",
required: true,
},
preCompletionNotes: {
type: String,
},
postCompletionNotes: {
type: String,
},
assignmentDate: {
type: Date,
default: Date.now(),
},
dueDate: {
type: Date,
default: Date.now() + 7 * 24 * 60 * 60 * 1000,
},
completionDate: {
type: Date,
default: Date.now() + 7 * 24 * 60 * 60 * 1000,
},
completed: {
type: Boolean,
required: true,
},
eventId: {
type: Schema.Types.ObjectId,
ref: "Event",
},
createdBy: {
type: Schema.Types.ObjectId,
ref: "User",
required: true,
},
updatedBy: {
type: Schema.Types.ObjectId,
ref: "User",
required: true,
},
},
{ timestamps: true }
);

const actionItemModel = (): Model<InterfaceActionItem> =>
model<InterfaceActionItem>("ActionItem", actionItemSchema);

// This syntax is needed to prevent Mongoose OverwriteModelError while running tests.
export const ActionItem = (models.ActionItem ||
actionItemModel()) as ReturnType<typeof actionItemModel>;
67 changes: 67 additions & 0 deletions src/models/Category.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import type { PopulatedDoc, Types, Document, Model } from "mongoose";
import { Schema, model, models } from "mongoose";
import type { InterfaceUser } from "./User";
import type { InterfaceOrganization } from "./Organization";

/**
* This is an interface that represents a database(MongoDB) document for Category.
*/

export interface InterfaceCategory {
_id: Types.ObjectId;
category: string;
org: PopulatedDoc<InterfaceOrganization & Document>;
disabled: boolean;
createdBy: PopulatedDoc<InterfaceUser & Document>;
updatedBy: PopulatedDoc<InterfaceUser & Document>;
createdAt: Date;
updatedAt: Date;
}

/**
* This describes the schema for a `category` that corresponds to `InterfaceCategory` document.
* @param category - A category to be selected for ActionItems.
* @param org - Organization the category belongs to, refer to the `Organization` model.
* @param disabled - Whether category is disabled or not.
* @param createdBy - Task creator, refer to `User` model.
* @param updatedBy - Task creator, refer to `User` model.
* @param createdAt - Time stamp of data creation.
* @param updatedAt - Time stamp of data updation.
*/

const categorySchema = new Schema(
{
category: {
type: String,
required: true,
},
org: {
type: Schema.Types.ObjectId,
ref: "Organization",
required: true,
},
disabled: {
type: Boolean,
default: false,
},
createdBy: {
type: Schema.Types.ObjectId,
ref: "User",
required: true,
},
updatedBy: {
type: Schema.Types.ObjectId,
ref: "User",
required: true,
},
},
{ timestamps: true }
);

const categoryModel = (): Model<InterfaceCategory> =>
model<InterfaceCategory>("Category", categorySchema);

// This syntax is needed to prevent Mongoose OverwriteModelError while running tests.
export const Category = (models.Category || categoryModel()) as ReturnType<
typeof categoryModel
>;
2 changes: 2 additions & 0 deletions src/models/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export * from "./ActionItem";
export * from "./Advertisement";
export * from "./Category";
export * from "./CheckIn";
export * from "./MessageChat";
export * from "./Comment";
Expand Down
8 changes: 8 additions & 0 deletions src/resolvers/Category/createdBy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { CategoryResolvers } from "../../types/generatedGraphQLTypes";
import { User } from "../../models";

export const createdBy: CategoryResolvers["createdBy"] = async (parent) => {
return User.findOne({
_id: parent.createdBy,
}).lean();
};
10 changes: 10 additions & 0 deletions src/resolvers/Category/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { CategoryResolvers } from "../../types/generatedGraphQLTypes";
import { org } from "./org";
import { createdBy } from "./createdBy";
import { updatedBy } from "./updatedBy";

export const Category: CategoryResolvers = {
org,
createdBy,
updatedBy,
};
8 changes: 8 additions & 0 deletions src/resolvers/Category/org.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { CategoryResolvers } from "../../types/generatedGraphQLTypes";
import { Organization } from "../../models";

export const org: CategoryResolvers["org"] = async (parent) => {
return Organization.findOne({
_id: parent.org,
}).lean();
};
8 changes: 8 additions & 0 deletions src/resolvers/Category/updatedBy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { CategoryResolvers } from "../../types/generatedGraphQLTypes";
import { User } from "../../models";

export const updatedBy: CategoryResolvers["updatedBy"] = async (parent) => {
return User.findOne({
_id: parent.updatedBy,
}).lean();
};
79 changes: 79 additions & 0 deletions src/resolvers/Mutation/createCategory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import type { MutationResolvers } from "../../types/generatedGraphQLTypes";
import { User, Category, Organization } from "../../models";
import { errors, requestContext } from "../../libraries";
import {
USER_NOT_FOUND_ERROR,
ORGANIZATION_NOT_FOUND_ERROR,
} from "../../constants";

import { adminCheck } from "../../utilities";
import { findOrganizationsInCache } from "../../services/OrganizationCache/findOrganizationsInCache";
import { cacheOrganizations } from "../../services/OrganizationCache/cacheOrganizations";

/**
* This function enables to create a task.
* @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
* @returns Created Category
*/

export const createCategory: MutationResolvers["createCategory"] = async (
_parent,
args,
context
) => {
const currentUser = await User.findOne({
_id: context.userId,
});

// Checks whether currentUser with _id == context.userId exists.
if (currentUser === null) {
throw new errors.NotFoundError(
requestContext.translate(USER_NOT_FOUND_ERROR.MESSAGE),
USER_NOT_FOUND_ERROR.CODE,
USER_NOT_FOUND_ERROR.PARAM
);
}

let organization;

const organizationFoundInCache = await findOrganizationsInCache([args.orgId]);

organization = organizationFoundInCache[0];

if (organizationFoundInCache[0] == null) {
organization = await Organization.findOne({
_id: args.orgId,
}).lean();

await cacheOrganizations([organization!]);
}

// Checks whether the organization with _id === args.orgId exists.
if (!organization) {
throw new errors.NotFoundError(
requestContext.translate(ORGANIZATION_NOT_FOUND_ERROR.MESSAGE),
ORGANIZATION_NOT_FOUND_ERROR.CODE,
ORGANIZATION_NOT_FOUND_ERROR.PARAM
);
}

// Checks whether the user is authorized to perform the operation
await adminCheck(context.userId, organization);

// Creates new category.
const createdCategory = await Category.create({
category: args.category,
org: args.orgId,
createdBy: context.userId,
updatedBy: context.userId,
});

// Returns created category.
return createdCategory.toObject();
};
4 changes: 4 additions & 0 deletions src/resolvers/Mutation/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { createAdvertisement } from "./createAdvertisement";
import { createPost } from "./createPost";
import { createSampleOrganization } from "./createSampleOrganization";
import { createTask } from "./createTask";
import { createCategory } from "./createCategory";
import { createUserTag } from "./createUserTag";
import { deleteDonationById } from "./deleteDonationById";
import { forgotPassword } from "./forgotPassword";
Expand Down Expand Up @@ -79,6 +80,7 @@ import { unblockUser } from "./unblockUser";
import { unlikeComment } from "./unlikeComment";
import { unlikePost } from "./unlikePost";
import { unregisterForEventByUser } from "./unregisterForEventByUser";
import { updateCategory } from "./updateCategory";
import { updateEvent } from "./updateEvent";
import { updateEventProject } from "./updateEventProject";
import { updateLanguage } from "./updateLanguage";
Expand Down Expand Up @@ -126,6 +128,7 @@ export const Mutation: MutationResolvers = {
createPost,
createSampleOrganization,
createTask,
createCategory,
createUserTag,
deleteDonationById,
deleteAdvertisementById,
Expand Down Expand Up @@ -174,6 +177,7 @@ export const Mutation: MutationResolvers = {
unlikeComment,
unlikePost,
unregisterForEventByUser,
updateCategory,
updateEvent,
updateEventProject,
updateLanguage,
Expand Down
Loading

0 comments on commit 8e7f186

Please sign in to comment.