Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Support for User Family Members #1787

Closed
Closed
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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"
]
}
]
21 changes: 21 additions & 0 deletions schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,14 @@ type ExtendSession {
refreshToken: String!
}

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

type Feedback {
_id: ID!
createdAt: DateTime!
Expand Down Expand Up @@ -521,6 +529,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 @@ -537,6 +546,7 @@ type Mutation {
createDonation(amount: Float!, nameOfOrg: String!, nameOfUser: String!, orgId: ID!, payPalId: ID!, userId: ID!): Donation!
createEvent(data: EventInput): Event!
createGroupChat(data: createGroupChatInput!): GroupChat!
createUserFamily(data: createUserFamilyInput): UserFamily!
createMember(input: UserAndOrganizationInput!): Organization!
createMessageChat(data: MessageChatInput!): MessageChat!
createOrganization(data: OrganizationInput, file: String): Organization!
Expand Down Expand Up @@ -566,6 +576,7 @@ type Mutation {
removeDirectChat(chatId: ID!, organizationId: ID!): DirectChat!
removeEvent(id: ID!): Event!
removeEventAttendee(data: EventAttendeeInput!): User!
removeUserFamily(familyId: ID!): UserFamily!
removeGroupChat(chatId: ID!): GroupChat!
removeMember(data: UserAndOrganizationInput!): Organization!
removeOrganization(id: ID!): User!
Expand All @@ -575,6 +586,7 @@ type Mutation {
removeSampleOrganization: Boolean!
removeUserCustomData(organizationId: ID!): UserCustomData!
removeUserFromGroupChat(chatId: ID!, userId: ID!): GroupChat!
removeUserFromUserFamily(familyId: ID!, userId: ID!): UserFamily!
removeUserImage: User!
removeUserTag(id: ID!): UserTag
revokeRefreshTokenForUser: Boolean!
Expand Down Expand Up @@ -856,6 +868,7 @@ type Query {
event(id: ID!): Event
eventsByOrganization(id: ID, orderBy: EventOrderByInput): [Event]
eventsByOrganizationConnection(first: Int, orderBy: EventOrderByInput, skip: Int, where: EventWhereInput): [Event!]!
userFamily(id: ID!): [UserFamily]!
getAdvertisements: [Advertisement]
getDonationById(id: ID!): Donation!
getDonationByOrgId(orgId: ID!): [Donation]
Expand Down Expand Up @@ -1024,11 +1037,13 @@ type User {
address: Address
adminApproved: Boolean
adminFor: [Organization]
adminForUserFamily: [UserFamily]
appLanguageCode: String!
birthDate: Date
createdAt: DateTime!
createdEvents: [Event]
createdOrganizations: [Organization]
createdUserFamily: [UserFamily]
educationGrade: EducationGrade
email: EmailAddress!
employmentStatus: EmploymentStatus
Expand All @@ -1037,6 +1052,7 @@ type User {
gender: Gender
image: String
joinedOrganizations: [Organization]
joinedUserFamily: [UserFamily]
lastName: String!
maritalStatus: MaritalStatus
membershipRequests: [MembershipRequest]
Expand Down Expand Up @@ -1204,4 +1220,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: undefined,
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
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
Loading