gfw-api/gfw-user-api

View on GitHub
src/routes/v1.user.router.ts

Summary

Maintainability
A
0 mins
Test Coverage
import Router from 'koa-router';
import { Context, DefaultState, Next } from 'koa';
import logger from 'logger';
import mongoose, { Error, FilterQuery } from 'mongoose';
import V1UserSerializer from 'serializers/v1.user.serializer';
import User, { IUser } from 'models/user';
import StoriesService from 'services/stories.service';
import SalesforceService from 'services/salesforce.service';
import { uniformizeSector } from 'services/sector-handler.service';

interface IRequestUser {
    id: string;
    role: string;
    extraUserData: Record<string, any>;
}

class V1UserRouter {

    static getUser(ctx: Context): IRequestUser {
        const { query, body } = ctx.request;

        let user: IRequestUser = { ...(query.loggedUser ? JSON.parse(query.loggedUser as string) : {}), ...ctx.request.body.loggedUser };
        if (body.fields && body.fields.loggedUser) {
            user = Object.assign(user, JSON.parse(body.fields.loggedUser));
        }
        return user;
    }

    static async getCurrentUser(ctx: Context): Promise<void> {
        try {
            logger.info('Obtaining logged in user');
            let loggedUser: Record<string, any> = null;
            if (ctx.request.query.loggedUser) {
                logger.info('logged user', ctx.request.query.loggedUser);
                loggedUser = JSON.parse(ctx.request.query.loggedUser as string);

                const user: IUser = await User.findById(loggedUser.id);
                logger.info('User found:', user);
                ctx.body = V1UserSerializer.serialize(user);
            } else {
                ctx.throw(403, 'Forbidden');
            }
        } catch (e) {
            logger.error(e);
            ctx.throw(400, 'Error parsing');
        }

    }

    static async getAllUsers(ctx: Context): Promise<void> {
        logger.info('Obtaining users');

        let defaultFilter: FilterQuery<IUser> = {};
        if (ctx.query.start && ctx.query.end) {
            defaultFilter = {
                createdAt: {
                    $gte: new Date(ctx.query.start as string),
                    $lt: new Date(ctx.query.end as string)
                }
            };
        }
        try {
            const users: IUser[] = await User.find(defaultFilter);
            ctx.body = V1UserSerializer.serialize(users);
        } catch (err) {
            logger.error(err);
        }
    }

    static async createUser(ctx: Context): Promise<void> {
        logger.info('Create user', ctx.request.body);
        const existingUser: IUser = await User.findById(ctx.request.body.loggedUser.id);
        if (existingUser) {
            logger.error('Duplicated user');
            ctx.throw(400, 'Duplicated user');
            return;
        }

        const newUserData: Record<string, any> = ctx.request.body;
        if (newUserData.sector) {
            const uniformizedSector: string = uniformizeSector(ctx.request.body.sector);
            if (!uniformizedSector) {
                logger.error('Unsupported sector');
                ctx.throw(400, 'Unsupported sector');
                return;
            }

            newUserData.sector = uniformizedSector;
        }

        ctx.request.body._id = new mongoose.Types.ObjectId(ctx.request.body.loggedUser.id);
        const user: IUser = new User(newUserData);
        const errors: Error.ValidationError | null = user.validateSync();
        if (errors) {
            logger.info(errors.message);
            ctx.throw(422, 'Can\'t create user, missing data');
            return;
        }
        const userCreate: IUser = await user.save();
        ctx.body = V1UserSerializer.serialize(userCreate);
    }

    static async getUserById(ctx: Context): Promise<void> {
        const user: IRequestUser = V1UserRouter.getUser(ctx);
        if (ctx.params.id !== user.id && user.role !== 'ADMIN' && user.id !== 'microservice') {
            ctx.throw(403, 'Forbidden');
            return;
        }

        logger.info('Obtaining users by id %s', ctx.params.id);
        if (!mongoose.Types.ObjectId.isValid(ctx.params.id)) {
            ctx.throw(404, 'User not found');
            return;
        }
        const userFind: IUser = await User.findById(ctx.params.id);
        if (!userFind) {
            ctx.throw(404, 'User not found');
            return;
        }
        ctx.body = V1UserSerializer.serialize(userFind);
    }

    static async updateUser(ctx: Context): Promise<void> {
        logger.info('Obtaining users by id %s', ctx.params.id);
        const userId: string = ctx.request.body.loggedUser.id;
        if (ctx.params.id !== userId) {
            ctx.throw(403, 'Forbidden');
            return;
        }
        let userFind: IUser = await User.findById(ctx.params.id);
        if (!userFind) {
            userFind = new User();
            userFind._id = new mongoose.Types.ObjectId(userId);
            await userFind.save();
        }
        // extend user
        if (ctx.request.body.fullName !== undefined) {
            userFind.fullName = ctx.request.body.fullName;
        }
        if (ctx.request.body.firstName !== undefined) {
            userFind.firstName = ctx.request.body.firstName;
        }
        if (ctx.request.body.lastName !== undefined) {
            userFind.lastName = ctx.request.body.lastName;
        }
        if (ctx.request.body.email !== undefined) {
            userFind.email = ctx.request.body.email;
        }
        if (ctx.request.body.sector !== undefined) {
            const uniformizedSector: string = uniformizeSector(ctx.request.body.sector);
            if (!uniformizedSector) {
                logger.error('Unsupported sector');
                ctx.throw(400, 'Unsupported sector');
                return;
            }

            userFind.sector = uniformizedSector;
        }
        if (ctx.request.body.subsector !== undefined) {
            userFind.subsector = ctx.request.body.subsector;
        }
        if (ctx.request.body.jobTitle !== undefined) {
            userFind.jobTitle = ctx.request.body.jobTitle;
        }
        if (ctx.request.body.company !== undefined) {
            userFind.company = ctx.request.body.company;
        }
        if (ctx.request.body.primaryResponsibilities !== undefined) {
            userFind.primaryResponsibilities = ctx.request.body.primaryResponsibilities;
        }
        if (ctx.request.body.country !== undefined) {
            userFind.country = ctx.request.body.country;
        }
        if (ctx.request.body.state !== undefined) {
            userFind.state = ctx.request.body.state;
        }
        if (ctx.request.body.city !== undefined) {
            userFind.city = ctx.request.body.city;
        }
        if (ctx.request.body.aoiCountry !== undefined) {
            userFind.aoiCountry = ctx.request.body.aoiCountry;
        }
        if (ctx.request.body.aoiCity !== undefined) {
            userFind.aoiCity = ctx.request.body.aoiCity;
        }
        if (ctx.request.body.aoiState !== undefined) {
            userFind.aoiState = ctx.request.body.aoiState;
        }
        if (ctx.request.body.howDoYouUse !== undefined) {
            userFind.howDoYouUse = ctx.request.body.howDoYouUse;
        }
        if (ctx.request.body.signUpForTesting !== undefined) {
            userFind.signUpForTesting = (ctx.request.body.signUpForTesting === 'true');
        }
        if (ctx.request.body.language !== undefined) {
            userFind.language = ctx.request.body.language;
        }
        if (ctx.request.body.profileComplete !== undefined) {
            userFind.profileComplete = ctx.request.body.profileComplete;
        }
        if (ctx.request.body.interests !== undefined) {
            userFind.interests = ctx.request.body.interests;
        }
        if (ctx.request.body.signUpToNewsletter !== undefined) {
            userFind.signUpToNewsletter = (ctx.request.body.signUpToNewsletter);
        }
        if (ctx.request.body.topics !== undefined) {
            userFind.topics = (ctx.request.body.topics);
        }

        await userFind.save();

        // Purposefully not waiting for this so that the main submission is not blocked
        SalesforceService.updateUserInformation(userFind, ctx.request.headers['x-api-key'] as string);

        ctx.body = V1UserSerializer.serialize(userFind);
    }

    static async deleteUser(ctx: Context): Promise<void> {
        logger.info('Obtaining users by id %s', ctx.params.id);
        const userId: string = JSON.parse(ctx.request.query.loggedUser as string).id;
        if (ctx.params.id !== userId) {
            ctx.throw(401, 'Not authorized');
            return;
        }
        const userFind: IUser = await User.findById(ctx.params.id);
        if (!userFind) {
            logger.error('User not found');
            ctx.throw(404, 'User not found');
            return;
        }
        await userFind.remove();
        ctx.body = V1UserSerializer.serialize(userFind);
    }

    static async getStories(ctx: Context): Promise<void> {
        logger.info('[V1UserRouter - getStories] Obtaining stories for logged in user');
        const userId: string = JSON.parse(ctx.request.query.loggedUser as string).id;
        ctx.body = await StoriesService.getStoriesByUser(userId, ctx.request.headers['x-api-key'] as string);
    }

    static async getUserByOldId(ctx: Context): Promise<void> {
        logger.info('Obtaining user by oldId %s', ctx.params.id);
        const user: IRequestUser = V1UserRouter.getUser(ctx);

        let userFind: IUser;
        try {
            userFind = await User.findOne({
                oldId: ctx.params.id
            });
            // eslint-disable-next-line no-empty
        } catch (e) {
        }

        if (!userFind) {
            ctx.throw(404, 'User not found');
            return;
        }

        if (userFind.id !== user.id && user.role !== 'ADMIN' && user.id !== 'microservice') {
            ctx.throw(403, 'Forbidden');
            return;
        }

        ctx.body = V1UserSerializer.serialize(userFind);
    }

}

const isLoggedIn = async (ctx: Context, next: Next): Promise<void> => {
    let loggedUser: IRequestUser = ctx.request.body ? ctx.request.body.loggedUser : null;
    if (!loggedUser) {
        loggedUser = ctx.query.loggedUser ? JSON.parse(ctx.query.loggedUser as string) : null;
    }
    if (!loggedUser) {
        ctx.throw(401, 'Unauthorized');
        return;
    }
    await next();
};

const isMicroserviceOrAdmin = async (ctx: Context, next: Next): Promise<void> => {
    let loggedUser: IRequestUser = ctx.request.body ? ctx.request.body.loggedUser : null;
    if (!loggedUser) {
        loggedUser = ctx.query.loggedUser ? JSON.parse(ctx.query.loggedUser as string) : null;
    }
    if (!loggedUser) {
        ctx.throw(401, 'Not authorized');
        return;
    }
    if (loggedUser.id === 'microservice') {
        await next();
        return;
    }
    if (loggedUser.role === 'ADMIN' && loggedUser.extraUserData && loggedUser.extraUserData.apps && loggedUser.extraUserData.apps.indexOf('gfw') >= 0) {
        await next();
        return;
    }
    ctx.throw(403, 'Forbidden');
};

const router: Router = new Router<DefaultState, Context>({ prefix: '/api/v1/user' });

router.get('/', isLoggedIn, V1UserRouter.getCurrentUser);
router.get('/obtain/all-users', isMicroserviceOrAdmin, V1UserRouter.getAllUsers);
router.post('/', isLoggedIn, V1UserRouter.createUser);
router.get('/stories', isLoggedIn, V1UserRouter.getStories);
router.get('/:id', isLoggedIn, V1UserRouter.getUserById);
router.get('/oldId/:id', isLoggedIn, V1UserRouter.getUserByOldId);
router.patch('/:id', isLoggedIn, V1UserRouter.updateUser);
router.delete('/:id', isLoggedIn, V1UserRouter.deleteUser);

export default router;