Asymmetrik/node-rest-starter

View on GitHub
src/app/core/teams/teams.controller.ts

Summary

Maintainability
D
1 day
Test Coverage
import { StatusCodes } from 'http-status-codes';

import { TeamRoles } from './team-role.model';
import teamsService from './teams.service';
import { utilService, auditService } from '../../../dependencies';
import { BadRequestError, NotFoundError } from '../../common/errors';
import userService from '../user/user.service';

/**
 * Create a new team. The team creator is automatically added as an admin
 */
export const create = async (req, res) => {
    const result = await teamsService.create(
        req.body.team,
        req.user,
        req.body.firstAdmin
    );

    // Audit the creation action
    await auditService.audit(
        'team created',
        'team',
        'create',
        req,
        result.auditCopy()
    );

    res.status(StatusCodes.OK).json(result);
};

/**
 * Read the team
 */
export const read = (req, res) => {
    res.status(StatusCodes.OK).json(req.team);
};

/**
 * Update the team metadata
 */
export const update = async (req, res) => {
    // Make a copy of the original team for auditing purposes
    const originalTeam = req.team.auditCopy();

    const result = await teamsService.update(req.team, req.body);

    await auditService.audit('team updated', 'team', 'update', req, {
        before: originalTeam,
        after: result.auditCopy()
    });

    res.status(StatusCodes.OK).json(result);
};

/**
 * Delete the team
 */
export const deleteTeam = async (req, res) => {
    await teamsService.delete(req.team);

    // Audit the team delete attempt
    await auditService.audit(
        'team deleted',
        'team',
        'delete',
        req,
        req.team.auditCopy()
    );

    res.status(StatusCodes.OK).json(req.team);
};

/**
 * Search the teams, includes paging and sorting
 */
export const search = async (req, res) => {
    // Get search and query parameters
    const search = req.body.s ?? null;
    const query = utilService.toMongoose(req.body.q ?? {});

    const result = await teamsService.search(req.query, query, search, req.user);
    res.status(StatusCodes.OK).json(result);
};

export const getAncestorTeamIds = async (req, res) => {
    const result = await teamsService.getAncestorTeamIds(req.body.teamIds);
    res.status(StatusCodes.OK).json(result);
};

export const requestNewTeam = async (req, res) => {
    const user = req.user;
    const org = req.body.org ?? null;
    const aoi = req.body.aoi ?? null;
    const description = req.body.description ?? null;

    await teamsService.requestNewTeam(org, aoi, description, user, req);

    await auditService.audit('new team requested', 'team', 'request', req, {
        org,
        aoi,
        description
    });

    res.status(StatusCodes.NO_CONTENT).end();
};

export const requestAccess = async (req, res) => {
    await teamsService.requestAccessToTeam(req.user, req.team, req);
    res.status(StatusCodes.NO_CONTENT).end();
};

/**
 * Search the members of the team, includes paging and sorting
 */
export const searchMembers = async (req, res) => {
    // Get search and query parameters
    const search = req.body.s ?? '';
    const query = teamsService.updateMemberFilter(
        utilService.toMongoose(req.body.q ?? {}),
        req.team
    );

    const results = await userService.searchUsers(req.query, query, search);

    // Create the return copy of the messages
    const mappedResults = {
        pageNumber: results.pageNumber,
        pageSize: results.pageSize,
        totalPages: results.totalPages,
        totalSize: results.totalSize,
        elements: results.elements.map((element) => {
            return {
                ...element.filteredCopy(),
                teams: element.teams.filter((team) => team._id.equals(req.team._id))
            };
        })
    };

    res.status(StatusCodes.OK).json(mappedResults);
};

/**
 * Add a member to a team, defaulting to read-only access
 */
export const addMember = async (req, res) => {
    const role: TeamRoles = req.body.role ?? TeamRoles.Member;

    await teamsService.addMemberToTeam(req.userParam, req.team, role);

    // Audit the member add request
    await auditService.audit(
        `team ${role} added`,
        'team-role',
        'user add',
        req,
        req.team.auditCopyTeamMember(req.userParam, role)
    );

    res.status(StatusCodes.NO_CONTENT).end();
};

/**
 * Add specified members with specified roles to a team
 */
export const addMembers = async (req, res) => {
    await Promise.all(
        req.body.newMembers
            .filter((member) => null != member._id)
            .map(async (member) => {
                const user = await userService.read(member._id);
                if (null != user) {
                    await teamsService.addMemberToTeam(user, req.team, member.role);
                    return auditService.audit(
                        `team ${member.role} added`,
                        'team-role',
                        'user add',
                        req,
                        req.team.auditCopyTeamMember(user, member.role)
                    );
                }
            })
    );
    res.status(StatusCodes.NO_CONTENT).end();
};

/**
 * Remove a member from a team
 */
export const removeMember = async (req, res) => {
    await teamsService.removeMemberFromTeam(req.userParam, req.team);

    // Audit the user remove
    await auditService.audit(
        'team member removed',
        'team-role',
        'user remove',
        req,
        req.team.auditCopyTeamMember(req.userParam)
    );

    res.status(StatusCodes.NO_CONTENT).end();
};

export const updateMemberRole = async (req, res) => {
    const role: TeamRoles = req.body.role || TeamRoles.Member;

    await teamsService.updateMemberRole(req.userParam, req.team, role);

    // Audit the member update request
    await auditService.audit(
        `team role changed to ${role}`,
        'team-role',
        'user add',
        req,
        req.team.auditCopyTeamMember(req.userParam, role)
    );

    res.status(StatusCodes.NO_CONTENT).end();
};

/**
 * Team middleware
 */
export const teamById = async (req, res, next, id: string) => {
    const populate = [
        {
            path: 'parent',
            select: ['name']
        },
        {
            path: 'ancestors',
            select: ['name']
        }
    ];

    const team = await teamsService.read(id, populate);
    if (!team) {
        return next(new NotFoundError('Could not find team'));
    }
    req.team = team;
    return next();
};

export const teamMemberById = async (req, res, next, id: string) => {
    const user = await userService.read(id);

    if (null == user) {
        return next(new Error('Failed to load team member'));
    }
    req.userParam = user;
    return next();
};

/**
 * Does the user have the referenced role in the team
 */
function requiresRole(role: TeamRoles): (req) => Promise<void> {
    return function (req) {
        // Verify that the user and team are on the request
        const user = req.user;
        if (null == user) {
            return Promise.reject(new BadRequestError('No user for request'));
        }
        const team = req.team;
        if (null == team) {
            return Promise.reject(new BadRequestError('No team for request'));
        }

        return teamsService.meetsRoleRequirement(user, team, role);
    };
}

export const requiresAdmin = (req) => {
    return requiresRole(TeamRoles.Admin)(req);
};

export const requiresEditor = (req) => {
    return requiresRole(TeamRoles.Editor)(req);
};

export const requiresMember = (req) => {
    return requiresRole(TeamRoles.Member)(req);
};