Skip to content
Open
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: 2 additions & 0 deletions codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ const config: CodegenConfig = {

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

Family: "../models/Family#InterfaceFamily",

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

// File: '../models/File#InterfaceFile',
Expand Down
20 changes: 20 additions & 0 deletions sample_data/family.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 @@ -315,6 +315,12 @@ type ExtendSession {
refreshToken: String!
}

type Family {
_id: ID!
title: String!
users: [User!]!
}

type Feedback {
_id: ID!
createdAt: DateTime!
Expand Down Expand Up @@ -486,6 +492,7 @@ type Mutation {
addUserCustomData(dataName: String!, dataValue: Any!, organizationId: ID!): UserCustomData!
addUserImage(file: String!): User!
addUserToGroupChat(chatId: ID!, userId: ID!): GroupChat!
addUserToFamily(familyId: ID!, userId: ID!): Family!
adminRemoveEvent(eventId: ID!): Event!
adminRemoveGroup(groupId: ID!): GroupChat!
assignUserTag(input: ToggleUserTagAssignInput!): User
Expand All @@ -500,6 +507,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!
createFamilyGroup(data: createFamilyGroupInput): Family!
createMember(input: UserAndOrganizationInput!): Organization!
createMessageChat(data: MessageChatInput!): MessageChat!
createOrganization(data: OrganizationInput, file: String): Organization!
Expand Down Expand Up @@ -528,6 +536,7 @@ type Mutation {
removeDirectChat(chatId: ID!, organizationId: ID!): DirectChat!
removeEvent(id: ID!): Event!
removeEventAttendee(data: EventAttendeeInput!): User!
removeFamily(familyId: ID!): Family!
removeGroupChat(chatId: ID!): GroupChat!
removeMember(data: UserAndOrganizationInput!): Organization!
removeOrganization(id: ID!): User!
Expand All @@ -537,6 +546,7 @@ type Mutation {
removeSampleOrganization: Boolean!
removeUserCustomData(organizationId: ID!): UserCustomData!
removeUserFromGroupChat(chatId: ID!, userId: ID!): GroupChat!
removeUserFromFamily(familyId: ID!, userId: ID!): Family!
removeUserImage: User!
removeUserTag(id: ID!): UserTag
revokeRefreshTokenForUser: Boolean!
Expand Down Expand Up @@ -810,6 +820,7 @@ type Query {
event(id: ID!): Event
eventsByOrganization(id: ID, orderBy: EventOrderByInput): [Event]
eventsByOrganizationConnection(first: Int, orderBy: EventOrderByInput, skip: Int, where: EventWhereInput): [Event!]!
family(id: ID!): [Family]!
getAdvertisements: [Advertisement]
getDonationById(id: ID!): Donation!
getDonationByOrgId(orgId: ID!): [Donation]
Expand Down Expand Up @@ -973,6 +984,7 @@ type User {
email: EmailAddress!
employmentStatus: EmploymentStatus
eventAdmin: [Event]
Family: [Family]
firstName: String!
gender: Gender
image: String
Expand Down Expand Up @@ -1144,4 +1156,9 @@ input createGroupChatInput {
organizationId: ID!
title: String!
userIds: [ID!]!
}

input createFamilyGroupInput {
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 @@ -150,12 +150,24 @@ export const LENGTH_VALIDATION_ERROR = {
PARAM: "stringValidation",
};

export const FAMILY_MIN_MEMBERS_ERROR_CODE = {
MESSAGE: "Error: Members in the family must be more than one",
CODE: "membersInFamilyLessThanOne",
PARAM: "membersInFamilyLessThanOne",
};

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

export const FAMILY_NOT_FOUND_ERROR = {
MESSAGE: "Error: Family Not Found",
CODE: "familyNotFound",
PARAM: "familyNotFound",
};

export const USER_NOT_AUTHORIZED_SUPERADMIN = {
MESSAGE: "Error: Current user must be a SUPERADMIN",
CODE: "role.notValid.superadmin",
Expand Down
47 changes: 47 additions & 0 deletions src/models/Family.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
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 InterfaceFamily {
_id: Types.ObjectId;
title: string;
users: PopulatedDoc<InterfaceUser & Document>[];
}

/**
* @param title - Name of the Family (type: String)
* Description: Name of the Family.
*/

/**
* @param users - Members associated with the Family (type: String)
* Description: Members associated with the Family.
*/
const FamilySchema = new Schema({
title: {
type: String,
required: true,
},
users: [
{
type: Schema.Types.ObjectId,
ref: "User",
required: true,
},
],
});

// $2a$12$usOiKueW0FsZfpUuGNrAQuxvM.AB/nCOsHLdpE0liAcUr5PHOuc2K

//token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlblZlcnNpb24iOjcsInVzZXJJZ

const familyModel = (): Model<InterfaceFamily> =>
model<InterfaceFamily>("Family", FamilySchema);

// This syntax is needed to prevent Mongoose OverwriteModelError while running tests.
export const Family = (models.Family || familyModel()) as ReturnType<
typeof familyModel
>;
3 changes: 3 additions & 0 deletions src/models/User.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import validator from "validator";
import type { InterfaceEvent } from "./Event";
import type { InterfaceMembershipRequest } from "./MembershipRequest";
import type { InterfaceOrganization } from "./Organization";
import type { InterfaceFamily } from "./Family";
/**
* This is an interface that represents a database(MongoDB) document for User.
*/
Expand Down Expand Up @@ -35,6 +36,7 @@ export interface InterfaceUser {
gender: string;
image: string | undefined | null;
joinedOrganizations: PopulatedDoc<InterfaceOrganization & Document>[];
Family: PopulatedDoc<InterfaceFamily & Document> | null;
lastName: string;
maritalStatus: string;
membershipRequests: PopulatedDoc<InterfaceMembershipRequest & Document>[];
Expand All @@ -51,6 +53,7 @@ export interface InterfaceUser {
token: string | undefined;
tokenVersion: number;
updatedAt: Date;
updatedAt: Date;
userType: string;
}
/**
Expand Down
6 changes: 6 additions & 0 deletions src/resolvers/Family/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import type { FamilyResolvers } from "../../types/generatedGraphQLTypes";
import { users } from "./users";

export const Family: FamilyResolvers = {
users,
};
14 changes: 14 additions & 0 deletions src/resolvers/Family/users.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { FamilyResolvers } from "../../types/generatedGraphQLTypes";
import { User } from "../../models";
/**
* This resolver function will fetch and return the list of all Members 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 Member data.
*/
export const users: FamilyResolvers["users"] = async (parent) => {
return await User.find({
_id: {
$in: parent.users,
},
}).lean();
};
86 changes: 86 additions & 0 deletions src/resolvers/Mutation/adminAddFamilyMember.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import "dotenv/config";
import type { MutationResolvers } from "../../types/generatedGraphQLTypes";
import { errors, requestContext } from "../../libraries";
import { superAdminCheck } from "../../utilities";
import { User } from "../../models";
import { Family } from "../../models/Family";
import {
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
* @returns Updated family
*/
export const addUserToFamily: MutationResolvers["addUserToFamily"] = async (
_parent,
args,
context
) => {
const family = await Family.findOne({
_id: args.familyId,
}).lean();

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

//check whether user is superadmin
if (currentUser) {
superAdminCheck(currentUser);
}

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

// 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
);
}

const isUserFamilyMember = family.users.some((user) => {
user.equals(args.userId);
});

// Checks whether user with _id === args.userId is already a member of Family.
if (isUserFamilyMember === true) {
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 Family.findOneAndUpdate(
{
_id: args.familyId,
},
{
$push: {
users: args.userId,
},
},
{
new: true,
}
).lean();
};
60 changes: 60 additions & 0 deletions src/resolvers/Mutation/adminRemoveFamilyMember.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import {
FAMILY_NOT_FOUND_ERROR,
USER_NOT_AUTHORIZED_ERROR,
} from "../../constants";
import type { MutationResolvers } from "../../types/generatedGraphQLTypes";
import { errors, requestContext } from "../../libraries";
import { User } from "../../models";
import { Family } from "../../models/Family";
import { superAdminCheck } from "../../utilities";
/**
* This function enables to remove a user from group chat.
* @param _parent - parent of current request
* @param args - payload provided with the request
* @param context - context of entire publication
* @remarks The following checks are done:
* 1. If the family exists.
* 2. If the user to be removed is member of the organisation.
* @returns Updated group chat.
*/
export const removeUserFromFamily: MutationResolvers["removeUserFromFamily"] =
async (_parent, args, context) => {
const family = await Family.findOne({
_id: args.familyId,
}).lean();

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

//check whether user is superadmin
if (currentUser) {
superAdminCheck(currentUser);
}

//Check whether family exists
if (!family) {
throw new errors.NotFoundError(
requestContext.translate(FAMILY_NOT_FOUND_ERROR.MESSAGE),
FAMILY_NOT_FOUND_ERROR.CODE,
FAMILY_NOT_FOUND_ERROR.PARAM
);
}

//Removes args.userId from users list of family ans return the updated family.
return await Family.findOneAndUpdate(
{
_id: args.familyId,
},
{
$set: {
users: family.users.filter(
(user) => user.toString() !== args.userId.toString()
),
},
},
{
new: true,
}
).lean();
};
Loading