Skip to content

Commit

Permalink
Added support for user family members (PalisadoesFoundation#1810)
Browse files Browse the repository at this point in the history
* added all the code

* new commit

* new commit

* fix linting

* fix linting

* fix linting
  • Loading branch information
AdityaRaimec22 authored Feb 8, 2024
1 parent cc5872e commit fa10711
Show file tree
Hide file tree
Showing 32 changed files with 1,839 additions and 3 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# NOTE!
#
# Please read the README.md file in this directory that defines what should
# be placed in this file
# be placed in this file.
#
##############################################################################
##############################################################################
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,4 @@ Core features include:

## Image Upload

To enable image upload functionalities create an images folder in the root of the project
To enable image upload functionalities create an images folder in the root of the project
2 changes: 2 additions & 0 deletions codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ const config: CodegenConfig = {

EventAttendee: "../models/EventAttendee#InterfaceEventAttendee",

UserFamily: "../models/userFamily#InterfaceUserFamily",

Feedback: "../models/Feedback#InterfaceFeedback",

// File: '../models/File#InterfaceFile',
Expand Down
20 changes: 20 additions & 0 deletions sample_data/userFamilies.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[
{
"_id": "60f18f31b7e5c4a2a4c3f905",
"title": "Smith Family",
"users": [
"64378abd85008f171cf2990d",
"65378abd85008f171cf2990d",
"66378abd85008f171cf2990d"
]
},
{
"_id": "60f18f31b7e5c4a2a4c3f906",
"title": "Johnson Family",
"users": [
"66378abd85008f171cf2990d",
"65378abd85008f171cf2990d",
"64378abd85008f171cf2990d"
]
}
]
17 changes: 17 additions & 0 deletions schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,7 @@ type Mutation {
addUserCustomData(dataName: String!, dataValue: Any!, organizationId: ID!): UserCustomData!
addUserImage(file: String!): User!
addUserToGroupChat(chatId: ID!, userId: ID!): GroupChat!
addUserToUserFamily(familyId: ID!, userId: ID!): UserFamily!
adminRemoveEvent(eventId: ID!): Event!
adminRemoveGroup(groupId: ID!): GroupChat!
assignUserTag(input: ToggleUserTagAssignInput!): User
Expand All @@ -543,6 +544,7 @@ type Mutation {
createPlugin(pluginCreatedBy: String!, pluginDesc: String!, pluginName: String!, uninstalledOrgs: [ID!]): Plugin!
createPost(data: PostInput!, file: String): Post
createSampleOrganization: Boolean!
createUserFamily(data: createUserFamilyInput!): UserFamily!
createUserTag(input: CreateUserTagInput!): UserTag
deleteAdvertisementById(id: ID!): DeletePayload!
deleteDonationById(id: ID!): DeletePayload!
Expand Down Expand Up @@ -574,7 +576,9 @@ type Mutation {
removePost(id: ID!): Post
removeSampleOrganization: Boolean!
removeUserCustomData(organizationId: ID!): UserCustomData!
removeUserFamily(familyId: ID!): UserFamily!
removeUserFromGroupChat(chatId: ID!, userId: ID!): GroupChat!
removeUserFromUserFamily(familyId: ID!, userId: ID!): UserFamily!
removeUserImage: User!
removeUserTag(id: ID!): UserTag
revokeRefreshTokenForUser: Boolean!
Expand Down Expand Up @@ -1073,6 +1077,14 @@ type UserEdge {
node: User!
}

type UserFamily {
_id: ID!
admins: [User!]!
creator: User!
title: String
users: [User!]!
}

input UserInput {
appLanguageCode: String
email: EmailAddress!
Expand Down Expand Up @@ -1204,4 +1216,9 @@ input createGroupChatInput {
organizationId: ID!
title: String!
userIds: [ID!]!
}

input createUserFamilyInput {
title: String!
userIds: [ID!]!
}
12 changes: 12 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,12 +177,24 @@ export const LENGTH_VALIDATION_ERROR = {
PARAM: "stringValidation",
};

export const USER_FAMILY_MIN_MEMBERS_ERROR_CODE = {
MESSAGE: "InputValidationError",
CODE: "membersInUserFamilyLessThanOne",
PARAM: "membersInUserFamilyLessThanOne",
};

export const REGEX_VALIDATION_ERROR = {
MESSAGE: "Error: Entered value must be a valid string",
CODE: "string.notValid",
PARAM: "stringValidation",
};

export const USER_FAMILY_NOT_FOUND_ERROR = {
MESSAGE: "Error: User Family Not Found",
CODE: "userfamilyNotFound",
PARAM: "userfamilyNotFound",
};

export const USER_NOT_AUTHORIZED_SUPERADMIN = {
MESSAGE: "Error: Current user must be a SUPERADMIN",
CODE: "role.notValid.superadmin",
Expand Down
1 change: 0 additions & 1 deletion src/models/User.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import type { InterfaceEvent } from "./Event";
import type { InterfaceMembershipRequest } from "./MembershipRequest";
import type { InterfaceOrganization } from "./Organization";
import { createLoggingMiddleware } from "../libraries/dbLogger";
import { LOG } from "../constants";

/**
* This is an interface that represents a database(MongoDB) document for User.
Expand Down
56 changes: 56 additions & 0 deletions src/models/userFamily.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
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.
*/

export interface InterfaceUserFamily {
_id: Types.ObjectId;
title: string;
users: PopulatedDoc<InterfaceUser & Document>[];
admins: PopulatedDoc<InterfaceUser & Document>[];
creator: PopulatedDoc<InterfaceUser & Document>[];
}

/**
* @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.
*/
const userFamilySchema = new Schema({
title: {
type: String,
required: true,
},
users: [
{
type: Schema.Types.ObjectId,
ref: "User",
required: true,
},
],
admins: [
{
type: Schema.Types.ObjectId,
ref: "User",
required: true,
},
],
creator: {
type: Schema.Types.ObjectId,
ref: "User",
required: true,
},
});

const userFamilyModel = (): Model<InterfaceUserFamily> =>
model<InterfaceUserFamily>("UserFamily", userFamilySchema);

// This syntax is needed to prevent Mongoose OverwriteModelError while running tests.
export const UserFamily = (models.UserFamily ||
userFamilyModel()) as ReturnType<typeof userFamilyModel>;
82 changes: 82 additions & 0 deletions src/resolvers/Mutation/addUserToUserFamily.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import "dotenv/config";
import type { MutationResolvers } from "../../types/generatedGraphQLTypes";
import { errors, requestContext } from "../../libraries";
import { adminCheck } from "../../utilities/userFamilyAdminCheck";
import { User } from "../../models";
import { UserFamily } from "../../models/userFamily";
import {
USER_FAMILY_NOT_FOUND_ERROR,
USER_ALREADY_MEMBER_ERROR,
USER_NOT_FOUND_ERROR,
} from "../../constants";
/**
* 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
*/
export const addUserToUserFamily: MutationResolvers["addUserToUserFamily"] =
async (_parent, args, context) => {
const userFamily = await UserFamily.findOne({
_id: args.familyId,
}).lean();

const currentUser = await User.findById({
_id: context.userId,
});

// Checks whether user with _id === args.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
);
}

//check wheather family exists
if (!userFamily) {
throw new errors.NotFoundError(
requestContext.translate(USER_FAMILY_NOT_FOUND_ERROR.MESSAGE),
USER_FAMILY_NOT_FOUND_ERROR.CODE,
USER_FAMILY_NOT_FOUND_ERROR.PARAM
);
}

//check whether user is 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.
if (isUserMemberOfUserFamily) {
throw new errors.ConflictError(
requestContext.translate(USER_ALREADY_MEMBER_ERROR.MESSAGE),
USER_ALREADY_MEMBER_ERROR.CODE,
USER_ALREADY_MEMBER_ERROR.PARAM
);
}

// Adds args.userId to users lists on family group and return the updated family.
return await UserFamily.findOneAndUpdate(
{
_id: args.familyId,
},
{
$push: {
users: args.userId,
},
},
{
new: true,
}
).lean();
};
81 changes: 81 additions & 0 deletions src/resolvers/Mutation/createUserFamily.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import type { MutationResolvers } from "../../types/generatedGraphQLTypes";
import { User } from "../../models";
import { errors, requestContext } from "../../libraries";
import {
LENGTH_VALIDATION_ERROR,
USER_FAMILY_MIN_MEMBERS_ERROR_CODE,
USER_NOT_FOUND_ERROR,
} from "../../constants";
import { isValidString } from "../../libraries/validators/validateString";
import { UserFamily } from "../../models/userFamily";
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
*/
export const createUserFamily: MutationResolvers["createUserFamily"] = async (
_parent,
args,
context
) => {
const currentUser = await User.findById({
_id: context.userId,
});

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

// Check whether the user is super admin.
superAdminCheck(currentUser);

let validationResultName = {
isLessThanMaxLength: false,
};

if (args && args.data && typeof args.data.title === "string") {
validationResultName = isValidString(args.data.title, 256);
}

if (!validationResultName.isLessThanMaxLength) {
throw new errors.InputValidationError(
requestContext.translate(
`${LENGTH_VALIDATION_ERROR.MESSAGE} 256 characters in name`
),
LENGTH_VALIDATION_ERROR.CODE
);
}

// Check if there are at least 2 members
if (args.data?.userIds.length < 2) {
throw new errors.InputValidationError(
requestContext.translate(USER_FAMILY_MIN_MEMBERS_ERROR_CODE.MESSAGE),
USER_FAMILY_MIN_MEMBERS_ERROR_CODE.CODE,
USER_FAMILY_MIN_MEMBERS_ERROR_CODE.PARAM
);
}

const userfamilyTitle = args.data?.title;

const createdUserFamily = await UserFamily.create({
...args.data,
title: userfamilyTitle,
users: [context.userId, ...args.data.userIds],
admins: [context.userId],
creator: context.userId,
});

return createdUserFamily.toObject();
};
8 changes: 8 additions & 0 deletions src/resolvers/Mutation/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ import { removeComment } from "./removeComment";
import { removeDirectChat } from "./removeDirectChat";
import { removeEvent } from "./removeEvent";
import { removeEventAttendee } from "./removeEventAttendee";
import { addUserToUserFamily } from "./addUserToUserFamily";
import { removeUserFromUserFamily } from "./removeUserFromUserFamily";
import { removeUserFamily } from "./removeUserFamily";
import { createUserFamily } from "./createUserFamily";
import { removeGroupChat } from "./removeGroupChat";
import { removeAdvertisement } from "./removeAdvertisement";
import { removeMember } from "./removeMember";
Expand Down Expand Up @@ -104,6 +108,10 @@ export const Mutation: MutationResolvers = {
addUserToGroupChat,
adminRemoveEvent,
adminRemoveGroup,
addUserToUserFamily,
removeUserFamily,
removeUserFromUserFamily,
createUserFamily,
assignUserTag,
blockPluginCreationBySuperadmin,
blockUser,
Expand Down
Loading

0 comments on commit fa10711

Please sign in to comment.