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 #1810

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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: 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

Check warning on line 43 in src/resolvers/Mutation/addUserToUserFamily.ts

View check run for this annotation

Codecov / codecov/patch

src/resolvers/Mutation/addUserToUserFamily.ts#L36-L43

Added lines #L36 - L43 were not covered by tests
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.

Check warning on line 68 in src/resolvers/Mutation/addUserToUserFamily.ts

View check run for this annotation

Codecov / codecov/patch

src/resolvers/Mutation/addUserToUserFamily.ts#L61-L68

Added lines #L61 - L68 were not covered by tests
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