MANAGING A TEAM WITH NODE.JS (PART 2)

candie_code

Candie

Posted on June 3, 2023

MANAGING A TEAM WITH NODE.JS (PART 2)

In case you missed the first part of this article, read here to catch up

In the last article, we were able to

  • Create a team
  • Send team invitation,
  • Accept team invitation

In this article, we will be talking about

  • Rejecting Invitation
  • Leaving a team
  • Removing member from a team
  • Managing roles in a team

Quickly,

  • let us create two new users,
  • Send invitation from one user to another,
  • then we continue from there

PAYLOAD

// user 1
{
    "name": "john doe",
    "email": "johndoe@gmail.com",
    "password": "test1234"
}
// user 2
{
    "name": "jane doe",
    "email": "janedoe@gmail.com",
    "password": "test1234"
}
Enter fullscreen mode Exit fullscreen mode

STEP 1

Let us send invitation from john doe to jane doe

PAYLOAD:

{
    "email": "janedoe@gmail.com"
}
Enter fullscreen mode Exit fullscreen mode

RESPONSE:

{
    "status": "success",
    "data": "invitation sent to janedoe@gmail.com"
}
Enter fullscreen mode Exit fullscreen mode

STEP 2

Let us take a look at the user object for john and jane

// john
 "data": {
        "user": {
            "_id": "647afd1914c2f48dfc18c846",
            "name": "john doe",
            "email": "johndoe@gmail.com",
            "teams": [
                {
                    "_id": "647aff306dcdb9d9546c7b1b",
                    "name": "nodejs team",
                    "members": [
                        {
                            "name": "john doe",
                            "email": "johndoe@gmail.com",
                            "id": "647afd1914c2f48dfc18c846",
                            "role": "admin"
                        }
                    ],
                    "userId": "647afd1914c2f48dfc18c846",
                    "createdAt": "2023-06-03T08:51:44.822Z",
                    "__v": 1,
                    "id": "647aff306dcdb9d9546c7b1b"
                }
            ],
            "invitations": [],
            "createdAt": "2023-06-03T08:41:06.544Z",
            "__v": 5,
            "id": "647afd1914c2f48dfc18c846"
        }
    }
Enter fullscreen mode Exit fullscreen mode

Inside john's object, the team array contains a team with only one member, which is john, and he is the admin

{
    "status": "success",
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY0N2FmZDQwMTRjMmY0OGRmYzE4Yzg0OSIsImlhdCI6MTY4NTc4MjUwMiwiZXhwIjoxNjkzNTU4NTAyfQ.NABFYJzvEvOfWUwy5cBnecIrHgDflrF3QVaAsYw_rcc",
    "data": {
        "user": {
            "_id": "647afd4014c2f48dfc18c849",
            "name": "jane doe",
            "email": "janedoe@gmail.com",
            "teams": [],
            "invitations": [
                {
                    "_id": "647aff306dcdb9d9546c7b1b",
                    "name": "nodejs team",
                    "id": "647aff306dcdb9d9546c7b1b"
                }
            ],
            "createdAt": "2023-06-03T08:41:06.544Z",
            "__v": 2,
            "id": "647afd4014c2f48dfc18c849"
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

In the jane user object, the team array is empty, because obviously, jane is not a member of any team yet,

And the invitation array contains one team, nodejs team, the invitation that was sent by john.

STEP 3

REJECTING INVITATION

Now let us compute how to reject the invitation sent by john.

Back to our team controller file

NOTE: Basically the concept is finding the index of the team in the invitation array and deleting it with the Array.splice() method.

// reject team invitation
exports.rejectInvitation = async (req, res) => {
  // find user in db

  const user = await User.findById(req.user.id);

  // find team in db
  // teamID will be gotted as a parameter when we run the request
  const team = await Team.findById(req.params.teamId);

  // check if team exists
  if (!team) {
    return res.status(404).json({
      status: "failed",
      message: "team not found",
    });
  }

  // if team exists, let us find the index of the team in the invitations array
  const teamIndex = user.invitations.indexOf(team.id);

  // remove team id from the invitations array

  user.invitations.splice(teamIndex);

  // save changes
  await user.save();

  return res.status(200).json({
    status: "success",
    message: "invitation rejected",
  });
};
Enter fullscreen mode Exit fullscreen mode

STEP 4

Let us create a route for this request and run it on post man,
NOTE: Pass the team ID as a parameter for this request

router.post(
  "/reject-invitation/:teamId",
  middleware.protect,
  teamController.rejectInvitation
);
Enter fullscreen mode Exit fullscreen mode

RESPONSE:

{
    "status": "success",
    "message": "invitation rejected"
}
Enter fullscreen mode Exit fullscreen mode

To confirm the invitation was rejected, let us take a look at the user object for jane again.

"data": {
        "user": {
            "_id": "647afd4014c2f48dfc18c849",
            "name": "jane doe",
            "email": "janedoe@gmail.com",
            "teams": [],
            "invitations": [],
            "createdAt": "2023-06-03T08:41:06.544Z",
            "__v": 3,
            "id": "647afd4014c2f48dfc18c849"
        }
    }
Enter fullscreen mode Exit fullscreen mode

Now the invitation array is empty, because we have successfully deleted the team from the invitation array.

STEP 5

LEAVING A TEAM

To compute this, let us send a new invitation to jane, then login to jane account to accept the invitation, then we can proceed to leave a team.

Because you have to be a member of a team to leave a team.

RESPONSE:

{
    "status": "success",
    "data": "invitation sent to janedoe@gmail.com"
}
Enter fullscreen mode Exit fullscreen mode

RESPONSE:

{
    "status": "success",
    "message": "invitation to nodejs team accepted"
}
Enter fullscreen mode Exit fullscreen mode

Let us take a look at jane user object to be sure she's now a member of nodejs team

"data": {
        "user": {
            "_id": "647afd4014c2f48dfc18c849",
            "name": "jane doe",
            "email": "janedoe@gmail.com",
            "teams": [
                {
                    "_id": "647aff306dcdb9d9546c7b1b",
                    "name": "nodejs team",
                    "members": [
                        {
                            "name": "john doe",
                            "email": "johndoe@gmail.com",
                            "id": "647afd1914c2f48dfc18c846",
                            "role": "admin"
                        },
                        {
                            "name": "jane doe",
                            "email": "janedoe@gmail.com",
                            "id": "647afd4014c2f48dfc18c849",
                            "role": "member"
                        }
                    ],
                    "userId": "647afd1914c2f48dfc18c846",
                    "createdAt": "2023-06-03T08:51:44.822Z",
                    "__v": 2,
                    "id": "647aff306dcdb9d9546c7b1b"
                }
            ],
            "invitations": [],
            "createdAt": "2023-06-03T08:41:06.544Z",
            "__v": 5,
            "id": "647afd4014c2f48dfc18c849"
        }
    }
Enter fullscreen mode Exit fullscreen mode

As you can see, the node.js team has two members, the admin which is john doe, and a jane doe, a member.

STEP 6

Let us proceed to our controller and compute leaving a team

exports.leaveTeam = async (req, res) => {
  // find user in db
  const user = await User.findById(req.user.id);

  // find team in db
  const team = await Team.findById(req.params.teamId);

  // check if team exists
  if (!team) {
    return res.status(404).json({
      status: "failed",
      message: "team not found",
    });
  }

  // if team exist, check if user is a member of the team
  const member = team.members.find((item) => item.id === user.id);

  // if user is not a member, throw an error and terminate
  if (!member) {
    return res.status(400).json({
      status: "failed",
      message: `you are not a member of ${team.name.toUpperCase()}`,
    });
  }

  // if user is a member, find index of team in the team array on the user object
  const teamIndex = user.teams.indexOf(team.id);

  // delete team from team array
  user.teams.splice(teamIndex);

  // remove user from member array on the team
  const newMembers = team.members.filter((item) => item.id !== member.id);
  team.members = newMembers;

  // save changes
  await team.save();
  await user.save();

  return res.status(200).json({
    status: "success",
    message: `successfully exited ${team.name}`,
  });
};
Enter fullscreen mode Exit fullscreen mode

STEP 7

Let us create a route for this request and run it on postman

router.post(
  "/leave-team/:teamId",
  middleware.protect,
  teamController.leaveTeam
);
Enter fullscreen mode Exit fullscreen mode

RESPONSE:

{
    "status": "success",
    "message": "successfully exited nodejs team"
}
Enter fullscreen mode Exit fullscreen mode

To confirm it was successful, let us take a look at the jane user object once again,

    "data": {
        "user": {
            "_id": "647afd4014c2f48dfc18c849",
            "name": "jane doe",
            "email": "janedoe@gmail.com",
            "teams": [],
            "invitations": [],
            "createdAt": "2023-06-03T08:41:06.544Z",
            "__v": 6,
            "id": "647afd4014c2f48dfc18c849"
        }
    }
Enter fullscreen mode Exit fullscreen mode

As expected, the teams array is empty, which means the request was successful.

STEP 8

REMOVING MEMBER FROM A TEAM

Let us repeat the process of sending an invitation to jane and accepting the invitation, then logging in back to john account to remove jane from the team

    "data": {
        "user": {
            "_id": "647afd4014c2f48dfc18c849",
            "name": "jane doe",
            "email": "janedoe@gmail.com",
            "teams": [
                {
                    "_id": "647aff306dcdb9d9546c7b1b",
                    "name": "nodejs team",
                    "members": [
                        {
                            "name": "john doe",
                            "email": "johndoe@gmail.com",
                            "id": "647afd1914c2f48dfc18c846",
                            "role": "admin"
                        },
                        {
                            "name": "jane doe",
                            "email": "janedoe@gmail.com",
                            "id": "647afd4014c2f48dfc18c849",
                            "role": "member"
                        }
                    ],
                    "userId": "647afd1914c2f48dfc18c846",
                    "createdAt": "2023-06-03T08:51:44.822Z",
                    "__v": 4,
                    "id": "647aff306dcdb9d9546c7b1b"
                }
            ],
            "invitations": [],
            "createdAt": "2023-06-03T08:41:06.544Z",
            "__v": 8,
            "id": "647afd4014c2f48dfc18c849"
        }
    }
Enter fullscreen mode Exit fullscreen mode

Jane is already a member of the nodejs team, now let us log back in to john's account to compute removing jane from the nodejs team.

exports.removeMemberFromTeam = async (req, res) => {
  // extract userId and teamId from the request query
  const { userId, teamId } = req.query;

  // find team in db
  const team = await Team.findById(teamId);
  const user = await User.findById(userId);

  // check if team exist
  if (!team) {
    return res.status(404).json({
      status: "failed",
      message: "team not found",
    });
  }

  // if team exist, check if user is a member of the team
  const member = team.members.find((item) => item.id === userId);

  // if user is not a member, throw an error and terminate
  if (!member) {
    return res.status(400).json({
      status: "failed",
      message: `you are not a member of ${team.name.toUpperCase()}`,
    });
  }

  // if user is a member, filter user out of the team
  const newTeamMember = team.members.filter((item) => item.id !== userId);
  team.members = newTeamMember;

  // remove teamId from teams array on user object
  const teamIndex = user.teams.indexOf(team.id);
  user.teams.splice(teamIndex);

  // save changes
  await team.save();
  await user.save();

  return res.status(200).json({
    status: "success",
    message: `${user.name} successfully removed from ${team.name}`,
  });
};
Enter fullscreen mode Exit fullscreen mode

STEP 9

Let us create a route for this request and run it in postman

router.post(
  "/remove-member",
  middleware.protect,
  teamController.removeMemberFromTeam
);
Enter fullscreen mode Exit fullscreen mode
// request url
http://localhost:8000/api/v1/team/remove-member?teamId=647aff306dcdb9d9546c7b1b&userId=647afd4014c2f48dfc18c849
Enter fullscreen mode Exit fullscreen mode

RESPONSE:

{
    "status": "success",
    "message": "jane doe successfully removed from nodejs team"
}
Enter fullscreen mode Exit fullscreen mode

To confirm, let us take a look at the nodejs team again

"teams": [
                {
                    "_id": "647aff306dcdb9d9546c7b1b",
                    "name": "nodejs team",
                    "members": [
                        {
                            "name": "john doe",
                            "email": "johndoe@gmail.com",
                            "id": "647afd1914c2f48dfc18c846",
                            "role": "admin"
                        }
                    ],
Enter fullscreen mode Exit fullscreen mode

Nodejs team only contains john, who is the admin, meaning our logic works too.

So far we have been able to

  • Create a team
  • Send team invitation
  • Accept team invitation
  • Reject team invitation
  • Leave team
  • Remove member from team

In the final part of this article, we will be talking about ROLE MANAGEMENT IN A TEAM

I hope you are excited.

Meanwhile Let me know your thoughts on this in the comments section.

Get my source code here
View my API documentation here

💖 💪 🙅 🚩
candie_code
Candie

Posted on June 3, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related